Quantum 3xplorer (Q3)
An AI-native quantum simulation engine
Loading...
Searching...
No Matches
bloch_overlaps.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * BlochOverlaps — M(k, b) for every k-point and neighbour direction, as the
5 * dense matrices the construction multiplies, rather than the nested vectors
6 * the record carries.
7 *
8 * A record stores each block flat and column-major so it can be written and
9 * read as bytes; the engine reads it as a matrix, restricted to whichever
10 * bands survive at k and at k+b, and rotated by whichever gauge is current.
11 * The restriction is done once for a construction, and the rotation is two
12 * products.
13 */
14
15#include <cstddef>
16#include <stdexcept>
17#include <string>
18#include <utility>
19#include <vector>
20
27
28namespace q3 {
29
31 public:
32 /**
33 * From a record's blocks, `blocks[kpoint][direction]` each a column-major
34 * `rows` x `columns` matrix with the k-side band as the row. A block of
35 * another size is refused naming its k-point and direction.
36 */
37 BlochOverlaps(const std::vector<std::vector<std::vector<Complex>>>& blocks,
38 int rows,
39 int columns) {
40 if (blocks.empty()) {
41 throw std::invalid_argument("BlochOverlaps: no k-points");
42 }
43 const std::size_t directions = blocks.front().size();
44 blocks_.reserve(blocks.size());
45 for (std::size_t kpoint = 0; kpoint < blocks.size(); kpoint++) {
46 if (blocks[kpoint].size() != directions) {
47 throw std::invalid_argument("BlochOverlaps: k-point " + std::to_string(kpoint) +
48 " carries " + std::to_string(blocks[kpoint].size()) +
49 " overlap blocks where k-point 0 carries " +
50 std::to_string(directions));
51 }
52 std::vector<DenseMatrix<Complex>> row;
53 row.reserve(directions);
54 for (std::size_t direction = 0; direction < directions; direction++) {
55 const std::vector<Complex>& block = blocks[kpoint][direction];
56 if (block.size() !=
57 static_cast<std::size_t>(rows) * static_cast<std::size_t>(columns)) {
58 throw std::invalid_argument(
59 "BlochOverlaps: the overlap block at k-point " + std::to_string(kpoint) +
60 ", direction " + std::to_string(direction) + " holds " +
61 std::to_string(block.size()) + " entries, not the " +
62 std::to_string(static_cast<std::size_t>(rows) * columns) + " a " +
63 std::to_string(rows) + " x " + std::to_string(columns) + " block requires");
64 }
65 row.emplace_back(rows, columns, block);
66 }
67 blocks_.push_back(std::move(row));
68 }
69 }
70
71 /// The square case: N x N at every (k, b).
72 [[nodiscard]] static BlochOverlaps
73 square(const std::vector<std::vector<std::vector<Complex>>>& blocks, int dimension) {
74 return BlochOverlaps(blocks, dimension, dimension);
75 }
76
77 /// From matrices already built, one row of directions per k-point.
78 explicit BlochOverlaps(std::vector<std::vector<DenseMatrix<Complex>>> blocks)
79 : blocks_(std::move(blocks)) {
80 if (blocks_.empty()) {
81 throw std::invalid_argument("BlochOverlaps: no k-points");
82 }
83 }
84
85 [[nodiscard]] int number_of_kpoints() const {
86 return static_cast<int>(blocks_.size());
87 }
88 [[nodiscard]] int number_of_directions() const {
89 return static_cast<int>(blocks_.front().size());
90 }
91
92 [[nodiscard]] const DenseMatrix<Complex>& at(int kpoint, int direction) const {
93 return blocks_[static_cast<std::size_t>(kpoint)][static_cast<std::size_t>(direction)];
94 }
95
96 /**
97 * M restricted to the kept bands: rows from k's keep-list, columns from
98 * k+b's. The two lists differ, because which bands survive is decided
99 * k-point by k-point; slicing both sides with one of them is the mistake
100 * this signature exists to make impossible.
101 */
102 [[nodiscard]] BlochOverlaps
103 restricted_to(const std::vector<KPointBandClassification>& classification,
104 const KPointStencil& stencil) const {
105 require_stencil(stencil);
106 if (static_cast<int>(classification.size()) != number_of_kpoints()) {
107 throw std::invalid_argument("BlochOverlaps: the classification covers " +
108 std::to_string(classification.size()) +
109 " k-points, the overlaps " +
110 std::to_string(number_of_kpoints()));
111 }
112 std::vector<std::vector<DenseMatrix<Complex>>> restricted(blocks_.size());
113 for (int kpoint = 0; kpoint < number_of_kpoints(); kpoint++) {
114 const std::vector<int>& rows =
115 classification[static_cast<std::size_t>(kpoint)].kept_bands;
116 require_bands_within(rows, kpoint);
117 for (int direction = 0; direction < number_of_directions(); direction++) {
118 const int neighbor = stencil.neighbor(kpoint, direction);
119 const std::vector<int>& columns =
120 classification[static_cast<std::size_t>(neighbor)].kept_bands;
121 require_bands_within(columns, neighbor);
122 restricted[static_cast<std::size_t>(kpoint)].push_back(
123 at(kpoint, direction).submatrix(rows, columns));
124 }
125 }
126 return BlochOverlaps(std::move(restricted));
127 }
128
129 /// left(k)^dagger M(k, b) right(k + b) at every (k, b).
130 [[nodiscard]] BlochOverlaps transformed_by(const std::vector<DenseMatrix<Complex>>& left,
131 const std::vector<DenseMatrix<Complex>>& right,
132 const KPointStencil& stencil) const {
133 require_stencil(stencil);
134 if (static_cast<int>(left.size()) != number_of_kpoints() ||
135 static_cast<int>(right.size()) != number_of_kpoints()) {
136 throw std::invalid_argument("BlochOverlaps: one left and one right matrix per k-point "
137 "are required, " +
138 std::to_string(number_of_kpoints()) + " of each, not " +
139 std::to_string(left.size()) + " and " +
140 std::to_string(right.size()));
141 }
142 std::vector<std::vector<DenseMatrix<Complex>>> transformed(blocks_.size());
143 for (int kpoint = 0; kpoint < number_of_kpoints(); kpoint++) {
144 for (int direction = 0; direction < number_of_directions(); direction++) {
145 const int neighbor = stencil.neighbor(kpoint, direction);
146 transformed[static_cast<std::size_t>(kpoint)].push_back(
148 left[static_cast<std::size_t>(kpoint)],
151 at(kpoint, direction),
153 right[static_cast<std::size_t>(neighbor)])));
154 }
155 }
156 return BlochOverlaps(std::move(transformed));
157 }
158
159 private:
160 void require_stencil(const KPointStencil& stencil) const {
161 if (stencil.number_of_kpoints() != number_of_kpoints() ||
163 throw std::invalid_argument(
164 "BlochOverlaps: the stencil covers " + std::to_string(stencil.number_of_kpoints()) +
165 " k-points and " + std::to_string(stencil.number_of_directions()) +
166 " directions, the overlaps " + std::to_string(number_of_kpoints()) + " and " +
167 std::to_string(number_of_directions()));
168 }
169 }
170
171 void require_bands_within(const std::vector<int>& bands, int kpoint) const {
172 const int captured = blocks_.front().front().rows();
173 for (const int band : bands) {
174 if (band < 0 || band >= captured) {
175 throw std::invalid_argument("BlochOverlaps: k-point " + std::to_string(kpoint) +
176 " keeps band " + std::to_string(band) +
177 ", outside the " + std::to_string(captured) +
178 " captured bands the overlaps are indexed by");
179 }
180 }
181 }
182
183 /// [kpoint][direction]
184 std::vector<std::vector<DenseMatrix<Complex>>> blocks_;
185};
186
187} // namespace q3
static BlochOverlaps square(const std::vector< std::vector< std::vector< Complex > > > &blocks, int dimension)
The square case: N x N at every (k, b).
int number_of_kpoints() const
BlochOverlaps transformed_by(const std::vector< DenseMatrix< Complex > > &left, const std::vector< DenseMatrix< Complex > > &right, const KPointStencil &stencil) const
left(k)^dagger M(k, b) right(k + b) at every (k, b).
BlochOverlaps(const std::vector< std::vector< std::vector< Complex > > > &blocks, int rows, int columns)
BlochOverlaps(std::vector< std::vector< DenseMatrix< Complex > > > blocks)
From matrices already built, one row of directions per k-point.
int number_of_directions() const
BlochOverlaps restricted_to(const std::vector< KPointBandClassification > &classification, const KPointStencil &stencil) const
const DenseMatrix< Complex > & at(int kpoint, int direction) const
DenseMatrix submatrix(const std::vector< int > &row_indices, const std::vector< int > &column_indices) const
The rows and columns named, in the order named; an index outside the matrix is refused.
int number_of_directions() const
int number_of_kpoints() const
int neighbor(int kpoint, int direction) const
The k-point that k + b lands on.
DenseMatrix< Complex > multiply(MatrixOperation left_operation, const DenseMatrix< Complex > &left, MatrixOperation right_operation, const DenseMatrix< Complex > &right, Complex alpha=Complex(1.0, 0.0))
alpha * op(left) * op(right), as a new matrix of the product's shape.