библиотека — линейная алгебраlibrary — linear algebra

std::linalg

Библиотека (P1673) с BLAS-подобными операциями линейной алгебры (умножение матрицы на вектор, матрицы на матрицу, скалярное произведение, нормы, треугольные решатели), работающими поверх представлений std::mdspan, а не владеющих контейнеров.A library (P1673) providing BLAS-like linear algebra operations (matrix-vector multiply, matrix-matrix multiply, dot products, norms, triangular solves) that operate on std::mdspan views rather than owning containers.

свободные функции поверх mdspan, в духе ranges-алгоритмов над viewfree functions over mdspan, in the spirit of ranges algorithms over views не замена высокооптимизированному вендорскому BLASnot a replacement for a highly-tuned vendor BLAS частый вопрос: зачем стандартизировать то, что уже даёт BLAScommon question: why standardize what BLAS already provides

Что этоWhat it is

std::linalg (P1673) — набор функций-шаблонов, параметризованных типом элемента, extents и layout-политикой mdspan (см. ../cpp23/mdspan.html), реализующих типичные BLAS Level 1/2/3 операции: скалярное произведение, умножение матрицы на вектор, умножение матрицы на матрицу, вычисление норм, треугольные решатели. Алгоритмы не владеют памятью — они принимают mdspan-представления входных и выходных данных, ровно так же, как ranges-алгоритмы принимают view вместо владеющих контейнеров.std::linalg (P1673) is a set of function templates, parametrized over the mdspan's element type, extents, and layout policy (see ../cpp23/mdspan.html), implementing typical BLAS Level 1/2/3-style operations: dot products, matrix-vector multiplication, matrix-matrix multiplication, norm computation, triangular solves. The algorithms don't own memory — they take mdspan views of the input and output data, exactly the way ranges algorithms take views instead of owning containers.

до C++26 — внешний BLAS или ручной циклbefore C++26 — external BLAS or a hand-written loop
// либо линковать OpenBLAS/MKL с C-соглашением вызова,// either link OpenBLAS/MKL with a C calling convention,
// либо писать цикл руками — медленнее и легче ошибиться// or write the loop by hand — slower and more error-prone
void matvec(const float* a, const float* x, float* y, std::size_t rows, std::size_t cols) {
    for (std::size_t i = 0; i < rows; ++i) {
        float sum = 0.0f;
        for (std::size_t j = 0; j < cols; ++j)
            sum += a[i * cols + j] * x[j];
        y[i] = sum;
    }
}
C++26 — std::linalg поверх mdspanC++26 — std::linalg over mdspan
void matvec(std::mdspan<const float, std::dextents<std::size_t, 2>> a,
            std::mdspan<const float, std::dextents<std::size_t, 1>> x,
            std::mdspan<float, std::dextents<std::size_t, 1>> y) {
    std::linalg::matrix_vector_product(a, x, y);  // y = a * x, без ручных индексов// y = a * x, no manual indices
}

Зачем ввелиWhy it was introduced

Отсутствие линейной алгебры в стандартной библиотеке десятилетиями решалось одним из двух способов:The absence of linear algebra from the standard library was, for decades, worked around in one of two ways:

01

Внешний BLAS/LAPACK — зависимость с чужим соглашением вызоваExternal BLAS/LAPACK — a dependency with a foreign calling convention

Численный/научный C++ код десятилетиями полагался на внешние биндинги к BLAS/LAPACK (часто библиотеки с C- или Fortran-соглашением вызова — OpenBLAS, MKL), потому что в стандартной библиотеке вообще не было словаря для линейной алгебры.Numerical/scientific C++ code relied for decades on external BLAS/LAPACK bindings (often C- or Fortran-calling-convention libraries — OpenBLAS, MKL), because the standard library had no linear algebra vocabulary at all.

02

Самописные циклы — хуже по производительности и подвержены ошибкамHand-written loops — worse performance, error-prone

Проекты, не желающие тянуть внешний BLAS, писали циклы вручную — почти всегда с худшей производительностью и большей вероятностью ошибки индексации за пределами тривиальных случаев.Projects unwilling to pull in an external BLAS wrote loops by hand — almost always with worse performance and a higher chance of indexing mistakes beyond the trivial cases.

03

std::linalg — стандартный словарь без обязательной внешней зависимостиstd::linalg — a standard vocabulary without a mandatory external dependency

std::linalg стандартизирует типичные BLAS Level 1/2/3 операции как шаблоны функций C++, работающие прямо над mdspan — базовая линейная алгебра больше не требует стороннего пакета, при этом вендор всё ещё может специализировать/оптимизировать реализацию под конкретное оборудование.std::linalg standardizes the common BLAS Level 1/2/3-style operations as C++ function templates working directly on mdspan — basic linear algebra no longer requires a third-party package, while a vendor can still specialize/optimize the implementation for specific hardware.

Как это работает под капотомHow it works under the hood

Алгоритмы принимают mdspan-объекты как обычные параметры функции — вход только для чтения через mdspan<const T, ...>, выход как изменяемый mdspan<T, ...>, который алгоритм заполняет на месте. matrix_vector_product(A, x, y) вычисляет y = A * x, где A — 2D mdspan, x и y — 1D mdspan; dot(v1, v2) — скалярное произведение двух векторов; matrix_product — произведение матрица на матрицу.The algorithms take mdspan objects as ordinary function parameters — read-only input via mdspan<const T, ...>, mutable output as an mdspan<T, ...> that the algorithm fills in place. matrix_vector_product(A, x, y) computes y = A * x, where A is a 2D mdspan and x, y are 1D mdspan; dot(v1, v2) is the dot product of two vectors; matrix_product is a matrix-matrix product.

#include <linalg>
#include <mdspan>

using Matrix = std::mdspan<const double, std::dextents<std::size_t, 2>>;
using Vector = std::mdspan<const double, std::dextents<std::size_t, 1>>;
using MutVector = std::mdspan<double, std::dextents<std::size_t, 1>>;

void apply(Matrix a, Vector x, MutVector y) {
    std::linalg::matrix_vector_product(a, x, y);   // y = a * x// y = a * x
}

double similarity(Vector v1, Vector v2) {
    return std::linalg::dot(v1, v2);   // скалярное произведение// vector dot product
}

void compose(Matrix a, Matrix b, std::mdspan<double, std::dextents<std::size_t, 2>> c) {
    std::linalg::matrix_product(a, b, c);   // c = a * b// c = a * b
}

Точное множество перегрузок и их окончательные имена ещё могут скорректироваться до ратификации — семантика в этом файле соответствует P1673, но детали сигнатур не стоит считать гарантированно финальными.The exact overload set and final function names may still be adjusted before ratification — the semantics in this file follow P1673, but the signature details shouldn't be taken as guaranteed final.

Примеры из реального кодаReal-world examples

типичные сценарии использованияtypical usage scenarios
01

Прямой проход нейросети — умножение матрицы весов на вектор входаNeural network forward pass — weights matrix times input vector

Простейший линейный слой: выходной вектор равен произведению матрицы весов на вектор признаков, без bias-члена, ради краткости примера.The simplest linear layer: the output vector equals the weights matrix times the feature vector, no bias term, for the sake of a short example.

void forward(std::mdspan<const float, std::dextents<std::size_t, 2>> weights,
             std::mdspan<const float, std::dextents<std::size_t, 1>> input,
             std::mdspan<float, std::dextents<std::size_t, 1>> output) {
    std::linalg::matrix_vector_product(weights, input, output);  // output = weights * input// output = weights * input
}
02

Косинусное сходство между векторами признаковCosine similarity between feature vectors

Типичная ML/поисковая задача — скалярное произведение двух эмбеддингов, делённое на произведение их норм.A common ML/search task — the dot product of two embeddings divided by the product of their norms.

double cosine_similarity(std::mdspan<const double, std::dextents<std::size_t, 1>> v1,
                          std::mdspan<const double, std::dextents<std::size_t, 1>> v2) {
    double dot = std::linalg::dot(v1, v2);
    double n1 = std::linalg::vector_norm2(v1);
    double n2 = std::linalg::vector_norm2(v2);
    return dot / (n1 * n2);
}
03

Произведение матриц преобразования в графике/робототехникеA matrix product of transformation matrices in graphics/robotics

Композиция двух матриц преобразования (например, поворот и перенос) в одну — типичная операция в графическом или робототехническом конвейере.Composing two transformation matrices (say, a rotation and a translation) into one — a typical operation in a graphics or robotics pipeline.

void compose_transforms(std::mdspan<const double, std::dextents<std::size_t, 2>> rotation,
                        std::mdspan<const double, std::dextents<std::size_t, 2>> translation,
                        std::mdspan<double, std::dextents<std::size_t, 2>> combined) {
    std::linalg::matrix_product(rotation, translation, combined);  // combined = rotation * translation// combined = rotation * translation
}
04

Нормализация вектора через вычисление L2-нормыVector normalization via an L2 norm computation

Вычисление L2-нормы вектора как части численного алгоритма — здесь для нормализации, но та же функция используется, например, для проверки сходимости итеративного решателя.Computing a vector's L2 norm as part of a numerical algorithm — here for normalization, but the same function is used, for instance, to check the convergence of an iterative solver.

void normalize(std::mdspan<double, std::dextents<std::size_t, 1>> v) {
    double norm = std::linalg::vector_norm2(v);   // L2-норма: sqrt(sum(v[i]^2))// L2 norm: sqrt(sum(v[i]^2))
    if (norm > 0.0) {
        for (std::size_t i = 0; i < v.extent(0); ++i)
            v[i] /= norm;
    }
}

ГраблиGotchas

mdspan не владеет памятью — ответственность за буфер остаётся на вызывающемmdspan doesn't own memory — the buffer's lifetime is the caller's responsibility

std::linalg работает поверх mdspan-представлений, которые не владеют памятью — вызывающий код отвечает за время жизни лежащего в основе хранилища, в отличие от самодостаточной матричной библиотеки, владеющей собственными буферами. Уничтожение или реаллокация исходных данных раньше времени превращает mdspan в висячий, а вызов linalg-алгоритма над ним — в неопределённое поведение.std::linalg operates on mdspan views, which don't own memory — the caller is responsible for the underlying storage's lifetime, unlike a self-contained matrix library that owns its own buffers. Destroying or reallocating the source data too early leaves the mdspan dangling, and calling a linalg algorithm on it is undefined behavior.

Не замена вендорскому BLAS для больших нагрузокNot a replacement for a vendor BLAS on large workloads

std::linalg — не drop-in замена высокооптимизированному вендорскому BLAS (MKL, OpenBLAS) для крупномасштабных численных задач: стандарт фиксирует интерфейс и базовую семантику, но поставляется с эталонной реализацией, чья производительность может отставать от специально настроенной под конкретное железо. Производительно-критичный код с большими матрицами стоит всё же сравнивать с выделенным BLAS.std::linalg is not a drop-in replacement for a highly-tuned vendor BLAS (MKL, OpenBLAS) for large-scale numerical workloads: the standard fixes the interface and basic semantics, but ships a reference implementation whose performance may lag one specifically tuned for the hardware. Performance-critical large-matrix code should still be benchmarked against a dedicated BLAS.

Layout — часть типа, несовпадение даёт неверный результат без ошибки компиляцииLayout is part of the type — a mismatch gives a wrong result, not a compile error

layout mdspan (row-major/layout_right против column-major/layout_left) должен совпадать с тем, что ожидает алгоритм и остальной код проекта — смешение layout'ов тихо даёт неверный численный результат, а не ошибку компиляции, поскольку layout — это просто параметр шаблона, а не проверяемое инвариантом свойство значения.The mdspan layout (row-major/layout_right versus column-major/layout_left) must match what the algorithm and the rest of the codebase expect — mixing layouts silently produces a wrong numerical result rather than a compile error, since a layout is just a template parameter, not an invariant checked on the value.