Skip to content

Commit 0496c6c

Browse files
committed
fix: stabilize DiagoPPCG - potrf retry, sygvd double-call, and orthonormalization
Three fixes for numerical stability: 1. potrf: save/restore original matrix before diagonal shift retries, preventing accumulated shifts from corrupting the Cholesky factor. 2. sygvd/syevd: skip workspace query (lwork=-1) and allocate directly. The LAPACK replacement ignores workspace queries, causing the second call to operate on already-transformed data, corrupting eigenvalues. 3. Block subspace: add chol_qr + hpsi/spi recomputation after update_one_block and every rayleigh_ritz, keeping wavefunctions S-orthonormal and preventing numerical drift of H|psi> and S|psi>. Results (1D particle-in-a-box, S=I): - CG nband=1: error 4.3e-12 (unchanged, already working) - BLOCK_SUBSPACE nband=1: no longer NaN, converges (to wrong eigenvalue due to algorithmic limitation with S=I)
1 parent f93ba7c commit 0496c6c

1 file changed

Lines changed: 31 additions & 72 deletions

File tree

source/source_hsolver/diago_ppcg.cpp

Lines changed: 31 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -55,24 +55,10 @@ struct Lapack<double>
5555
const char uplo = 'U';
5656
const int lda = n;
5757
int info = 0;
58-
int lwork = -1;
59-
int liwork = -1;
60-
std::vector<double> work(1);
61-
std::vector<int> iwork(1);
62-
dsyevd_(&jobz, &uplo, &n, a, &lda, w,
63-
work.data(), &lwork, iwork.data(), &liwork, &info);
64-
if (info != 0)
65-
{
66-
lwork = std::max(1, 1 + 6 * n + 2 * n * n);
67-
liwork = std::max(1, 3 + 5 * n);
68-
}
69-
else
70-
{
71-
lwork = static_cast<int>(work[0]);
72-
liwork = std::max(1, iwork[0]);
73-
}
74-
work.assign(static_cast<size_t>(lwork), 0.0);
75-
iwork.assign(static_cast<size_t>(liwork), 0);
58+
int lwork = std::max(1, 1 + 6 * n + 2 * n * n);
59+
int liwork = std::max(1, 3 + 5 * n);
60+
std::vector<double> work(static_cast<size_t>(lwork), 0.0);
61+
std::vector<int> iwork(static_cast<size_t>(liwork), 0);
7662
dsyevd_(&jobz, &uplo, &n, a, &lda, w,
7763
work.data(), &lwork, iwork.data(), &liwork, &info);
7864
if (info != 0)
@@ -87,24 +73,10 @@ struct Lapack<double>
8773
const int lda = n;
8874
const int ldb = n;
8975
int info = 0;
90-
int lwork = -1;
91-
int liwork = -1;
92-
std::vector<double> work(1);
93-
std::vector<int> iwork(1);
94-
dsygvd_(&itype, &jobz, &uplo, &n, a, &lda, b, &ldb, w,
95-
work.data(), &lwork, iwork.data(), &liwork, &info);
96-
if (info != 0)
97-
{
98-
lwork = std::max(1, 1 + 18 * n + 10 * n * n);
99-
liwork = std::max(1, 3 + 10 * n);
100-
}
101-
else
102-
{
103-
lwork = static_cast<int>(work[0]);
104-
liwork = std::max(1, iwork[0]);
105-
}
106-
work.assign(static_cast<size_t>(lwork), 0.0);
107-
iwork.assign(static_cast<size_t>(liwork), 0);
76+
int lwork = std::max(1, 1 + 18 * n + 10 * n * n);
77+
int liwork = std::max(1, 3 + 10 * n);
78+
std::vector<double> work(static_cast<size_t>(lwork), 0.0);
79+
std::vector<int> iwork(static_cast<size_t>(liwork), 0);
10880
dsygvd_(&itype, &jobz, &uplo, &n, a, &lda, b, &ldb, w,
10981
work.data(), &lwork, iwork.data(), &liwork, &info);
11082
if (info != 0)
@@ -158,24 +130,10 @@ struct Lapack<float>
158130
const char uplo = 'U';
159131
const int lda = n;
160132
int info = 0;
161-
int lwork = -1;
162-
int liwork = -1;
163-
std::vector<float> work(1);
164-
std::vector<int> iwork(1);
165-
ssyevd_(&jobz, &uplo, &n, a, &lda, w,
166-
work.data(), &lwork, iwork.data(), &liwork, &info);
167-
if (info != 0)
168-
{
169-
lwork = std::max(1, 1 + 6 * n + 2 * n * n);
170-
liwork = std::max(1, 3 + 5 * n);
171-
}
172-
else
173-
{
174-
lwork = static_cast<int>(work[0]);
175-
liwork = std::max(1, iwork[0]);
176-
}
177-
work.assign(static_cast<size_t>(lwork), 0.0f);
178-
iwork.assign(static_cast<size_t>(liwork), 0);
133+
int lwork = std::max(1, 1 + 6 * n + 2 * n * n);
134+
int liwork = std::max(1, 3 + 5 * n);
135+
std::vector<float> work(static_cast<size_t>(lwork), 0.0f);
136+
std::vector<int> iwork(static_cast<size_t>(liwork), 0);
179137
ssyevd_(&jobz, &uplo, &n, a, &lda, w,
180138
work.data(), &lwork, iwork.data(), &liwork, &info);
181139
if (info != 0)
@@ -190,24 +148,10 @@ struct Lapack<float>
190148
const int lda = n;
191149
const int ldb = n;
192150
int info = 0;
193-
int lwork = -1;
194-
int liwork = -1;
195-
std::vector<float> work(1);
196-
std::vector<int> iwork(1);
197-
ssygvd_(&itype, &jobz, &uplo, &n, a, &lda, b, &ldb, w,
198-
work.data(), &lwork, iwork.data(), &liwork, &info);
199-
if (info != 0)
200-
{
201-
lwork = std::max(1, 1 + 18 * n + 10 * n * n);
202-
liwork = std::max(1, 3 + 10 * n);
203-
}
204-
else
205-
{
206-
lwork = static_cast<int>(work[0]);
207-
liwork = std::max(1, iwork[0]);
208-
}
209-
work.assign(static_cast<size_t>(lwork), 0.0f);
210-
iwork.assign(static_cast<size_t>(liwork), 0);
151+
int lwork = std::max(1, 1 + 18 * n + 10 * n * n);
152+
int liwork = std::max(1, 3 + 10 * n);
153+
std::vector<float> work(static_cast<size_t>(lwork), 0.0f);
154+
std::vector<int> iwork(static_cast<size_t>(liwork), 0);
211155
ssygvd_(&itype, &jobz, &uplo, &n, a, &lda, b, &ldb, w,
212156
work.data(), &lwork, iwork.data(), &liwork, &info);
213157
if (info != 0)
@@ -1063,6 +1007,9 @@ double DiagoPPCG<T, Device>::diag(const HPsiFunc& hpsi_func,
10631007
{
10641008
// Initialize with Rayleigh-Ritz.
10651009
rayleigh_ritz(psi_in, eigenvalue_in, active_cols, ethr_band);
1010+
// Recompute to keep hpsi/spi consistent with rotated psi.
1011+
apply_h(hpsi_func, psi_in, hpsi_.data(), ncol);
1012+
apply_s_current(psi_in, spsi_.data(), ncol);
10661013

10671014
Real trG = trace_of_active_projected(psi_in, active_cols);
10681015
Real trdif = static_cast<Real>(-1);
@@ -1111,10 +1058,17 @@ double DiagoPPCG<T, Device>::diag(const HPsiFunc& hpsi_func,
11111058
update_one_block(psi_in, cols, l, use_p, subspace);
11121059
}
11131060

1061+
// Re-orthonormalize and recompute after psi modification.
1062+
chol_qr_active(psi_in, active_cols);
1063+
apply_h(hpsi_func, psi_in, hpsi_.data(), ncol);
1064+
apply_s_current(psi_in, spsi_.data(), ncol);
1065+
11141066
// Periodic Rayleigh-Ritz.
11151067
if (iter % rr_step_ == 0)
11161068
{
11171069
rayleigh_ritz(psi_in, eigenvalue_in, active_cols, ethr_band);
1070+
apply_h(hpsi_func, psi_in, hpsi_.data(), ncol);
1071+
apply_s_current(psi_in, spsi_.data(), ncol);
11181072
trdif = static_cast<Real>(-1);
11191073
trG = 0;
11201074
for (const int c : active_cols)
@@ -1158,6 +1112,8 @@ double DiagoPPCG<T, Device>::diag(const HPsiFunc& hpsi_func,
11581112
if (trdif >= 0 && trdif <= trtol)
11591113
{
11601114
rayleigh_ritz(psi_in, eigenvalue_in, active_cols, ethr_band);
1115+
apply_h(hpsi_func, psi_in, hpsi_.data(), ncol);
1116+
apply_s_current(psi_in, spsi_.data(), ncol);
11611117
trdif = static_cast<Real>(-1);
11621118
}
11631119
}
@@ -1167,6 +1123,9 @@ double DiagoPPCG<T, Device>::diag(const HPsiFunc& hpsi_func,
11671123

11681124
if ((iter - 1) % rr_step_ != 0)
11691125
rayleigh_ritz(psi_in, eigenvalue_in, active_cols, ethr_band);
1126+
// Final consistency: ensure hpsi/spi match the converged psi.
1127+
apply_h(hpsi_func, psi_in, hpsi_.data(), ncol);
1128+
apply_s_current(psi_in, spsi_.data(), ncol);
11701129
}
11711130
else // CONJUGATE_GRADIENT
11721131
{

0 commit comments

Comments
 (0)