Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FCI and GENCI initial core guess determinants for Davidson-Liu solver #377

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
8 changes: 8 additions & 0 deletions docs/source/options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1863,6 +1863,14 @@ Type: int

Default value: 10

**CORE_GUESS**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need a better name for this. Core_guess does not make sense to me. Making it longer and clearer what it means.


Integer boolean. Include determinants with single- and none-occupation in the first orbital position as the initial guess space for the DL solver

Type: int_list

Default value: [0]

**PRINT_NO**

Print the NO from the rdm of FCI
Expand Down
2 changes: 2 additions & 0 deletions forte/base_classes/active_space_method.cc
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ void ActiveSpaceMethod::set_r_convergence(double value) { r_convergence_ = value

void ActiveSpaceMethod::set_maxiter(size_t value) { maxiter_ = value; }

void ActiveSpaceMethod::set_core_guess(bool core_guess) { core_guess_ = core_guess; }

void ActiveSpaceMethod::set_read_wfn_guess(bool read) { read_wfn_guess_ = read; }

void ActiveSpaceMethod::set_dump_wfn(bool dump) { dump_wfn_ = dump; }
Expand Down
6 changes: 6 additions & 0 deletions forte/base_classes/active_space_method.h
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,9 @@ class ActiveSpaceMethod {
/// @param value the maximum number of iterations
void set_maxiter(size_t value);

/// Enable/disable core determinants as initial guess for Davidson-Liu
void set_core_guess(bool core_guess);

/// Set if we dump the wave function to disk
void set_read_wfn_guess(bool read);

Expand Down Expand Up @@ -379,6 +382,9 @@ class ActiveSpaceMethod {
/// The maximum number of iterations
size_t maxiter_ = 100;

/// Use core determinants as initial guess for Davidson-Liu?
bool core_guess_ = false;

/// The root used to compute properties (zero based, default = 0)
int root_ = 0;

Expand Down
41 changes: 39 additions & 2 deletions forte/base_classes/active_space_solver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ const std::map<StateInfo, std::vector<double>>& ActiveSpaceSolver::compute_energ
method->set_e_convergence(e_convergence_);
method->set_r_convergence(r_convergence_);
method->set_maxiter(maxiter_);

// set boolean for using core determinants in Davidson-Liu algorithm
method->set_core_guess(state.core_guess());

if (read_initial_guess_) {
state_filename_map_[state] = method->wfn_filename();
Expand Down Expand Up @@ -423,6 +426,9 @@ make_state_weights_map(std::shared_ptr<ForteOptions> options,
// check if the user provided a AVG_STATE list
py::list avg_state = options->get_gen_list("AVG_STATE");

bool core_guess;
auto core_guess_list = options->get_int_list("CORE_GUESS");

std::vector<size_t> gas_min(6, 0);
std::vector<size_t> gas_max(6);
for (int i = 0; i < 6; ++i) {
Expand Down Expand Up @@ -458,8 +464,21 @@ make_state_weights_map(std::shared_ptr<ForteOptions> options,
gas_max[gasn] = gas_space_max[0];
}
}

// if 'CORE_GUESS' list is given convert valid first 'CORE_GUESS' entry to boolean
if (core_guess_list.empty()){
core_guess = false;
} else if (!(core_guess_list[0] != 0 || core_guess_list[0] != 1)) {
psi::outfile->Printf("\n Error: wrong entry value for CORE_GUESS (%d). "
"Only values of 0 or 1 are acceptable",
core_guess_list[0]);
throw std::runtime_error("Wrong input value for CORE_GUESS.");
} else {
core_guess = static_cast<bool>(core_guess_list[0]);
}

StateInfo state_this(state.na(), state.nb(), state.multiplicity(), state.twice_ms(),
state.irrep(), state.irrep_label(), gas_min, gas_max);
state.irrep(), state.irrep_label(), gas_min, gas_max, core_guess);
state_weights_map[state_this] = weights;
} else {
double sum_of_weights = 0.0;
Expand Down Expand Up @@ -560,8 +579,26 @@ make_state_weights_map(std::shared_ptr<ForteOptions> options,
}
}

// if 'CORE_GUESS' list is given convert valid 'CORE_GUESS' entries to boolean
if (core_guess_list.empty()){
core_guess = false;
} else if (core_guess_list.size() != nentry) {
psi::outfile->Printf("\n Error: mismatched number of entries in AVG_STATE "
"(%d) and CORE_GUESS (%d).",
nentry, core_guess_list.size());
throw std::runtime_error(
"Mismatched number of entries in AVG_STATE and CORE_GUESS.");
} else if (!(core_guess_list[i] == 0 || core_guess_list[i] == 1)) {
psi::outfile->Printf("\n Error: wrong entry value for CORE_GUESS (%d). "
"Only values of 0 or 1 are acceptable",
core_guess_list[i]);
throw std::runtime_error("Wrong input value for CORE_GUESS.");
} else {
core_guess = static_cast<bool>(core_guess_list[i]);
}

StateInfo state_this(state.na(), state.nb(), multi, state.twice_ms(), irrep,
irrep_label, gas_min, gas_max);
irrep_label, gas_min, gas_max, core_guess);
state_weights_map[state_this] = weights;
}

Expand Down
25 changes: 17 additions & 8 deletions forte/base_classes/state_info.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ namespace forte {

StateInfo::StateInfo(int na, int nb, int multiplicity, int twice_ms, int irrep,
const std::string& irrep_label, const std::vector<size_t> gas_min,
const std::vector<size_t> gas_max)
const std::vector<size_t> gas_max, bool core_guess)
: na_(na), nb_(nb), multiplicity_(multiplicity), twice_ms_(twice_ms), irrep_(irrep),
irrep_label_(irrep_label), gas_min_(gas_min), gas_max_(gas_max) {}
irrep_label_(irrep_label), gas_min_(gas_min), gas_max_(gas_max), core_guess_(core_guess) {}

const std::vector<std::string> StateInfo::multiplicity_labels{
"Singlet", "Doublet", "Triplet", "Quartet", "Quintet", "Sextet", "Septet", "Octet",
Expand All @@ -58,6 +58,8 @@ int StateInfo::twice_ms() const { return twice_ms_; }

int StateInfo::irrep() const { return irrep_; }

bool StateInfo::core_guess() const { return core_guess_; }

const std::string& StateInfo::irrep_label() const { return irrep_label_; }

const std::string& StateInfo::multiplicity_label() const {
Expand All @@ -71,8 +73,8 @@ const std::vector<size_t>& StateInfo::gas_max() const { return gas_max_; }
bool StateInfo::operator<(const StateInfo& rhs) const {
// Make sure the roots are in increasing energy order for core-excited state calcualtions
if ((gas_min_ == rhs.gas_min_) && (gas_max_ == rhs.gas_max_)) {
return std::tie(na_, nb_, multiplicity_, twice_ms_, irrep_) <
std::tie(rhs.na_, rhs.nb_, rhs.multiplicity_, rhs.twice_ms_, rhs.irrep_);
return std::tie(na_, nb_, multiplicity_, twice_ms_, irrep_, core_guess_) <
std::tie(rhs.na_, rhs.nb_, rhs.multiplicity_, rhs.twice_ms_, rhs.irrep_, rhs.core_guess_);
} else if (gas_max_ == rhs.gas_max_) {
// The state with a smaller gas occupation in the first gas space is 'bigger'.
// Ground state is smaller than core-excited state under this definition.
Expand All @@ -83,15 +85,15 @@ bool StateInfo::operator<(const StateInfo& rhs) const {
}

bool StateInfo::operator!=(const StateInfo& rhs) const {
return std::tie(na_, nb_, multiplicity_, twice_ms_, irrep_, gas_min_, gas_max_) !=
return std::tie(na_, nb_, multiplicity_, twice_ms_, irrep_, gas_min_, gas_max_, core_guess_) !=
std::tie(rhs.na_, rhs.nb_, rhs.multiplicity_, rhs.twice_ms_, rhs.irrep_, rhs.gas_min_,
rhs.gas_max_);
rhs.gas_max_, rhs.core_guess_);
}

bool StateInfo::operator==(const StateInfo& rhs) const {
return std::tie(na_, nb_, multiplicity_, twice_ms_, irrep_, gas_min_, gas_max_) ==
return std::tie(na_, nb_, multiplicity_, twice_ms_, irrep_, gas_min_, gas_max_, core_guess_) ==
std::tie(rhs.na_, rhs.nb_, rhs.multiplicity_, rhs.twice_ms_, rhs.irrep_, rhs.gas_min_,
rhs.gas_max_);
rhs.gas_max_, rhs.core_guess_);
}

StateInfo make_state_info_from_psi(std::shared_ptr<ForteOptions> options) {
Expand Down Expand Up @@ -194,6 +196,13 @@ std::size_t StateInfo::hash() const {
repr += "_" + std::to_string(i);
for (size_t i : gas_max_)
repr += "_" + std::to_string(i);

if (core_guess_) {
repr += "_" + std::to_string(1);
} else {
repr += "_" + std::to_string(0);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

repr += "" + std::to_string(core_guess ? 1 : 0);

return std::hash<std::string>{}(repr);
}

Expand Down
6 changes: 5 additions & 1 deletion forte/base_classes/state_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class StateInfo {
StateInfo(int na, int nb, int multiplicity, int twice_ms, int irrep,
const std::string& irrep_label = "",
const std::vector<size_t> gas_min = std::vector<size_t>(),
const std::vector<size_t> gas_max = std::vector<size_t>());
const std::vector<size_t> gas_max = std::vector<size_t>(), bool core_guess = false);

StateInfo() = default;

Expand All @@ -62,6 +62,8 @@ class StateInfo {
int twice_ms() const;
/// return the irrep
int irrep() const;
/// return the bool value for running Davidson-Liu with core determinants
bool core_guess() const;
/// return the multiplicity symbol
const std::string& multiplicity_label() const;
/// return the irrep symbol
Expand Down Expand Up @@ -98,6 +100,8 @@ class StateInfo {
int twice_ms_;
// Irrep
int irrep_;
/// Use core determinants as initial guess for Davidson-Liu?
bool core_guess_;
// Irrep label
std::string irrep_label_;
// minimum number of electrons in each gas space
Expand Down
19 changes: 16 additions & 3 deletions forte/fci/fci_solver_initial_guess.cc
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,26 @@ std::vector<Determinant> FCISolver::initial_guess_generate_dets(std::shared_ptr<
vec_e_I.begin(), vec_e_I.end(),
[&e](const std::tuple<double, size_t>& t) { return e < std::get<0>(t); });
vec_e_I.insert(it, std::make_tuple(e, I));
emax = std::get<0>(vec_e_I.back());
// Do not update the maximum energy threshold if using core determinants as initial guess
if (!(core_guess_)){
emax = std::get<0>(vec_e_I.back());
}
added++;
}
}

std::vector<Determinant> guess_dets;
for (const auto& [e, I] : vec_e_I) {
guess_dets.push_back(lists_->determinant(I, symmetry_));
const auto& det = lists_->determinant(I, symmetry_);
// If using core determinants as initial guess include those with
// single and double holes in first bit position
if (core_guess_){
if (!(det.get_alfa_bit(0) and det.get_beta_bit(0))){
guess_dets.push_back(det);
}
} else {
guess_dets.push_back(det);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let us change push_back to emplace_back here, though the speed does not matter at this place. I can barely find a place where emplace_back is worse than push_back

}
}

// Make sure that the spin space is complete
Expand Down Expand Up @@ -99,7 +111,8 @@ FCISolver::initial_guess_det(std::shared_ptr<psi::Vector> diag, size_t num_guess
// here we use a standard guess procedure
return find_initial_guess_det(guess_dets, guess_dets_pos, num_guess_states, fci_ints,
state().multiplicity(), true, print_ >= PrintLevel::Default,
std::vector<std::vector<std::pair<size_t, double>>>());
std::vector<std::vector<std::pair<size_t, double>>>(),
core_guess_);
}

sparse_mat FCISolver::initial_guess_csf(std::shared_ptr<psi::Vector> diag,
Expand Down
19 changes: 16 additions & 3 deletions forte/genci/genci_solver_initial_guess.cc
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,26 @@ std::vector<Determinant> GenCISolver::initial_guess_generate_dets(std::shared_pt
vec_e_I.begin(), vec_e_I.end(),
[&e](const std::tuple<double, size_t>& t) { return e < std::get<0>(t); });
vec_e_I.insert(it, std::make_tuple(e, I));
emax = std::get<0>(vec_e_I.back());
// Do not update the maximum energy threshold if using core determinants as initial guess
if (!(core_guess_)){
emax = std::get<0>(vec_e_I.back());
}
added++;
}
}

std::vector<Determinant> guess_dets;
for (const auto& [e, I] : vec_e_I) {
guess_dets.push_back(lists_->determinant(I));
const auto& det = lists_->determinant(I);
// If using core determinants as initial guess include those with
// single and double holes in first bit position
if (core_guess_){
if (!(det.get_alfa_bit(0) and det.get_beta_bit(0))){
guess_dets.push_back(det);
}
} else {
guess_dets.push_back(det);
}
}

// Make sure that the spin space is complete
Expand Down Expand Up @@ -99,7 +111,8 @@ GenCISolver::initial_guess_det(std::shared_ptr<psi::Vector> diag, size_t num_gue
// here we use a standard guess procedure
return find_initial_guess_det(guess_dets, guess_dets_pos, num_guess_states, fci_ints,
state().multiplicity(), true, print_ >= PrintLevel::Default,
std::vector<std::vector<std::pair<size_t, double>>>());
std::vector<std::vector<std::pair<size_t, double>>>(),
core_guess_);
}

sparse_mat GenCISolver::initial_guess_csf(std::shared_ptr<psi::Vector> diag,
Expand Down
2 changes: 2 additions & 0 deletions forte/register_forte_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,8 @@ def register_aci_options(options):

options.add_str("DIAG_ALGORITHM", "SPARSE", ["DYNAMIC", "FULL", "SPARSE"], "The diagonalization method")

options.add_int_list("CORE_GUESS", "Add core determinants as initial guess")

options.add_bool("FORCE_DIAG_METHOD", False, "Force the diagonalization procedure?")

options.add_bool("ONE_CYCLE", False, "Doing only one cycle of ACI (FCI) ACI iteration?")
Expand Down
9 changes: 7 additions & 2 deletions forte/sparse_ci/sparse_initial_guess.cc
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,17 @@ find_initial_guess_det(const std::vector<Determinant>& guess_dets,
const std::vector<size_t>& guess_dets_pos, size_t num_guess_states,
const std::shared_ptr<ActiveSpaceIntegrals>& as_ints, int multiplicity,
bool do_spin_project, bool print,
const std::vector<std::vector<std::pair<size_t, double>>>& user_guess) {
const std::vector<std::vector<std::pair<size_t, double>>>& user_guess,
bool core_guess) {
size_t num_guess_dets = guess_dets.size();

if (print) {
print_h2("Initial Guess");
psi::outfile->Printf("\n Initial guess determinants: %zu", guess_dets.size());
std::string guess = "guess";
if (core_guess) {
guess = "(core) guess";
}
psi::outfile->Printf("\n Initial %s determinants: %zu", guess.c_str(), guess_dets.size());
}

auto [HS2full, S2evals, S2evecs] =
Expand Down
3 changes: 2 additions & 1 deletion forte/sparse_ci/sparse_initial_guess.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ find_initial_guess_det(const std::vector<Determinant>& guess_dets,
const std::vector<size_t>& guess_dets_pos, size_t num_guess_states,
const std::shared_ptr<ActiveSpaceIntegrals>& as_ints, int multiplicity,
bool do_spin_project, bool print,
const std::vector<std::vector<std::pair<size_t, double>>>& user_guess);
const std::vector<std::vector<std::pair<size_t, double>>>& user_guess,
bool core_guess = false);

/// @brief Generate initial guess vectors for the Davidson-Liu solver starting from a set of guess
/// configurations
Expand Down
41 changes: 41 additions & 0 deletions tests/methods/fci-core-1/input.dat
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# LiH 6-31g basis FCI core excited roots
import forte

refscf = -7.9791777935853290
reffci = -5.851395993559
reffci_avg = -5.5360452551438

molecule {
0 1
Li
H 1 R

R = 3.0
units bohr
}

set {
basis 6-31g
scf_type pk
e_convergence 12
}

set forte {
active_space_solver genci
core_guess 1
}

energy('scf')
compare_values(refscf, variable("CURRENT ENERGY"),11, "SCF energy") #TEST

energy('forte')
compare_values(reffci, variable("CURRENT ENERGY"),11, "FCI energy") #TEST

set forte {
active_space_solver genci
avg_state [[1,1,3]]
core_guess [1]
}

energy('forte')
compare_values(reffci_avg, variable("CURRENT ENERGY"),11, "FCI avg energy") #TEST
Loading