TRIQS/TRIQS 4.0.0
Researching Interacting Quantum Systems
Loading...
Searching...
No Matches
det_manip.hpp
Go to the documentation of this file.
1// Copyright (c) 2013-2018 Commissariat à l'énergie atomique et aux énergies alternatives (CEA)
2// Copyright (c) 2013-2018 Centre national de la recherche scientifique (CNRS)
3// Copyright (c) 2018-2023 Simons Foundation
4// Copyright (c) 2016 Igor Krivenko
5//
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10//
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You may obtain a copy of the License at
17// https://www.gnu.org/licenses/gpl-3.0.txt
18//
19// Authors: Michel Ferrero, JaksaVucicevic, Igor Krivenko, Henri Menke, Laura Messio, Olivier Parcollet, Priyanka Seth, Hugo U. R. Strand, Nils Wentzell
20
25
26#pragma once
27
30#include "../arrays.hpp"
31
32#include <nda/nda.hpp>
33
34#include <algorithm>
35#include <cmath>
36#include <cstdint>
37#include <iterator>
38#include <numeric>
39#include <vector>
40
41namespace triqs::det_manip {
42
43 namespace blas = nda::blas;
44
45 // ================ Work Data Types =====================
46
47 // Working data for single-row/column operations (insert, remove, change_col, change_row, change_col_row).
48 //
49 // - x and y: matrix builder arguments for the new/changed row and column.
50 // - i and j: positions of the row and column in the original matrix F^{(n)}.
51 // - ireal and jreal: positions of the row and column in the matrix G^{(n)}.
52 // - B and C: new column and row of the matrix G^{(n)} (also reused as scratch by the change operations).
53 // - MB and MC: products M^{(n)} B and C M^{(n)} of the current inverse matrix with the new column/row.
54 // - ksi: determinant-ratio factor det(G^{new}) / det(G^{(n)}) (= newdet / det).
55 template <typename x_type, typename y_type, typename value_type> struct work_data_type1 {
56 x_type x;
57 y_type y;
58 long i, j, ireal, jreal;
59 nda::vector<value_type> MB, MC, B, C;
60 value_type ksi;
61
62 // Resize the working vectors for a matrix of size N.
63 void resize(long N) {
64 MB.resize(N);
65 MC.resize(N);
66 B.resize(N);
67 C.resize(N);
68 }
69 };
70
71 // Working data for multiple-row/column operations (insert_k, insert2, remove_k, remove2).
72 //
73 // - x and y: matrix builder arguments for the k new rows and columns.
74 // - i and j: positions of the rows and columns in the original matrix F^{(n)}.
75 // - ireal and jreal: positions of the rows and columns in the matrix G^{(n)}.
76 // - B and C: new columns and rows of the matrix G^{(n)}.
77 // - MB and MC: products M^{(n)} B and C M^{(n)} of the current inverse matrix with the new columns/rows.
78 // - ksi: k x k block used to form the determinant ratio (see det_ksi()).
79 template <typename x_type, typename y_type, typename value_type> struct work_data_typek {
80 std::vector<x_type> x;
81 std::vector<y_type> y;
82 std::vector<long> i, j, ireal, jreal;
83 nda::matrix<value_type> MB, MC, B, C, ksi;
84
85 // Resize the working data for a matrix of size N and up to k inserted/removed rows and columns.
86 void resize(long N, long k) {
87 if (k < 2) return;
88 x.resize(k);
89 y.resize(k);
90 i.resize(k);
91 j.resize(k);
92 ireal.resize(k);
93 jreal.resize(k);
94 MB.resize(N, k);
95 MC.resize(k, N);
96 B.resize(N, k);
97 C.resize(k, N);
98 ksi.resize(k, k);
99 }
100
101 // Determinant of the leading k x k block of ksi, with fast paths for k = 2 and the k = 3 rule of Sarrus.
102 value_type det_ksi(long k) const {
103 if (k == 2) {
104 return ksi(0, 0) * ksi(1, 1) - ksi(1, 0) * ksi(0, 1);
105 } else if (k == 3) {
106 return // Rule of Sarrus
107 ksi(0, 0) * ksi(1, 1) * ksi(2, 2) + //
108 ksi(0, 1) * ksi(1, 2) * ksi(2, 0) + //
109 ksi(0, 2) * ksi(1, 0) * ksi(2, 1) - //
110 ksi(2, 0) * ksi(1, 1) * ksi(0, 2) - //
111 ksi(2, 1) * ksi(1, 2) * ksi(0, 0) - //
112 ksi(2, 2) * ksi(1, 0) * ksi(0, 1); //
113 } else {
114 auto Rk = range(k);
115 return nda::linalg::det(ksi(Rk, Rk));
116 };
117 }
118 };
119
120 // Working data for the refill operation.
121 //
122 // - x_values and y_values: new matrix builder arguments.
123 // - M: new matrix G built from the new arguments (later inverted in place).
124 template <typename x_type, typename y_type, typename value_type> struct work_data_type_refill {
125 std::vector<x_type> x_values;
126 std::vector<y_type> y_values;
127 nda::matrix<value_type> M;
128
129 // Reserve memory and resize the data storages for a matrix of size N.
130 void reserve(long N) {
131 x_values.reserve(N);
132 y_values.reserve(N);
133 M.resize(N, N);
134 }
135 };
136
137 // ================ det_manip implementation =====================
138
166 template <typename FunctionType> class det_manip {
167 private:
169 static_assert(f_tr::arity == 2, "det_manip : the function must take two arguments !");
170
171 public:
172 using x_type = typename f_tr::template decay_arg_t<0>;
173 using y_type = typename f_tr::template decay_arg_t<1>;
174 using value_type = typename f_tr::result_type;
175 using det_type = value_type;
176 static_assert(std::is_floating_point_v<value_type> || nda::is_complex_v<value_type>,
177 "det_manip : the function must return a floating number or a complex number");
178
179 using matrix_type = nda::matrix<value_type>;
180
181 protected: // the data
182 FunctionType f;
183
184 det_type det{1};
185 long Nmax{0}, N{0};
186 long kmax_tried{1}, k_tried{0};
187 enum {
188 NoTry,
189 Insert,
190 Remove,
191 ChangeCol,
192 ChangeRow,
193 ChangeRowCol,
194 InsertK,
195 RemoveK,
196 Refill
197 } last_try = NoTry; // keep in memory the last operation not completed
198 std::vector<long> row_num, col_num;
199 std::vector<x_type> x_values;
200 std::vector<y_type> y_values;
201 int sign = 1;
202 matrix_type mat_inv;
203 uint64_t n_opts = 0; // count the number of operation
204 uint64_t n_opts_max_before_check = 100; // max number of ops before the test of deviation of the det, M^-1 is performed.
205 double singular_threshold = -1; // the test to see if the matrix is singular is abs(det) > singular_threshold. If <0, it is !isnormal(abs(det))
206 double precision_warning = 1.e-8; // bound for warning message in check for singular matrix
207 double precision_error = 1.e-5; // bound for throwing error in check for singular matrix
208
216 friend void h5_write(h5::group fg, std::string subgroup_name, det_manip const &g) {
217 auto gr = fg.create_group(subgroup_name);
218 h5_write(gr, "N", g.N);
219 h5_write(gr, "mat_inv", g.mat_inv);
220 h5_write(gr, "det", g.det);
221 h5_write(gr, "sign", g.sign);
222 h5_write(gr, "row_num", g.row_num);
223 h5_write(gr, "col_num", g.col_num);
224 h5_write(gr, "x_values", g.x_values);
225 h5_write(gr, "y_values", g.y_values);
226 h5_write(gr, "n_opts", g.n_opts);
227 h5_write(gr, "n_opts_max_before_check", g.n_opts_max_before_check);
228 h5_write(gr, "singular_threshold", g.singular_threshold);
229 }
230
238 friend void h5_read(h5::group fg, std::string subgroup_name, det_manip &g) {
239 auto gr = fg.open_group(subgroup_name);
240 h5_read(gr, "N", g.N);
241 h5_read(gr, "mat_inv", g.mat_inv);
242 g.Nmax = first_dim(g.mat_inv); // restore Nmax
243 g.last_try = NoTry;
244 h5_read(gr, "det", g.det);
245 h5_read(gr, "sign", g.sign);
246 h5_read(gr, "row_num", g.row_num);
247 h5_read(gr, "col_num", g.col_num);
248 h5_read(gr, "x_values", g.x_values);
249 h5_read(gr, "y_values", g.y_values);
250 h5_read(gr, "n_opts", g.n_opts);
251 h5_read(gr, "n_opts_max_before_check", g.n_opts_max_before_check);
252 h5_read(gr, "singular_threshold", g.singular_threshold);
253 }
254
255 private:
256 work_data_type1<x_type, y_type, value_type> w1;
257 work_data_typek<x_type, y_type, value_type> wk;
258 work_data_type_refill<x_type, y_type, value_type> w_refill;
259 det_type newdet{1};
260 int newsign{1};
261
262 private: // for the move constructor, I need to separate the swap since f may not be defaulted constructed
263 void swap_but_f(det_manip &rhs) noexcept {
264 using std::swap;
265#define SW(a) swap(this->a, rhs.a)
266 SW(det);
267 SW(Nmax);
268 SW(N);
269 SW(last_try);
270 SW(row_num);
271 SW(col_num);
272 SW(x_values);
273 SW(y_values);
274 SW(sign);
275 SW(mat_inv);
276 SW(n_opts);
277 SW(n_opts_max_before_check);
278 SW(w1);
279 SW(wk);
280 SW(newdet);
281 SW(newsign);
282#undef SW
283 }
284
285 friend void swap(det_manip &lhs, det_manip &rhs) noexcept {
286 using std::swap;
287 swap(lhs.f, rhs.f);
288 lhs.swap_but_f(rhs);
289 }
290
291 public:
303 void reserve(long new_N, long new_k = 1) {
304 if (new_k > kmax_tried) {
305 kmax_tried = new_k;
306 if (new_N <= Nmax) wk.resize(Nmax, kmax_tried);
307 }
308 if (new_N > Nmax) {
309 Nmax = 2 * new_N;
310
311 matrix_type mcpy(mat_inv);
312 mat_inv.resize(Nmax, Nmax);
313 auto Rcpy = range(mcpy.extent(0));
314 mat_inv(Rcpy, Rcpy) = mcpy;
315
316 row_num.reserve(Nmax);
317 col_num.reserve(Nmax);
318 x_values.reserve(Nmax);
319 y_values.reserve(Nmax);
320
321 w1.resize(Nmax);
322 wk.resize(Nmax, kmax_tried);
323 }
324 }
325
331 [[nodiscard]] double get_singular_threshold() const { return singular_threshold; }
332
343 void set_singular_threshold(double threshold) { singular_threshold = threshold; }
344
350 [[nodiscard]] double get_n_operations_before_check() const { return n_opts_max_before_check; }
351
357 void set_n_operations_before_check(uint64_t n) { n_opts_max_before_check = n; }
358
364 [[nodiscard]] double get_precision_warning() const { return precision_warning; }
365
378 void set_precision_warning(double threshold) { precision_warning = threshold; }
379
385 [[nodiscard]] double get_precision_error() const { return precision_error; }
386
392 void set_precision_error(double threshold) { precision_error = threshold; }
393
403 det_manip(FunctionType F, long init_size) : f(std::move(F)) {
404 reserve(init_size);
405 mat_inv() = 0;
406 }
407
418 template <typename ArgumentContainer1, typename ArgumentContainer2>
419 det_manip(FunctionType F, ArgumentContainer1 const &X, ArgumentContainer2 const &Y) : f(std::move(F)) {
420 if (X.size() != Y.size()) TRIQS_RUNTIME_ERROR << " X.size != Y.size";
421 N = X.size();
422 if (N == 0) {
423 det = 1;
424 reserve(30);
425 return;
426 }
427 reserve(N);
428 std::copy(X.begin(), X.end(), std::back_inserter(x_values));
429 std::copy(Y.begin(), Y.end(), std::back_inserter(y_values));
430 mat_inv() = 0;
431 for (long i = 0; i < N; ++i) {
432 row_num.push_back(i);
433 col_num.push_back(i);
434 for (long j = 0; j < N; ++j) mat_inv(i, j) = f(x_values[i], y_values[j]);
435 }
436 range RN(N);
437 det = nda::linalg::det(mat_inv(RN, RN));
438 mat_inv(RN, RN) = nda::linalg::inv(mat_inv(RN, RN));
439 }
440
441 // Special member functions. All of them should be defaulted.
442 det_manip(det_manip const &) = default;
443 det_manip(det_manip &&rhs) noexcept : f(std::move(rhs.f)) {
444 this->swap_but_f(rhs);
445 } // f need not have a default constructor and we dont swap the temp data...
446 //det_manip& operator=(const det_manip&) = default;
447 det_manip &operator=(const det_manip &) = delete;
448 det_manip &operator=(det_manip &&rhs) noexcept {
449 assert((last_try == NoTry) && (rhs.last_try == NoTry));
450 swap(*this, rhs);
451 return *this;
452 }
453
457 void clear() {
458 N = 0;
459 sign = 1;
460 det = 1;
461 last_try = NoTry;
462 row_num.clear();
463 col_num.clear();
464 x_values.clear();
465 y_values.clear();
466 }
467
468 //----------------------- READ ACCESS TO DATA ----------------------------------
469
474 [[nodiscard]] long size() const { return N; }
475
482 x_type const &get_x(long i) const { return x_values[row_num[i]]; }
483
490 y_type const &get_y(long j) const { return y_values[col_num[j]]; }
491
497 std::vector<x_type> get_x() const {
498 std::vector<x_type> res;
499 res.reserve(N);
500 for (long i : range(N)) res.emplace_back(x_values[row_num[i]]);
501 return res;
502 }
503
509 std::vector<y_type> get_y() const {
510 std::vector<y_type> res;
511 res.reserve(N);
512 for (long i : range(N)) res.emplace_back(y_values[col_num[i]]);
513 return res;
514 }
515
525 std::vector<x_type> const &get_x_internal_order() const { return x_values; }
526
532 std::vector<y_type> const &get_y_internal_order() const { return y_values; }
533
538 FunctionType const &get_function() const { return f; }
539
544 det_type determinant() {
545 if (is_singular()) regenerate();
546 return sign * det;
547 }
548
561 // warning : need to invert the 2 permutations: (AP)^-1= P^-1 A^-1.
562 value_type inverse_matrix(int i, int j) const { return mat_inv(col_num[i], row_num[j]); }
563
570 matrix_type inverse_matrix() const {
571 matrix_type res(N, N);
572 for (long i = 0; i < N; i++)
573 for (long j = 0; j < N; j++) res(i, j) = inverse_matrix(i, j);
574 return res;
575 }
576
584 value_type inverse_matrix_internal_order(int i, int j) const { return mat_inv(i, j); }
585
591 nda::matrix_const_view<value_type> inverse_matrix_internal_order() const { return mat_inv(range(N), range(N)); }
592
598 matrix_type matrix() const {
599 matrix_type res(N, N);
600 for (long i = 0; i < N; i++)
601 for (long j = 0; j < N; j++) res(i, j) = f(get_x(i), get_y(j));
602 return res;
603 }
604
616 template <typename LambdaType> friend void foreach (det_manip const &d, LambdaType const &fn) {
617 nda::for_each(std::array{d.N, d.N}, [&fn, &d](int i, int j) { return fn(d.x_values[i], d.y_values[j], d.mat_inv(j, i)); });
618 }
619
620 // ------------------------- OPERATIONS -----------------------------------------------
621
635 void swap_row(long i, long j) {
636 if (i == j) return;
637 std::swap(row_num[i], row_num[j]);
638 sign = -sign;
639 // we do not need to change the det, or the matrix, just the permutation
640 }
641
655 void swap_col(long i, long j) {
656 if (i == j) return;
657 std::swap(col_num[i], col_num[j]);
658 sign = -sign;
659 }
660
681 value_type try_insert(long i, long j, x_type const &x, y_type const &y) {
682
683 // check input and store it for complete_operation
684 TRIQS_ASSERT(last_try == NoTry);
685 TRIQS_ASSERT(0 <= i and i <= N);
686 TRIQS_ASSERT(0 <= j and j <= N);
687 reserve(N + 1);
688 last_try = Insert;
689 w1.i = i;
690 w1.j = j;
691 w1.x = x;
692 w1.y = y;
693
694 // treat empty matrix separately
695 if (N == 0) {
696 newdet = f(x, y);
697 newsign = 1;
698 return value_type(newdet);
699 }
700
701 // I add the row and col and the end. If the move is rejected,
702 // no effect since N will not be changed : Minv(i,j) for i,j>=N has no meaning.
703 for (long l = 0; l < N; l++) {
704 w1.B(l) = f(x_values[l], y);
705 w1.C(l) = f(x, y_values[l]);
706 }
707 range RN(N);
708 //w1.MB(R) = mat_inv(R,R) * w1.B(R);// OPTIMIZE BELOW
709 blas::gemv(1.0, mat_inv(RN, RN), w1.B(RN), 0.0, w1.MB(RN));
710 w1.ksi = f(x, y) - nda::blas::dot(w1.C(RN), w1.MB(RN));
711 newdet = det * w1.ksi;
712 newsign = ((i + j) % 2 == 0 ? sign : -sign); // since N-i0 + N-j0 = i0+j0 [2]
713 return w1.ksi * (newsign * sign); // sign is unity, hence 1/sign == sign
714 }
715
735 template <typename Fx, typename Fy> value_type try_insert_from_function(long i, long j, Fx fx, Fy fy, value_type const ksi) {
736
737 // check input and store it for complete_operation
738 TRIQS_ASSERT(last_try == NoTry);
739 TRIQS_ASSERT(0 <= i and i <= N);
740 TRIQS_ASSERT(0 <= j and j <= N);
741 reserve(N + 1);
742 last_try = Insert;
743 w1.i = i;
744 w1.j = j;
745
746 // treat empty matrix separately
747 if (N == 0) {
748 newdet = ksi;
749 newsign = 1;
750 return newdet;
751 }
752
753 // I add the row and col and the end. If the move is rejected,
754 // no effect since N will not be changed : Minv(i,j) for i,j>=N has no meaning.
755 for (long l = 0; l < N; l++) {
756 w1.B(l) = fx(x_values[l]);
757 w1.C(l) = fy(y_values[l]);
758 }
759 range RN(N);
760 //w1.MB(R) = mat_inv(R,R) * w1.B(R);// OPTIMIZE BELOW
761 blas::gemv(1.0, mat_inv(RN, RN), w1.B(RN), 0.0, w1.MB(RN));
762 w1.ksi = ksi - nda::blas::dot(w1.C(RN), w1.MB(RN));
763 newdet = det * w1.ksi;
764 newsign = ((i + j) % 2 == 0 ? sign : -sign); // since N-i0 + N-j0 = i0+j0 [2]
765 return w1.ksi * (newsign * sign); // sign is unity, hence 1/sign == sign
766 }
767
768 //------------------------------------------------------------------------------------------
769 private:
770 // Complete the insert operation.
771 void complete_insert() {
772 // store the new value of x,y. They are seen through the same permutations as rows and cols resp.
773 x_values.push_back(w1.x);
774 y_values.push_back(w1.y);
775 row_num.push_back(0);
776 col_num.push_back(0);
777
778 // special empty case again
779 if (N == 0) {
780 N = 1;
781 mat_inv(0, 0) = 1 / value_type(newdet);
782 return;
783 }
784
785 range RN(N);
786 //w1.MC(R1) = transpose(mat_inv(R1,R1)) * w1.C(R1); //OPTIMIZE BELOW
787 blas::gemv(1.0, transpose(mat_inv(RN, RN)), w1.C(RN), 0.0, w1.MC(RN));
788 w1.MC(N) = -1;
789 w1.MB(N) = -1;
790
791 N++;
792 RN = range(N);
793
794 // keep the real position of the row/col
795 // since we insert a col/row, we have first to push the col at the right
796 // and then say that col w1.i is stored in N, the last col.
797 // same for rows
798 for (long i = N - 2; i >= w1.i; i--) row_num[i + 1] = row_num[i];
799 row_num[w1.i] = N - 1;
800 for (long i = N - 2; i >= w1.j; i--) col_num[i + 1] = col_num[i];
801 col_num[w1.j] = N - 1;
802
803 // Minv is ok, we need to complete
804 w1.ksi = 1 / w1.ksi;
805
806 // compute the change to the inverse
807 // M += w1.ksi w1.MB w1.MC with BLAS. first put the 0
808 mat_inv(RN, N - 1) = 0;
809 mat_inv(N - 1, RN) = 0;
810 //mat_inv(R,R) += w1.ksi* w1.MB(R) * w1.MC(R)// OPTIMIZE BELOW
811 blas::ger(w1.ksi, w1.MB(RN), w1.MC(RN), mat_inv(RN, RN));
812 }
813
814 public:
815 //------------------------------------------------------------------------------------------
816
857 value_type try_insert_k(std::vector<long> i, std::vector<long> j, std::vector<x_type> x, std::vector<y_type> y) {
858 TRIQS_ASSERT(last_try == NoTry);
859 TRIQS_ASSERT(i.size() == j.size());
860 TRIQS_ASSERT(j.size() == x.size());
861 TRIQS_ASSERT(x.size() == y.size());
862
863 k_tried = static_cast<long>(i.size());
864 reserve(N + k_tried, k_tried);
865 last_try = InsertK;
866
867 auto const argsort = [](auto const &vec) {
868 std::vector<long> idx(vec.size());
869 std::iota(idx.begin(), idx.end(), static_cast<long>(0));
870 std::stable_sort(idx.begin(), idx.end(), [&vec](long const lhs, long const rhs) { return vec[lhs] < vec[rhs]; });
871 return idx;
872 };
873 std::vector<long> idx = argsort(i);
874 std::vector<long> idy = argsort(j);
875
876 // store it for complete_operation
877 for (long l = 0; l < k_tried; ++l) {
878 wk.i[l] = i[idx[l]];
879 wk.x[l] = x[idx[l]];
880 wk.j[l] = j[idy[l]];
881 wk.y[l] = y[idy[l]];
882 };
883
884 // check consistency
885 for (int l = 0; l < k_tried - 1; ++l) {
886 TRIQS_ASSERT(wk.i[l] != wk.i[l + 1] and 0 <= wk.i[l] and wk.i[l] < N + k_tried);
887 TRIQS_ASSERT(wk.j[l] != wk.j[l + 1] and 0 <= wk.j[l] and wk.j[l] < N + k_tried);
888 }
889
890 // w1.ksi = Delta(x_values,y_values) - Cw.MB using BLAS
891 for (long m = 0; m < k_tried; ++m) {
892 for (long n = 0; n < k_tried; ++n) { wk.ksi(m, n) = f(wk.x[m], wk.y[n]); }
893 }
894
895 // treat empty matrix separately
896 if (N == 0) {
897 newdet = wk.det_ksi(k_tried);
898 newsign = 1;
899 return value_type(newdet);
900 }
901
902 // I add the rows and cols and the end. If the move is rejected,
903 // no effect since N will not be changed : inv_mat(i,j) for i,j>=N has no meaning.
904 for (long n = 0; n < N; n++) {
905 for (long l = 0; l < k_tried; ++l) {
906 wk.B(n, l) = f(x_values[n], wk.y[l]);
907 wk.C(l, n) = f(wk.x[l], y_values[n]);
908 }
909 }
910 range RN(N), Rk(k_tried);
911 //wk.MB(RN,Rk) = mat_inv(RN,N) * wk.B(RN,Rk); // OPTIMIZE BELOW
912 blas::gemm(1.0, mat_inv(RN, RN), wk.B(RN, Rk), 0.0, wk.MB(RN, Rk));
913 //ksi -= wk.C (Rk, RN) * wk.MB(RN, Rk); // OPTIMIZE BELOW
914 blas::gemm(-1.0, wk.C(Rk, RN), wk.MB(RN, Rk), 1.0, wk.ksi(Rk, Rk));
915 auto ksi = wk.det_ksi(k_tried);
916 newdet = det * ksi;
917 long idx_sum = 0;
918 for (long l = 0; l < k_tried; ++l) { idx_sum += wk.i[l] + wk.j[l]; }
919 newsign = (idx_sum % 2 == 0 ? sign : -sign); // since N-i0 + N-j0 + N + 1 -i1 + N+1 -j1 = i0+j0 [2]
920 return ksi * (newsign * sign); // sign is unity, hence 1/sign == sign
921 }
922
943 value_type try_insert2(long i0, long i1, long j0, long j1, x_type const &x0, x_type const &x1, y_type const &y0, y_type const &y1) {
944 return try_insert_k({i0, i1}, {j0, j1}, {x0, x1}, {y0, y1});
945 }
946
947 // ---- Helper: flatten an nda::Array to a std::vector in C-order ----
948 private:
949 template <nda::Array A> static auto flatten_array(A const &a) {
950 auto v = std::vector<typename A::value_type>(a.size());
951 long flat = 0;
952 nda::for_each(a.shape(), [&](auto... idx) { v[flat++] = a(idx...); });
953 return v;
954 }
955
956 public:
973 template <nda::Array X, nda::Array Y>
974 requires(nda::get_rank<X> == nda::get_rank<Y>)
975 auto insert_ratios(long i, long j, X const &xs, Y const &ys) const -> nda::array<value_type, nda::get_rank<X>> {
976 constexpr int R = nda::get_rank<X>;
977 TRIQS_ASSERT(xs.shape() == ys.shape());
978 TRIQS_ASSERT(0 <= i and i <= N);
979 TRIQS_ASSERT(0 <= j and j <= N);
980
981 long nbatch = xs.size();
982 value_type sign_fac = ((i + j) % 2 == 0 ? 1 : -1);
983 nda::array<value_type, R> result(xs.shape());
984
985 if (nbatch == 0) return result;
986
987 // Flatten inputs for BLAS
988 auto xs_flat = flatten_array(xs);
989 auto ys_flat = flatten_array(ys);
990
991 if (N == 0) {
992 for (long m = 0; m < nbatch; ++m) result.data()[m] = sign_fac * f(xs_flat[m], ys_flat[m]);
993 return result;
994 }
995
996 range RN(N);
997
998 // Build B(N, nbatch) and C(nbatch, N) matrices
999 nda::matrix<value_type> B(N, nbatch), C(nbatch, N), MB(N, nbatch);
1000 for (long l = 0; l < N; ++l)
1001 for (long m = 0; m < nbatch; ++m) B(l, m) = f(x_values[l], ys_flat[m]);
1002 for (long m = 0; m < nbatch; ++m)
1003 for (long l = 0; l < N; ++l) C(m, l) = f(xs_flat[m], y_values[l]);
1004
1005 // MB = mat_inv * B -- single BLAS3 gemm
1006 blas::gemm(1.0, mat_inv(RN, RN), B, 0.0, MB);
1007
1008 // Compute each ratio: ksi_m = f(xs[m], ys[m]) - C[m,:] . MB[:,m]
1009 for (long m = 0; m < nbatch; ++m) {
1010 value_type dot = 0;
1011 for (long l = 0; l < N; ++l) dot += C(m, l) * MB(l, m);
1012 result.data()[m] = sign_fac * (f(xs_flat[m], ys_flat[m]) - dot);
1013 }
1014
1015 return result;
1016 }
1017
1018 //------------------------------------------------------------------------------------------
1019 private:
1020 // Complete the insert_k operation.
1021 void complete_insert_k() {
1022
1023 // store the new value of x,y. They are seen through the same permutations as rows and cols resp.
1024 for (int l = 0; l < k_tried; ++l) {
1025 x_values.push_back(wk.x[l]);
1026 y_values.push_back(wk.y[l]);
1027 row_num.push_back(0);
1028 col_num.push_back(0);
1029 }
1030
1031 range Rk(0, k_tried);
1032 // treat empty matrix separately
1033 if (N == 0) {
1034 N = k_tried;
1035 mat_inv(Rk, Rk) = nda::linalg::inv(wk.ksi(Rk, Rk));
1036 for (long l = 0; l < k_tried; ++l) {
1037 row_num[wk.i[l]] = l;
1038 col_num[wk.j[l]] = l;
1039 }
1040 return;
1041 }
1042
1043 range RN(N);
1044 //wk.MC(Rk,RN) = wk.C(Rk,RN) * mat_inv(RN,RN);// OPTIMIZE BELOW
1045 blas::gemm(1.0, wk.C(Rk, RN), mat_inv(RN, RN), 0.0, wk.MC(Rk, RN));
1046 wk.MC(Rk, range(N, N + k_tried)) = -1; // -identity matrix
1047 wk.MB(range(N, N + k_tried), Rk) = -1; // -identity matrix !
1048
1049 // keep the real position of the row/col
1050 // since we insert a col/row, we have first to push the col at the right
1051 // and then say that col wk.i[0] is stored in N, the last col.
1052 // same for rows
1053 for (int l = 0; l < k_tried; ++l) {
1054 N++;
1055 for (long i = N - 2; i >= wk.i[l]; i--) row_num[i + 1] = row_num[i];
1056 row_num[wk.i[l]] = N - 1;
1057 for (long i = N - 2; i >= wk.j[l]; i--) col_num[i + 1] = col_num[i];
1058 col_num[wk.j[l]] = N - 1;
1059 }
1060 RN = range(N);
1061
1062 wk.ksi(Rk, Rk) = nda::linalg::inv(wk.ksi(Rk, Rk));
1063 mat_inv(RN, range(N - k_tried, N)) = 0;
1064 mat_inv(range(N - k_tried, N), RN) = 0;
1065 //mat_inv(RN,RN) += wk.MB(RN,Rk) * (wk.ksi(Rk, Rk) * wk.MC(Rk,RN)); // OPTIMIZE BELOW
1066 blas::gemm(1.0, wk.MB(RN, Rk), (wk.ksi(Rk, Rk) * wk.MC(Rk, RN)), 1.0, mat_inv(RN, RN));
1067 }
1068 // Complete the insert2 operation.
1069 void complete_insert2() { complete_insert_k(); }
1070
1071 public:
1072 //------------------------------------------------------------------------------------------
1073
1088 value_type try_remove(long i, long j) {
1089 TRIQS_ASSERT(last_try == NoTry);
1090 TRIQS_ASSERT(0 <= i and i < N);
1091 TRIQS_ASSERT(0 <= j and j < N);
1092 w1.i = i;
1093 w1.j = j;
1094 last_try = Remove;
1095 w1.jreal = col_num[w1.j];
1096 w1.ireal = row_num[w1.i];
1097 // compute the newdet
1098 // first we resolve the w1.ireal,w1.jreal, with the permutation of the Minv, then we pick up what
1099 // will become the 'corner' coefficient, if the move is accepted, after the exchange of row and col.
1100 w1.ksi = mat_inv(w1.jreal, w1.ireal);
1101 auto ksi = w1.ksi;
1102 newdet = det * ksi;
1103 newsign = ((i + j) % 2 == 0 ? sign : -sign);
1104 return ksi * (newsign * sign); // sign is unity, hence 1/sign == sign
1105 }
1106 //------------------------------------------------------------------------------------------
1107 private:
1108 // Complete the remove operation.
1109 void complete_remove() {
1110 if (N == 1) {
1111 clear();
1112 return;
1113 }
1114
1115 // Move rows and cols to be removed to the end.
1116 // Adjust the x_values and y_values vector accordingly and
1117 // swap the associated row_num and col_num elements
1118 // Remember that for M row/col is interchanged by inversion, transposition.
1119 range RN(N);
1120 if (w1.ireal != N - 1) {
1121 deep_swap(mat_inv(RN, w1.ireal), mat_inv(RN, N - 1));
1122 x_values[w1.ireal] = x_values[N - 1];
1123 auto iitr = std::ranges::find(row_num, w1.ireal);
1124 auto titr = std::ranges::find(row_num, N - 1);
1125 std::swap(*iitr, *titr);
1126 }
1127 if (w1.jreal != N - 1) {
1128 deep_swap(mat_inv(w1.jreal, RN), mat_inv(N - 1, RN));
1129 y_values[w1.jreal] = y_values[N - 1];
1130 auto jitr = std::ranges::find(col_num, w1.jreal);
1131 auto titr = std::ranges::find(col_num, N - 1);
1132 std::swap(*jitr, *titr);
1133 }
1134 N--;
1135 RN = range(N);
1136
1137 auto it1 [[maybe_unused]] = std::ranges::remove(row_num, N);
1138 auto it2 [[maybe_unused]] = std::ranges::remove(col_num, N);
1139
1140 row_num.pop_back();
1141 col_num.pop_back();
1142 x_values.pop_back();
1143 y_values.pop_back();
1144
1145 // M <- a - d^-1 b c with BLAS
1146 w1.ksi = -1 / mat_inv(N, N);
1147 ASSERT(std::isfinite(std::abs(w1.ksi)));
1148
1149 //mat_inv(RN,RN) += w1.ksi, * mat_inv(RN,N) * mat_inv(N,RN);
1150 blas::ger(w1.ksi, mat_inv(RN, N), mat_inv(N, RN), mat_inv(RN, RN));
1151 }
1152
1153 public:
1154 //------------------------------------------------------------------------------------------
1155
1223 value_type try_remove_k(std::vector<long> i, std::vector<long> j) {
1224
1225 std::ranges::sort(i);
1226 std::ranges::sort(j);
1227
1228 TRIQS_ASSERT(last_try == NoTry);
1229 TRIQS_ASSERT(N >= 2);
1230 TRIQS_ASSERT(i.size() == j.size());
1231
1232 k_tried = static_cast<long>(i.size());
1233 reserve(N - k_tried, k_tried);
1234 last_try = RemoveK;
1235
1236 // check inputs
1237 for (int l = 0; l < k_tried - 1; ++l) {
1238 TRIQS_ASSERT(i[l] != i[l + 1] and 0 <= i[l] and i[l] < N);
1239 TRIQS_ASSERT(j[l] != j[l + 1] and 0 <= j[l] and j[l] < N);
1240 }
1241
1242 for (long l = 0; l < k_tried; ++l) {
1243 wk.i[l] = i[l];
1244 wk.j[l] = j[l];
1245 wk.ireal[l] = row_num[wk.i[l]];
1246 wk.jreal[l] = col_num[wk.j[l]];
1247 }
1248
1249 // compute the newdet
1250 for (long l1 = 0; l1 < k_tried; ++l1) {
1251 for (long l2 = 0; l2 < k_tried; ++l2) { wk.ksi(l1, l2) = mat_inv(wk.jreal[l1], wk.ireal[l2]); }
1252 }
1253 auto det_ksi = wk.det_ksi(k_tried);
1254 newdet = det * det_ksi;
1255 long idx_sum = 0;
1256 for (long l = 0; l < k_tried; ++l) { idx_sum += wk.i[l] + wk.j[l]; }
1257 newsign = (idx_sum % 2 == 0 ? sign : -sign);
1258
1259 return det_ksi * (newsign * sign); // sign is unity, hence 1/sign == sign
1260 }
1261
1277 value_type try_remove2(long i0, long i1, long j0, long j1) { return try_remove_k({i0, i1}, {j0, j1}); }
1278 //------------------------------------------------------------------------------------------
1279 private:
1280 // Complete the remove_k operation.
1281 void complete_remove_k() {
1282 if (N == k_tried) {
1283 clear();
1284 return;
1285 } // put the sign to 1 also .... Change complete_remove...
1286
1287 std::vector<long> ireal = wk.ireal;
1288 std::vector<long> jreal = wk.jreal;
1289 std::sort(ireal.begin(), ireal.begin() + k_tried);
1290 std::sort(jreal.begin(), jreal.begin() + k_tried);
1291
1292 // Move rows and cols to be removed to the end, starting from the right.
1293 // Adjust the x_values and y_values vector accordingly and
1294 // swap the associated row_num and col_num elements
1295 // Remember that for M row/col is interchanged by inversion, transposition.
1296 range RN(N);
1297 for (long m = k_tried - 1, target = N - 1; m >= 0; --m, --target) {
1298 if (ireal[m] != target) {
1299 deep_swap(mat_inv(RN, ireal[m]), mat_inv(RN, target));
1300 x_values[ireal[m]] = x_values[target];
1301 auto iitr = std::ranges::find(row_num, ireal[m]);
1302 auto titr = std::ranges::find(row_num, target);
1303 std::swap(*iitr, *titr);
1304 }
1305 if (jreal[m] != target) {
1306 deep_swap(mat_inv(jreal[m], RN), mat_inv(target, RN));
1307 y_values[jreal[m]] = y_values[target];
1308 auto jitr = std::ranges::find(col_num, jreal[m]);
1309 auto titr = std::ranges::find(col_num, target);
1310 std::swap(*jitr, *titr);
1311 }
1312 }
1313 N -= k_tried;
1314 RN = range(N);
1315
1316 // Clean up removed elements from row_num and col_num
1317 auto gtN = [&](auto i) { return i >= N; };
1318
1319 auto it1 [[maybe_unused]] = std::remove_if(row_num.begin(), row_num.end(), gtN);
1320 auto it2 [[maybe_unused]] = std::remove_if(col_num.begin(), col_num.end(), gtN);
1321
1322 row_num.resize(N);
1323 col_num.resize(N);
1324 x_values.resize(N);
1325 y_values.resize(N);
1326
1327 // M <- a - d^-1 b c with BLAS
1328 range Rl(N, N + k_tried), Rk(k_tried);
1329 wk.ksi(Rk, Rk) = nda::linalg::inv(mat_inv(Rl, Rl));
1330
1331 // write explicitely the second product on ksi for speed ?
1332 //mat_inv(RN,RN) -= mat_inv(RN,Rl) * (wk.ksi * mat_inv(Rl,RN)); // OPTIMIZE BELOW
1333 blas::gemm(-1.0, mat_inv(RN, Rl), wk.ksi(Rk, Rk) * mat_inv(Rl, RN), 1.0, mat_inv(RN, RN));
1334 }
1335 // Complete the remove2 operation.
1336 void complete_remove2() { complete_remove_k(); }
1337
1338 //------------------------------------------------------------------------------------------
1339 public:
1375 value_type try_change_col(long j, y_type const &y) {
1376 TRIQS_ASSERT(last_try == NoTry);
1377 TRIQS_ASSERT(0 <= j and j < N);
1378 w1.j = j;
1379 last_try = ChangeCol;
1380 w1.jreal = col_num[j];
1381 w1.y = y;
1382
1383 // Compute the col B.
1384 for (long i = 0; i < N; i++) w1.MC(i) = f(x_values[i], w1.y) - f(x_values[i], y_values[w1.jreal]);
1385 range RN(N);
1386 //w1.MB(R) = mat_inv(R,R) * w1.MC(R);// OPTIMIZE BELOW
1387 blas::gemv(1.0, mat_inv(RN, RN), w1.MC(RN), 0.0, w1.MB(RN));
1388
1389 // compute the newdet
1390 w1.ksi = (1 + w1.MB(w1.jreal));
1391 auto ksi = w1.ksi;
1392 newdet = det * ksi;
1393 newsign = sign;
1394
1395 return ksi; // newsign/sign is unity
1396 }
1397 //------------------------------------------------------------------------------------------
1398 private:
1399 // Complete the change column operation.
1400 void complete_change_col() {
1401 range RN(N);
1402 y_values[w1.jreal] = w1.y;
1403
1404 // modifying M : Mij += w1.ksi Bi Mnj
1405 // using Shermann Morrison formula.
1406 // implemented in 2 times : first Bn=0 so that Mnj is not modified ! and then change Mnj
1407 // Cf notes : simply multiply by -w1.ksi
1408 w1.ksi = -1 / w1.ksi;
1409 w1.MB(w1.jreal) = 0;
1410 //mat_inv(R,R) += w1.ksi * w1.MB(R) * mat_inv(w1.jreal,R)); // OPTIMIZE BELOW
1411 blas::ger(w1.ksi, w1.MB(RN), mat_inv(w1.jreal, RN), mat_inv(RN, RN));
1412 mat_inv(w1.jreal, RN) *= -w1.ksi;
1413 }
1414
1415 //------------------------------------------------------------------------------------------
1416 public:
1432 value_type try_change_row(long i, x_type const &x) {
1433 TRIQS_ASSERT(last_try == NoTry);
1434 TRIQS_ASSERT(i < N);
1435 w1.i = i;
1436 last_try = ChangeRow;
1437 w1.ireal = row_num[i];
1438 w1.x = x;
1439
1440 // Compute the col B.
1441 for (long idx = 0; idx < N; idx++) w1.MB(idx) = f(w1.x, y_values[idx]) - f(x_values[w1.ireal], y_values[idx]);
1442 range RN(N);
1443 //w1.MC(R) = transpose(mat_inv(R,R)) * w1.MB(R); // OPTIMIZE BELOW
1444 blas::gemv(1.0, transpose(mat_inv(RN, RN)), w1.MB(RN), 0.0, w1.MC(RN));
1445
1446 // compute the newdet
1447 w1.ksi = (1 + w1.MC(w1.ireal));
1448 auto ksi = w1.ksi;
1449 newdet = det * ksi;
1450 newsign = sign;
1451 return ksi; // newsign/sign is unity
1452 }
1453 //------------------------------------------------------------------------------------------
1454 private:
1455 // Complete the change row operation.
1456 void complete_change_row() {
1457 range RN(N);
1458 x_values[w1.ireal] = w1.x;
1459
1460 // modifying M : M ij += w1.ksi Min Cj
1461 // using Shermann Morrison formula.
1462 // impl. Cf case 3
1463 w1.ksi = -1 / w1.ksi;
1464 w1.MC(w1.ireal) = 0;
1465 //mat_inv(R,R) += w1.ksi * mat_inv(R,w1.ireal) * w1.MC(R);
1466 blas::ger(w1.ksi, mat_inv(RN, w1.ireal), w1.MC(RN), mat_inv(RN, RN));
1467 mat_inv(RN, w1.ireal) *= -w1.ksi;
1468 }
1469
1470 //------------------------------------------------------------------------------------------
1471 public:
1531 value_type try_change_col_row(long i, long j, x_type const &x, y_type const &y) {
1532 TRIQS_ASSERT(last_try == NoTry);
1533 TRIQS_ASSERT(0 <= i and i < N);
1534 TRIQS_ASSERT(0 <= j and j < N);
1535
1536 last_try = ChangeRowCol;
1537 w1.i = i;
1538 w1.j = j;
1539 w1.ireal = row_num[i];
1540 w1.jreal = col_num[j];
1541 w1.x = x;
1542 w1.y = y;
1543
1544 // Compute the col B.
1545 for (long idx = 0; idx < N; idx++) { // MC : delta_x, MB : delta_y
1546 w1.MC(idx) = f(x_values[idx], y) - f(x_values[idx], y_values[w1.jreal]);
1547 w1.MB(idx) = f(x, y_values[idx]) - f(x_values[w1.ireal], y_values[idx]);
1548 }
1549 w1.MC(w1.ireal) = f(x, y) - f(x_values[w1.ireal], y_values[w1.jreal]);
1550 w1.MB(w1.jreal) = 0;
1551
1552 range RN(N);
1553 // C : X, B : Y
1554 //w1.C(R) = mat_inv(R,R) * w1.MC(R);// OPTIMIZE BELOW
1555 blas::gemv(1.0, mat_inv(RN, RN), w1.MC(RN), 0.0, w1.C(RN));
1556 //w1.B(R) = transpose(mat_inv(R,R)) * w1.MB(R); // OPTIMIZE BELOW
1557 blas::gemv(1.0, transpose(mat_inv(RN, RN)), w1.MB(RN), 0.0, w1.B(RN));
1558
1559 // compute the det_ratio
1560 auto Xn = w1.C(w1.jreal);
1561 auto Yn = w1.B(w1.ireal);
1562 auto Z = nda::blas::dot(w1.MB(RN), w1.C(RN));
1563 auto Mnn = mat_inv(w1.jreal, w1.ireal);
1564 auto det_ratio = (1 + Xn) * (1 + Yn) - Mnn * Z;
1565 w1.ksi = det_ratio;
1566 newdet = det * det_ratio;
1567 newsign = sign;
1568 return det_ratio; // newsign/sign is unity
1569 }
1570 //------------------------------------------------------------------------------------------
1571 private:
1572 // Complete the change row and column operation.
1573 void complete_change_col_row() {
1574 range RN(N);
1575 x_values[w1.ireal] = w1.x;
1576 y_values[w1.jreal] = w1.y;
1577
1578 // FIXME : Use blas for this ? Is it better
1579 auto Xn = w1.C(w1.jreal);
1580 auto Yn = w1.B(w1.ireal);
1581 auto Mnn = mat_inv(w1.jreal, w1.ireal);
1582
1583 auto D = w1.ksi; // get back
1584 auto a = -(1 + Yn) / D; // D in the notes
1585 auto b = -(1 + Xn) / D;
1586 auto Z = nda::blas::dot(w1.MB(RN), w1.C(RN));
1587 Z = Z / D;
1588 Mnn = Mnn / D;
1589 w1.MB(RN) = mat_inv(w1.jreal, RN); // Mnj
1590 w1.MC(RN) = mat_inv(RN, w1.ireal); // Min
1591
1592 for (long i = 0; i < N; ++i)
1593 for (long j = 0; j < N; ++j) {
1594 auto Xi = w1.C(i);
1595 auto Yj = w1.B(j);
1596 auto Mnj = w1.MB(j);
1597 auto Min = w1.MC(i);
1598 mat_inv(i, j) += a * Xi * Mnj + b * Min * Yj + Mnn * Xi * Yj + Z * Min * Mnj;
1599 }
1600 }
1601
1602 //------------------------------------------------------------------------------------------
1603 public:
1624 template <typename ArgumentContainer1, typename ArgumentContainer2>
1625 value_type try_refill(ArgumentContainer1 const &X, ArgumentContainer2 const &Y) {
1626 TRIQS_ASSERT(last_try == NoTry);
1627 TRIQS_ASSERT(X.size() == Y.size());
1628
1629 last_try = Refill;
1630
1631 long s = X.size();
1632 // treat empty matrix separately
1633 if (s == 0) {
1634 w_refill.x_values.clear();
1635 w_refill.y_values.clear();
1636 return 1 / (sign * det);
1637 }
1638
1639 w_refill.reserve(s);
1640 w_refill.x_values.clear();
1641 w_refill.y_values.clear();
1642 std::copy(X.begin(), X.end(), std::back_inserter(w_refill.x_values));
1643 std::copy(Y.begin(), Y.end(), std::back_inserter(w_refill.y_values));
1644
1645 for (long i = 0; i < s; ++i)
1646 for (long j = 0; j < s; ++j) w_refill.M(i, j) = f(w_refill.x_values[i], w_refill.y_values[j]);
1647 range R(s);
1648 newdet = nda::linalg::det(w_refill.M(R, R));
1649 newsign = 1;
1650
1651 return newdet / (sign * det);
1652 }
1653
1654 //------------------------------------------------------------------------------------------
1655 private:
1656 // Complete the refill operation.
1657 void complete_refill() {
1658 N = w_refill.x_values.size();
1659
1660 // special empty case again
1661 if (N == 0) {
1662 clear();
1663 newdet = 1;
1664 newsign = 1;
1665 return;
1666 }
1667
1668 reserve(N);
1669 std::swap(x_values, w_refill.x_values);
1670 std::swap(y_values, w_refill.y_values);
1671
1672 row_num.resize(N, 0); // Zero Initialization avoids ASAN false positive
1673 col_num.resize(N, 0);
1674 std::iota(row_num.begin(), row_num.end(), 0);
1675 std::iota(col_num.begin(), col_num.end(), 0);
1676
1677 range RN(N);
1678 mat_inv(RN, RN) = nda::linalg::inv(w_refill.M(RN, RN));
1679 }
1680
1681 //------------------------------------------------------------------------------------------
1682 private:
1683 // Regenerate the inverse matrix, determinant and sign from the matrix builder, optionally checking the freshly
1684 // computed values against the stored ones.
1685 void _regenerate_with_check(bool do_check, double prec_warning, double prec_error) {
1686 if (N == 0) {
1687 det = 1;
1688 sign = 1;
1689 return;
1690 }
1691
1692 range RN(N);
1693 matrix_type res(N, N);
1694 for (int i = 0; i < N; i++)
1695 for (int j = 0; j < N; j++) res(i, j) = f(x_values[i], y_values[j]);
1696 det = nda::linalg::det(res);
1697
1698 if (is_singular()) TRIQS_RUNTIME_ERROR << "ERROR in det_manip regenerate: Determinant is singular";
1699 res = nda::linalg::inv(res);
1700
1701 if (do_check) { // check that mat_inv is close to res
1702 const bool relative = true;
1703 double r = max_element(abs(res - mat_inv(RN, RN)));
1704 double r2 = max_element(abs(res + mat_inv(RN, RN)));
1705 bool err = !(r < (relative ? prec_error * r2 : prec_error));
1706 bool war = !(r < (relative ? prec_warning * r2 : prec_warning));
1707 if (err || war) {
1708 std::cerr << "matrix = " << matrix() << std::endl;
1709 std::cerr << "inverse_matrix = " << inverse_matrix() << std::endl;
1710 }
1711 if (war)
1712 std::cerr << "Warning : det_manip deviation above warning threshold "
1713 << "check "
1714 << "N = " << N << " "
1715 << "\n max(abs(M^-1 - M^-1_true)) = " << r
1716 << "\n precision*max(abs(M^-1 + M^-1_true)) = " << (relative ? prec_warning * r2 : prec_warning) << " " << std::endl;
1717 if (err) TRIQS_RUNTIME_ERROR << "Error : det_manip deviation above critical threshold !! ";
1718 }
1719
1720 // since we have the proper inverse, replace the matrix and the det
1721 mat_inv(RN, RN) = res;
1722 n_opts = 0;
1723
1724 // find the sign (there must be a better way...)
1725 double s = 1.0;
1726 nda::matrix<double> m(N, N);
1727 m() = 0.0;
1728 for (int i = 0; i < N; i++) m(i, row_num[i]) = 1;
1729 s *= nda::linalg::det(m);
1730 m() = 0.0;
1731 for (int i = 0; i < N; i++) m(i, col_num[i]) = 1;
1732 s *= nda::linalg::det(m);
1733 sign = (s > 0 ? 1 : -1);
1734 }
1735
1736 // Regenerate and check the consistency of the stored inverse matrix, determinant and sign.
1737 void check_mat_inv() { _regenerate_with_check(true, precision_warning, precision_error); }
1738
1739 // Check whether the determinant is considered singular: (singular_threshold < 0 ? not
1740 // std::isnormal(std::abs(det)) : (std::abs(det) < singular_threshold)). See set_singular_threshold().
1741 [[nodiscard]] bool is_singular() const {
1742 return (singular_threshold < 0 ? not std::isnormal(std::abs(det)) : (std::abs(det) < singular_threshold));
1743 }
1744
1745 //------------------------------------------------------------------------------------------
1746 public:
1760 void regenerate() { _regenerate_with_check(false, 0, 0); }
1761
1762 public:
1777 switch (last_try) {
1778 case (Insert): complete_insert(); break;
1779 case (Remove): complete_remove(); break;
1780 case (ChangeCol): complete_change_col(); break;
1781 case (ChangeRow): complete_change_row(); break;
1782 case (ChangeRowCol): complete_change_col_row(); break;
1783 case (InsertK): complete_insert_k(); break;
1784 case (RemoveK): complete_remove_k(); break;
1785 case (Refill): complete_refill(); break;
1786 case (NoTry): return; break;
1787 default: TRIQS_RUNTIME_ERROR << "Misuing det_manip"; // Never used?
1788 }
1789
1790 det = newdet;
1791 sign = newsign;
1792 ++n_opts;
1793 if (n_opts > n_opts_max_before_check) check_mat_inv();
1794 last_try = NoTry;
1795 }
1796
1801 void reject_last_try() { last_try = NoTry; }
1802
1803 // ----------------- A few short cuts -----------------
1804
1805 public:
1815 value_type insert(long i, long j, x_type const &x, y_type const &y) {
1816 auto r = try_insert(i, j, x, y);
1818 return r;
1819 }
1820
1828 value_type insert_at_end(x_type const &x, y_type const &y) { return insert(N, N, x, y); }
1829
1839 value_type insert2(long i0, long i1, long j0, long j1, x_type const &x0, x_type const &x1, y_type const &y0, y_type const &y1) {
1840 auto r = try_insert2(i0, i1, j0, j1, x0, x1, y0, y1);
1842 return r;
1843 }
1844
1852 value_type insert2_at_end(x_type const &x0, x_type const &x1, y_type const &y0, y_type const &y1) {
1853 return insert2(N, N + 1, N, N + 1, x0, x1, y0, y1);
1854 }
1855
1863 value_type remove(long i, long j) {
1864 auto r = try_remove(i, j);
1866 return r;
1867 }
1868
1874 value_type remove_at_end() { return remove(N - 1, N - 1); }
1875
1883 value_type remove2(long i0, long i1, long j0, long j1) {
1884 auto r = try_remove2(i0, i1, j0, j1);
1886 return r;
1887 }
1888
1894 value_type remove2_at_end() { return remove2(N - 1, N - 2, N - 1, N - 2); }
1895
1903 value_type change_col(long j, y_type const &y) {
1904 auto r = try_change_col(j, y);
1906 return r;
1907 }
1908
1916 value_type change_row(long i, x_type const &x) {
1917 auto r = try_change_row(i, x);
1919 return r;
1920 }
1921
1932 value_type change_one_row_and_one_col(long i, long j, x_type const &x, y_type const &y) {
1933 auto r = try_change_col_row(i, j, x, y);
1935 return r;
1936 }
1937
1950 enum RollDirection { None, Up, Down, Left, Right };
1951
1965 long tmp = 0;
1966 const long NN = N;
1967 switch (roll) {
1968 case (None): return 1;
1969 case (Down):
1970 tmp = row_num[N - 1];
1971 for (long i = NN - 2; i >= 0; i--) row_num[i + 1] = row_num[i];
1972 row_num[0] = tmp;
1973 break;
1974 case (Up):
1975 tmp = row_num[0];
1976 for (long i = 0; i < N - 1; i++) row_num[i] = row_num[i + 1];
1977 row_num[N - 1] = tmp;
1978 break;
1979 case (Right):
1980 tmp = col_num[N - 1];
1981 for (long i = NN - 2; i >= 0; i--) col_num[i + 1] = col_num[i];
1982 col_num[0] = tmp;
1983 break;
1984 case (Left):
1985 tmp = col_num[0];
1986 for (long i = 0; i < N - 1; i++) col_num[i] = col_num[i + 1];
1987 col_num[N - 1] = tmp;
1988 break;
1989 default: assert(0);
1990 }
1991 // signature of the cycle of order N : (-1)^(N-1)
1992 if ((N - 1) % 2 == 1) {
1993 sign *= -1;
1994 return -1;
1995 }
1996 return 1;
1997 }
1998 };
1999} // namespace triqs::det_manip
Backward-compatibility umbrella header pulling in the nda array library.
Provides a trait inspecting the operator() of a callable type.
Manipulate determinants and ratios of determinants for CTQMC solvers.
void reserve(long new_N, long new_k=1)
Reserve memory and resize the data storages.
std::vector< y_type > const & get_y_internal_order() const
Get the matrix builder arguments in the order of the matrix .
void complete_operation()
Complete the last try-operation.
friend void h5_write(h5::group fg, std::string subgroup_name, det_manip const &g)
Write a triqs::det_manip::det_manip object to HDF5.
nda::matrix_const_view< value_type > inverse_matrix_internal_order() const
Get the full inverse matrix .
void set_precision_error(double threshold)
Set the precision threshold that determines when to throw an exception (default: 1e-5).
value_type try_change_row(long i, x_type const &x)
Try to change one row in the original matrix .
void set_precision_warning(double threshold)
Set the precision threshold that determines when to print a warning (default: 1e-8).
void reject_last_try()
Reject the last try-operation.
void regenerate()
Regenerate the inverse matrix , the determinant and the sign from scratch using the matrix builder.
value_type try_insert_from_function(long i, long j, Fx fx, Fy fy, value_type const ksi)
Try to insert one row and column, providing the new elements through callables instead of the matrix ...
friend void h5_read(h5::group fg, std::string subgroup_name, det_manip &g)
Read a triqs::det_manip::det_manip object from HDF5.
std::vector< x_type > get_x() const
Get a vector with all matrix builder arguments .
value_type remove(long i, long j)
Remove one row and column.
long size() const
Get the current size of the matrix.
FunctionType const & get_function() const
Get the callable FunctionType object used as the matrix builder.
value_type try_change_col_row(long i, long j, x_type const &x, y_type const &y)
Try to change one column and one row in the original matrix .
det_type determinant()
Get the determinant of the original matrix .
value_type remove2(long i0, long i1, long j0, long j1)
Remove two rows and columns.
value_type try_insert_k(std::vector< long > i, std::vector< long > j, std::vector< x_type > x, std::vector< y_type > y)
Try to insert rows and columns.
value_type inverse_matrix(int i, int j) const
Get an element of the inverse matrix.
value_type change_one_row_and_one_col(long i, long j, x_type const &x, y_type const &y)
Change one row and one column.
value_type try_refill(ArgumentContainer1 const &X, ArgumentContainer2 const &Y)
Try to fill the original matrix with new elements.
value_type try_remove(long i, long j)
Try to remove one row and column.
void swap_row(long i, long j)
Swap two rows.
RollDirection
Direction of the roll_matrix() operation.
value_type insert_at_end(x_type const &x, y_type const &y)
Insert one row and column at the end of the matrix.
matrix_type inverse_matrix() const
Get the full inverse matrix .
value_type try_remove_k(std::vector< long > i, std::vector< long > j)
Try to remove rows and columns.
value_type try_remove2(long i0, long i1, long j0, long j1)
Try to remove two rows and two columns.
value_type remove_at_end()
Remove the last row and column of the matrix.
void set_n_operations_before_check(uint64_t n)
Set the number of operations before a consistency check is performed (default: 100).
double get_precision_error() const
Get the precision threshold that determines when to throw an exception.
matrix_type matrix() const
Get the original matrix .
double get_precision_warning() const
Get the precision threshold that determines when to print a warning.
void swap_col(long i, long j)
Swap two columns.
value_type try_insert2(long i0, long i1, long j0, long j1, x_type const &x0, x_type const &x1, y_type const &y0, y_type const &y1)
Try to insert two rows and columns.
value_type inverse_matrix_internal_order(int i, int j) const
Get an element of the matrix .
double get_singular_threshold() const
Get the threshold being used when testing for a singular matrix.
auto insert_ratios(long i, long j, X const &xs, Y const &ys) const -> nda::array< value_type, nda::get_rank< X > >
Compute independent single-insertion determinant ratios at position for paired elements of xs and ys...
value_type try_change_col(long j, y_type const &y)
Try to change one column in the original matrix .
x_type const & get_x(long i) const
Get the matrix builder argument that determines the elements of the ith row in the original matrix .
value_type change_col(long j, y_type const &y)
Change one column.
value_type insert(long i, long j, x_type const &x, y_type const &y)
Insert one row and column.
void clear()
Clear the data storages and reset the matrix to size zero.
std::vector< x_type > const & get_x_internal_order() const
Get the matrix builder arguments in the order of the matrix .
value_type insert2_at_end(x_type const &x0, x_type const &x1, y_type const &y0, y_type const &y1)
Insert two rows and columns at the end of the matrix.
value_type change_row(long i, x_type const &x)
Change one row.
det_manip(FunctionType F, ArgumentContainer1 const &X, ArgumentContainer2 const &Y)
Construct a det_manip object with a callable FunctionType and two containers holding the arguments fo...
value_type insert2(long i0, long i1, long j0, long j1, x_type const &x0, x_type const &x1, y_type const &y0, y_type const &y1)
Insert two rows and columns.
det_manip(FunctionType F, long init_size)
Construct a det_manip object with a callable FunctionType and an initial capacity for the data storag...
double get_n_operations_before_check() const
Get the number of operations before a consistency check is performed.
value_type try_insert(long i, long j, x_type const &x, y_type const &y)
Try to insert one row and column.
int roll_matrix(RollDirection roll)
Perform a circular shift permutation on the rows or columns of the matrix .
y_type const & get_y(long j) const
Get the matrix builder argument that determines the elements of the jth column in the original matri...
std::vector< y_type > get_y() const
Get a vector with all matrix builder arguments .
void set_singular_threshold(double threshold)
Set the threshold being used when testing for a singular matrix (default: -1).
value_type remove2_at_end()
Remove the last two rows and columns of the matrix.
Compiler / platform glue and the dcomplex alias (must be included before any Boost header).
#define TRIQS_RUNTIME_ERROR
Throw a triqs::runtime_error with the current source location.
#define TRIQS_ASSERT(X)
Throw a triqs::runtime_error if the boolean expression X evaluates to false.
Type trait for a callable type with a single, non-overloaded operator().