b2api
B2000++ API Reference Manual, VERSION 4.6
 
Loading...
Searching...
No Matches
b2matrix_rectangle.H
1//------------------------------------------------------------------------
2// b2matrix_rectangle.H --
3//
4// A framework for generic linear algebraic (matrix) computations.
5//
6// written by Mathias Doreille
7// Thomas Blome <thomas.blome@dlr.de>
8//
9// Copyright (c) 2004-2012,2017 SMR Engineering & Development SA
10// 2502 Bienne, Switzerland
11//
12// Copyright (c) 2023 Deutsches Zentrum für Luft- und Raumfahrt (DLR) e.V.
13// Linder Höhe, 51147 Köln
14//
15// All Rights Reserved. Proprietary source code. The contents of
16// this file may not be disclosed to third parties, copied or
17// duplicated in any form, in whole or in part, without the prior
18// written permission of SMR.
19//------------------------------------------------------------------------
20
21#ifndef __B2MATRIX_RECTANGLE_H__
22#define __B2MATRIX_RECTANGLE_H__
23
24#include <algorithm>
25#include <vector>
26
27#include "b2linear_algebra_def.H"
28
29namespace b2000::b2linalg {
30
31struct Mrectangle {
32 using base = Mrectangle_increment_ref;
33 using const_base = Mrectangle_increment_st_constref;
34 using copy = Mrectangle;
35};
36
37template <typename T>
38class Matrix<T, Mrectangle> {
39public:
40 Matrix() = default;
41
42 explicit Matrix(size_t s1_, size_t s2_) : s1(s1_), s2(s2_), m(s1_ * s2_) {}
43
44 Matrix(size_t s1_, size_t s2_, const T& t) : s1(s1_), s2(s2_), m(s1_ * s2_, t) {}
45
46 Matrix(const Matrix& m_) : s1(m_.s1), s2(m_.s2), m(m_.m) {}
47
48 template <typename T1>
49 Matrix(const Matrix<T1, Mrectangle>& m_)
50 : s1(m_.s1.begin(), m_.s1.end()),
51 s2(m_.s2.begin(), m_.s2.end()),
52 m(m_.m.begin(), m_.m.end()) {}
53
54 template <typename T1>
55 Matrix& operator=(const Matrix<T1, Mrectangle>& m_) {
56 s1 = m_.s1;
57 s2 = m_.s2;
58 m.assign(m_.m.begin(), m_.m.end());
59 return *this;
60 }
61
62 template <typename STORAGE>
63 Matrix(const Matrix<T, STORAGE>& m_)
64 : s1(m_.size1()), s2(m_.size2()), m(m_.size1() * m_.size2()) {
65 Matrix<T, Mrectangle_increment_ref>(*this) = m_;
66 }
67
68 template <typename T1, typename STORAGE>
69 Matrix& operator=(const Matrix<T1, STORAGE>& m_) {
70 s1 = m_.size1();
71 s2 = m_.size2();
72 m.resize(m_.size1() * m_.size2());
73 for (size_t j = 0; j != size2(); ++j) {
74 for (size_t i = 0; i != size1(); ++i) { (*this)(i, j) = m_(i, j); }
75 }
76 return *this;
77 }
78
79 double norm_inf() const {
80 double res = 0;
81 for (typename std::vector<T>::const_iterator i = m.begin(); i != m.end(); ++i) {
82 res = std::max(res, norm(*i));
83 }
84 return res;
85 }
86
87 void copy_real(const Matrix<b2000::csda<T>, Mrectangle_constref>& m_) {
88 s1 = m_.size1();
89 s2 = m_.size2();
90 m.resize(m_.size1() * m_.size2());
91 for (size_t i = 0; i != m.size(); ++i) { m[i] = std::real(m_.m[i]); }
92 }
93
94 void copy_real(const Matrix<std::complex<T>, Mrectangle_constref>& m_) {
95 s1 = m_.size1();
96 s2 = m_.size2();
97 m.resize(m_.size1() * m_.size2());
98 for (size_t i = 0; i != m.size(); ++i) { m[i] = std::real(m_.m[i]); }
99 }
100
101 void copy_imag(const Matrix<b2000::csda<T>, Mrectangle_constref>& m_) {
102 s1 = m_.size1();
103 s2 = m_.size2();
104 m.resize(m_.size1() * m_.size2());
105 for (size_t i = 0; i != m.size(); ++i) { m[i] = std::imag(m_.m[i]); }
106 }
107
108 void copy_imag(const Matrix<std::complex<T>, Mrectangle_constref>& m_) {
109 s1 = m_.size1();
110 s2 = m_.size2();
111 m.resize(m_.size1() * m_.size2());
112 for (size_t i = 0; i != m.size(); ++i) { m[i] = std::imag(m_.m[i]); }
113 }
114
115 bool is_null() const { return this == &null; }
116
117 void set_zero() { std::fill(m.begin(), m.end(), 0); }
118
119 void swap(Matrix& m_) {
120 std::swap(s1, m_.s1);
121 std::swap(s2, m_.s2);
122 m.swap(m_.m);
123 }
124
125 Matrix& operator=(const Matrix& m_) {
126 s1 = m_.s1;
127 s2 = m_.s2;
128 m = m_.m;
129 return *this;
130 }
131
132 template <typename STORAGE>
133 Matrix& operator=(const Matrix<T, STORAGE>& m_) {
134 resize(m_.size());
135 Matrix<T, Mrectangle_increment_ref>(*this) = m_;
136 return *this;
137 }
138
139 template <typename STORAGE>
140 Matrix& operator+=(const Matrix<T, STORAGE>& m_) {
141 *this = *this + m_;
142 return *this;
143 }
144
145 template <typename STORAGE>
146 Matrix& operator-=(const Matrix<T, STORAGE>& m_) {
147 *this = *this - m_;
148 return *this;
149 }
150
151 Matrix& operator*=(const T& scalar) {
152 blas::scal(m.size(), scalar, &m[0], 1);
153 return *this;
154 }
155
156 std::pair<size_t, size_t> size() const { return std::pair<size_t, size_t>(s1, s2); }
157
158 size_t size1() const { return s1; }
159
160 size_t size2() const { return s2; }
161
162 size_t esize() const { return s1 * s2; }
163
164 void resize(std::pair<size_t, size_t> s) {
165 s1 = s.first;
166 s2 = s.second;
167 m.resize(s1 * s2);
168 }
169
170 void resize(size_t s1_, size_t s2_) {
171 s1 = s1_;
172 s2 = s2_;
173 m.resize(s1 * s2);
174 }
175
176 const T& operator()(size_t i, size_t j) const { return m[i + j * s1]; }
177
178 T& operator()(size_t i, size_t j) { return m[i + j * s1]; }
179
180 Vector<T, Vdense_ref> operator[](size_t i) { return Vector<T, Vdense_ref>(s1, &m[i * s1]); }
181
182 Vector<T, Vdense_constref> operator[](size_t i) const {
183 return Vector<T, Vdense_constref>(s1, &m[i * s1]);
184 }
185
186 Matrix<T, Mrectangle_ref> operator()(const Interval& i) {
187 return Matrix<T, Mrectangle_ref>(s1, i.size(), &m[i[0] * s1]);
188 }
189
190 Matrix<T, Mrectangle_constref> operator()(const Interval& i) const {
191 return Matrix<T, Mrectangle_constref>(s1, i.size(), &m[i[0] * s1]);
192 }
193
194 Matrix<T, Mrectangle_increment_ref> operator()(const Interval& i1, const Interval& i2) {
195 return Matrix<T, Mrectangle_increment_ref>(
196 i1.size(), i2.size(), &m[i1[0] + i2[0] * s1], s1);
197 }
198
199 Matrix<T, Mrectangle_increment_constref> operator()(
200 const Interval& i1, const Interval& i2) const {
201 return Matrix<T, Mrectangle_increment_constref>(
202 i1.size(), i2.size(), &m[i1[0] + i2[0] * s1], s1);
203 }
204
205 Vector<T, Vdense_ref> operator()(const Interval& i1, size_t i2) {
206 return Vector<T, Vdense_ref>(i1.size(), &m[i1[0] + i2 * s1]);
207 }
208
209 Vector<T, Vdense_constref> operator()(const Interval& i1, size_t i2) const {
210 return Vector<T, Vdense_constref>(i1.size(), &m[i1[0] + i2 * s1]);
211 }
212
222 Matrix& operator=(const Matrix<T, Mpacked>& m_) {
223 s1 = m_.s;
224 s2 = s1;
225 m.reserve(s1 * s2);
226
227 std::vector<T> cache(s2);
228
229 if (m_.upper) {
230 auto it1e{std::end(m)};
231 auto it2b{std::next(std::cend(m_.m), -s1)};
232 auto it2e{std::cend(m_.m)};
233
235 for (size_t j = 0; j < s2; ++j) {
237 it1e = m.insert(it1e, std::begin(cache), std::next(std::begin(cache), j));
238 it1e = m.insert(it1e, it2b, it2e);
239
240 it2e = it2b;
241 it2b = it2b - (s1 - j - 1);
242
244 if (j != s2 - 1) {
245 for (size_t i = 0; i <= j; ++i) { cache[j - i] = m_(s1 - j - 2, s2 - i - 1); }
246 }
247 }
248 } else {
249 auto it1e{std::end(m)};
250 auto it2b{std::cbegin(m_.m)};
251 auto it2e{std::next(std::cbegin(m_.m), s1)};
252
254 for (size_t j = 0; j < s2; ++j) {
256 m.insert(it1e, std::begin(cache), std::next(std::begin(cache), j));
257 it1e = std::end(m);
258 m.insert(it1e, it2b, it2e);
259
260 it1e = std::end(m);
261 it2b = it2e;
262 it2e = it2e + (s1 - j - 1);
263
265 if (j != s2 - 1) {
266 for (size_t i = 0; i <= j; ++i) { cache[i] = m_(j + 1, i); }
267 }
268 }
269 }
270
271 return *this;
272 }
273
274 Matrix& operator=(const Matrix<
275 T, MMProd<
276 Minverse_constref<Msym_compressed_col_update_inv>,
277 Mrectangle_increment_st_constref> >& m_) {
278 if (size() != m_.size()) { resize(m_.size()); }
279 if (m_.scale != T(1) || m_.trans) { UnimplementedError() << THROW; }
280 m_.m1.m.resolve(
281 s1, s2, m_.m2.m, m_.m2.ldm, &m[0], s1, ' ', m_.m1.null_space,
282 m_.m1.max_null_space_vector);
283 return *this;
284 }
285
286 Matrix& operator=(const Matrix<
287 T, MMProd<
288 Minverse_constref<Mcompressed_col_update_inv>,
289 Mrectangle_increment_st_constref> >& m_) {
290 if (size() != m_.size()) { resize(m_.size()); }
291 if (m_.scale != T(1) || m_.trans) { UnimplementedError() << THROW; }
292 m_.m1.m.resolve(
293 s1, s2, m_.m2.m, m_.m2.ldm, &m[0], s1, ' ', m_.m1.null_space,
294 m_.m1.max_null_space_vector);
295 return *this;
296 }
297
298 friend logging::Logger& operator<<(logging::Logger& l, const Matrix& m) {
299 l << "rectangular matrix ";
300 l.write(m.s1, m.s2, &m.m[0], m.s1);
301 return l;
302 }
303
304 // TODO: Not optimised.
305 // https://en.wikipedia.org/wiki/In-place_matrix_transposition
306 void transposed() {
307 if (s1 == s2) {
308 for (size_t j = 0; j != s2; ++j) {
309 for (size_t i = 0; i != j; ++i) { std::swap((*this)(i, j), (*this)(j, i)); }
310 }
311 } else if (s1 * s2 > 0) {
312 std::vector<T> mt;
313 mt.reserve(m.size());
314 const size_t nm1 = s1 * s2 - 1;
315 for (size_t a = 0; a != s1 * s2; ++a) {
316 const size_t pa = (a == nm1 ? nm1 : s1 * a % nm1);
317 mt.push_back(m[pa]);
318 }
319 std::swap(m, mt);
320 std::swap(s1, s2);
321 }
322 }
323
324 static Matrix null;
325
326private:
327 size_t s1{};
328 size_t s2{};
329 std::vector<T> m;
330 MVFRIEND;
331};
332
333template <typename T>
334Matrix<T, Mrectangle> Matrix<T, Mrectangle>::null;
335
336struct Mrectangle_ref {
337 using base = Mrectangle_ref;
338 using const_base = Mrectangle_increment_st_constref;
339 using copy = Mrectangle;
340};
341
342template <typename T>
343class Matrix<T, Mrectangle_ref> {
344public:
345 Matrix() = default;
346
347 Matrix(size_t s1_, size_t s2_, T* m_) : s1(s1_), s2(s2_), m(m_) {}
348
349 Matrix(const Matrix& m_) : s1(m_.s1), s2(m_.s2), m(m_.m) {}
350
351 Matrix(Matrix<T, Mrectangle>& m_) : s1(m_.s1), s2(m_.s2), m(&m_.m[0]) {}
352
353 explicit Matrix(Vector<T, Vdense_ref> v) : s1(v.s), s2(1), m(v.v) {}
354
355 Matrix(size_t s1_, size_t s2_, Vector<T, Vdense_ref> v) : s1(s1_), s2(s2_), m(v.v) {
356 if (v.s != s1_ * s2_) { Exception() << THROW; }
357 }
358
359 bool is_null() const { return m == 0; }
360
361 void set_zero() { std::fill_n(m, s1 * s2, 0); }
362
363 template <typename STORAGE>
364 Matrix& operator+=(const Matrix<T, STORAGE>& m_) {
365 *this = *this + m_;
366 return *this;
367 }
368
369 template <typename STORAGE>
370 Matrix& operator-=(const Matrix<T, STORAGE>& m_) {
371 *this = *this - m_;
372 return *this;
373 }
374
375 Matrix& operator=(const Matrix& m_) {
376 if (size() != m_.size()) {
377 Exception() << "The two matrices do not have the same size." << THROW;
378 }
379 std::copy(m_.m, m_.m + s1 * s2, m);
380 return *this;
381 }
382
383 template <typename STORAGE>
384 Matrix& operator=(const Matrix<T, STORAGE>& m_) {
385 Matrix<T, Mrectangle_increment_ref>(*this) = m_;
386 return *this;
387 }
388
389 std::pair<size_t, size_t> size() const { return std::pair<size_t, size_t>(s1, s2); }
390
391 size_t size1() const { return s1; }
392
393 size_t size2() const { return s2; }
394
395 size_t esize() const { return s1 * s2; }
396
397 const T& operator()(size_t i, size_t j) const { return m[i + j * s1]; }
398
399 T& operator()(size_t i, size_t j) { return m[i + j * s1]; }
400
401 Vector<T, Vdense_ref> operator[](size_t i) { return Vector<T, Vdense_ref>(s1, m + i * s1); }
402
403 Vector<T, Vdense_constref> operator[](size_t i) const {
404 return Vector<T, Vdense_constref>(s1, m + i * s1);
405 }
406
407 Matrix<T, Mrectangle_ref> operator()(const Interval& i) {
408 return Matrix<T, Mrectangle_ref>(s1, i.size(), m + (i[0] * s1));
409 }
410
411 Matrix<T, Mrectangle_constref> operator()(const Interval& i) const {
412 return Matrix<T, Mrectangle_constref>(s1, i.size(), m + (i[0] * s1));
413 }
414
415 Matrix<T, Mrectangle_increment_ref> operator()(const Interval& i1, const Interval& i2) {
416 return Matrix<T, Mrectangle_increment_ref>(
417 i1.size(), i2.size(), m + i1[0] + i2[0] * s1, s1);
418 }
419
420 Matrix<T, Mrectangle_increment_constref> operator()(
421 const Interval& i1, const Interval& i2) const {
422 return Matrix<T, Mrectangle_increment_constref>(
423 i1.size(), i2.size(), m + i1[0] + i2[0] * s1, s1);
424 }
425
426 static Matrix null;
427
428 friend logging::Logger& operator<<(logging::Logger& l, const Matrix& m) {
429 l << "rectangular matrix ";
430 l.write(m.s1, m.s2, m.m, m.s1);
431 return l;
432 }
433
434private:
435 size_t s1{};
436 size_t s2{};
437 T* m{};
438 MVFRIEND;
439};
440
441template <typename T>
442Matrix<T, Mrectangle_ref> Matrix<T, Mrectangle_ref>::null;
443
444struct Mrectangle_constref {
445 using base = Mrectangle_constref;
446 using const_base = Mrectangle_increment_st_constref;
447 using copy = Mrectangle;
448};
449
450template <typename T>
451class Matrix<T, Mrectangle_constref> {
452public:
453 Matrix() = default;
454
455 Matrix(size_t s1_, size_t s2_, const T* m_) : s1(s1_), s2(s2_), m(m_) {}
456
457 Matrix(const Matrix& m_) : s1(m_.s1), s2(m_.s2), m(m_.m) {}
458
459 Matrix(const Matrix<T, Mrectangle>& m_) : s1(m_.s1), s2(m_.s2), m(&m_.m[0]) {}
460
461 Matrix(const Matrix<T, Mrectangle_ref>& m_) : s1(m_.s1), s2(m_.s2), m(m_.m) {}
462
463 explicit Matrix(const Vector<T, Vdense_constref>& v) : s1(v.s), s2(1), m(v.v) {}
464
465 Matrix(size_t s1_, size_t s2_, const Vector<T, Vdense_constref>& v) : s1(s1_), s2(s2_), m(v.v) {
466 if (v.s != s1_ * s2_) { Exception() << THROW; }
467 }
468
469 bool is_null() const { return m == 0; }
470
471 std::pair<size_t, size_t> size() const { return std::pair<size_t, size_t>(s1, s2); }
472
473 size_t size1() const { return s1; }
474
475 size_t size2() const { return s2; }
476
477 size_t esize() const { return s1 * s2; }
478
479 const T& operator()(size_t i, size_t j) const { return m[i + j * s1]; }
480
481 Vector<T, Vdense_constref> operator[](size_t i) const {
482 return Vector<T, Vdense_constref>(s1, m + i * s1);
483 }
484
485 Matrix<T, Mrectangle_constref> operator()(const Interval& i) const {
486 return Matrix<T, Mrectangle_constref>(s1, i.size(), m + (i[0] * s1));
487 }
488
489 Matrix<T, Mrectangle_increment_constref> operator()(
490 const Interval& i1, const Interval& i2) const {
491 return Matrix<T, Mrectangle_increment_constref>(
492 i1.size(), i2.size(), m + i1[0] + i2[0] * s1, s1);
493 }
494
495 static Matrix null;
496
497 friend logging::Logger& operator<<(logging::Logger& l, const Matrix& m) {
498 l << "rectangular matrix ";
499 l.write(m.s1, m.s2, m.m, m.s1);
500 return l;
501 }
502
503private:
504 size_t s1{};
505 size_t s2{};
506 const T* m{};
507 MVFRIEND;
508};
509
510template <typename T>
511Matrix<T, Mrectangle_constref> Matrix<T, Mrectangle_constref>::null;
512
513struct Mrectangle_increment_ref {
514 using base = Mrectangle_increment_ref;
515 using const_base = Mrectangle_increment_st_constref;
516 using copy = Mrectangle;
517};
518
519template <typename T>
520class Matrix<T, Mrectangle_increment_ref> {
521public:
522 Matrix() = default;
523
524 Matrix(size_t s1_, size_t s2_, T* m_, size_t ldm_) : s1(s1_), s2(s2_), m(m_), ldm(ldm_) {}
525
526 Matrix(const Matrix& m_) : s1(m_.s1), s2(m_.s2), m(m_.m), ldm(m_.ldm) {}
527
528 Matrix(Matrix<T, Mrectangle>& m_) : s1(m_.s1), s2(m_.s2), m(&m_.m[0]), ldm(m_.s1) {}
529
530 Matrix(Matrix<T, Mrectangle_ref> m_) : s1(m_.s1), s2(m_.s2), m(m_.m), ldm(m_.s1) {}
531
532 Matrix(size_t s1_, size_t s2_, Vector<T, Vincrement_ref> v)
533 : s1(s1_), s2(s2_), m(v.v), ldm(s1_) {
534 if (v.s != s1_ * s2_) { Exception() << THROW; }
535 }
536
537 bool is_null() const { return m == 0; }
538
539 void set_zero() {
540 T* ptr = m;
541 for (size_t j = 0; j != s2; ++j, ptr += ldm) { std::fill_n(ptr, s1, 0.0); }
542 }
543
544 std::pair<size_t, size_t> size() const { return std::pair<size_t, size_t>(s1, s2); }
545
546 size_t size1() const { return s1; }
547
548 size_t size2() const { return s2; }
549
550 size_t esize() const { return s1 * s2; }
551
552 const T& operator()(size_t i, size_t j) const { return m[i + j * ldm]; }
553
554 T& operator()(size_t i, size_t j) { return m[i + j * ldm]; }
555
556 Vector<T, Vdense_ref> operator[](size_t i) { return Vector<T, Vdense_ref>(s1, m + i * ldm); }
557
558 Vector<T, Vdense_constref> operator[](size_t i) const {
559 return Vector<T, Vdense_constref>(s1, m + i * ldm);
560 }
561
562 Matrix operator()(const Interval& i1, const Interval& i2) {
563 return Matrix(i1.size(), i2.size(), m + i1[0] + i2[0] * ldm, s1);
564 }
565
566 Matrix<T, Mrectangle_increment_constref> operator()(
567 const Interval& i1, const Interval& i2) const {
568 return Matrix<T, Mrectangle_increment_constref>(
569 i1.size(), i2.size(), m + i1[0] + i2[0] * ldm, s1);
570 }
571
572 Matrix& operator*=(T s) {
573 T* ptr = m;
574 for (size_t j = 0; j != s2; ++j) {
575 T* ptr_end = ptr + s1;
576 for (; ptr != ptr_end; ++ptr) { *ptr *= s; }
577 ptr += ldm - s1;
578 }
579 return *this;
580 }
581
582 template <typename STORAGE>
583 Matrix& operator+=(const Matrix<T, STORAGE>& m_) {
584 *this = *this + m_;
585 return *this;
586 }
587
588 template <typename STORAGE>
589 Matrix& operator-=(const Matrix<T, STORAGE>& m_) {
590 *this = *this - m_;
591 return *this;
592 }
593
594 Matrix& operator=(const Matrix& m_) {
595 if (size() != m_.size()) {
596 Exception() << "The two matrices do not have the same size." << THROW;
597 }
598 if (m == m_.m && ldm == m_.ldm) { return *this; }
599 if (ldm == s1 && m_.ldm == m_.s1) {
600 T* m1 = m;
601 const T* m2 = m_.m;
602 const T* m2_end = m2 + s1 * s2;
603 while (m2 != m2_end) { *(m1++) = *(m2++); }
604 } else {
605 for (size_t i = 0; i != s2; ++i) { (*this)[i] = m_[i]; }
606 }
607 return *this;
608 }
609
610 Matrix& operator=(const Matrix<T, Mrectangle_constref>& m_) {
611 if (size1() != m_.size1() || size2() != m_.size2()) {
612 Exception() << "The two matrices do not have the same size." << THROW;
613 }
614 const T* ptrin = m_.m;
615 T* ptrout = m;
616 for (size_t j = 0; j != s2; ++j, ptrout += ldm - s1) {
617 for (size_t i = 0; i != s1; ++i, ++ptrout, ++ptrin) { *ptrout = *ptrin; }
618 }
619 return *this;
620 }
621
622 Matrix& operator=(const Matrix<T, Mpacked_st_constref>& m_) {
623 if (size1() != m_.size1() || size2() != m_.size2()) {
624 Exception() << "The two matrices do not have the same size." << THROW;
625 }
626 for (size_t j = 0; j != size2(); ++j) {
627 for (size_t i = 0; i != size1(); ++i) { (*this)(i, j) = m_(i, j); }
628 }
629 return *this;
630 }
631
632 Matrix& operator=(const Matrix<T, Mrectangle_increment_st_constref>& m_) {
633 if (size() != m_.size()) {
634 Exception() << "The two matrices do not have the same size." << THROW;
635 }
636 if (m == m_.m && ldm == m_.ldm && m_.trans == false && m_.scale == T(1)) { return *this; }
637 if (m_.trans == false && ldm == s1 && m_.ldm == m_.s1) {
638 T* m1 = m;
639 const T* m2 = m_.m;
640 const T* m2_end = m2 + s1 * s2;
641 while (m2 != m2_end) { *(m1++) = m_.scale * *(m2++); }
642 } else {
643 for (size_t i = 0; i != s2; ++i) { (*this)[i] = m_[i]; }
644 }
645 return *this;
646 }
647
648 Matrix& operator=(
649 const Matrix<T, Sum<Mrectangle_increment_st_constref, Mrectangle_increment_st_constref> >&
650 m_) {
651 if (size() != m_.size()) {
652 Exception() << "The two matrices do not have the same size." << THROW;
653 }
654 if (!m_.m1.trans && !m_.m2.trans && ldm == s1 && m_.m1.ldm == s1 && m_.m2.ldm == s1
655 && m_.m1.scale == T(1) && m_.m2.scale == T(1)) {
656 const size_t i_end = s1 * s2;
657 for (size_t i = 0; i != i_end; ++i) { m[i] = m_.m1.m[i] + m_.m2.m[i]; }
658 } else {
659 for (size_t i = 0; i != s2; ++i) { (*this)[i] = m_.scale * (m_.m1[i] + m_.m2[i]); }
660 }
661 return *this;
662 }
663
664 Matrix& operator=(
665 const Matrix<
666 T, MMProd<Mrectangle_increment_st_constref, Mrectangle_increment_st_constref> >&
667 m_) {
668 if (size() != m_.size()) {
669 Exception() << "The two matrices do not have the same size." << THROW;
670 }
671 if (m_.trans) {
672 blas::gemm(
673 m_.m2.trans ? 'n' : 't', m_.m1.trans ? 'n' : 't', s1, s2, m_.m2.size1(),
674 m_.scale * m_.m1.scale * m_.m2.scale, m_.m2.m, m_.m2.ldm, m_.m1.m, m_.m1.ldm, 0,
675 m, ldm);
676 } else {
677 blas::gemm(
678 m_.m1.trans ? 't' : 'n', m_.m2.trans ? 't' : 'n', s1, s2, m_.m1.size2(),
679 m_.scale * m_.m1.scale * m_.m2.scale, m_.m1.m, m_.m1.ldm, m_.m2.m, m_.m2.ldm, 0,
680 m, ldm);
681 }
682 return *this;
683 }
684
685 Matrix& operator=(
686 const Matrix<
687 T,
688 Sum<Mrectangle_increment_st_constref,
689 MMProd<Mrectangle_increment_st_constref, Mrectangle_increment_st_constref> > >&
690 m_) {
691 if (size() != m_.size()) {
692 Exception() << "The two matrices do not have the same size." << THROW;
693 }
694 if (m_.trans) {
695 (*this) = transposed(m_.scale * m_.m1);
696 } else {
697 (*this) = m_.scale * m_.m1;
698 }
699 if (m_.m2.trans ^ m_.trans) {
700 blas::gemm(
701 m_.m2.m2.trans ? 'n' : 't', m_.m2.m1.trans ? 'n' : 't', s1, s2, m_.m2.m2.size1(),
702 m_.scale * m_.m2.scale * m_.m2.m1.scale * m_.m2.m2.scale, m_.m2.m2.m,
703 m_.m2.m2.ldm, m_.m2.m1.m, m_.m2.m1.ldm, m_.scale * m_.m1.scale, m, ldm);
704 } else {
705 blas::gemm(
706 m_.m2.m1.trans ? 't' : 'n', m_.m2.m2.trans ? 't' : 'n', s1, s2, m_.m2.m1.size2(),
707 m_.scale * m_.m2.scale * m_.m2.m1.scale * m_.m2.m2.scale, m_.m2.m1.m,
708 m_.m2.m1.ldm, m_.m2.m2.m, m_.m2.m2.ldm, m_.scale * m_.m1.scale, m, ldm);
709 }
710 return *this;
711 }
712
713 Matrix& operator=(
714 const Matrix<T, MMProd<Mcompressed_col_st_constref, Mrectangle_increment_st_constref> >&
715 m_) {
716 if (size() != m_.size()) {
717 Exception() << "The two matrices do not have the same size." << THROW;
718 }
719 for (size_t j = 0; j != s2; ++j) { (*this)[j] = m_.m1 * m_.m2[j]; }
720 return *this;
721 }
722
723 Matrix& operator=(
724 const Matrix<
725 T, Sum<Mrectangle_increment_st_constref,
726 MMProd<Mcompressed_col_st_constref, Mrectangle_increment_st_constref> > >&
727 m_) {
728 if (size() != m_.size()) {
729 Exception() << "The two matrices do not have the same size." << THROW;
730 }
731
732 for (size_t i = 0; i != s2; ++i) {
733 (*this)[i] = m_.scale * m_.m1[i] + (m_.scale * m_.m2.scale) * (m_.m2.m1 * m_.m2.m2[i]);
734 }
735 return *this;
736 }
737
738 template <typename ATYPE, typename BTYPE>
739 Matrix& operator=(const Matrix<T, Eigenvector<ATYPE, BTYPE> >& eigenvector) {
740 if (size() != eigenvector.size()) {
741 Exception() << "The two matrices do not have the same size." << THROW;
742 }
743 const_cast<Matrix<T, Eigenvector<ATYPE, BTYPE> >&>(eigenvector).resolve(*this);
744 return *this;
745 }
746
747 template <typename ATYPE, typename BTYPE>
748 Matrix& operator=(const Matrix<T, PolyEigenvector<ATYPE, BTYPE> >& eigenvector) {
749 if (size() != eigenvector.size()) {
750 Exception() << "The two matrices do not have the same size." << THROW;
751 }
752 const_cast<Matrix<T, PolyEigenvector<ATYPE, BTYPE> >&>(eigenvector).resolve(*this);
753 return *this;
754 }
755
756 Matrix& operator=(
757 const Matrix<
758 T, MMProd<Msym_compressed_col_st_constref, Mrectangle_increment_st_constref> >&
759 m_) {
760 if (size() != m_.size()) {
761 Exception() << "The two matrices do not have the same size." << THROW;
762 }
763
764 for (size_t i = 0; i != s2; ++i) { (*this)[i] = m_.scale * (m_.m1 * m_.m2[i]); }
765 return *this;
766 }
767
768 Matrix& operator=(
769 const Matrix<
770 T,
771 Sum<Mrectangle_increment_st_constref,
772 MMProd<Msym_compressed_col_st_constref, Mrectangle_increment_st_constref> > >&
773 m_) {
774 if (size() != m_.size()) {
775 Exception() << "The two matrices do not have the same size." << THROW;
776 }
777
778 for (size_t i = 0; i != s2; ++i) {
779 (*this)[i] = m_.scale * m_.m1[i] + (m_.scale * m_.m2.scale) * (m_.m2.m1 * m_.m2.m2[i]);
780 }
781 return *this;
782 }
783
784 template <typename M_FORMAT>
785 Matrix& operator+=(
786 const Matrix<
787 T, MMProd<Mstructured_constref<M_FORMAT>, Mrectangle_increment_st_constref> >& m_) {
788 if (size() != m_.size()) {
789 Exception() << "The two matrices do not have the same size." << THROW;
790 }
791
792 if (m_.scale == T(1) && m_.trans == false && m_.m2.trans == false) {
793 for (size_t k = 0; k != s2; ++k) {
794 Vector<T, Vindex_scale_constref> tmp1(
795 m_.m1.m.size1(), m_.m2.m + k * m_.m2.ldm, m_.m1.index, m_.m2.scale);
796 Vector<T, Vindex_ref> tmp2(m_.m1.m.size1(), &(*this)(0, k), m_.m1.index);
797 tmp2 += m_.m1.m * tmp1;
798 }
799 } else {
801 }
802 return *this;
803 }
804
805 template <typename M_FORMAT>
806 Matrix& operator=(
807 const Matrix<
808 T, Sum<Mrectangle_increment_st_constref,
809 MMProd<Mstructured_constref<M_FORMAT>, Mrectangle_increment_st_constref> > >&
810 m_) {
811 if (size() != m_.size()) {
812 Exception() << "The two matrices do not have the same size." << THROW;
813 }
814
815 if (m_.m1.m == m && m_.m1.ldm == ldm && m_.m1.scale == T(1) && m_.m1.trans == false
816 && m_.scale == T(1) && m_.trans == false && m_.m2.m2.trans == false) {
817 for (size_t k = 0; k != s2; ++k) {
818 Vector<T, Vindex_scale_constref> tmp1(
819 m_.m2.m1.m.size1(), m_.m2.m2.m + k * m_.m2.m2.ldm, m_.m2.m1.index,
820 m_.m2.m2.scale);
821 Vector<T, Vindex_ref> tmp2(m_.m2.m1.m.size1(), &(*this)(0, k), m_.m2.m1.index);
822 tmp2 = Vector<T, Vindex_scale_constref>(tmp2) + m_.m2.m1.m * tmp1;
823 }
824 } else {
826 }
827 return *this;
828 }
829
830 template <typename M_FORMAT>
831 Matrix& operator=(
832 const Matrix<T, Sum<Mrectangle_increment_st_constref, Mstructured_constref<M_FORMAT> > >&
833 m_) {
834 if (size() != m_.size()) {
835 Exception() << "The two matrices do not have the same size." << THROW;
836 }
837
838 if (m_.m1.m != m) {
839 (*this) = m_.scale * m_.m1;
840 } else if (m_.m1.ldm != ldm) {
842 } else if (m_.scale != T(1)) {
843 (*this) *= m_.scale;
844 }
845 for (size_t j = 0; j != m_.m2.m.size2(); ++j) {
846 const size_t jj = (m_.m2.index2 ? m_.m2.index2 : m_.m2.index)[j];
847 for (size_t i = 0; i != m_.m2.m.size1(); ++i) {
848 (*this)(m_.m2.index[i], jj) += m_.m2.m(i, j) * m_.scale;
849 }
850 }
851 return *this;
852 }
853
854 Matrix& operator=(
855 const Matrix<T, MMProd<Mrectangle_inv, Mrectangle_increment_st_constref> >& m_) {
856 if (m_.scale != T(1)) { UnimplementedError() << THROW; }
857 *this = m_.m2;
858 m_.m1.resolve(*this);
859 return *this;
860 }
861
862 Matrix& operator=(
863 const Matrix<T, MMProd<Mpacked_st_constref, Mrectangle_increment_st_constref> >& m_) {
864 if (size() != m_.size()) {
865 Exception() << "The two matrices do not have the same size." << THROW;
866 }
867 const T alpha = m_.m1.scale * m_.m2.scale * m_.scale;
868 const char m1_up = m_.m1.upper ? 'U' : 'L';
869 if (m_.m2.trans) {
870 for (size_t i = 0; i != s1; ++i) {
871 blas::spmv(
872 m1_up, m_.m1.s, alpha, m_.m1.m, &m_.m2.m[i], m_.m2.ldm, 0, &m[i * ldm], 1);
873 }
874 } else {
875 for (size_t j = 0; j != s2; ++j) {
876 blas::spmv(
877 m1_up, m_.m1.s, alpha, m_.m1.m, &m_.m2.m[j * m_.m2.ldm], 1, 0, &m[j * ldm],
878 1);
879 }
880 }
881 return *this;
882 }
883
884 Matrix& operator=(
885 const Matrix<T, MMProd<Mcompressed_col_st_constref, Mcompressed_col_st_constref> >& m_) {
886 if (size() != m_.size()) {
887 Exception() << "The two matrices do not have the same size." << THROW;
888 }
889 if (m_.m1.trans || m_.m2.trans) { UnimplementedError() << THROW; }
890 const T scale = m_.scale * m_.m1.scale * m_.m2.scale;
891 T* ptr = m;
892 for (size_t j = 0; j != s2; ++j, ptr += ldm) {
893 std::fill_n(ptr, s1, T(0));
894 size_t k = m_.m2.si[j];
895 const size_t k_end = m_.m2.si[j + 1];
896 for (; k != k_end; ++k) {
897 const size_t kk = m_.m2.index[k];
898 const T kv = scale * m_.m2.m[k];
899 size_t i = m_.m1.si[kk];
900 const size_t i_end = m_.m1.si[kk + 1];
901 for (; i != i_end; ++i) { ptr[m_.m1.index[i]] += kv * m_.m1.m[i]; }
902 }
903 }
904 return *this;
905 }
906
907 void in_place_invert() {
908 if (s1 != s2) { Exception() << THROW; }
909 int info;
910 int* ipiv = TemporaryBuffer<int>::get(s1);
911 lapack::getrf(s1, s1, m, ldm, ipiv, info);
912 if (info != 0) { Exception() << THROW; }
913 lapack::getri(s1, m, ldm, ipiv, TemporaryBuffer<double>::get(s1 * s1), s1 * s1, info);
914 if (info != 0) { Exception() << THROW; }
915 }
916
917 size_t diagonalise_base_vector_on_observation_matrix(
918 const Index& observation_matrix, const double tol = 1e-13) {
919 if (observation_matrix.size() > s2) { Exception() << THROW; }
920 Matrix<T, Mrectangle> u(observation_matrix.size(), s2);
921 {
922 T* orig = m;
923 T* dest = &u(0, 0);
924 for (size_t j = 0; j != s2; ++j, orig += ldm) {
925 for (size_t i = 0; i != observation_matrix.size(); ++i, ++dest) {
926 *dest = orig[observation_matrix[i]];
927 }
928 }
929 }
930 Vector<T, Vdense> sv(u.size1());
931 Matrix<T, Mrectangle> vt(s2, s2);
932 int info;
933 int lwork;
934 {
935 T lwork_tmp;
936 lapack::gesvd(
937 'O', 'A', u.size1(), s2, &u(0, 0), u.size1(), &sv[0], NULL, 1, &vt(0, 0), s2,
938 &lwork_tmp, -1, info);
939 lwork = int(lwork_tmp);
940 }
941 if (info != 0) { Exception() << THROW; }
942 {
943 std::vector<T> work(lwork);
944 lapack::gesvd(
945 'O', 'A', u.size1(), s2, &u(0, 0), u.size1(), &sv[0], NULL, 1, &vt(0, 0), s2,
946 &work[0], lwork, info);
947 if (info < 0) { Exception() << THROW; }
948 if (info > 0) {
949 Exception() << "could not compute the svd decomposition of the matrix." << THROW;
950 }
951 u.resize(u.size1(), u.size1());
952 }
953
954 Matrix<T, Mrectangle> tmp;
955 tmp = (*this) * transposed(vt);
956 size_t rank;
957 for (rank = 0; rank != sv.size(); ++rank) {
958 if (std::abs(sv[rank]) < tol) { break; }
959 tmp[rank] *= 1.0 / sv[rank];
960 }
961 Interval inter(0, u.size1());
962 (*this)(Interval(0, s1), inter) = tmp(Interval(0, s1), inter) * transposed(u(inter, inter));
963 (*this)(Interval(0, s1), Interval(u.size1(), s2)) =
964 tmp(Interval(0, s1), Interval(u.size1(), s2));
965
966 return s2 + rank - observation_matrix.size();
967 }
968
969 static Matrix null;
970
971 friend logging::Logger& operator<<(logging::Logger& l, const Matrix& m) {
972 l << "rectangular matrix ";
973 l.write(m.s1, m.s2, m.m, m.ldm);
974 return l;
975 }
976
977private:
978 size_t s1{};
979 size_t s2{};
980 T* m{};
981 size_t ldm{};
982 MVFRIEND;
983};
984
985template <typename T>
986Matrix<T, Mrectangle_increment_ref> Matrix<T, Mrectangle_increment_ref>::null;
987
988struct Mrectangle_increment_constref {
989 using base = Mrectangle_increment_constref;
990 using const_base = Mrectangle_increment_st_constref;
991 using copy = Mrectangle;
992};
993
994template <typename T>
995class Matrix<T, Mrectangle_increment_constref> {
996public:
997 Matrix() = default;
998
999 Matrix(size_t s1_, size_t s2_, const T* m_, size_t ldm_)
1000 : s1(s1_), s2(s2_), m(m_), ldm(std::max(size_t(1), ldm_)) {}
1001
1002 Matrix(const Matrix& m_) : s1(m_.s1), s2(m_.s2), m(m_.m), ldm(m_.ldm) {}
1003
1004 Matrix(const Matrix<T, Mrectangle>& m_)
1005 : s1(m_.s1), s2(m_.s2), m(&m_.m[0]), ldm(std::max(size_t(1), m_.s1)) {}
1006
1007 Matrix(const Matrix<T, Mrectangle_ref>& m_)
1008 : s1(m_.s1), s2(m_.s2), m(m_.m), ldm(std::max(size_t(1), m_.s1)) {}
1009
1010 Matrix(const Matrix<T, Mrectangle_constref>& m_)
1011 : s1(m_.s1), s2(m_.s2), m(m_.m), ldm(std::max(size_t(1), m_.s1)) {}
1012
1013 Matrix(const Matrix<T, Mrectangle_increment_ref>& m_)
1014 : s1(m_.s1), s2(m_.s2), m(m_.m), ldm(m_.ldm) {}
1015
1016 Matrix(size_t s1_, size_t s2_, const Vector<T, Vincrement_constref>& v)
1017 : s1(s1_), s2(s2_), m(v.v), ldm(std::max(size_t(1), s1_)) {
1018 if (v.s != s1 * s2) { Exception() << THROW; }
1019 }
1020
1021 bool is_null() const { return m == 0; }
1022
1023 std::pair<size_t, size_t> size() const { return std::pair<size_t, size_t>(s1, s2); }
1024
1025 size_t size1() const { return s1; }
1026
1027 size_t size2() const { return s2; }
1028
1029 size_t esize() const { return s1 * s2; }
1030
1031 const T& operator()(size_t i, size_t j) const { return m[i + j * ldm]; }
1032
1033 Vector<T, Vdense_constref> operator[](size_t i) const {
1034 return Vector<T, Vdense_constref>(s1, m + i * ldm);
1035 }
1036
1037 Matrix<T, Mrectangle_increment_constref> operator()(
1038 const Interval& i1, const Interval& i2) const {
1039 return Matrix<T, Mrectangle_increment_constref>(
1040 i1.size(), i2.size(), m + i1[0] + i2[0] * ldm, ldm);
1041 }
1042
1043 static Matrix null;
1044
1045 friend logging::Logger& operator<<(logging::Logger& l, const Matrix& m) {
1046 l << "rectangular matrix ";
1047 l.write(m.s1, m.s2, m.m, m.ldm);
1048 return l;
1049 }
1050
1051private:
1052 size_t s1{};
1053 size_t s2{};
1054 const T* m{};
1055 size_t ldm{};
1056 MVFRIEND;
1057};
1058
1059template <typename T>
1060Matrix<T, Mrectangle_increment_constref> Matrix<T, Mrectangle_increment_constref>::null;
1061
1062struct Mrectangle_increment_st_constref {
1063 using base = Mrectangle_increment_st_constref;
1064 using const_base = Mrectangle_increment_st_constref;
1065 using copy = Mrectangle;
1066};
1067
1068template <typename T>
1069class Matrix<T, Mrectangle_increment_st_constref> {
1070public:
1071 Matrix() = default;
1072
1073 Matrix(size_t s1_, size_t s2_, const T* m_, size_t ldm_, T scale_, bool transpose_ = false)
1074 : s1(s1_),
1075 s2(s2_),
1076 m(m_),
1077 ldm(std::max(size_t(1), ldm_)),
1078 scale(scale_),
1079 trans(transpose_) {}
1080
1081 Matrix(const Matrix& m_)
1082 : s1(m_.s1), s2(m_.s2), m(m_.m), ldm(m_.ldm), scale(m_.scale), trans(m_.trans) {}
1083
1084 Matrix(const Matrix<T, Mrectangle>& m_)
1085 : s1(m_.s1),
1086 s2(m_.s2),
1087 m(&m_.m[0]),
1088 ldm(std::max(size_t(1), m_.s1)),
1089 scale(1),
1090 trans(false) {}
1091
1092 Matrix(const Matrix<T, Mrectangle_ref>& m_)
1093 : s1(m_.s1), s2(m_.s2), m(m_.m), ldm(std::max(size_t(1), m_.s1)), scale(1), trans(false) {}
1094
1095 Matrix(const Matrix<T, Mrectangle_constref>& m_)
1096 : s1(m_.s1), s2(m_.s2), m(m_.m), ldm(std::max(size_t(1), m_.s1)), scale(1), trans(false) {}
1097
1098 Matrix(const Matrix<T, Mrectangle_increment_ref>& m_)
1099 : s1(m_.s1), s2(m_.s2), m(m_.m), ldm(m_.ldm), scale(1), trans(false) {}
1100
1101 Matrix(const Matrix<T, Mrectangle_increment_constref>& m_)
1102 : s1(m_.s1), s2(m_.s2), m(m_.m), ldm(m_.ldm), scale(1), trans(false) {}
1103
1104 Matrix(size_t s1_, size_t s2_, const Vector<T, Vincrement_scale_constref>& v)
1105 : s1(s1_), s2(s2_), m(v.v), ldm(std::max(size_t(1), s1_)), scale(v.scale), trans(false) {
1106 if (v.s != s1 * s2) { Exception() << THROW; }
1107 }
1108
1109 bool is_null() const { return m == 0; }
1110
1111 std::pair<size_t, size_t> size() const {
1112 if (trans) {
1113 return std::pair<size_t, size_t>(s2, s1);
1114 } else {
1115 return std::pair<size_t, size_t>(s1, s2);
1116 }
1117 }
1118
1119 size_t size1() const {
1120 if (trans) {
1121 return s2;
1122 } else {
1123 return s1;
1124 }
1125 }
1126
1127 size_t size2() const {
1128 if (trans) {
1129 return s1;
1130 } else {
1131 return s2;
1132 }
1133 }
1134
1135 size_t esize() const { return s1 * s2; }
1136
1137 T operator()(size_t i, size_t j) const {
1138 if (trans) {
1139 return scale * m[j + i * ldm];
1140 } else {
1141 return scale * m[i + j * ldm];
1142 }
1143 }
1144
1145 Vector<T, Vincrement_scale_constref> operator[](size_t i) const {
1146 if (trans) {
1147 return Vector<T, Vincrement_scale_constref>(s2, m + i, ldm, scale);
1148 } else {
1149 return Vector<T, Vincrement_scale_constref>(s1, m + i * ldm, 1, scale);
1150 }
1151 }
1152
1153 Matrix<T, Mrectangle_increment_st_constref> operator()(
1154 const Interval& i1, const Interval& i2) const {
1155 if (trans) {
1156 return Matrix<T, Mrectangle_increment_st_constref>(
1157 i2.size(), i1.size(), m + i2[0] + i1[0] * ldm, ldm, scale, trans);
1158 } else {
1159 return Matrix<T, Mrectangle_increment_st_constref>(
1160 i1.size(), i2.size(), m + i1[0] + i2[0] * ldm, ldm, scale, trans);
1161 }
1162 }
1163
1164 Matrix& operator*(T s) {
1165 scale *= s;
1166 return *this;
1167 }
1168
1169 Matrix& transpose() {
1170 trans = trans ? false : true;
1171 return *this;
1172 }
1173
1174 static Matrix null;
1175
1176private:
1177 size_t s1{};
1178 size_t s2{};
1179 const T* m{};
1180 size_t ldm{};
1181 T scale{};
1182 bool trans{};
1183 MVFRIEND;
1184};
1185
1186template <typename T>
1187Matrix<T, Mrectangle_increment_st_constref> Matrix<T, Mrectangle_increment_st_constref>::null;
1188
1189struct Mrectangle_inv {
1190 using base = Mrectangle_inv;
1191 using const_base = Mrectangle_inv;
1192 using copy = Mrectangle_inv;
1193};
1194
1195template <typename T>
1196class Matrix<T, Mrectangle_inv> {
1197public:
1198 Matrix(const Matrix<T, Mrectangle>& m_) : s(m_.s1), m(m_.m), ipiv(m_.s1) {
1199 if (m_.s1 != m_.s2) { UnimplementedError() << THROW; }
1200 int info;
1201 lapack::getrf(s, s, &m[0], s, &ipiv[0], info);
1202 if (info < 0) { Exception() << THROW; }
1203 if (info > 0) { Exception() << "The matrix is singular." << THROW; }
1204 }
1205
1206 size_t size1() const { return s; }
1207
1208 size_t size2() const { return s; }
1209
1210 std::pair<size_t, size_t> size() const { return std::pair<size_t, size_t>(s, s); }
1211
1212 void resolve(Vector<T, Vincrement_ref> v_) const {
1213 if (v_.incr != 1) { UnimplementedError() << THROW; }
1214 int info;
1215 lapack::getrs('N', s, 1, &m[0], s, &ipiv[0], v_.v, s, info);
1216 if (info < 0) { Exception() << THROW; }
1217 }
1218
1219 void resolve(Matrix<T, Mrectangle_increment_ref> m_) const {
1220 int info;
1221 lapack::getrs('N', s, m_.size2(), &m[0], s, &ipiv[0], m_.m, m_.ldm, info);
1222 if (info < 0) { Exception() << THROW; }
1223 }
1224
1225private:
1226 size_t s;
1227 std::vector<T> m;
1228 std::vector<int> ipiv;
1229 MVFRIEND;
1230};
1231
1232} // namespace b2000::b2linalg
1233
1234#endif
csda< T > norm(const csda< T > &a)
Definition b2csda.H:343
#define THROW
Definition b2exception.H:198
static T * get(size_t size)
Definition b2linear_algebra_utils.H:50
GenericException< UnimplementedError_name > UnimplementedError
Definition b2exception.H:314