83 const std::vector<KPointBandClassification>& classification,
84 int number_of_wannier_functions,
87 , classification_(classification)
88 , number_of_wannier_functions_(number_of_wannier_functions)
89 , settings_(settings) {
90 if (number_of_wannier_functions < 1) {
91 throw std::invalid_argument(
92 "SubspaceDisentangler: number_of_wannier_functions must be positive, got " +
93 std::to_string(number_of_wannier_functions));
96 throw std::invalid_argument(
"SubspaceDisentangler: the classification covers " +
97 std::to_string(classification.size()) +
98 " k-points and the stencil " +
102 throw std::invalid_argument(
103 "SubspaceDisentangler: maximum_iterations must be at least 1, got " +
107 throw std::invalid_argument(
"SubspaceDisentangler: damping must lie in (0, 1], got " +
108 std::to_string(settings.
damping));
121 require_subspace_shapes(initial_subspaces);
124 std::vector<DenseMatrix<Complex>> subspaces = std::move(initial_subspaces);
125 std::vector<DenseMatrix<Complex>> damped_z(
static_cast<std::size_t
>(number_of_kpoints));
130 std::vector<DenseMatrix<Complex>> next_subspaces(
131 static_cast<std::size_t
>(number_of_kpoints));
133 for (
int kpoint = 0; kpoint < number_of_kpoints; kpoint++) {
135 subspace_matrix_at(kpoint, sliced, subspaces, spread);
137 damped = damped.
empty() ? z : mixed(damped, z);
138 next_subspaces[
static_cast<std::size_t
>(kpoint)] =
139 choose_subspace(damped, classification_[
static_cast<std::size_t
>(kpoint)]);
142 subspaces = std::move(next_subspaces);
143 spread /=
static_cast<Real>(number_of_kpoints);
144 require_nonnegative(spread);
157 const Real final_spread = spread_of(sliced, subspaces);
158 log::info(
"disentanglement: converged in {} iterations, Omega_I = {:.9f} bohr^2",
161 return {std::move(subspaces), final_spread, iteration};
167 "disentanglement did not converge in " + std::to_string(settings_.
maximum_iterations) +
168 " iterations. Last gauge-invariant spreads (bohr^2): " +
170 ". A subspace that has not converged is not returned as if it had.",
188 subspace_matrix_at(
int kpoint,
191 Real& spread)
const {
192 const int kept = classification_[
static_cast<std::size_t
>(kpoint)].number_kept();
195 const Real weight = stencil_.
weight(direction);
198 sliced.
at(kpoint, direction),
200 subspaces[
static_cast<std::size_t
>(stencil_.
neighbor(kpoint, direction))]);
208 weight * (
static_cast<Real>(number_of_wannier_functions_) -
209 retained_weight(subspaces[
static_cast<std::size_t
>(kpoint)], carried));
215 [[nodiscard]] DenseMatrix<Complex> mixed(
const DenseMatrix<Complex>& previous,
216 const DenseMatrix<Complex>& fresh)
const {
217 DenseMatrix<Complex> result = previous.scaled(1.0 - settings_.
damping);
218 result.add_scaled(settings_.
damping, fresh);
232 void require_nonnegative(
Real spread)
const {
234 static_cast<Real>(number_of_wannier_functions_)) {
235 std::ostringstream value;
236 value << std::scientific << spread;
237 throw WannierDiagnosticError(
239 "disentanglement: the gauge-invariant spread came out negative (" + value.str() +
240 " bohr^2), which no overlap between orthonormal states can produce. Some "
241 "M_mn(k,b) has a singular value above one, so the record's overlaps are "
242 "corrupt or were not built from orthonormal Bloch states.");
246 void require_subspace_shapes(
const std::vector<DenseMatrix<Complex>>& subspaces)
const {
248 throw std::invalid_argument(
249 "SubspaceDisentangler: " + std::to_string(subspaces.size()) +
250 " initial subspaces were given for " +
253 for (std::size_t kpoint = 0; kpoint < subspaces.size(); kpoint++) {
254 const int kept = classification_[kpoint].number_kept();
255 if (subspaces[kpoint].rows() != kept ||
256 subspaces[kpoint].columns() != number_of_wannier_functions_) {
257 throw std::invalid_argument(
258 "SubspaceDisentangler: the initial subspace at k-point " +
259 std::to_string(kpoint) +
" is " + std::to_string(subspaces[kpoint].rows()) +
260 " x " + std::to_string(subspaces[kpoint].columns()) +
", not the " +
261 std::to_string(kept) +
" x " + std::to_string(number_of_wannier_functions_) +
262 " its kept bands and the Wannier functions require");
268 [[nodiscard]]
static Real retained_weight(
const DenseMatrix<Complex>& subspace,
269 const DenseMatrix<Complex>& carried) {
280 [[nodiscard]]
Real spread_of(
const BlochOverlaps& sliced,
281 const std::vector<DenseMatrix<Complex>>& subspaces)
const {
285 const int neighbor = stencil_.
neighbor(kpoint, direction);
286 const DenseMatrix<Complex> carried =
288 sliced.at(kpoint, direction),
290 subspaces[
static_cast<std::size_t
>(neighbor)]);
291 spread += stencil_.
weight(direction) *
292 (
static_cast<Real>(number_of_wannier_functions_) -
293 retained_weight(subspaces[
static_cast<std::size_t
>(kpoint)], carried));
303 [[nodiscard]] DenseMatrix<Complex>
304 choose_subspace(
const DenseMatrix<Complex>& z,
const KPointBandClassification& entry)
const {
305 const int kept = entry.number_kept();
306 const int frozen = entry.number_frozen();
307 DenseMatrix<Complex> subspace(kept, number_of_wannier_functions_,
Complex(0.0, 0.0));
309 for (
int index = 0; index < frozen; index++) {
310 subspace(entry.frozen_positions[
static_cast<std::size_t
>(index)], index) =
313 const int wanted = number_of_wannier_functions_ - frozen;
325 std::vector<int> complement;
326 complement.reserve(
static_cast<std::size_t
>(kept - frozen));
327 std::vector<bool> is_frozen(
static_cast<std::size_t
>(kept),
false);
328 for (
const int position : entry.frozen_positions) {
329 is_frozen[
static_cast<std::size_t
>(position)] =
true;
331 for (
int position = 0; position < kept; position++) {
332 if (!is_frozen[
static_cast<std::size_t
>(position)]) {
333 complement.push_back(position);
337 const HermitianEigenDecomposition decomposition =
338 HermitianEigensolver(
"SubspaceDisentangler: the subspace matrix Z(k)")
339 .solve(z.submatrix(complement, complement));
342 const int dimension =
static_cast<int>(complement.size());
343 for (
int index = 0; index < wanted; index++) {
344 const int source = dimension - 1 - index;
345 const int target = frozen + index;
346 for (
int row = 0; row < dimension; row++) {
347 subspace(complement[
static_cast<std::size_t
>(row)], target) =
348 decomposition.eigenvectors(row, source);
354 const KPointStencil& stencil_;
355 const std::vector<KPointBandClassification>& classification_;
356 int number_of_wannier_functions_;
357 DisentanglementSettings settings_;
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.