Skip to content

00_core_storage_and_helpers.cpp

Vectors, matrices, sparse matrices, expressions and the helpers around them.

vec<T> and mat<T> own aligned, contiguous storage, and mat is row-major. Arithmetic comes in two forms. Expressions under num::ops return new values and suit formulas. Functions with an output parameter, such as matvec and axpy, allocate nothing and suit loops. spmat stores a sparse matrix in CSR form and is built from triplets.

#include <array>
#include <cstdio>
#include <numerics.hpp>
#include <span>
#include <vector>

using namespace num;

namespace {

void print(const char *name, const vec<real> &v) {
    std::printf("%-27s[", name);
    for (idx i = 0; i < v.size(); ++i) {
        std::printf(i ? ", %g" : "%g", v[i]);
    }
    std::printf("]\n");
}

void print(const char *name, const mat<real> &M) {
    for (idx i = 0; i < M.rows(); ++i) {
        std::printf("%-27s[", i ? "" : name);
        for (idx j = 0; j < M.cols(); ++j) {
            std::printf(j ? " %5g" : "%5g", M(i, j));
        }
        std::printf("]\n");
    }
}

} // namespace

int main() {
    // Construction and the in-place forms: nothing below allocates.
    vec<real> x{1.0, 2.0, 3.0};
    vec<real> y(3, 2.0), z(3, 0.0);
    scale(y, 0.5);    // y <- 0.5 y
    add(x, y, z);     // z <- x + y
    axpy(-1.0, x, z); // z <- z - x
    print("x", x);
    print("z = (x + 0.5 y) - x", z);
    std::printf("%-27s%g, %g\n", "dot(x, y), norm(x)", dot(x, y), norm(x));

    mat<real> A(3, 3, 0.0);
    set_diagonal(A, std::array<real, 3>{4.0, 5.0, 6.0});
    A(0, 1) = A(1, 0) = 1.0;
    vec<real> ax(3);
    matvec(A, x, ax);
    print("A", A);
    print("A x", ax);

    // The same arithmetic as expressions, for formulas.
    {
        using namespace num::ops;
        const mat<real> C = A * transpose(A) + 2.0 * identity(3);
        const vec<real> w = A * x - x / 2.0;
        print("A A^T + 2 I", C);
        print("A x - x / 2", w);
    }

    // Constructors for common shapes, and the diagonal.
    print("unit_vector(3, 1)", unit_vector(3, 1));
    print("diagonal(A)", diagonal(A));
    print("linspace(0, 1, 5)", linspace(0.0, 1.0, 5));
    std::printf("%-27s%g\n", "accu(A) (sum of entries)", accu(A));

    // CSR from triplets; duplicates are summed.
    const spmat S = spmat::from_triplets(3, 3, std::vector<idx>{0, 0, 1, 2, 2},
                                         std::vector<idx>{0, 1, 1, 2, 2},
                                         std::vector<real>{2.0, 1.0, 3.0, 4.0, 0.5});
    vec<real> sx(3);
    sparse_matvec(S, x, sx);
    std::printf("%-27s%zu stored entries\n", "S", static_cast<std::size_t>(S.nnz()));
    print("S x", sx);
    print("dense(transpose(S))", dense(transpose(S)));

    // Properties checked exactly, and selection helpers.
    std::printf("%-27ssymmetric %d, SPD %d\n", "A", linear::is_symmetric(A), linear::is_spd(A));
    const vec<real> d = diagonal(A);
    const auto smallest = smallest_indices(std::span<const real>(d.data(), d.size()), 2);
    std::printf("%-27sargmax %zu, two smallest %zu %zu\n", "diagonal(A)",
                static_cast<std::size_t>(argmax(std::span<const real>(d.data(), d.size()))),
                static_cast<std::size_t>(smallest[0]), static_cast<std::size_t>(smallest[1]));
    vec<real> p{0.2, -0.1, 0.8};
    clip_and_normalize_nonnegative(std::span<real>(p.data(), p.size()));
    print("clip and normalize", p);
}

Possible output:

x                          [1, 2, 3]
z = (x + 0.5 y) - x        [1, 1, 1]
dot(x, y), norm(x)         6, 3.74166
A                          [    4     1     0]
                           [    1     5     0]
                           [    0     0     6]
A x                        [6, 11, 18]
A A^T + 2 I                [   19     9     0]
                           [    9    28     0]
                           [    0     0    38]
A x - x / 2                [5.5, 10, 16.5]
unit_vector(3, 1)          [0, 1, 0]
diagonal(A)                [4, 5, 6]
linspace(0, 1, 5)          [0, 0.25, 0.5, 0.75, 1]
accu(A) (sum of entries)   17
S                          4 stored entries
S x                        [4, 6, 13.5]
dense(transpose(S))        [    2     0     0]
                           [    1     3     0]
                           [    0     0   4.5]
A                          symmetric 1, SPD 1
diagonal(A)                argmax 2, two smallest 0 1
clip and normalize         [0.2, 0, 0.8]