JUK1
DynamicMatrix.hpp
Go to the documentation of this file.
1 #ifndef _MATRIX_HPP_INC_
2 #define _MATRIX_HPP_INC_
3 #include <string>
4 #include <sstream>
5 #include <iomanip>
6 #include <vector>
7 #include <concepts>
8 #include <array>
9 #include <assert.h>
10 #include <complex>
11 
12 template<typename T>
13 std::complex<double>
14 operator>(const std::complex<T> & a, const std::complex<T> & b) {
15  return std::norm(a) > std::norm(b);
16 }
17 
18 template<typename T>
19 std::complex<double>
20 operator<(const std::complex<T> & a, const std::complex<T> & b) {
21  return std::norm(a) < std::norm(b);
22 }
23 
24 template<typename T>
25 std::complex<double>
26 operator>=(const std::complex<T> & a, const std::complex<T> & b) {
27  return std::norm(a) >= std::norm(b);
28 }
29 
30 template<typename T>
31 std::complex<double>
32 operator<=(const std::complex<T> & a, const std::complex<T> & b) {
33  return std::norm(a) <= std::norm(b);
34 }
35 
36 template<typename T>
37 concept arithmetic = requires(T a, T b) {
38  {a == b};
39  {a != b};
40  {a >= b};
41  {a <= b};
42  {a * b};
43  {a / b};
44  {a + b};
45  {a - b};
46 };
47 
48 template<typename T>
49 struct LUPair;
50 
54 template<typename T>
55 requires arithmetic<T> struct Matrix {
56  std::vector<T> data;
57  size_t M;
58  size_t N;
59 
60  Matrix(size_t M, size_t N) : M(M), N(N) {
61  data.resize(M * N);
62  }
63 
64  Matrix(size_t M, size_t N, T initialValue) : M(M), N(N) {
65  data.resize(M * N, initialValue);
66  }
67 
68  T & operator()(size_t m, size_t n) {
69  return data[m * N + n];
70  }
71 
72  const T & operator()(size_t m, size_t n) const {
73  return data[m * N + n];
74  }
75 
76  void fill(T fillVal) {
77  for (auto & entry : data) {
78  entry = fillVal;
79  }
80  }
81 
82  void rowAddition(size_t destinationRow, size_t sourceRow, T scalingFactor) {
83  assert(0 <= destinationRow && destinationRow <= N);
84  assert(0 <= sourceRow && sourceRow <= M);
85  for (size_t n = 0; n < N; n++) {
86  data[destinationRow * N + n] += scalingFactor * data[sourceRow * N + n];
87  }
88  }
89 
90  void swapRows(size_t row1, size_t row2) {
91  std::swap_ranges(data.begin() + row1 * N, data.begin() + (row1 + 1) * N,
92  data.begin() + row2 * N);
93  }
94 
96  Matrix<T> toRet(N, M);
97  for (size_t m = 0; m < M; m++) {
98  for (size_t n = 0; n < N; n++) {
99  toRet.data[n * M + m] = data[m * N + n];
100  }
101  }
102  return toRet;
103  }
104 
105  Matrix<T> multiply(const Matrix<T> & rhs) const {
106  assert(N == rhs.M);
107  Matrix<T> toRet(M, rhs.N);
108  multiply(rhs, toRet, 0);
109  return toRet;
110  }
111 
112  void multiply(const Matrix<T> & rhs, Matrix<T> & dest) const {
113  // for simd/cache reasons, the order of operations is slightly weird. This is
114  // to minimise row changes which may cause cache misses. This is due to the
115  // fact that in this model rows are represented contiguously in memory, so
116  // streaming instructions and local caching can be used to our advantage
117  //
118  // It is also worth stating that there is strong potential for multithreading
119  // here, and especially if paired with the Strassen Method for matrix
120  // multiplication However for small sizes, naive multiplication can be better
121  // due to less memory allocations
122 
123  for (size_t m = 0; m < M; m++) {
124  for (size_t k = 0; k < N; k++) {
125  for (size_t n = 0; n2 < dest.N; n2++) {
126  dest.data[m * destination.N + n] += data[m * N + k] *
127  rhs.data[k * N + n];
128  }
129  }
130  }
131  }
132 
133  Matrix<T> add(const Matrix<T> & rhs) const {
134  assert(N == rhs.N && M == rhs.M);
136  add(rhs, toRet);
137  return toRet;
138  }
139 
140  void add(const Matrix<T> & rhs, Matrix<T> & dest) const {
141  for (size_t m = 0; m < M; m++) {
142  for (size_t n = 0; n < N; n++) {
143  dest.data[m * N + n] = data[m * N + n] + rhs.data[m * N + n];
144  }
145  }
146  }
147 
148  Matrix<T> subtract(const Matrix<T> & rhs) const {
149  assert(N == rhs.N && M == rhs.M);
150  Matrix<T> toRet(M, N);
151  add(rhs, toRet);
152  return toRet;
153  }
154 
155  void subtract(const Matrix<T> & rhs, Matrix<T> & dest) const {
156  for (size_t m = 0; m < M; m++) {
157  for (size_t n = 0; n < N; n++) {
158  dest.data[m * N + n] = data[m * N + n] - rhs.data[m * N + n]
159  }
160  }
161  }
162 
163  std::string toString() const {
164  std::stringstream toRet;
165  for (size_t m = 0; m < M; m++) {
166  for (size_t n = 0; n < N; n++) {
167  toRet << std::setw(5) << std::setprecision(2) << data[m * N + n]
168  << " ";
169  }
170 
171  toRet << std::endl;
172  }
173 
174  return toRet.str();
175  }
176 
177  LUPair<T> luPair() const {
178  assert(N == M);
179 
180  LUPair<T> toRet(M);
181  luPair(toRet);
182  return toRet;
183  }
184 
185  void luPair(LUPair<T> & dest) const {
186  dest.u = *this;
187  dest.l.fill(0.0);
188  for (size_t n = 0; n < N; n++) {
189  dest.l.data[n * N + n] = 1.0;
190  dest.p[n] = n;
191  }
192 
193  for (size_t r = 0; r < M - 1; r++) {
194  // find largest in column
195  size_t largestRow = r;
196  auto maxV = abs(dest.u.data[r * N + r]);
197  for (size_t r2 = r + 1; r2 < M; r2++) {
198  if (abs(dest.u.data[r2 * N + r]) > maxV) {
199  maxV = abs(dest.u.data[r2 * N + r]);
200  largestRow = r2;
201  }
202  }
203 
204  // swap rows in U and indices in p
205  dest.u.swapRows(r, largestRow);
206  std::swap(dest.p[r], dest.p[largestRow]);
207  // swap subdiagonal entries in L
208  for (size_t n = 0; n < r; n++) {
209  std::swap(dest.l.data[r * N + n], dest.l.data[largestRow * N + n]);
210  }
211 
212  // Gaussian elimination
213  for (size_t m = r + 1; m < M; m++) {
214  // TODO: potential for multithreading here
215  // need to take into account how many processors there are
216  // https://en.cppreference.com/w/cpp/thread/thread/hardware_concurrency
217  T multiplier = dest.u.data[m * N + r] / dest.u.data[r * N + r];
218  dest.u.rowAddition(m, r, -multiplier);
219  dest.l.data[m * N + r] = multiplier;
220  }
221  // std::cout << "After Gaussian\n " << dest.toString();
222  }
223  }
224 
225  Matrix<T> leftDivide(const Matrix<T> & rhs) const {
226  auto lu = luPair();
227  Matrix<T> scratchSpace(M, 1);
228  Matrix<T> toRet(M, 1);
229  leftDivide(rhs, lu, scratchSpace, toRet);
230  return toRet;
231  }
232 
233  void leftDivide(const Matrix<T> & rhs, const LUPair<T> & lu,
234  Matrix<T> & scratchSpace, Matrix<T> & dest) const {
235  leftDivide(rhs, lu, scratchSpace, dest.data.begin(), dest.data.end());
236  }
237 
238  template<typename Iterator>
239  void
240  leftDivide(const Matrix<T> & rhs, const LUPair<T> & lu, Matrix<T> & scratchSpace,
241  Iterator destBegin, Iterator destEnd) const {
242  assert(destEnd - destBegin == scratchSpace.M);
243  for (size_t m = 0; m < M; m++) {
244  destBegin[m] = rhs.data[lu.p[m]];
245  }
246 
247  // scratchSpace: solve LY = Pb for y using substitution
248  for (size_t m = 0; m < M; m++) {
249  T val = destBegin[m];
250  for (size_t n = 0; n < m; n++) {
251  val -= scratchSpace.data[n] * lu.l.data[m * N + n];
252  }
253  scratchSpace.data[m] = val / lu.l.data[m * N + m];
254  }
255 
256  // stage2: solve Ux = Y for x using substitution
257  for (size_t m = 0; m < M; m++) {
258  T val = scratchSpace.data[M - m - 1];
259  for (size_t n = 0; n < m; n++) {
260  val -= destBegin[(M - n - 1)] *
261  lu.u.data[(M - m - 1) * N + N - n - 1];
262  }
263  destBegin[M - m - 1] = val / lu.u.data[(M - m - 1) * N + M - m - 1];
264  }
265  }
266 };
267 
272 template<typename T>
273 struct LUPair {
276  std::vector<size_t> p;
277  size_t M;
278 
279  LUPair(size_t _M) : l(_M, _M, 0), u(_M, _M), p(_M), M(_M) {
280  }
281 
282  LUPair(const LUPair<T> & other)
283  : l(other.l), u(other.u), p(other.p), M(other.M) {
284  }
285 
286 
287  std::string toString() {
288  std::stringstream toRet;
289  toRet << " U\n" << u.toString();
290  toRet << " L\n" << l.toString();
291  toRet << " p\n";
292  for (size_t i = 0; i < p.size(); i++) {
293  toRet << std::setw(5) << p[i] << " ";
294  }
295  toRet << std::endl;
296  return toRet.str();
297  }
298 };
299 // --------------------------------------------------------------------------
300 // Implementation
301 // --------------------------------------------------------------------------
302 
303 
304 #endif
std::complex< double > operator>(const std::complex< T > &a, const std::complex< T > &b)
std::complex< double > operator<(const std::complex< T > &a, const std::complex< T > &b)
concept arithmetic
std::complex< double > operator>=(const std::complex< T > &a, const std::complex< T > &b)
std::complex< double > operator<=(const std::complex< T > &a, const std::complex< T > &b)
PURPOSE j m
Definition: QPpassive.m:21
Find a complex rational model for freq domain data for a
for i
b
Definition: getResidues.m:47
for k
Definition: interch.m:15
end if abs(real(dotprod))>rstoerst rstoerst
requires(std::is_arithmetic< ValType >::value &&NumVars >=0) struct DiffVar
A helper class to store the L U and pivot matrices. Mainly useful in solving systems of equations.
Matrix< T > u
LUPair(const LUPair< T > &other)
std::string toString()
size_t M
std::vector< size_t > p
LUPair(size_t _M)
Matrix< T > l
A matrix class with support for LU-decomposition, and left division.
void leftDivide(const Matrix< T > &rhs, const LUPair< T > &lu, Matrix< T > &scratchSpace, Iterator destBegin, Iterator destEnd) const
const T & operator()(size_t m, size_t n) const
void swapRows(size_t row1, size_t row2)
void leftDivide(const Matrix< T > &rhs, const LUPair< T > &lu, Matrix< T > &scratchSpace, Matrix< T > &dest) const
size_t N
void subtract(const Matrix< T > &rhs, Matrix< T > &dest) const
void luPair(LUPair< T > &dest) const
Matrix< T > add(const Matrix< T > &rhs) const
Matrix(size_t M, size_t N)
void multiply(const Matrix< T > &rhs, Matrix< T > &dest) const
size_t M
Matrix< T > transpose()
std::string toString() const
Matrix< T > leftDivide(const Matrix< T > &rhs) const
LUPair< T > luPair() const
Matrix(size_t M, size_t N, T initialValue)
std::vector< T > data
void rowAddition(size_t destinationRow, size_t sourceRow, T scalingFactor)
Matrix< T > multiply(const Matrix< T > &rhs) const
T & operator()(size_t m, size_t n)
Matrix< T > subtract(const Matrix< T > &rhs) const
void fill(T fillVal)
void add(const Matrix< T > &rhs, Matrix< T > &dest) const