1 //
2 // BAGEL - Brilliantly Advanced General Electronic Structure Library
3 // Filename: asd_dmrg_base.cc
4 // Copyright (C) 2014 Toru Shiozaki
5 //
6 // Author: Shane Parker <shane.parker@u.northwestern.edu>
7 // Maintainer: Shiozaki Group
8 //
9 // This file is part of the BAGEL package.
10 //
11 // This program is free software: you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation, either version 3 of the License, or
14 // (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 //
24 
25 #include <src/asd/dmrg/asd_dmrg.h>
26 #include <src/scf/hf/fock.h>
27 #include <src/mat1e/overlap.h>
28 #include <src/wfn/localization.h>
29 
30 using namespace std;
31 using namespace bagel;
32 
ASD_DMRG(shared_ptr<const PTree> input,shared_ptr<const Reference> iref)33 ASD_DMRG::ASD_DMRG(shared_ptr<const PTree> input, shared_ptr<const Reference> iref) : input_(input) {
34   // initialize parameters
35   nsites_ = input_->get<int>("nsites", 1);
36   if (nsites_ <= 1) throw runtime_error("nsites_ has to be specified and should be no smaller than two");
37   nstate_ = input_->get<int>("nstate", 1);
38   charge_ = input_->get<int>("charge", 0);
39   nspin_ = input_->get<int>("nspin", 0);
40   active_sizes_ = input_->get_vector<int>("active_sizes");
41   active_electrons_ = input_->get_vector<int>("active_electrons");
42   region_sizes_ = input_->get_vector<int>("region_sizes");
43   assert(accumulate(region_sizes_.begin(), region_sizes_.end(), 0) == iref->geom()->natom());
44 
45   ntrunc_ = input_->get<int>("ntrunc", 0);
46   if (ntrunc_ == 0) throw runtime_error("ntrunc_ has to be provided");
47   thresh_ = input_->get<double>("thresh", 1.0e-6);
48   maxiter_ = input_->get<int>("maxiter", 50);
49   // perturbation parameters
50   perturb_ = input_->get<double>("perturb", 1.0e-3);
51   perturb_thresh_ = input_->get<double>("perturb_thresh", 1.0e-4);
52   perturb_min_ = input_->get<double>("perturb_min", 1.0e-5);
53 
54   down_thresh_ = input_->get<double>("down_thresh", 1.0e-8);
55   auto down = input_->get_child_optional("down_sweep_truncs");
56   down_sweep_ = static_cast<bool>(down);
57   if (down_sweep_)
58     down_sweep_truncs_ = input_->get_vector<int>("down_sweep_truncs");
59 
60   auto winput = input_->get_child_optional("weights");
61   if (winput)
62     weights_ = input_->get_vector<double>("weights", nstate_);
63   else
64     weights_.resize(nstate_, 1.0/static_cast<double>(nstate_));
65 
66   energies_.resize(nstate_);
67   sweep_energies_.resize(nstate_);
68 
69   // reorder coeff to closed-active-virtual
70   rearrange_orbitals(iref);
71 }
72 
73 
rearrange_orbitals(shared_ptr<const Reference> iref)74 void ASD_DMRG::rearrange_orbitals(shared_ptr<const Reference> iref) {
75   auto out_coeff = iref->coeff()->clone();
76 
77   const int nactele = accumulate(active_electrons_.begin(), active_electrons_.end(), 0);
78   const int nclosed = (iref->geom()->nele() - charge_ - nactele) / 2;
79   assert((iref->geom()->nele() - charge_ - nactele) % 2 == 0);
80   const int nactive = accumulate(active_sizes_.begin(), active_sizes_.end(), 0);
81   const int nvirt = iref->coeff()->mdim() - nclosed - nactive;
82   const int nbasis = iref->geom()->nbasis();
83 
84   set<int> active_set;
85   {
86     vector<int> act_vec = input_->get_vector<int>("active_orbitals");
87     for_each(act_vec.begin(), act_vec.end(), [](int& x) { --x; });
88     active_set.insert(act_vec.begin(), act_vec.end());
89   }
90 
91   int pos = nclosed;
92   for (const int& aorb : active_set)
93     copy_n(iref->coeff()->element_ptr(0, aorb), nbasis, out_coeff->element_ptr(0, pos++));
94 
95   int closed_position = 0;
96   int virt_position = nclosed + nactive;
97   for (int iorb = 0; iorb != out_coeff->mdim(); ++iorb)
98     if (active_set.count(iorb) == 0)
99       copy_n(iref->coeff()->element_ptr(0, iorb), nbasis, out_coeff->element_ptr(0, (closed_position < nclosed ? closed_position++ : virt_position++)));
100   assert(closed_position == nclosed);
101   assert(virt_position == out_coeff->mdim());
102 
103   sref_ = make_shared<Reference>(iref->geom(), make_shared<Coeff>(move(*out_coeff)), nclosed, nactive, nvirt);
104 }
105 
106 
project_active()107 void ASD_DMRG::project_active() {
108 
109   auto out_coeff = sref_->coeff()->copy();
110 
111   vector<pair<int, int>> region_bounds;
112   {
113     int atom_start = 0;
114     int current = 0;
115     for (int natom : region_sizes_) {
116       const int bound_start = current;
117       for (int iatom = 0; iatom != natom; ++iatom)
118         current += sref_->geom()->atoms(atom_start+iatom)->nbasis();
119       region_bounds.emplace_back(bound_start, current);
120       atom_start += natom;
121     }
122   }
123   assert(region_bounds.size() == nsites_);
124 
125   const Matrix ovlp = static_cast<Matrix>(Overlap(sref_->geom()));
126 
127   const int nclosed = sref_->nclosed();
128 
129   shared_ptr<const Matrix> fock;
130   if (nclosed) {// construct Fock
131     shared_ptr<const Matrix> density = sref_->coeff()->form_density_rhf(nclosed);
132     const MatView ccoeff = sref_->coeff()->slice(0, nclosed);
133     fock = make_shared<const Fock<1>>(sref_->geom(), sref_->hcore(), density, ccoeff);
134   } else {
135     fock = sref_->hcore();
136   }
137 
138   const int nactive = sref_->nact();
139 
140   shared_ptr<const PTree> localization_data = input_->get_child_optional("localization");
141   if (localization_data) {
142     // do localization
143     string localizemethod = localization_data->get<string>("algorithm", "pm");
144     shared_ptr<OrbitalLocalization> localization;
145     auto input_data = make_shared<PTree>(*localization_data);
146     input_data->erase("occupied");input_data->put("occupied", false);
147     input_data->erase("active");  input_data->put("active", true);
148     input_data->erase("virtual"); input_data->put("virtual", false);
149     if (localizemethod == "region") {
150       localization = make_shared<RegionLocalization>(input_data, sref_, region_sizes_);
151     } else if (localizemethod == "pm" || localizemethod == "pipek-mezey") {
152       input_data->erase("type"); input_data->put("type", "region");
153       localization = make_shared<PMLocalization>(input_data, sref_, region_sizes_);
154     } else throw runtime_error("Unrecognized orbital localization method");
155 
156     shared_ptr<const Matrix> active_coeff = localization->localize()->get_submatrix(0, nclosed, sref_->geom()->nbasis(), nactive);
157 
158     Matrix ShalfC(ovlp);
159     ShalfC.sqrt();
160     ShalfC *= *active_coeff;
161 
162     { // assign localized active orbitals to site by their lowdin population
163       vector<set<int>> orbital_sets(nsites_);
164 
165       vector<vector<double>> lowdin_populations(nactive);
166       for (auto& p : lowdin_populations) { p.resize(nsites_); }
167       Matrix Q(nactive, nactive);
168       for (int site = 0; site != nsites_; ++site) {
169         const pair<int, int> bounds = region_bounds[site];
170         const int nregion_basis = bounds.second - bounds.first;
171         dgemm_("T", "N", nactive, nactive, nregion_basis, 1.0, ShalfC.element_ptr(bounds.first, 0), ShalfC.ndim(),
172                                                                ShalfC.element_ptr(bounds.first, 0), ShalfC.ndim(),
173                                                           0.0, Q.data(), Q.ndim());
174         for (int orb = 0; orb != nactive; ++orb)
175           lowdin_populations[orb][site] = Q(orb, orb) * Q(orb, orb);
176       }
177 
178       for (int orb = 0; orb != nactive; ++orb) {
179         //pick the largest value to assign it to sites
180         vector<double>& pops = lowdin_populations[orb];
181         auto maxiter = max_element(pops.begin(), pops.end());
182         const int maxsite = maxiter - pops.begin();
183         orbital_sets[maxsite].insert(orb);
184       }
185 
186       // check if orbital_sets agree with input active_sizes
187       vector<int> orb_set;
188       for (auto iset : orbital_sets) orb_set.push_back(iset.size());
189       if (orb_set != active_sizes_) {
190         cout << "active_sizes : "; for (auto& e : active_sizes_) cout << e << " ";
191         cout << "localized active sizes : "; for (auto& e : orb_set) cout<< e << " ";
192         throw runtime_error("Localized active orbital partition does not agree with input active_sizes, redefine active subspaces");
193       }
194 
195       int imo = nclosed;
196       for (auto& subset : orbital_sets) {
197         if (subset.empty())
198           throw runtime_error("one of the active subspaces has bad definition, no matching active orbitals are localized on it, please redefine subspaces");
199 
200         Matrix subspace(out_coeff->ndim(), subset.size());
201         int pos = 0;
202         for (const int& i : subset)
203           copy_n(active_coeff->element_ptr(0, i), active_coeff->ndim(), subspace.element_ptr(0, pos++));
204 
205         // canonicalize within active subspaces
206         Matrix subfock(subspace % *fock * subspace);
207         VectorB eigs(subspace.ndim());
208         subfock.diagonalize(eigs);
209         subspace *= subfock;
210 
211         copy_n(subspace.data(), subspace.size(), out_coeff->element_ptr(0, imo));
212         imo += subset.size();
213       }
214     }
215   } else {
216     // do projection
217     auto active_coeff = sref_->coeff()->get_submatrix(0, nclosed, sref_->geom()->nbasis(), nactive);
218 
219     Matrix transform(nactive, nactive);
220     {
221       int ipos = 0;
222       for (int isite = 0; isite != nsites_; ++isite) {
223         pair<int, int> bounds = region_bounds[isite];
224         const int nsub = active_sizes_[isite];
225         const int nbasis = bounds.second - bounds.first;
226         shared_ptr<const Matrix> sub_ovlp = ovlp.get_submatrix(bounds.first, bounds.first, nbasis, nbasis);
227         shared_ptr<const Matrix> sub_actorb = active_coeff->get_submatrix(bounds.first, 0, nbasis, nactive);
228         Matrix SVD(*sub_actorb % *sub_ovlp * *sub_actorb);
229         VectorB eigs(nactive);
230         SVD.diagonalize(eigs);
231         const MatView sub_trans = SVD.slice(nactive-nsub, nactive);
232         copy_n(sub_trans.data(), sub_trans.size(), transform.element_ptr(0, ipos));
233         ipos += nsub;
234       }
235       assert(ipos == nactive);
236       // Lowdin orthonormalization
237       Matrix inv_hf(transform % transform);
238       inv_hf.inverse_half();
239       transform *= inv_hf;
240       cout << endl << string(6, '*') << "  If linear dependency is detected, you have to find better active orbitals to do projection  " << string(6, '*') << endl << endl;
241     }
242 
243     // projected coeff
244     Matrix projected(*active_coeff * transform);
245     // canonicalization
246     Matrix fockmo(projected % *fock * projected);
247     VectorB eigs(projected.mdim());
248     shared_ptr<const Matrix> active_transformation = fockmo.diagonalize_blocks(eigs, active_sizes_);
249     const Matrix transformed_mos(projected * *active_transformation);
250 
251     copy_n(transformed_mos.data(), transformed_mos.size(), out_coeff->element_ptr(0, nclosed));
252   }
253 
254   sref_ = make_shared<Reference>(*sref_, make_shared<Coeff>(move(*out_coeff)));
255 }
256 
257 
build_reference(const int site,const vector<bool> meanfield) const258 shared_ptr<Reference> ASD_DMRG::build_reference(const int site, const vector<bool> meanfield) const {
259   assert(meanfield.size() == nsites_ && site < nsites_ && site >= 0);
260 
261   vector<shared_ptr<const MatView>> closed_orbitals = { make_shared<MatView>(sref_->coeff()->slice(0, sref_->nclosed())) };
262   const int act_start = accumulate(active_sizes_.begin(), active_sizes_.begin()+site, sref_->nclosed());
263   const int act_fence = act_start + active_sizes_.at(site);
264   const MatView active_orbitals = sref_->coeff()->slice(act_start, act_fence);
265 
266   int current = sref_->nclosed();
267   for (int i = 0; i != nsites_; ++i) {
268     if (meanfield[i] && i != site)
269       closed_orbitals.push_back(make_shared<const MatView>(sref_->coeff()->slice(current, current+(active_electrons_.at(i)+1)/2)));
270     current += active_sizes_.at(i);
271   }
272 
273   const int nclosed = accumulate(closed_orbitals.begin(), closed_orbitals.end(), 0, [](int x, shared_ptr<const MatView>m) { return x + m->mdim(); });
274   const int nact = active_orbitals.mdim();
275 
276   auto out = make_shared<Matrix>(sref_->geom()->nbasis(), nclosed+nact);
277   current = 0;
278   closed_orbitals.push_back(make_shared<MatView>(active_orbitals));
279   for (auto& orbitals : closed_orbitals) {
280     copy_n(orbitals->data(), orbitals->size(), out->element_ptr(0, current));
281     current += orbitals->mdim();
282   }
283 
284   return make_shared<Reference>(sref_->geom(), make_shared<Coeff>(move(*out)), nclosed, nact, 0);
285 }
286 
287 
print_progress(const int position,const string left_symbol,const string right_symbol) const288 string ASD_DMRG::print_progress(const int position, const string left_symbol, const string right_symbol) const {
289   stringstream out;
290   for (int i = 0; i < position; ++i) out << left_symbol << " ";
291   out << "** ";
292   for (int i = position+1; i < nsites_; ++i) out << right_symbol << " ";
293 
294   return out.str();
295 }
296 
297 
prepare_growing_input(const int site) const298 vector<shared_ptr<PTree>> ASD_DMRG::prepare_growing_input(const int site) const {
299   vector<shared_ptr<PTree>> out;
300 
301   shared_ptr<PTree> base_input = input_->get_child_optional("ras");
302   if (!base_input) base_input = make_shared<PTree>();
303 
304   base_input->erase("charge");
305   base_input->erase("nspin");
306   base_input->erase("nstate");
307 
308   auto space = input_->get_child_optional("spaces");
309   if (!space) throw runtime_error("spaces must be specified");
310   else if ( !(space->size()==1 || space->size()==nsites_) )
311     throw runtime_error("Must specify either one \"space\" object or one per site");
312 
313   auto space_iter = space->begin();
314   if (space->size() == nsites_)
315     advance(space_iter, site);
316 
317   // Quirk of PTree class requires this intermediate step
318   auto space_input = make_shared<PTree>(**space_iter);
319 
320   for (auto& siter : *space_input) {
321     auto tmp = make_shared<PTree>(*base_input);
322     tmp->put("charge", siter->get<string>("charge"));
323     tmp->put("nspin", siter->get<string>("nspin"));
324     tmp->put("nstate", siter->get<string>("nstate"));
325     out.push_back(tmp);
326   }
327 
328   return out;
329 }
330 
331 
prepare_sweeping_input(const int site) const332 shared_ptr<PTree> ASD_DMRG::prepare_sweeping_input(const int site) const {
333   shared_ptr<PTree> out = input_->get_child_optional("ras");
334   if (!out) out = make_shared<PTree>();
335 
336   out->erase("charge"); out->put("charge", charge_);
337   out->erase("nspin");  out->put("nspin", nspin_);
338   out->erase("nstate"); out->put("nstate", input_->get<string>("nstate", "1"));
339 
340   return out;
341 }
342 
343 
344