Quantum 3xplorer (Q3)
An AI-native quantum simulation engine
Loading...
Searching...
No Matches
hermitian_eigensolver.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * HermitianEigensolver — every eigenvalue and eigenvector of a Hermitian
5 * matrix, through zheev, in one place.
6 *
7 * The workspace query, the second call and the check on `info` are the same
8 * wherever a small Hermitian matrix needs diagonalising. The caller's name is
9 * the one thing that differs, so it is the constructor argument, and the
10 * failure names it.
11 *
12 * SubspaceDiagonalizer (layer 4) is not this: it solves the generalized
13 * problem with an overlap, reports `info` for its callers to interpret, and
14 * sits two layers up, where nothing in a lower layer can reach it.
15 */
16
17#include <algorithm>
18#include <stdexcept>
19#include <string>
20#include <utility>
21#include <vector>
22
27
28namespace q3 {
29
31 public:
32 /// `context` opens every error this solver raises: the caller, and what it was diagonalising.
33 explicit HermitianEigensolver(std::string context) : context_(std::move(context)) {}
34
35 /**
36 * The decomposition of `matrix`, whose upper triangle is read. Taken by
37 * value because zheev overwrites its input with the eigenvectors, which is
38 * where they are then read from.
39 */
41 if (matrix.rows() != matrix.columns()) {
42 throw std::invalid_argument(context_ +
43 ": a Hermitian eigenproblem needs a square "
44 "matrix, got " +
45 std::to_string(matrix.rows()) + " x " +
46 std::to_string(matrix.columns()));
47 }
48 int dimension = matrix.rows();
49 HermitianEigenDecomposition decomposition;
50 decomposition.eigenvalues.assign(static_cast<std::size_t>(dimension), 0.0);
51 if (dimension == 0) {
52 decomposition.eigenvectors = std::move(matrix);
53 return decomposition;
54 }
55
56 int info = 0;
57 int workspace_size = -1;
58 Complex workspace_query;
59 std::vector<Real> real_workspace(static_cast<std::size_t>(std::max(1, 3 * dimension - 2)));
61 "U",
62 &dimension,
63 matrix.data(),
64 &dimension,
65 decomposition.eigenvalues.data(),
66 &workspace_query,
67 &workspace_size,
68 real_workspace.data(),
69 &info);
70 if (info != 0) {
71 throw std::runtime_error(
72 context_ + ": zheev workspace query failed with info = " + std::to_string(info));
73 }
74 workspace_size = std::max(1, static_cast<int>(workspace_query.real()));
75 std::vector<Complex> workspace(static_cast<std::size_t>(workspace_size));
77 "U",
78 &dimension,
79 matrix.data(),
80 &dimension,
81 decomposition.eigenvalues.data(),
82 workspace.data(),
83 &workspace_size,
84 real_workspace.data(),
85 &info);
86 if (info != 0) {
87 throw std::runtime_error(
88 context_ + ": zheev failed with info = " + std::to_string(info) +
89 (info > 0 ? "; the iteration did not converge" : "; an argument was invalid"));
90 }
91 decomposition.eigenvectors = std::move(matrix);
92 return decomposition;
93 }
94
95 private:
96 std::string context_;
97};
98
99} // namespace q3
int rows() const
T * data()
The column-major buffer: with rows() it is the (pointer, leading dimension) BLAS takes.
int columns() const
HermitianEigensolver(std::string context)
context opens every error this solver raises: the caller, and what it was diagonalising.
HermitianEigenDecomposition solve(DenseMatrix< Complex > matrix) const
void zheev_(const char *jobz, const char *uplo, const int *n, std::complex< double > *a, const int *lda, double *w, std::complex< double > *work, const int *lwork, double *rwork, int *info)
std::complex< double > Complex