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