TRIQS/nda 1.3.0
Multi-dimensional array library for C++
Loading...
Searching...
No Matches
det_and_inverse.hpp
Go to the documentation of this file.
1// Copyright (c) 2019-2023 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: Harrison LaBollita, Olivier Parcollet, Nils Wentzell
16
22#pragma once
23
24#include "../basic_array.hpp"
26#include "../clef/make_lazy.hpp"
27#include "../concepts.hpp"
28#include "../exceptions.hpp"
29#include "../lapack/getrf.hpp"
30#include "../lapack/getri.hpp"
34#include "../mem/policies.hpp"
35#include "../print.hpp"
36#include "../traits.hpp"
37
38#include <iostream>
39#include <type_traits>
40#include <utility>
41
42namespace nda {
43
60 template <typename A>
61 bool is_matrix_square(A const &a, bool print_error = false) {
62 bool r = (a.shape()[0] == a.shape()[1]);
63 if (not r and print_error)
64 std::cerr << "Error in nda::detail::is_matrix_square: Dimensions are: (" << a.shape()[0] << "," << a.shape()[1] << ")\n" << std::endl;
65 return r;
66 }
67
79 template <typename A>
80 bool is_matrix_diagonal(A const &a, bool print_error = false) {
81 bool r = is_matrix_square(a) and a == diag(diagonal(a));
82 if (not r and print_error) std::cerr << "Error in nda::detail::is_matrix_diagonal: Non-diagonal matrix: " << a << std::endl;
83 return r;
84 }
85
99 template <typename M>
101 requires(is_matrix_or_view_v<M>)
102 {
103 using value_t = get_value_t<M>;
104 static_assert(std::is_convertible_v<value_t, double> or std::is_convertible_v<value_t, std::complex<double>>,
105 "Error in nda::determinant_in_place: Value type needs to be convertible to double or std::complex<double>");
106 static_assert(not std::is_const_v<M>, "Error in nda::determinant_in_place: Value type cannot be const");
107
108 // special case for an empty matrix
109 if (m.empty()) return value_t{1};
110
111 // check if the matrix is square
112 if (m.extent(0) != m.extent(1)) NDA_RUNTIME_ERROR << "Error in nda::determinant_in_place: Matrix is not square: " << m.shape();
113
114 // calculate the LU decomposition using lapack getrf
115 const int dim = m.extent(0);
116 basic_array<int, 1, C_layout, 'A', sso<100>> ipiv(dim);
117 int info = lapack::getrf(m, ipiv); // it is ok to be in C order
118 if (info < 0) NDA_RUNTIME_ERROR << "Error in nda::determinant_in_place: info = " << info;
119
120 // calculate the determinant from the LU decomposition
121 auto det = value_t{1};
122 int n_flips = 0;
123 for (int i = 0; i < dim; i++) {
124 det *= m(i, i);
125 // count the number of column interchanges performed by getrf
126 if (ipiv(i) != i + 1) ++n_flips;
127 }
128
129 return ((n_flips % 2 == 1) ? -det : det);
130 }
131
142 template <typename M>
143 auto determinant(M const &m) {
144 auto m_copy = make_regular(m);
145 return determinant_in_place(m_copy);
146 }
147
148 // For small matrices (2x2 and 3x3), we directly
149 // compute the matrix inversion rather than calling the
150 // LaPack routine
151 // ---------- Small Inverse Benchmarks ---------
152 // Run on (16 X 2400 MHz CPUs) (see benchmarks/small_inv.cpp)
153 // ---------------------------------------------
154 // Matrix Size Time (old) Time (new)
155 // 1 502 ns 59.0 ns
156 // 2 595 ns 61.7 ns
157 // 3 701 ns 67.5 ns
158
167 template <MemoryMatrix M>
168 requires(get_algebra<M> == 'M' and mem::on_host<M>)
169 void inverse1_in_place(M &&m) { // NOLINT (temporary views are allowed here)
170 if (m(0, 0) == 0.0) NDA_RUNTIME_ERROR << "Error in nda::inverse1_in_place: Matrix is not invertible";
171 m(0, 0) = 1.0 / m(0, 0);
172 }
173
182 template <MemoryMatrix M>
183 requires(get_algebra<M> == 'M' and mem::on_host<M>)
184 void inverse2_in_place(M &&m) { // NOLINT (temporary views are allowed here)
185 // calculate the adjoint of the matrix
186 std::swap(m(0, 0), m(1, 1));
187
188 // calculate the inverse determinant of the matrix
189 auto det = (m(0, 0) * m(1, 1) - m(0, 1) * m(1, 0));
190 if (det == 0.0) NDA_RUNTIME_ERROR << "Error in nda::inverse2_in_place: Matrix is not invertible";
191 auto detinv = 1.0 / det;
192
193 // multiply the adjoint by the inverse determinant
194 m(0, 0) *= +detinv;
195 m(1, 1) *= +detinv;
196 m(1, 0) *= -detinv;
197 m(0, 1) *= -detinv;
198 }
199
208 template <MemoryMatrix M>
209 requires(get_algebra<M> == 'M' and mem::on_host<M>)
210 void inverse3_in_place(M &&m) { // NOLINT (temporary views are allowed here)
211 // calculate the cofactors of the matrix
212 auto b00 = +m(1, 1) * m(2, 2) - m(1, 2) * m(2, 1);
213 auto b10 = -m(1, 0) * m(2, 2) + m(1, 2) * m(2, 0);
214 auto b20 = +m(1, 0) * m(2, 1) - m(1, 1) * m(2, 0);
215 auto b01 = -m(0, 1) * m(2, 2) + m(0, 2) * m(2, 1);
216 auto b11 = +m(0, 0) * m(2, 2) - m(0, 2) * m(2, 0);
217 auto b21 = -m(0, 0) * m(2, 1) + m(0, 1) * m(2, 0);
218 auto b02 = +m(0, 1) * m(1, 2) - m(0, 2) * m(1, 1);
219 auto b12 = -m(0, 0) * m(1, 2) + m(0, 2) * m(1, 0);
220 auto b22 = +m(0, 0) * m(1, 1) - m(0, 1) * m(1, 0);
221
222 // calculate the inverse determinant of the matrix
223 auto det = m(0, 0) * b00 + m(0, 1) * b10 + m(0, 2) * b20;
224 if (det == 0.0) NDA_RUNTIME_ERROR << "Error in nda::inverse3_in_place: Matrix is not invertible";
225 auto detinv = 1.0 / det;
226
227 // fill the matrix by multiplying the cofactors by the inverse determinant
228 m(0, 0) = detinv * b00;
229 m(0, 1) = detinv * b01;
230 m(0, 2) = detinv * b02;
231 m(1, 0) = detinv * b10;
232 m(1, 1) = detinv * b11;
233 m(1, 2) = detinv * b12;
234 m(2, 0) = detinv * b20;
235 m(2, 1) = detinv * b21;
236 m(2, 2) = detinv * b22;
237 }
238
252 template <MemoryMatrix M>
253 requires(get_algebra<M> == 'M')
254 void inverse_in_place(M &&m) { // NOLINT (temporary views are allowed here)
255 EXPECTS(is_matrix_square(m, true));
256
257 // nothing to do if the matrix/view is empty
258 if (m.empty()) return;
259
260 // use optimized routines for small matrices
261 if constexpr (mem::on_host<M>) {
262 if (m.shape()[0] == 1) {
264 return;
265 }
266
267 if (m.shape()[0] == 2) {
269 return;
270 }
271
272 if (m.shape()[0] == 3) {
274 return;
275 }
276 }
277
278 // use getrf and getri from lapack for larger matrices
279 array<int, 1> ipiv(m.extent(0));
280 int info = lapack::getrf(m, ipiv); // it is ok to be in C order
281 if (info != 0) NDA_RUNTIME_ERROR << "Error in nda::inverse_in_place: Matrix is not invertible: info = " << info;
282 info = lapack::getri(m, ipiv);
283 if (info != 0) NDA_RUNTIME_ERROR << "Error in nda::inverse_in_place: Matrix is not invertible: info = " << info;
284 }
285
296 template <Matrix M>
297 auto inverse(M const &m)
298 requires(get_algebra<M> == 'M')
299 {
300 EXPECTS(is_matrix_square(m, true));
301 auto r = make_regular(m);
303 return r;
304 }
305
308} // namespace nda
309
310namespace nda::clef {
311
317
318} // namespace nda::clef
Provides definitions and type traits involving the different memory address spaces supported by nda.
Provides the generic class for arrays.
Provides basic functions to create and manipulate arrays and views.
A generic multi-dimensional array.
Provides concepts for the nda library.
Provides a custom runtime error class and macros to assert conditions and throw exceptions.
Provides a generic interface to the LAPACK getrf routine.
Provides a generic interface to the LAPACK getri routine.
decltype(auto) make_regular(A &&a)
Make a given object regular.
ArrayOfRank< 1 > auto diagonal(M &m)
Get a view of the diagonal of a 2-dimensional array/view.
ArrayOfRank< 2 > auto diag(V const &v)
Get a new nda::matrix with the given values on the diagonal.
constexpr char get_algebra
Constexpr variable that specifies the algebra of a type.
Definition traits.hpp:126
constexpr bool is_matrix_or_view_v
Constexpr variable that is true if type A is a regular matrix or a view of a matrix.
Definition traits.hpp:167
std::decay_t< decltype(get_first_element(std::declval< A const >()))> get_value_t
Get the value type of an array/view or a scalar type.
Definition traits.hpp:192
#define CLEF_MAKE_FNT_LAZY(name)
Macro to make any function lazy, i.e. accept lazy arguments and return a function call expression nod...
int getri(A &&a, IPIV const &ipiv)
Interface to the LAPACK getri routine.
Definition getri.hpp:59
int getrf(A &&a, IPIV &&ipiv)
Interface to the LAPACK getrf routine.
Definition getrf.hpp:62
auto determinant_in_place(M &m)
Compute the determinant of a square matrix/view.
void inverse3_in_place(M &&m)
Compute the inverse of a 3-by-3 matrix.
auto determinant(A &&...__a)
Lazy version of nda::determinant.
void inverse1_in_place(M &&m)
Compute the inverse of a 1-by-1 matrix.
void inverse2_in_place(M &&m)
Compute the inverse of a 2-by-2 matrix.
auto determinant(M const &m)
Compute the determinant of a square matrix/view.
auto inverse(M const &m)
Compute the inverse of an n-by-n matrix.
void inverse_in_place(M &&m)
Compute the inverse of an n-by-n matrix.
bool is_matrix_diagonal(A const &a, bool print_error=false)
Check if a given array/view is diagonal, i.e. if it is square (see nda::is_matrix_square) and all the...
bool is_matrix_square(A const &a, bool print_error=false)
Check if a given array/view is square, i.e. if the first dimension has the same extent as the second ...
static constexpr bool on_host
Constexpr variable that is true if all given types have a Host address space.
Provides definitions of various layout policies.
Provides functionality to make objects, functions and methods lazy.
Provides functions to create and manipulate matrices, i.e. arrays/view with 'M' algebra.
Defines various memory handling policies.
Provides various overloads of the operator<< for nda related objects.
Contiguous layout policy with C-order (row-major order).
Definition policies.hpp:47
Memory policy using an nda::mem::handle_sso.
Definition policies.hpp:70
Provides type traits for the nda library.