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