GQCP
Loading...
Searching...
No Matches
SquareMatrix.hpp
Go to the documentation of this file.
1// This file is part of GQCG-GQCP.
2//
3// Copyright (C) 2017-2020 the GQCG developers
4//
5// GQCG-GQCP is free software: you can redistribute it and/or modify
6// it under the terms of the GNU Lesser General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// GQCG-GQCP is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU Lesser General Public License for more details.
14//
15// You should have received a copy of the GNU Lesser General Public License
16// along with GQCG-GQCP. If not, see <http://www.gnu.org/licenses/>.
17
18#pragma once
19
20
23
24#include <boost/numeric/conversion/converter.hpp>
25
26#include <numeric>
27
28
29namespace GQCP {
30
31
37template <typename _Scalar>
39 public MatrixX<_Scalar> {
40public:
41 // The scalar type.
42 using Scalar = _Scalar;
43
44 // The type of 'this'.
46
47
48public:
49 /*
50 * MARK: Constructors
51 */
52
57 MatrixX<Scalar>() {}
58
59
65 SquareMatrix(const size_t dim) :
66 MatrixX<Scalar>(dim, dim) {}
67
68
74 SquareMatrix(const size_t rows, const size_t cols) :
75 MatrixX<Scalar>(rows, cols) {}
76
77
84 MatrixX<Scalar>(M) {
85
86 // Check if the given matrix is square.
87 if (this->cols() != this->rows()) {
88 throw std::invalid_argument("SquareMatrix::SquareMatrix(const MatrixX<Scalar>&): The given matrix is not square.");
89 }
90 }
91
92
100 template <typename DerivedExpression>
101 SquareMatrix(const Eigen::MatrixBase<DerivedExpression>& expression) :
102 Self(MatrixX<Scalar>(expression)) // The base constructor returns the required type for the square-checking constructor.
103 {}
104
105
106 /*
107 * MARK: Named constructors.
108 */
109
116
117 size_t dim = strictTriangularRootOf(v.size());
118 Self A = Self::Zero(dim);
119
120 size_t column_index = 0;
121 size_t row_index = column_index + 1; // fill the lower triangle
122 for (size_t vector_index = 0; vector_index < v.size(); vector_index++) {
123 A(row_index, column_index) = v(vector_index);
124
125 if (row_index == dim - 1) { // -1 because of computers
126 column_index++;
127 row_index = column_index + 1;
128 } else {
129 row_index++;
130 }
131 }
132 return A;
133 }
134
135
143 static Self Identity(const size_t dim) { return Self {MatrixX<Scalar>::Identity(dim, dim)}; }
144
145
152 static Self PartitionMatrix(const std::vector<size_t>& indices, const size_t M) {
153
154 Self A = Self::Zero(M);
155
156 for (const auto& index : indices) {
157 if (index >= M) {
158 throw std::invalid_argument("SquareMatrix::PartitionMatrix(std::vector<size_t>, size_t): index is larger than matrix dimension");
159 }
160
161 A(index, index) = 1;
162 }
163
164 return A;
165 }
166
167
175 static Self PartitionMatrix(const size_t start, const size_t range, const size_t M) {
176
177 std::vector<size_t> l(range);
178 std::iota(std::begin(l), std::end(l), start);
179
180 return PartitionMatrix(l, M);
181 }
182
183
191 static Self Random(const size_t dim) { return Self {MatrixX<Scalar>::Random(dim, dim)}; }
192
193
201 static Self RandomSymmetric(const size_t dim) {
202
203 // Any matrix (A + A.adjoint) is by construction symmetric.
204 const Self A_random = Self::Random(dim);
205 return A_random + A_random.adjoint();
206 }
207
208
216 static Self RandomUnitary(const size_t dim) {
217
218 // Get a random unitary matrix by diagonalizing a random symmetric matrix.
219 const Self A_symmetric = Self::RandomSymmetric(dim);
220 Eigen::SelfAdjointEigenSolver<Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>> unitary_solver {A_symmetric};
221
222 return unitary_solver.eigenvectors();
223 }
224
225
232
233 size_t dim = triangularRootOf(v.size());
234
235 Self A = Self::Zero(dim);
236
237 size_t k = 0; // vector index
238 for (size_t i = 0; i < dim; i++) { // row index
239 for (size_t j = i; j < dim; j++) { // column index
240 if (i != j) {
241 A(i, j) = v(k);
242 A(j, i) = v(k);
243 } else {
244 A(i, i) = v(k);
245 }
246
247 k++;
248 }
249 }
250
251 return A;
252 }
253
254
261
262 double D = 1 + 8 * v.size();
263 double dim = (1 + std::sqrt(D)) / 2;
264
265 Self A = Self::Zero(dim);
266
267 size_t k = 0; // vector index
268 for (size_t i = 0; i < dim; i++) { // row index
269 for (size_t j = i; j < dim; j++) { // column index
270 if (i != j) {
271 A(i, j) = v(k);
272 A(j, i) = v(k);
273 k++;
274 }
275 }
276 }
277
278 return A;
279 }
280
281
289 static Self Zero(const size_t dim) { return Self {MatrixX<Scalar>::Zero(dim, dim)}; }
290
291
292 /*
293 * MARK: General information
294 */
295
299 size_t dimension() const { return static_cast<size_t>(this->cols()); } // equals this->rows()
300
301
305 bool isSelfAdjoint(const double threshold = 1.0e-08) const { return (*this).adjoint().isApprox(*this, threshold); }
306
307
311 bool isHermitian(const double threshold = 1.0e-08) const { return this->isSelfAdjoint(threshold); }
312
313
317 bool isAntiHermitian(const double threshold = 1.0e-08) const { return (*this).adjoint().isApprox(-*this, threshold); }
318
319
323 bool isSymmetric(const double threshold = 1.0e-08) const { return (*this).transpose().isApprox(*this, threshold); }
324
325
326 /*
327 * MARK: Algebraic manipulations
328 */
329
335 std::array<Self, 2> noPivotLUDecompose() const {
336
337 const auto M = this->dimension();
338
339 Self L = Self::Zero(M);
340 Self U = Self::Zero(M);
341
342 // Algorithm from "https://www.geeksforgeeks.org/doolittle-algorithm-lu-decomposition/"
343 for (size_t i = 0; i < M; i++) {
344
345 // Systematically solve for the entries of the upper triangular matrix U:
346 // U_ik = A_ik - (LU)_ik
347 for (size_t k = i; k < M; k++) {
348
349 // Summation of L(i, j) * U(j, k)
350 double sum = 0.0;
351 for (size_t j = 0; j < i; j++) {
352 sum += (L(i, j) * U(j, k));
353 }
354
355 // Evaluating U(i, k)
356 U(i, k) = this->operator()(i, k) - sum;
357 }
358
359 // Systematically solve for the entries of the lower triangular matrix L:
360 // L_ik = (A_ik - (LU)_ik) / U_kk
361 for (size_t k = i; k < M; k++) {
362 if (i == k) {
363 L(i, i) = 1; // diagonal as 1
364 } else {
365
366 // Summation of L(k, j) * U(j, i)
367 double sum = 0.0;
368 for (size_t j = 0; j < i; j++) {
369 sum += (L(k, j) * U(j, i));
370 }
371
372 // Evaluating L(k, i)
373 L(k, i) = (this->operator()(k, i) - sum) / U(i, i);
374 }
375 }
376 }
377
378 // Test if the decomposition was stable
379 Self A = L * U;
380 if (A.isApprox(*this)) {
381 return std::array<Self, 2>({L, U});
382 } else {
383 throw std::runtime_error("SquareMatrix<Scalar>::noPivotLUDecompose(): The decomposition was not stable");
384 }
385 }
386
387
402
403 const auto dim = this->dimension();
404
405 VectorX<Scalar> m = VectorX<Scalar>::Zero((dim * (dim - 1) / 2)); // strictly lower triangle has dim(dim-1)/2 parameters
406
407 size_t vector_index = 0;
408 for (size_t q = 0; q < dim; q++) { // "column major" ordering for, so we do p first, then q
409 for (size_t p = q + 1; p < dim; p++) { // strict lower triangle means p > q
410 m(vector_index) = this->operator()(p, q);
411 vector_index++;
412 }
413 }
414
415 return m;
416 }
417
418
419 /*
420 * MARK: Permanents
421 */
422
427
428 // The recursion ends when the given 'matrix' is just a number
429 if ((this->rows() == 1) && (this->cols() == 1)) {
430 return this->operator()(0, 0);
431 }
432
433 size_t j = 0; // develop by the first column
434 double value = 0.0;
435 for (size_t i = 0; i < this->rows(); i++) {
436 value += this->operator()(i, j) * SquareMatrix<Scalar>(this->calculateMinor(i, j)).calculatePermanentCombinatorial(); // need to convert because 'minor' is a base function and isn't guaranteed to be square
437 }
438
439 return value;
440 }
441
442
448 double calculatePermanentRyser() const {
449
450 size_t n = this->dimension();
451
452 // Loop over all submatrices of A
453 double value = 0.0; // value of the permanent
454 size_t number_of_submatrices = boost::numeric::converter<size_t, double>::convert(std::pow(2, n));
455 for (size_t S = 1; S < number_of_submatrices; S++) { // there are no 'chosen columns' in S=0
456
457 // Generate the current submatrix through the Gray code of S: if the bit is 1, the column is chosen
458 size_t gray_code_value = grayCodeOf(S);
459 size_t k = __builtin_popcountll(gray_code_value); // number of columns
460
462 size_t j = 0; // the column index in X
463 while (gray_code_value != 0) { // loop over the set bits in the Gray code
464 size_t index = __builtin_ctzll(gray_code_value); // the index in the original matrix
465
466 X.col(j) = this->col(index);
467
468 gray_code_value ^= gray_code_value & -gray_code_value; // flip the first set bit
469 j++;
470 }
471
472
473 // Calculate the product of all the row sums and multiply by the sign
474 double product_of_rowsums = X.array().rowwise().sum().prod();
475
476 size_t t = n - k; // number of deleted columns
477 int sign = std::pow(-1, t);
478 value += sign * product_of_rowsums;
479 }
480
481 return value;
482 }
483};
484
485
486} // namespace GQCP
Definition: Matrix.hpp:47
enable_if_t< Self::is_vector &&(Rows==3), Z > operator()(const CartesianDirection direction) const
Definition: Matrix.hpp:275
enable_if_t< Self::is_matrix, Z > calculateMinor(size_t i, size_t j) const
Definition: Matrix.hpp:308
Definition: SquareMatrix.hpp:39
static Self PartitionMatrix(const std::vector< size_t > &indices, const size_t M)
Definition: SquareMatrix.hpp:152
bool isSelfAdjoint(const double threshold=1.0e-08) const
Definition: SquareMatrix.hpp:305
SquareMatrix(const MatrixX< Scalar > &M)
Definition: SquareMatrix.hpp:83
bool isSymmetric(const double threshold=1.0e-08) const
Definition: SquareMatrix.hpp:323
size_t dimension() const
Definition: SquareMatrix.hpp:299
SquareMatrix(const size_t dim)
Definition: SquareMatrix.hpp:65
static Self Random(const size_t dim)
Definition: SquareMatrix.hpp:191
SquareMatrix(const size_t rows, const size_t cols)
Definition: SquareMatrix.hpp:74
static Self RandomUnitary(const size_t dim)
Definition: SquareMatrix.hpp:216
bool isHermitian(const double threshold=1.0e-08) const
Definition: SquareMatrix.hpp:311
static Self Identity(const size_t dim)
Definition: SquareMatrix.hpp:143
SquareMatrix(const Eigen::MatrixBase< DerivedExpression > &expression)
Definition: SquareMatrix.hpp:101
_Scalar Scalar
Definition: SquareMatrix.hpp:42
std::array< Self, 2 > noPivotLUDecompose() const
Definition: SquareMatrix.hpp:335
static Self SymmetricFromUpperTriangleWithoutDiagonal(const VectorX< Scalar > &v)
Definition: SquareMatrix.hpp:260
double calculatePermanentCombinatorial() const
Definition: SquareMatrix.hpp:426
static Self Zero(const size_t dim)
Definition: SquareMatrix.hpp:289
SquareMatrix()
Definition: SquareMatrix.hpp:56
static Self SymmetricFromUpperTriangle(const VectorX< Scalar > &v)
Definition: SquareMatrix.hpp:231
double calculatePermanentRyser() const
Definition: SquareMatrix.hpp:448
VectorX< Scalar > pairWiseStrictReduced() const
Definition: SquareMatrix.hpp:401
static Self FromStrictTriangle(const VectorX< Scalar > &v)
Definition: SquareMatrix.hpp:115
static Self RandomSymmetric(const size_t dim)
Definition: SquareMatrix.hpp:201
static Self PartitionMatrix(const size_t start, const size_t range, const size_t M)
Definition: SquareMatrix.hpp:175
bool isAntiHermitian(const double threshold=1.0e-08) const
Definition: SquareMatrix.hpp:317
Definition: BaseOneElectronIntegralBuffer.hpp:25
size_t strictTriangularRootOf(const size_t x)
Definition: miscellaneous.cpp:226
size_t triangularRootOf(const size_t x)
Definition: miscellaneous.cpp:237
size_t grayCodeOf(const size_t S)
Definition: miscellaneous.cpp:165