TRIQS/nda 1.3.0
Multi-dimensional array library for C++
Loading...
Searching...
No Matches
gelss_worker.hpp
1// Copyright (c) 2020-2024 Simons Foundation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0.txt
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15// Authors: Thomas Hahn, Jason Kaye, Olivier Parcollet, Nils Wentzell
16
17#pragma once
18
19#include "./gesvd.hpp"
20#include "../algorithms.hpp"
21#include "../basic_array.hpp"
22#include "../declarations.hpp"
23#include "../exceptions.hpp"
26#include "../linalg.hpp"
29
30#include <itertools/itertools.hpp>
31
32#include <algorithm>
33#include <array>
34#include <cmath>
35#include <complex>
36#include <optional>
37#include <utility>
38#include <vector>
39
40namespace nda::lapack {
41
63 template <typename T>
65 // Number of rows (M) and columns (N) of the Matrix A.
66 long M, N;
67
68 // FIXME Do we need to store it ? only use n_var
69 // Matrix to be decomposed by SVD.
70 matrix<T> A;
71
72 // (Pseudo) Inverse of A, i.e. V * Diag(S_vec)^{-1} * UH, for the least square problem.
73 matrix<T> V_x_InvS_x_UH;
74
75 // Part of UH fixing the error of the least square problem.
76 matrix<T> UH_NULL;
77
78 // Array containing the singular values.
79 array<double, 1> s_vec;
80
81 public:
86 int n_var() const { return A.extent(1); }
87
92 [[nodiscard]] array<double, 1> const &S_vec() const { return s_vec; }
93
103 gelss_worker(matrix<T> A_) : M(A_.extent(0)), N(A_.extent(1)), A(std::move(A_)), s_vec(std::min(M, N)) {
104 if (N > M) NDA_RUNTIME_ERROR << "Error in nda::lapack::gelss_worker: Matrix A cannot have more columns than rows";
105
106 // initialize matrices
107 matrix<T, F_layout> A_FL{A};
108 matrix<T, F_layout> U(M, M);
109 matrix<T, F_layout> VH(N, N);
110
111 // calculate the SVD: A = U * Diag(S_vec) * VH
112 gesvd(A_FL, s_vec, U, VH);
113
114 // calculate the matrix V * Diag(S_vec)^{-1} * UH for the least square procedure
115 matrix<double, F_layout> S_inv(N, M);
116 S_inv = 0.;
117 for (long i : range(std::min(M, N))) S_inv(i, i) = 1.0 / s_vec(i);
118 V_x_InvS_x_UH = dagger(VH) * S_inv * dagger(U);
119
120 // read off UH_Null for defining the error of the least square procedure
121 if (N < M) UH_NULL = dagger(U)(range(N, M), range(M));
122 }
123
130 std::pair<matrix<T>, double> operator()(matrix_const_view<T> B, std::optional<long> /* inner_matrix_dim */ = {}) const {
131 using std::sqrt;
132 double err = 0.0;
133 if (M != N) {
134 std::vector<double> err_vec;
135 for (long i : range(B.shape()[1])) err_vec.push_back(frobenius_norm(UH_NULL * B(range::all, range(i, i + 1))) / sqrt(B.shape()[0]));
136 err = *std::max_element(err_vec.begin(), err_vec.end());
137 }
138 return std::make_pair(V_x_InvS_x_UH * B, err);
139 }
140
147 std::pair<vector<T>, double> operator()(vector_const_view<T> b, std::optional<long> /*inner_matrix_dim*/ = {}) const {
148 using std::sqrt;
149 double err = 0.0;
150 if (M != N) { err = norm(UH_NULL * b) / sqrt(b.size()); }
151 return std::make_pair(V_x_InvS_x_UH * b, err);
152 }
153 };
154
162 private:
163 // Complex double type.
164 using dcomplex = std::complex<double>;
165
166 // Matrix to be decomposed by SVD.
168
169 // Solver for the associated real-valued least-squares problem.
171
172 // Solver for the associated real-valued least-squares problem imposing hermiticity.
173 gelss_worker<dcomplex> _lss_matrix;
174
175 public:
180 int n_var() const { return static_cast<int>(A.extent(1)); }
181
186 array<double, 1> const &S_vec() const { return _lss.S_vec(); }
187
192 gelss_worker_hermitian(matrix<dcomplex> A_) : A(std::move(A_)), _lss(A), _lss_matrix(vstack(A, conj(A))) {}
193
200 std::pair<matrix<dcomplex>, double> operator()(matrix_const_view<dcomplex> B, std::optional<long> inner_matrix_dim = {}) const {
201 if (not inner_matrix_dim.has_value())
202 NDA_RUNTIME_ERROR << "Error in nda::lapack::gelss_worker_hermitian: Inner matrix dimension required for hermitian least square fitting";
203 long d = *inner_matrix_dim;
204
205 // Construction of an inner 'adjoint' matrix by performing the following steps
206 // * reshape B from (M, M1) to (M, N, d, d)
207 // * for each M and N take the adjoint matrix (d, d)
208 // * reshape to (M, M)
209 auto inner_adjoint = [d](auto &M) {
210 auto idx_map = M.indexmap();
211 auto l = idx_map.lengths();
212 //auto s = idx_map.strides();
213
214 NDA_ASSERT2(l[1] % (d * d) == 0, "Error in nda::lapack::gelss_worker_hermitian: Data shape incompatible with given dimension");
215 long N = l[1] / (d * d);
216
217 // We reshape the Matrix into a dim=4 array and swap the two innermost indices
218
219 // FIXME OLD CODE SURPRESS AFTER PORTING
220 // FIXME We would like to write: transpose(reshape(idx_map, {l[0], N, d, d}), {0, 1, 3, 2})
221 // auto idx_map_inner_transpose = array_view<dcomplex, 4>::layout_t{{l[0], N, d, d}, {s[0], d * d * s[1], s[1], d * s[1]}};
222 // Deep copy
223 //array<dcomplex, 4> arr_dag = conj(array_const_view<dcomplex, 4>{idx_map_inner_transpose, M.storage()});
224 //return matrix<dcomplex>{matrix<dcomplex>::layout_t{l, s}, std::move(arr_dag).storage()};
225
226 // FIXME C++20 remove encode
227 array<dcomplex, 4> arr_dag = conj(permuted_indices_view<encode(std::array{0, 1, 3, 2})>(reshape(M, std::array{l[0], N, d, d})));
228
229 return matrix<dcomplex>{reshape(std::move(arr_dag), l)}; // move into a matrix
230 };
231
232 // Solve the enlarged system vstack(A, A*) * x = vstack(B, B_dag)
233 matrix<dcomplex> B_dag = inner_adjoint(B);
234 auto B_stack = vstack(B, B_dag);
235 auto [x, err] = _lss_matrix(B_stack);
236
237 // Resymmetrize results to cure small hermiticity violations
238 return {0.5 * (x + inner_adjoint(x)), err};
239 }
240 };
241
244} // namespace nda::lapack
Provides various algorithms to be used with nda::Array objects.
Provides the generic class for arrays.
A generic view of a multi-dimensional array.
auto const & shape() const noexcept
Get the shape of the view/array.
long size() const noexcept
Get the total size of the view/array.
A generic multi-dimensional array.
long extent(int i) const noexcept
Get the extent of the ith dimension.
Layout that specifies how to map multi-dimensional indices to a linear/flat index.
Definition idx_map.hpp:103
std::array< long, Rank > const & lengths() const noexcept
Get the extents of all dimensions.
Definition idx_map.hpp:178
Worker class for solving linear least square problems.
array< double, 1 > const & S_vec() const
Get the singular value array.
gelss_worker(matrix< T > A_)
Construct a new worker object for a given matrix .
std::pair< vector< T >, double > operator()(vector_const_view< T > b, std::optional< long >={}) const
Solve the least-square problem for a given right hand side vector .
int n_var() const
Get the number of variables of the given problem.
std::pair< matrix< T >, double > operator()(matrix_const_view< T > B, std::optional< long >={}) const
Solve the least-square problem for a given right hand side matrix .
Provides various convenient aliases and helper functions for nda::basic_array and nda::basic_array_vi...
Provides a custom runtime error class and macros to assert conditions and throw exceptions.
Provides a generic interface to the LAPACK gesvd routine.
auto permuted_indices_view(A &&a)
Permute the indices/dimensions of an nda::basic_array or nda::basic_array_view.
auto reshape(A &&a, std::array< Int, R > const &new_shape)
Reshape an nda::basic_array or nda::basic_array_view.
matrix< get_value_t< A > > vstack(A const &a, B const &b)
Stack two 2-dimensional arrays/views vertically.
auto sqrt(A &&a)
Function sqrt for non-matrix nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types...
ArrayOfRank< 2 > auto dagger(M const &m)
Get the conjugate transpose of 2-dimensional array/view.
double frobenius_norm(A const &a)
Calculate the Frobenius norm of a 2-dimensional array.
decltype(auto) conj(A &&a)
Function conj for nda::ArrayOrScalar types (lazy and coefficient-wise for nda::Array types with a com...
basic_array< ValueType, 2, Layout, 'M', ContainerPolicy > matrix
Alias template of an nda::basic_array with rank 2 and an 'M' algebra.
int gesvd(A &&a, S &&s, U &&u, VT &&vt)
Interface to the LAPACK gesvd routine.
Definition gesvd.hpp:75
double norm(A const &a, double p=2.0)
Calculate the p-norm of an nda::ArrayOfRank<1> object with scalar values. The p-norm is defined as.
Definition norm.hpp:58
constexpr uint64_t encode(std::array< int, N > const &a)
Encode a std::array<int, N> in a uint64_t.
Provides definitions of various layout policies.
Provides functions to transform the memory layout of an nda::basic_array or nda::basic_array_view.
Includes all relevant headers for the linear algebra functionality.
Provides some custom implementations of standard mathematical functions used for lazy,...
Provides functions to create and manipulate matrices, i.e. arrays/view with 'M' algebra.
Worker class for solving linear least square problems for hermitian tail-fitting.
int n_var() const
Get the number of variables of the given problem.
array< double, 1 > const & S_vec() const
Get the singular value array.
std::pair< matrix< dcomplex >, double > operator()(matrix_const_view< dcomplex > B, std::optional< long > inner_matrix_dim={}) const
Solve the least-square problem for a given right hand side matrix .
gelss_worker_hermitian(matrix< dcomplex > A_)
Construct a new worker object for a given matrix .