numerics 0.1.0
Loading...
Searching...
No Matches
small_matrix.hpp
Go to the documentation of this file.
1/// @file small_matrix.hpp
2/// @brief Constexpr fixed-size stack-allocated matrix and Givens rotation.
3///
4/// These types complement the heap-allocated Matrix / Vector for small inner
5/// kernels where heap traffic is undesirable:
6///
7/// SmallVec<N> -- constexpr dense vector backed by std::array<real,N>
8/// SmallMatrix<M,N> -- constexpr row-major matrix backed by
9/// std::array<real,M*N> GivensRotation -- constexpr (c,s) pair; apply()
10/// / apply_t() are branchless
11///
12/// Because these are literal types every operation can be evaluated at
13/// compile time (C++17: arithmetic only; C++20+: also sqrt/hypot via
14/// constexpr `<cmath>`). At runtime the compiler places them entirely in
15/// registers for small M, N.
16///
17/// Typical uses:
18/// - GMRES: Hessenberg column updates via GivensRotation::from + apply
19/// - Jacobi SVD / eig: 2x2 eigensystem kernel as SmallMatrix<2,2>
20/// - Register-tile accumulation: SmallMatrix<4,4> replaces 16 named doubles
21/// - Unit tests: known-answer matmuls computed at compile time
22#pragma once
23
24#include "core/types.hpp"
25#include <algorithm>
26#include <array>
27#include <cmath>
28
29namespace num {
30
31// SmallVec<N>
32
33/// @brief Constexpr fixed-size dense vector (stack-allocated).
34template <idx N> struct SmallVec {
35 std::array<real, N> data{};
36
37 constexpr real &operator[](idx i) noexcept { return data[i]; }
38 constexpr const real &operator[](idx i) const noexcept { return data[i]; }
39 static constexpr idx size() noexcept { return N; }
40
41 constexpr SmallVec &operator+=(const SmallVec &o) noexcept {
42 for (idx i = 0; i < N; ++i)
43 data[i] += o.data[i];
44 return *this;
45 }
46 constexpr SmallVec &operator-=(const SmallVec &o) noexcept {
47 for (idx i = 0; i < N; ++i)
48 data[i] -= o.data[i];
49 return *this;
50 }
51 constexpr SmallVec &operator*=(real s) noexcept {
52 for (idx i = 0; i < N; ++i)
53 data[i] *= s;
54 return *this;
55 }
56
57 constexpr real dot(const SmallVec &o) const noexcept {
58 real s = 0;
59 for (idx i = 0; i < N; ++i)
60 s += data[i] * o.data[i];
61 return s;
62 }
63 /// @brief Sum of squares (avoid sqrt to stay constexpr in C++17).
64 constexpr real norm_sq() const noexcept { return dot(*this); }
65};
66
67template <idx N>
68constexpr SmallVec<N> operator+(SmallVec<N> a, const SmallVec<N> &b) noexcept {
69 return a += b;
70}
71template <idx N>
72constexpr SmallVec<N> operator*(real s, SmallVec<N> v) noexcept {
73 return v *= s;
74}
75
76// SmallMatrix<M, N>
77
78/// @brief Constexpr fixed-size row-major matrix (stack-allocated).
79template <idx M, idx N> struct SmallMatrix {
80 std::array<real, M * N> data{};
81
82 constexpr real &operator()(idx i, idx j) noexcept { return data[i * N + j]; }
83 constexpr const real &operator()(idx i, idx j) const noexcept {
84 return data[i * N + j];
85 }
86
87 static constexpr idx rows() noexcept { return M; }
88 static constexpr idx cols() noexcept { return N; }
89
90 constexpr void fill(real v) noexcept { data.fill(v); }
91
92 static constexpr SmallMatrix zeros() noexcept { return SmallMatrix{}; }
93
94 static constexpr SmallMatrix identity() noexcept {
95 static_assert(M == N, "identity() requires a square matrix");
96 SmallMatrix m{};
97 for (idx i = 0; i < M; ++i)
98 m(i, i) = real(1);
99 return m;
100 }
101
102 constexpr SmallMatrix<N, M> transposed() const noexcept {
104 for (idx i = 0; i < M; ++i)
105 for (idx j = 0; j < N; ++j)
106 t(j, i) = (*this)(i, j);
107 return t;
108 }
109
110 /// @brief Matrix multiplication: (M x N) * (N x K) -> (M x K).
111 template <idx K>
112 constexpr SmallMatrix<M, K>
113 operator*(const SmallMatrix<N, K> &B) const noexcept {
115 for (idx i = 0; i < M; ++i)
116 for (idx k = 0; k < N; ++k)
117 for (idx j = 0; j < K; ++j)
118 C(i, j) += (*this)(i, k) * B(k, j);
119 return C;
120 }
121
122 /// @brief Matrix-vector product: (MxN) * (N) -> (M).
123 constexpr SmallVec<M> operator*(const SmallVec<N> &x) const noexcept {
124 SmallVec<M> y{};
125 for (idx i = 0; i < M; ++i)
126 for (idx j = 0; j < N; ++j)
127 y[i] += (*this)(i, j) * x[j];
128 return y;
129 }
130
131 constexpr SmallMatrix &operator+=(const SmallMatrix &o) noexcept {
132 for (idx k = 0; k < M * N; ++k)
133 data[k] += o.data[k];
134 return *this;
135 }
136 constexpr SmallMatrix &operator*=(real s) noexcept {
137 for (idx k = 0; k < M * N; ++k)
138 data[k] *= s;
139 return *this;
140 }
141};
142
143// GivensRotation
144
145/// @brief Givens plane rotation: G = [c, s; -s, c].
146///
147/// Constructed so that G * [a; b]^T = [r; 0] with r = hypot(a, b).
148///
149/// @code
150/// auto g = GivensRotation::from(h_jj, h_jj1); // zero H[j,j+1] in GMRES
151/// g.apply(g_j, g_j1); // rotate RHS vector too
152/// @endcode
155
156 /// Compute (c,s) such that G*[a;b] = [r;0], r = hypot(a,b).
157 /// Stable for any (a,b) including b==0.
158 static constexpr GivensRotation from(real a, real b) noexcept {
159 if (b == real(0))
160 return {real(1), real(0)};
161 // Use the standard stable form. std::hypot is constexpr in GCC/Clang
162 // as a built-in extension even in C++17; becomes standard in C++23.
163 real r = std::sqrt(a * a + b * b);
164 return {a / r, b / r};
165 }
166
167 /// Apply G from the left: [x; y] <- G * [x; y].
168 constexpr void apply(real &x, real &y) const noexcept {
169 real tmp = c * x + s * y;
170 y = -s * x + c * y;
171 x = tmp;
172 }
173
174 /// Apply G^T (inverse rotation): [x; y] <- G^T * [x; y].
175 constexpr void apply_t(real &x, real &y) const noexcept {
176 real tmp = c * x - s * y;
177 y = s * x + c * y;
178 x = tmp;
179 }
180
181 /// Return the 2x2 rotation matrix.
182 constexpr SmallMatrix<2, 2> as_matrix() const noexcept {
183 return SmallMatrix<2, 2>{{c, s, -s, c}};
184 }
185};
186
187} // namespace num
Core type definitions.
double real
Definition types.hpp:10
constexpr SmallVec< N > operator*(real s, SmallVec< N > v) noexcept
constexpr SmallVec< N > operator+(SmallVec< N > a, const SmallVec< N > &b) noexcept
std::size_t idx
Definition types.hpp:11
Givens plane rotation: G = [c, s; -s, c].
constexpr void apply(real &x, real &y) const noexcept
Apply G from the left: [x; y] <- G * [x; y].
constexpr void apply_t(real &x, real &y) const noexcept
Apply G^T (inverse rotation): [x; y] <- G^T * [x; y].
constexpr SmallMatrix< 2, 2 > as_matrix() const noexcept
Return the 2x2 rotation matrix.
static constexpr GivensRotation from(real a, real b) noexcept
Constexpr fixed-size row-major matrix (stack-allocated).
static constexpr SmallMatrix identity() noexcept
static constexpr idx cols() noexcept
constexpr SmallMatrix & operator+=(const SmallMatrix &o) noexcept
constexpr SmallMatrix< N, M > transposed() const noexcept
static constexpr SmallMatrix zeros() noexcept
constexpr real & operator()(idx i, idx j) noexcept
constexpr SmallVec< M > operator*(const SmallVec< N > &x) const noexcept
Matrix-vector product: (MxN) * (N) -> (M).
constexpr void fill(real v) noexcept
static constexpr idx rows() noexcept
constexpr SmallMatrix & operator*=(real s) noexcept
std::array< real, M *N > data
constexpr const real & operator()(idx i, idx j) const noexcept
constexpr SmallMatrix< M, K > operator*(const SmallMatrix< N, K > &B) const noexcept
Matrix multiplication: (M x N) * (N x K) -> (M x K).
Constexpr fixed-size dense vector (stack-allocated).
static constexpr idx size() noexcept
constexpr real norm_sq() const noexcept
Sum of squares (avoid sqrt to stay constexpr in C++17).
constexpr SmallVec & operator+=(const SmallVec &o) noexcept
constexpr real dot(const SmallVec &o) const noexcept
constexpr SmallVec & operator-=(const SmallVec &o) noexcept
constexpr const real & operator[](idx i) const noexcept
constexpr real & operator[](idx i) noexcept
std::array< real, N > data
constexpr SmallVec & operator*=(real s) noexcept