Skip to content

Commit 4643b43

Browse files
committed
Make PPCG usable from PW solver
1 parent 2824e76 commit 4643b43

6 files changed

Lines changed: 150 additions & 30 deletions

File tree

docs/advanced/input_files/input-main.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -977,7 +977,7 @@
977977
### pw_diag_thr
978978

979979
- **Type**: Real
980-
- **Description**: Only used when you use ks_solver = cg/dav/dav_subspace/bpcg. It indicates the threshold for the first electronic iteration, from the second iteration the pw_diag_thr will be updated automatically. For nscf calculations with planewave basis set, pw_diag_thr should be <= 1e-3.
980+
- **Description**: Only used when you use ks_solver = cg/dav/dav_subspace/bpcg/ppcg. It indicates the threshold for the first electronic iteration, from the second iteration the pw_diag_thr will be updated automatically. For nscf calculations with planewave basis set, pw_diag_thr should be <= 1e-3.
981981
- **Default**: 0.01
982982

983983
### diago_smooth_ethr
@@ -996,8 +996,8 @@
996996
### pw_diag_nmax
997997

998998
- **Type**: Integer
999-
- **Availability**: *basis_type==pw, ks_solver==cg/dav/dav_subspace/bpcg*
1000-
- **Description**: Only useful when you use ks_solver = cg/dav/dav_subspace/bpcg. It indicates the maximal iteration number for cg/david/dav_subspace/bpcg method.
999+
- **Availability**: *basis_type==pw, ks_solver==cg/dav/dav_subspace/bpcg/ppcg*
1000+
- **Description**: Only useful when you use ks_solver = cg/dav/dav_subspace/bpcg/ppcg. It indicates the maximal iteration number for cg/david/dav_subspace/bpcg/ppcg method.
10011001
- **Default**: 50
10021002

10031003
### pw_diag_ndim
@@ -1112,6 +1112,7 @@
11121112
- bpcg: The BPCG method, which is a block-parallel Conjugate Gradient (CG) method, typically exhibits higher acceleration in a GPU environment.
11131113
- dav: The Davidson algorithm.
11141114
- dav_subspace: The Davidson algorithm without orthogonalization operation, this method is the most recommended for efficiency. pw_diag_ndim can be set to 2 for this method.
1115+
- ppcg: The projection preconditioned conjugate-gradient method, currently available for CPU plane-wave calculations.
11151116

11161117
For numerical atomic orbitals basis,
11171118

docs/parameters.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,7 @@ parameters:
521521
* bpcg: The BPCG method, which is a block-parallel Conjugate Gradient (CG) method, typically exhibits higher acceleration in a GPU environment.
522522
* dav: The Davidson algorithm.
523523
* dav_subspace: The Davidson algorithm without orthogonalization operation, this method is the most recommended for efficiency. `pw_diag_ndim` can be set to 2 for this method.
524+
* ppcg: The projection preconditioned conjugate-gradient method, currently available for CPU plane-wave calculations.
524525
525526
For numerical atomic orbitals basis,
526527
@@ -942,7 +943,7 @@ parameters:
942943
category: Plane wave related variables
943944
type: Real
944945
description: |
945-
Only used when you use ks_solver = cg/dav/dav_subspace/bpcg. It indicates the threshold for the first electronic iteration, from the second iteration the pw_diag_thr will be updated automatically. For nscf calculations with planewave basis set, pw_diag_thr should be <= 1e-3.
946+
Only used when you use ks_solver = cg/dav/dav_subspace/bpcg/ppcg. It indicates the threshold for the first electronic iteration, from the second iteration the pw_diag_thr will be updated automatically. For nscf calculations with planewave basis set, pw_diag_thr should be <= 1e-3.
946947
default_value: "0.01"
947948
unit: ""
948949
availability: ""
@@ -966,10 +967,10 @@ parameters:
966967
category: Plane wave related variables
967968
type: Integer
968969
description: |
969-
Only useful when you use ks_solver = cg/dav/dav_subspace/bpcg. It indicates the maximal iteration number for cg/david/dav_subspace/bpcg method.
970+
Only useful when you use ks_solver = cg/dav/dav_subspace/bpcg/ppcg. It indicates the maximal iteration number for cg/david/dav_subspace/bpcg/ppcg method.
970971
default_value: "50"
971972
unit: ""
972-
availability: "basis_type==pw, ks_solver==cg/dav/dav_subspace/bpcg"
973+
availability: "basis_type==pw, ks_solver==cg/dav/dav_subspace/bpcg/ppcg"
973974
- name: pw_diag_ndim
974975
category: Plane wave related variables
975976
type: Integer

source/source_hsolver/diago_ppcg.h

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#ifndef DIAGO_PPCG_H
22
#define DIAGO_PPCG_H
33

4+
#include "source_base/module_device/types.h"
5+
46
#include <vector>
57
#include <functional>
68
#include <cmath>
@@ -17,21 +19,17 @@ namespace hsolver {
1719
// -----------------------------------------------------------------------------
1820
//
1921
// Supports two algorithmic strategies:
20-
// BLOCK_SUBSPACE — block subspace diagonalization (File 1 approach).
2122
// CONJUGATE_GRADIENT — band-by-band Polak-Ribiere CG with line minimization
2223
// (File 2 approach).
24+
// BLOCK_SUBSPACE — block subspace diagonalization (File 1 approach).
2325
//
24-
// The block-subspace strategy tends to be more robust near convergence;
25-
// conjugate-gradient is more memory efficient for large systems.
26+
// CONJUGATE_GRADIENT is the default because it is the tested production path.
27+
// BLOCK_SUBSPACE is kept as an explicit experimental strategy.
2628
// -----------------------------------------------------------------------------
2729

2830
enum class PpcgStrategy { BLOCK_SUBSPACE, CONJUGATE_GRADIENT };
2931

30-
// Device tags (extensible for GPU backends).
31-
namespace base_device {
32-
struct DEVICE_CPU {};
33-
struct DEVICE_GPU {};
34-
}
32+
namespace base_device = ::base_device;
3533

3634
template <typename T, typename Device>
3735
class DiagoPPCG
@@ -54,7 +52,7 @@ class DiagoPPCG
5452
const int& sbsize,
5553
const int& rr_step,
5654
const bool gamma_g0_real,
57-
const PpcgStrategy strategy = PpcgStrategy::BLOCK_SUBSPACE);
55+
const PpcgStrategy strategy = PpcgStrategy::CONJUGATE_GRADIENT);
5856

5957
// -------------------------------------------------------------------------
6058
// Main entry point

source/source_hsolver/hsolver_pw.cpp

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,81 @@
1111
#include "source_hsolver/diago_cg.h"
1212
#include "source_hsolver/diago_dav_subspace.h"
1313
#include "source_hsolver/diago_david.h"
14+
#include "source_hsolver/diago_ppcg.h"
1415
#include "source_hsolver/diago_iter_assist.h"
1516
#include "source_io/module_parameter/parameter.h"
1617
#include "source_psi/psi.h"
1718
#include "source_estate/elecstate_tools.h"
1819

1920

2021
#include <algorithm>
22+
#include <type_traits>
2123
#include <vector>
2224

2325
namespace hsolver
2426
{
2527

28+
namespace
29+
{
30+
template <typename T, typename Device, typename Real, typename HPsiFunc, typename SPsiFunc>
31+
double run_ppcg_pw(const HPsiFunc& hpsi_func,
32+
const SPsiFunc& spsi_func,
33+
const int ld_psi,
34+
const int nband,
35+
const int dim,
36+
T* psi,
37+
Real* eigenvalue,
38+
const std::vector<double>& ethr_band,
39+
const Real* pre_condition,
40+
const double diag_thr,
41+
const int diag_iter_max,
42+
const int pw_diag_ndim,
43+
const bool gamma_only,
44+
std::true_type)
45+
{
46+
const int sbsize = std::max(1, std::min(nband, pw_diag_ndim));
47+
const int rr_step = std::max(1, pw_diag_ndim);
48+
49+
DiagoPPCG<T, Device> ppcg(static_cast<Real>(diag_thr),
50+
diag_iter_max,
51+
sbsize,
52+
rr_step,
53+
gamma_only,
54+
PpcgStrategy::CONJUGATE_GRADIENT);
55+
56+
return ppcg.diag(hpsi_func,
57+
spsi_func,
58+
ld_psi,
59+
nband,
60+
dim,
61+
psi,
62+
eigenvalue,
63+
ethr_band,
64+
pre_condition);
65+
}
66+
67+
template <typename T, typename Device, typename Real, typename HPsiFunc, typename SPsiFunc>
68+
double run_ppcg_pw(const HPsiFunc&,
69+
const SPsiFunc&,
70+
const int,
71+
const int,
72+
const int,
73+
T*,
74+
Real*,
75+
const std::vector<double>&,
76+
const Real*,
77+
const double,
78+
const int,
79+
const int,
80+
const bool,
81+
std::false_type)
82+
{
83+
ModuleBase::WARNING_QUIT("HSolverPW::hamiltSolvePsiK",
84+
"PPCG is currently implemented for CPU PW calculations only.");
85+
return 0.0;
86+
}
87+
} // namespace
88+
2689
template <typename T, typename Device>
2790
void HSolverPW<T, Device>::cal_smooth_ethr(const double& wk,
2891
const double* wg,
@@ -83,7 +146,7 @@ void HSolverPW<T, Device>::solve(hamilt::Hamilt<T, Device>* pHamilt,
83146
this->nproc_in_pool = nproc_in_pool_in;
84147

85148
// report if the specified diagonalization method is not supported
86-
const std::initializer_list<std::string> _methods = {"cg", "dav", "dav_subspace", "bpcg"};
149+
const std::initializer_list<std::string> _methods = {"cg", "dav", "dav_subspace", "bpcg", "ppcg"};
87150
if (std::find(std::begin(_methods), std::end(_methods), this->method) == std::end(_methods))
88151
{
89152
ModuleBase::WARNING_QUIT("HSolverPW::solve", "This type of eigensolver is not supported!");
@@ -379,6 +442,24 @@ void HSolverPW<T, Device>::hamiltSolvePsiK(hamilt::Hamilt<T, Device>* hm,
379442
ntry_max,
380443
notconv_max));
381444
}
445+
else if (this->method == "ppcg")
446+
{
447+
DiagoIterAssist<T, Device>::avg_iter += run_ppcg_pw<T, Device, Real>(
448+
hpsi_func,
449+
spsi_func,
450+
psi.get_nbasis(),
451+
psi.get_nbands(),
452+
psi.get_current_ngk(),
453+
psi.get_pointer(),
454+
eigenvalue,
455+
this->ethr_band,
456+
pre_condition.data(),
457+
this->diag_thr,
458+
this->diag_iter_max,
459+
PARAM.inp.pw_diag_ndim,
460+
PARAM.globalv.gamma_only_pw,
461+
std::is_same<Device, base_device::DEVICE_CPU>());
462+
}
382463
ModuleBase::timer::end("HSolverPW", "solve_psik");
383464
return;
384465
}

source/source_hsolver/test/diago_ppcg_test.cpp

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,9 @@
88
* Exact eigenvalues are the diagonal entries. Simplest possible
99
* smoke test — should converge in very few iterations.
1010
*
11-
* Tests use the CONJUGATE_GRADIENT strategy which has a try/catch fallback
12-
* for LAPACK sygvd failures and is therefore more portable across different
13-
* LAPACK implementations.
14-
*
15-
* BLOCK_SUBSPACE strategy tests exist in git history but are disabled here
16-
* because they require a LAPACK with reliable dsygvd for small ill-conditioned
17-
* generalized eigenvalue problems.
11+
* Tests primarily exercise the default CONJUGATE_GRADIENT strategy, with a
12+
* BLOCK_SUBSPACE smoke test to keep the explicit experimental path finite on a
13+
* small Hermitian problem.
1814
*/
1915

2016
#include "../diago_ppcg.h"
@@ -335,7 +331,7 @@ TEST_F(DiagoPPCG2x2Test, ConjugateGradient)
335331
<< "2x2 CG: too many iterations";
336332
}
337333

338-
TEST(DiagoPPCGComplexHermitianTest, ConjugateGradientKeepsImaginaryProjection)
334+
TEST(DiagoPPCGComplexHermitianTest, DefaultKeepsImaginaryProjection)
339335
{
340336
const int n_dim = 2;
341337
const int nband = 2;
@@ -362,8 +358,7 @@ TEST(DiagoPPCGComplexHermitianTest, ConjugateGradientKeepsImaginaryProjection)
362358
/* max_iter = */ 10,
363359
/* sbsize = */ 2,
364360
/* rr_step = */ 1,
365-
/* gamma_g0 = */ false,
366-
hsolver::PpcgStrategy::CONJUGATE_GRADIENT
361+
/* gamma_g0 = */ false
367362
);
368363

369364
auto h_op = [&H_mat, n_dim](T* in, T* out, int ld_in, int ncol) {
@@ -378,6 +373,49 @@ TEST(DiagoPPCGComplexHermitianTest, ConjugateGradientKeepsImaginaryProjection)
378373
EXPECT_NEAR(eval[1], 2.5 + delta, 1e-10);
379374
}
380375

376+
TEST(DiagoPPCGComplexHermitianTest, BlockSubspaceSmokeNoNaN)
377+
{
378+
const int n_dim = 2;
379+
const int nband = 2;
380+
const int ld = n_dim;
381+
382+
std::vector<T> H_mat(n_dim * n_dim, T(0));
383+
H_mat[0 + 0 * n_dim] = T(2.0, 0.0);
384+
H_mat[1 + 1 * n_dim] = T(3.0, 0.0);
385+
H_mat[0 + 1 * n_dim] = T(0.0, 1.0);
386+
H_mat[1 + 0 * n_dim] = T(0.0, -1.0);
387+
388+
std::vector<T> psi(ld * nband, T(0));
389+
psi[0 + 0 * ld] = T(1.0, 0.0);
390+
psi[1 + 1 * ld] = T(1.0, 0.0);
391+
392+
std::vector<Real> prec(n_dim, 2.0);
393+
std::vector<double> ethr(nband, 1e-10);
394+
std::vector<Real> eval(nband, 0.0);
395+
396+
hsolver::DiagoPPCG<T, hsolver::base_device::DEVICE_CPU> solver(
397+
/* diag_thr = */ 1e-10,
398+
/* max_iter = */ 8,
399+
/* sbsize = */ 2,
400+
/* rr_step = */ 1,
401+
/* gamma_g0 = */ false,
402+
hsolver::PpcgStrategy::BLOCK_SUBSPACE
403+
);
404+
405+
auto h_op = [&H_mat, n_dim](T* in, T* out, int ld_in, int ncol) {
406+
dense_h_multiply(H_mat.data(), n_dim, in, out, ld_in, ncol);
407+
};
408+
409+
solver.diag(h_op, nullptr, ld, nband, n_dim,
410+
psi.data(), eval.data(), ethr, prec.data());
411+
412+
const Real delta = std::sqrt(1.25);
413+
for (int i = 0; i < nband; ++i)
414+
EXPECT_TRUE(std::isfinite(eval[i])) << "BLOCK_SUBSPACE produced NaN/Inf";
415+
EXPECT_NEAR(eval[0], 2.5 - delta, 1e-8);
416+
EXPECT_NEAR(eval[1], 2.5 + delta, 1e-8);
417+
}
418+
381419
// =============================================================================
382420
// Test fixture: degenerate eigenvalues
383421
// H = I + J (identity plus all-ones), 4×4.

source/source_io/module_parameter/read_input_item_elec_stru.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ For plane-wave basis,
5656
* bpcg: The BPCG method, which is a block-parallel Conjugate Gradient (CG) method, typically exhibits higher acceleration in a GPU environment.
5757
* dav: The Davidson algorithm.
5858
* dav_subspace: The Davidson algorithm without orthogonalization operation, this method is the most recommended for efficiency. `pw_diag_ndim` can be set to 2 for this method.
59+
* ppcg: The projection preconditioned conjugate-gradient method, currently available for CPU plane-wave calculations.
5960
6061
For numerical atomic orbitals basis,
6162
@@ -131,7 +132,7 @@ Then the user has to correct the input file and restart the calculation.)";
131132
};
132133
item.check_value = [](const Input_Item& item, const Parameter& para) {
133134
const std::string& ks_solver = para.input.ks_solver;
134-
const std::vector<std::string> pw_solvers = {"cg", "dav", "bpcg", "dav_subspace"};
135+
const std::vector<std::string> pw_solvers = {"cg", "dav", "bpcg", "dav_subspace", "ppcg"};
135136
const std::vector<std::string> lcao_solvers = {
136137
"genelpa",
137138
"elpa",
@@ -1040,7 +1041,7 @@ Use case: When experimental or high-level theoretical results suggest that the S
10401041
item.annotation = "threshold for eigenvalues is cg electron iterations";
10411042
item.category = "Plane wave related variables";
10421043
item.type = "Real";
1043-
item.description = "Only used when you use ks_solver = cg/dav/dav_subspace/bpcg. It indicates the threshold for the first electronic iteration, from the second iteration the pw_diag_thr will be updated automatically. For nscf calculations with planewave basis set, pw_diag_thr should be <= 1e-3.";
1044+
item.description = "Only used when you use ks_solver = cg/dav/dav_subspace/bpcg/ppcg. It indicates the threshold for the first electronic iteration, from the second iteration the pw_diag_thr will be updated automatically. For nscf calculations with planewave basis set, pw_diag_thr should be <= 1e-3.";
10441045
item.default_value = "0.01";
10451046
item.unit = "";
10461047
item.availability = "";
@@ -1102,10 +1103,10 @@ Use case: When experimental or high-level theoretical results suggest that the S
11021103
item.annotation = "max iteration number for cg";
11031104
item.category = "Plane wave related variables";
11041105
item.type = "Integer";
1105-
item.description = "Only useful when you use ks_solver = cg/dav/dav_subspace/bpcg. It indicates the maximal iteration number for cg/david/dav_subspace/bpcg method.";
1106+
item.description = "Only useful when you use ks_solver = cg/dav/dav_subspace/bpcg/ppcg. It indicates the maximal iteration number for cg/david/dav_subspace/bpcg/ppcg method.";
11061107
item.default_value = "50";
11071108
item.unit = "";
1108-
item.availability = "basis_type==pw, ks_solver==cg/dav/dav_subspace/bpcg";
1109+
item.availability = "basis_type==pw, ks_solver==cg/dav/dav_subspace/bpcg/ppcg";
11091110
read_sync_int(input.pw_diag_nmax);
11101111
this->add_item(item);
11111112
}

0 commit comments

Comments
 (0)