1 //
2 // BAGEL - Brilliantly Advanced General Electronic Structure Library
3 // Filename: cassecond.cc
4 // Copyright (C) 2016 Toru Shiozaki
5 //
6 // Author: Toru Shiozaki <shiozaki@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/util/math/aughess.h>
26 #include <src/scf/hf/fock.h>
27 #include <src/multi/casscf/qvec.h>
28 #include <src/multi/casscf/cassecond.h>
29 #include <src/prop/hyperfine.h>
30
31 using namespace std;
32 using namespace bagel;
33
compute()34 void CASSecond::compute() {
35 assert(nvirt_ && nact_);
36 Timer timer;
37
38 muffle_->mute();
39 for (int iter = 0; iter != max_iter_; ++iter) {
40
41 // first perform CASCI to obtain RDMs
42 {
43 if (iter) fci_->update(coeff_);
44 Timer fci_time(0);
45 if (external_rdm_.empty()) {
46 fci_->compute();
47 fci_->compute_rdm12();
48 } else {
49 if (iter != 0)
50 throw runtime_error("\"external_rdm\" should be used with maxiter == 1");
51 fci_->read_external_rdm12_av(external_rdm_);
52 }
53 trans_natorb();
54 fci_time.tick_print("FCI and RDMs");
55 energy_ = fci_->energy();
56 }
57
58 shared_ptr<const Matrix> cfockao = fci_->jop()->core_fock();
59 shared_ptr<const Matrix> afockao = compute_active_fock(coeff_->slice(nclosed_, nocc_), fci_->rdm1_av());
60 shared_ptr<const Matrix> cfock = make_shared<Matrix>(*coeff_ % *cfockao * *coeff_);
61 shared_ptr<const Matrix> afock = make_shared<Matrix>(*coeff_ % *afockao * *coeff_);
62 shared_ptr<const Qvec> qxr = make_shared<Qvec>(coeff_->mdim(), nact_, coeff_, nclosed_, fci_, fci_->rdm2_av());
63
64 shared_ptr<const RotFile> grad = compute_gradient(cfock, afock, qxr);
65
66 // check gradient and break if converged
67 const double gradient = grad->rms();
68 print_iteration(iter, energy_, gradient, timer.tick());
69 if (gradient < thresh_) {
70 muffle_->unmute();
71 cout << endl << " * Second-order optimization converged. * " << endl << endl;
72 break;
73 }
74
75 // half-transformed integrals (with JJ)
76 shared_ptr<const DFHalfDist> half_1j = nclosed_ ? dynamic_pointer_cast<const Fock<1>>(cfockao)->half() : nullptr;
77 shared_ptr<const DFHalfDist> half = nclosed_ ? half_1j->apply_J() : nullptr;
78 shared_ptr<const DFHalfDist> halfa = fci_->jop()->mo2e_1ext()->apply_JJ();
79
80 // compute denominator...
81 shared_ptr<const RotFile> denom = compute_denom(half, half_1j, halfa, cfock, afock);
82
83 AugHess<RotFile> solver(max_micro_iter_, grad);
84 // initial trial vector
85 shared_ptr<RotFile> trot = apply_denom(grad, denom, 0.001, 1.0);
86 trot->normalize();
87
88 for (int miter = 0; miter != max_micro_iter_; ++miter) {
89 Timer mtimer;
90 shared_ptr<const RotFile> sigma = compute_hess_trial(trot, half, halfa, cfock, afock, qxr);
91 shared_ptr<const RotFile> residual;
92 double lambda, epsilon, stepsize;
93 tie(residual, lambda, epsilon, stepsize) = solver.compute_residual(trot, sigma);
94 const double err = residual->norm() / lambda;
95 muffle_->unmute();
96 if (!miter) cout << endl;
97 cout << " res : " << setw(8) << setprecision(2) << scientific << err
98 << " lamb: " << setw(8) << setprecision(2) << scientific << lambda
99 << " eps : " << setw(8) << setprecision(2) << scientific << epsilon
100 << " step: " << setw(8) << setprecision(2) << scientific << stepsize
101 << setw(8) << fixed << setprecision(2) << mtimer.tick() << endl;
102 muffle_->mute();
103 if (err < max(thresh_micro_, stepsize*thresh_microstep_))
104 break;
105
106 trot = apply_denom(residual, denom, -epsilon, 1.0/lambda);
107 for (int i = 0; i != 10; ++i) {
108 const double norm = solver.orthog(trot);
109 if (norm > 0.25) break;
110 }
111 }
112
113 shared_ptr<const RotFile> sol = solver.civec();
114 shared_ptr<const Matrix> a = sol->unpack();
115 Matrix w(*a * *a);
116 VectorB eig(a->ndim());
117 w.diagonalize(eig);
118 Matrix wc(w);
119 Matrix ws(w);
120 for (int i = 0; i != a->ndim(); ++i) {
121 const double tau = sqrt(fabs(eig(i)));
122 blas::scale_n(cos(tau), wc.element_ptr(0,i), wc.ndim());
123 blas::scale_n(tau > 1.0e-15 ? sin(tau)/tau : 1.0, ws.element_ptr(0,i), ws.ndim());
124 }
125 const Matrix R = (wc ^ w) + (ws ^ w) * *a;
126
127 coeff_ = make_shared<Coeff>(*coeff_ * R);
128
129 if (iter == max_iter_-1) {
130 if (external_rdm_.empty() && !conv_ignore_) {
131 throw runtime_error("Max iteration reached during the second-order optimization.");
132 } else {
133 muffle_->unmute();
134 cout << endl << " * Max iteration reached during the second-order optimization. Convergence not reached! * " << endl << endl;
135 }
136 }
137 #ifndef DISABLE_SERIALIZATION
138 if (restart_cas_) {
139 stringstream ss; ss << "casscf_" << iter;
140 OArchive archive(ss.str());
141 auto ref = make_shared<const Reference>(geom_, coeff_, nclosed_, nact_, nvirt_, energy_);
142 archive << ref;
143 }
144 #endif
145 }
146 muffle_->unmute();
147
148 // block diagonalize coeff_ in nclosed and nvirt
149 if (max_iter_ > 0) {
150 auto tmp = semi_canonical_orb();
151 coeff_ = get<0>(tmp);
152 eig_ = get<1>(tmp);
153 occup_ = get<2>(tmp);
154 }
155
156 // this is not needed for energy, but for consistency we want to have this...
157 // update construct Jop from scratch
158 if (nact_ && external_rdm_.empty()) {
159 fci_->update(coeff_);
160 fci_->compute();
161 fci_->compute_rdm12();
162 }
163
164 // calculate the HFCCs
165 if (do_hyperfine_ && !geom_->external() && nstate_ == 1) {
166 HyperFine hfcc(geom_, spin_density(), fci_->det()->nspin(), "CASSCF");
167 hfcc.compute();
168 }
169 }
170
171
apply_denom(shared_ptr<const RotFile> grad,shared_ptr<const RotFile> denom,const double shift,const double scale) const172 shared_ptr<RotFile> CASSecond::apply_denom(shared_ptr<const RotFile> grad, shared_ptr<const RotFile> denom, const double shift, const double scale) const {
173 shared_ptr<RotFile> out = grad->copy();
174 for (int i = 0; i != out->size(); ++i)
175 if (fabs(denom->data(i)*scale+shift) > 1.0e-12)
176 out->data(i) /= denom->data(i)*scale+shift;
177 return out;
178 }
179
180
compute_gradient(shared_ptr<const Matrix> cfock,shared_ptr<const Matrix> afock,shared_ptr<const Matrix> qxr) const181 shared_ptr<RotFile> CASSecond::compute_gradient(shared_ptr<const Matrix> cfock, shared_ptr<const Matrix> afock, shared_ptr<const Matrix> qxr) const {
182 auto sigma = make_shared<RotFile>(nclosed_, nact_, nvirt_);
183 shared_ptr<const RDM<1>> rdm1 = fci_->rdm1_av();
184 if (nclosed_) {
185 double* target = sigma->ptr_vc();
186 for (int i = 0; i != nclosed_; ++i, target += nvirt_) {
187 blas::ax_plus_y_n(4.0, cfock->element_ptr(nocc_,i), nvirt_, target);
188 blas::ax_plus_y_n(4.0, afock->element_ptr(nocc_,i), nvirt_, target);
189 }
190 }
191 {
192 double* target = sigma->ptr_va();
193 for (int i = 0; i != nact_; ++i, target += nvirt_) {
194 blas::ax_plus_y_n(2.0, qxr->element_ptr(nocc_, i), nvirt_, target);
195 for (int j = 0; j != nact_; ++j)
196 blas::ax_plus_y_n(2.0*rdm1->element(j,i), cfock->element_ptr(nocc_, nclosed_+j), nvirt_, target);
197 }
198 }
199 if (nclosed_) {
200 double* target = sigma->ptr_ca();
201 for (int i = 0; i != nact_; ++i, target += nclosed_) {
202 blas::ax_plus_y_n(4.0, cfock->element_ptr(0,nclosed_+i), nclosed_, target);
203 blas::ax_plus_y_n(4.0, afock->element_ptr(0,nclosed_+i), nclosed_, target);
204 blas::ax_plus_y_n(-2.0, qxr->element_ptr(0, i), nclosed_, target);
205 for (int j = 0; j != nact_; ++j)
206 blas::ax_plus_y_n(-2.0*rdm1->element(j,i), cfock->element_ptr(0,nclosed_+j), nclosed_, target);
207 }
208 }
209 return sigma;
210 }
211
212
compute_denom(shared_ptr<const DFHalfDist> half,shared_ptr<const DFHalfDist> half_1j,shared_ptr<const DFHalfDist> halfa,shared_ptr<const Matrix> cfock,shared_ptr<const Matrix> afock) const213 shared_ptr<RotFile> CASSecond::compute_denom(shared_ptr<const DFHalfDist> half, shared_ptr<const DFHalfDist> half_1j, shared_ptr<const DFHalfDist> halfa,
214 shared_ptr<const Matrix> cfock, shared_ptr<const Matrix> afock) const {
215 auto denom = make_shared<RotFile>(nclosed_, nact_, nvirt_);
216 const MatView ccoeff = coeff_->slice(0, nclosed_);
217 const MatView acoeff = coeff_->slice(nclosed_, nocc_);
218 const MatView vcoeff = coeff_->slice(nocc_, nocc_+nvirt_);
219
220 Matrix rdm1(nact_, nact_);
221 copy_n(fci_->rdm1_av()->data(), nact_*nact_, rdm1.data());
222 {
223 const Matrix fcd = *cfock->get_submatrix(nclosed_, nclosed_, nact_, nact_) * rdm1;
224 const Matrix fock = *cfock + *afock;
225 for (int i = 0; i != nact_; ++i)
226 for (int j = 0; j != nclosed_; ++j)
227 denom->ele_ca(j, i) += 4.0 * fock(i+nclosed_, i+nclosed_) - 4.0 * fock(j, j) - 2.0 * fcd(i, i) + 2.0 * (*cfock)(j, j) * rdm1(i, i);
228 for (int i = 0; i != nclosed_; ++i)
229 for (int j = 0; j != nvirt_; ++j)
230 denom->ele_vc(j, i) += 4.0 * fock(j+nocc_, j+nocc_) - 4.0 * fock(i, i);
231 for (int i = 0; i != nact_; ++i)
232 for (int j = 0; j != nvirt_; ++j)
233 denom->ele_va(j, i) += 2.0 * (*cfock)(j+nocc_, j+nocc_) * rdm1(i, i) - 2.0 * fcd(i, i);
234
235 const int nao = coeff_->ndim();
236 if (nclosed_) {
237 auto vvc = half_1j->compute_second_transform(vcoeff)->form_4index_diagonal()->transpose();
238 denom->ax_plus_y_vc(12.0, *vvc);
239
240 shared_ptr<const DFFullDist> vgcc = half->compute_second_transform(ccoeff);
241 const int nri = vgcc->block(0)->asize();
242 Matrix tmp(nao, nao);
243 for (int i = 0; i != nclosed_; ++i) {
244 dgemv_("T", nri, nao*nao, 1.0, geom_->df()->block(0)->data(), nri, vgcc->block(0)->data()+nri*(i+nclosed_*i), 1, 0.0, tmp.data(), 1);
245 if (!vgcc->serial())
246 tmp.allreduce();
247 Matrix tmp0 = vcoeff % tmp * vcoeff;
248 blas::ax_plus_y_n(-4.0, tmp0.diag().data(), nvirt_, denom->ptr_vc()+nvirt_*i);
249 }
250 }
251 shared_ptr<const DFFullDist> vaa = halfa->compute_second_transform(acoeff);
252 const int nri = vaa->block(0)->asize();
253 {
254 shared_ptr<const DFFullDist> vgaa = vaa->apply_2rdm(*fci_->rdm2_av());
255 Matrix tmp(nao, nao);
256 for (int i = 0; i != nact_; ++i) {
257 dgemv_("T", nri, nao*nao, 1.0, geom_->df()->block(0)->data(), nri, vgaa->block(0)->data()+nri*(i+nact_*i), 1, 0.0, tmp.data(), 1);
258 if (!vgaa->serial())
259 tmp.allreduce();
260 Matrix tmp0 = vcoeff % tmp * vcoeff;
261 blas::ax_plus_y_n(2.0, tmp0.diag().data(), nvirt_, denom->ptr_va()+nvirt_*i);
262 if (nclosed_) {
263 Matrix tmp1 = ccoeff % tmp * ccoeff;
264 blas::ax_plus_y_n(2.0, tmp1.diag().data(), nclosed_, denom->ptr_ca()+nclosed_*i);
265 }
266 }
267 shared_ptr<const DFFullDist> vaa_noj = fci_->jop()->mo2e_1ext()->compute_second_transform(acoeff);
268 shared_ptr<const Matrix> mo2e = vaa->form_4index(vaa_noj, 1.0);
269 for (int i = 0; i != nact_; ++i) {
270 const double e2 = -2.0 * blas::dot_product(mo2e->element_ptr(0, i*nact_), nact_*nact_*nact_, fci_->rdm2_av()->element_ptr(0,0,0,i));
271 for (int j = 0; j != nvirt_; ++j)
272 denom->ele_va(j, i) += e2;
273 for (int j = 0; j != nclosed_; ++j)
274 denom->ele_ca(j, i) += e2;
275 }
276
277 Matrix rdmk(nact_*nact_, nact_);
278 for (int i = 0; i != nact_; ++i)
279 for (int j = 0; j != nact_; ++j)
280 for (int k = 0; k != nact_; ++k)
281 rdmk(k+nact_*j, i) = fci_->rdm2_av()->element(k, i, j, i) + fci_->rdm2_av()->element(k, i, i, j);
282 shared_ptr<const DFFullDist> vav = fci_->jop()->mo2e_1ext()->compute_second_transform(vcoeff)->apply_J();
283 denom->ax_plus_y_va(2.0, *(rdmk % *vav->form_4index_diagonal_part()).transpose());
284 if (nclosed_) {
285 shared_ptr<const DFFullDist> vac = fci_->jop()->mo2e_1ext()->compute_second_transform(ccoeff)->apply_J();
286 shared_ptr<const Matrix> mcaa = vac->form_4index_diagonal_part()->transpose();
287 denom->ax_plus_y_ca(2.0, *mcaa * rdmk);
288 shared_ptr<Matrix> mcaad = mcaa->copy();
289 dgemm_("N", "N", nclosed_*nact_, nact_, nact_, -1.0, mcaa->data(), nclosed_*nact_, rdm1.data(), nact_, 1.0, mcaad->data(), nclosed_*nact_);
290 for (int i = 0; i != nact_; ++i)
291 blas::ax_plus_y_n(12.0, mcaad->element_ptr(0, i+nact_*i), nclosed_, denom->ptr_ca()+i*nclosed_);
292 }
293 }
294 if (nclosed_) {
295 Matrix tmp(nao, nao);
296 shared_ptr<DFFullDist> vgaa = vaa->copy();
297 vgaa = vgaa->transform_occ1(make_shared<Matrix>(rdm1));
298 vgaa->ax_plus_y(-1.0, vaa);
299 for (int i = 0; i != nact_; ++i) {
300 dgemv_("T", nri, nao*nao, 1.0, geom_->df()->block(0)->data(), nri, vgaa->block(0)->data()+nri*(i+nact_*i), 1, 0.0, tmp.data(), 1);
301 if (!vgaa->serial())
302 tmp.allreduce();
303 Matrix tmp0 = ccoeff % tmp * ccoeff;
304 blas::ax_plus_y_n(4.0, tmp0.diag().data(), nclosed_, denom->ptr_ca()+nclosed_*i);
305 }
306 }
307 }
308 return denom;
309 }
310
311
compute_hess_trial(shared_ptr<const RotFile> trot,shared_ptr<const DFHalfDist> half,shared_ptr<const DFHalfDist> halfa,shared_ptr<const Matrix> cfock,shared_ptr<const Matrix> afock,shared_ptr<const Matrix> qxr) const312 shared_ptr<RotFile> CASSecond::compute_hess_trial(shared_ptr<const RotFile> trot, shared_ptr<const DFHalfDist> half, shared_ptr<const DFHalfDist> halfa,
313 shared_ptr<const Matrix> cfock, shared_ptr<const Matrix> afock, shared_ptr<const Matrix> qxr) const {
314 shared_ptr<RotFile> sigma = trot->clone();
315
316 shared_ptr<const Matrix> va = trot->va_mat();
317 shared_ptr<const Matrix> ca = nclosed_ ? trot->ca_mat() : nullptr;
318 shared_ptr<const Matrix> vc = nclosed_ ? trot->vc_mat() : nullptr;
319
320 shared_ptr<const Matrix> fcaa = cfock->get_submatrix(nclosed_, nclosed_, nact_, nact_);
321 shared_ptr<const Matrix> faaa = afock->get_submatrix(nclosed_, nclosed_, nact_, nact_);
322 shared_ptr<const Matrix> fcva = cfock->get_submatrix(nocc_, nclosed_, nvirt_, nact_);
323 shared_ptr<const Matrix> fava = afock->get_submatrix(nocc_, nclosed_, nvirt_, nact_);
324 shared_ptr<const Matrix> fcvv = cfock->get_submatrix(nocc_, nocc_, nvirt_, nvirt_);
325 shared_ptr<const Matrix> favv = afock->get_submatrix(nocc_, nocc_, nvirt_, nvirt_);
326 shared_ptr<const Matrix> fccc = nclosed_ ? cfock->get_submatrix(0, 0, nclosed_, nclosed_) : nullptr;
327 shared_ptr<const Matrix> facc = nclosed_ ? afock->get_submatrix(0, 0, nclosed_, nclosed_) : nullptr;
328 shared_ptr<const Matrix> fcca = nclosed_ ? cfock->get_submatrix(0, nclosed_, nclosed_, nact_) : nullptr;
329 shared_ptr<const Matrix> faca = nclosed_ ? afock->get_submatrix(0, nclosed_, nclosed_, nact_) : nullptr;
330 shared_ptr<const Matrix> fcvc = nclosed_ ? cfock->get_submatrix(nocc_, 0, nvirt_, nclosed_) : nullptr;
331 shared_ptr<const Matrix> favc = nclosed_ ? afock->get_submatrix(nocc_, 0, nvirt_, nclosed_) : nullptr;
332
333 const MatView ccoeff = coeff_->slice(0, nclosed_);
334 const MatView acoeff = coeff_->slice(nclosed_, nocc_);
335 const MatView vcoeff = coeff_->slice(nocc_, nocc_+nvirt_);
336
337 Matrix rdm1(nact_, nact_);
338 copy_n(fci_->rdm1_av()->data(), nact_*nact_, rdm1.data());
339
340 // lambda for computing g(D)
341 auto compute_gd = [&,this](shared_ptr<const DFHalfDist> halft, shared_ptr<const DFHalfDist> halfjj, const MatView pcoeff) {
342 shared_ptr<const Matrix> pcoefft = make_shared<Matrix>(pcoeff)->transpose();
343 shared_ptr<Matrix> gd = geom_->df()->compute_Jop(halft, pcoefft);
344 shared_ptr<Matrix> ex0 = halfjj->form_2index(halft, 1.0);
345 ex0->symmetrize();
346 gd->ax_plus_y(-0.5, ex0);
347 return gd;
348 };
349
350 // g(t_vc) operator and g(t_ac) operator
351 if (nclosed_) {
352 const Matrix tcoeff = vcoeff * *vc + acoeff * *ca->transpose();
353 auto halft = geom_->df()->compute_half_transform(tcoeff);
354 const Matrix gt = *compute_gd(halft, half, ccoeff);
355 sigma->ax_plus_y_ca(32.0, ccoeff % gt * acoeff);
356 sigma->ax_plus_y_vc(32.0, vcoeff % gt * ccoeff);
357 sigma->ax_plus_y_va(16.0, vcoeff % gt * acoeff * rdm1);
358 sigma->ax_plus_y_ca(-16.0, ccoeff % gt * acoeff * rdm1);
359 }
360 // g(t_va - t_ca)
361 const Matrix tcoeff = nclosed_ ? (vcoeff * *va - ccoeff * *ca) : vcoeff * *va;
362 shared_ptr<const DFHalfDist> halfta = geom_->df()->compute_half_transform(tcoeff);
363 if (nclosed_) {
364 shared_ptr<DFHalfDist> halftad = halfta->copy();
365 halftad = halftad->transform_occ(make_shared<Matrix>(rdm1));
366 const Matrix gt = *compute_gd(halftad, halfa, acoeff);
367 sigma->ax_plus_y_ca(16.0, ccoeff % gt * acoeff);
368 sigma->ax_plus_y_vc(16.0, vcoeff % gt * ccoeff);
369 }
370 // terms with Qvec
371 {
372 shared_ptr<const Matrix> qaa = qxr->cut(nclosed_, nocc_);
373 sigma->ax_plus_y_va(-2.0, *va ^ *qaa);
374 sigma->ax_plus_y_va(-2.0, *va * *qaa);
375 if (nclosed_) {
376 shared_ptr<const Matrix> qva = qxr->cut(nocc_, nocc_+nvirt_);
377 shared_ptr<const Matrix> qca = qxr->cut(0, nclosed_);
378 sigma->ax_plus_y_vc(-2.0, *va ^ *qca);
379 sigma->ax_plus_y_va(-2.0, *vc * *qca);
380 sigma->ax_plus_y_ca(-2.0, *vc % *qva);
381 sigma->ax_plus_y_vc(-2.0, *qva ^ *ca);
382 sigma->ax_plus_y_ca(-2.0, *ca ^ *qaa);
383 sigma->ax_plus_y_ca(-2.0, *ca * *qaa);
384 }
385 }
386 // compute Q' and Q''
387 {
388 shared_ptr<const DFFullDist> fullaa = halfa->compute_second_transform(acoeff);
389 shared_ptr<DFFullDist> fullta = halfta->compute_second_transform(acoeff);
390 shared_ptr<const DFFullDist> fulltas = fullta->swap();
391 fullta->ax_plus_y(1.0, fulltas);
392 shared_ptr<const DFFullDist> fullaaD = fullaa->apply_2rdm(*fci_->rdm2_av());
393 shared_ptr<const DFFullDist> fulltaD = fullta->apply_2rdm(*fci_->rdm2_av());
394 shared_ptr<const Matrix> qp = halfa->form_2index(fulltaD, 1.0);
395 shared_ptr<const Matrix> qpp = halfta->form_2index(fullaaD, 1.0);
396
397 sigma->ax_plus_y_va( 4.0, vcoeff % (*qp + *qpp));
398 if (nclosed_)
399 sigma->ax_plus_y_ca(-4.0, ccoeff % (*qp + *qpp));
400 }
401
402 // next 1-electron contribution...
403 {
404 sigma->ax_plus_y_va( 4.0, *fcvv * *va * rdm1);
405 sigma->ax_plus_y_va(-2.0, *va * (rdm1 * *fcaa + *fcaa * rdm1));
406 if (nclosed_) {
407 sigma->ax_plus_y_ca( 8.0, *ca * (*fcaa + *faaa));
408 sigma->ax_plus_y_ca( 8.0, *vc % (*fcva + *fava));
409 sigma->ax_plus_y_vc(-8.0, *vc * (*fccc + *facc));
410 sigma->ax_plus_y_va(-4.0, *vc * (*fcca + *faca));
411 sigma->ax_plus_y_vc(-4.0, *va ^ (*fcca + *faca));
412 sigma->ax_plus_y_ca(-2.0, *ca * (rdm1 * *fcaa + *fcaa * rdm1));
413 sigma->ax_plus_y_vc( 8.0, (*fcvv + *favv) * *vc);
414 sigma->ax_plus_y_ca(-8.0, (*fccc + *facc) * *ca);
415 sigma->ax_plus_y_va( 4.0, (*fcvc + *favc) * *ca);
416 sigma->ax_plus_y_ca( 4.0, (*fcvc + *favc) % *va);
417 sigma->ax_plus_y_vc( 8.0, (*fcva + *fava) ^ *ca);
418 sigma->ax_plus_y_ca( 4.0, *fccc * *ca * rdm1);
419 sigma->ax_plus_y_ca(-4.0, *fcvc % *va * rdm1);
420 sigma->ax_plus_y_va(-4.0, *fcvc * *ca * rdm1);
421 sigma->ax_plus_y_vc(-2.0, *fcva * rdm1 ^ *ca);
422 sigma->ax_plus_y_vc(-2.0, *va * rdm1 ^ *fcca);
423 sigma->ax_plus_y_ca(-2.0, *vc % *fcva * rdm1);
424 sigma->ax_plus_y_va(-2.0, *vc * *fcca * rdm1);
425 }
426 }
427 sigma->scale(0.5);
428 return sigma;
429 }
430
431
trans_natorb()432 void CASSecond::trans_natorb() {
433 auto trans = make_shared<Matrix>(nact_, nact_);
434 trans->add_diag(2.0);
435 blas::ax_plus_y_n(-1.0, fci_->rdm1_av()->data(), nact_*nact_, trans->data());
436
437 VectorB occup(nact_);
438 trans->diagonalize(occup);
439
440 if (natocc_) {
441 cout << " " << endl;
442 cout << " ======== state-averaged ======== " << endl;
443 cout << " ======== natural occupation numbers ======== " << endl;
444 int cnt = 0;
445 for (auto& i : occup)
446 cout << setprecision(4) << " Orbital " << cnt++ << " : " << (i < 2.0 ? 2.0 - i : 0.0) << endl;
447 cout << " ============================================ " << endl;
448 }
449
450 fci_->rotate_rdms(trans);
451
452 auto cnew = make_shared<Coeff>(*coeff_);
453 cnew->copy_block(0, nclosed_, cnew->ndim(), nact_, coeff_->slice(nclosed_, nocc_) * *trans);
454 coeff_ = cnew;
455 }
456