|
| 1 | +""" |
| 2 | +A reference implementation of stochastic orbital resolution of identity |
| 3 | +(or density-fitted) MP2 (sRI-MP2) from a RHF reference. |
| 4 | +
|
| 5 | +Reference: |
| 6 | +Stochastic Formulation of the Resolution of Identity: Application to |
| 7 | +Second Order Moller-Plesset Perturbation Theory |
| 8 | +J. Chem. Theory Comput., 2017, 13 (10), pp 4605-4610 |
| 9 | +DOI: 10.1021/acs.jctc.7b00343 |
| 10 | +
|
| 11 | +Tyler Y. Takeshita |
| 12 | +Department of Chemistry, University of California Berkeley |
| 13 | +Materials Sciences Division, Lawrence Berkeley National Laboratory |
| 14 | +
|
| 15 | +Wibe A. de Jong |
| 16 | +Computational Research Division, Lawrence Berkeley National Laboratory |
| 17 | +
|
| 18 | +Daniel Neuhauser |
| 19 | +Department of Chemistry and Biochemistry, University of California, Los Angeles |
| 20 | +
|
| 21 | +Roi Baer |
| 22 | +Fritz Harber Center for Molecular Dynamics, Institute of Chemistry, The Hebrew University of Jerusalem |
| 23 | +
|
| 24 | +Eran Rabani |
| 25 | +Department of Chemistry, University of California Berkeley |
| 26 | +Materials Sciences Division, Lawrence Berkeley National Laboratory |
| 27 | +The Sackler Center for Computational Molecular Science, Tel Aviv University |
| 28 | +""" |
| 29 | + |
| 30 | +__authors__ = ["Tyler Y. Takeshita", "Daniel G. A. Smith"] |
| 31 | +__credits__ = ["Tyler Y. Takeshita", "Daniel G. A. Smith"] |
| 32 | + |
| 33 | +__copyright__ = "(c) 2014-2017, The Psi4NumPy Developers" |
| 34 | +__license__ = "BSD-3-Clause" |
| 35 | +__date__ = "2018-04-14" |
| 36 | + |
| 37 | +import numpy as np |
| 38 | +import psi4 |
| 39 | +import time |
| 40 | + |
| 41 | +# Set numpy defaults |
| 42 | +np.set_printoptions(precision=5, linewidth=200, suppress=True) |
| 43 | +# psi4.set_output_file("output.dat") |
| 44 | + |
| 45 | +# ==> Geometry <== |
| 46 | +# Note: Symmetry was turned off |
| 47 | +mol = psi4.geometry(""" |
| 48 | +O |
| 49 | +H 1 0.96 |
| 50 | +H 1 0.96 2 104 |
| 51 | +symmetry c1 |
| 52 | +""") |
| 53 | + |
| 54 | +# How many samples to run? |
| 55 | +nsample = 5000 |
| 56 | + |
| 57 | +# ==> Basis sets <== |
| 58 | +psi4.set_options({ |
| 59 | + 'basis': 'aug-cc-pvdz', |
| 60 | + 'scf_type': 'df', |
| 61 | + 'e_convergence': 1e-10, |
| 62 | + 'd_convergence': 1e-10 |
| 63 | +}) |
| 64 | + |
| 65 | +wfn = psi4.core.Wavefunction.build(mol, psi4.core.get_global_option('basis')) |
| 66 | + |
| 67 | +# Build auxiliary basis set |
| 68 | +aux = psi4.core.BasisSet.build(mol, "DF_BASIS_SCF", "", "RIFIT", "aug-cc-pVDZ") |
| 69 | + |
| 70 | +# Get orbital basis & build zero basis |
| 71 | +orb = wfn.basisset() |
| 72 | + |
| 73 | +# The zero basis set |
| 74 | +zero_bas = psi4.core.BasisSet.zero_ao_basis_set() |
| 75 | + |
| 76 | +# Build instance of MintsHelper |
| 77 | +mints = psi4.core.MintsHelper(orb) |
| 78 | + |
| 79 | +# ==> Build Density-Fitted Integrals <== |
| 80 | + |
| 81 | +# Build (P|pq) raw 3-index ERIs, dimension (1, Naux, nbf, nbf) |
| 82 | +Ppq = mints.ao_eri(zero_bas, aux, orb, orb) |
| 83 | + |
| 84 | +# Build & invert Coulomb metric, dimension (1, Naux, 1, Naux) |
| 85 | +metric = mints.ao_eri(zero_bas, aux, zero_bas, aux) |
| 86 | +metric.power(-0.5, 1.e-14) |
| 87 | + |
| 88 | +# Remove excess dimensions of Ppq, & metric |
| 89 | +Ppq = np.squeeze(Ppq) |
| 90 | +metric = np.squeeze(metric) |
| 91 | + |
| 92 | +# Build the Qso object |
| 93 | +Qpq = np.einsum('QP,Ppq->Qpq', metric, Ppq) |
| 94 | + |
| 95 | +# ==> Perform HF <== |
| 96 | +energy, scf_wfn = psi4.energy('scf', return_wfn=True) |
| 97 | +print("Finished SCF...") |
| 98 | + |
| 99 | +# ==> AO -> MO Transformation of integrals <== |
| 100 | +# Get the MO coefficients and energies |
| 101 | +evecs = np.array(scf_wfn.epsilon_a()) |
| 102 | + |
| 103 | +Co = scf_wfn.Ca_subset("AO", "OCC") |
| 104 | +Cv = scf_wfn.Ca_subset("AO", "VIR") |
| 105 | + |
| 106 | +Qia = np.einsum("Qpq,pi,qa->Qia", Qpq, Co, Cv, optimize=True) |
| 107 | + |
| 108 | +nocc = scf_wfn.nalpha() |
| 109 | +nvirt = scf_wfn.nmo() - nocc |
| 110 | + |
| 111 | +denom = 1.0 / (evecs[:nocc].reshape(-1, 1, 1, 1) + evecs[:nocc].reshape(-1, 1, 1) - |
| 112 | + evecs[nocc:].reshape(-1, 1) - evecs[nocc:]) |
| 113 | + |
| 114 | +t = time.time() |
| 115 | +e_srimp2 = 0.0 |
| 116 | +print("Transformed ERIs...") |
| 117 | + |
| 118 | +# Loop over samples to reduce stochastic noise |
| 119 | +print("Starting sample loop...") |
| 120 | +for x in range(nsample): |
| 121 | + |
| 122 | + # ==> Build Stochastic Integral Matrices <== |
| 123 | + # Create two random vector |
| 124 | + vec = np.random.choice([-1, 1], size=(Qia.shape[0])) |
| 125 | + vecp = np.random.choice([-1, 1], size=(Qia.shape[0])) |
| 126 | + |
| 127 | + # Generate first R matrices |
| 128 | + ia = np.einsum("Q,Qia->ia", vec, Qia) |
| 129 | + iap = np.einsum("Q,Qia->ia", vecp, Qia) |
| 130 | + |
| 131 | + # ==> Calculate a single stochastic RI-MP2 (sRI-MP2) sample <== |
| 132 | + |
| 133 | + # Caculate sRI-MP2 correlation energy |
| 134 | + e_srimp2 += 2.0 * np.einsum('ijab,ia,ia,jb,jb->', denom, ia, iap, ia, iap) |
| 135 | + e_srimp2 -= np.einsum('ijab,ia,ib,jb,ja->', denom, ia, iap, ia, iap) |
| 136 | + |
| 137 | +e_srimp2 /= float(nsample) |
| 138 | +total_time = time.time() - t |
| 139 | +time_per_sample = total_time / float(nsample) |
| 140 | + |
| 141 | +# Print sample energy to output |
| 142 | +print("\nNumber of samples: % 16d" % nsample) |
| 143 | +print("Total time (s): % 16.2f" % total_time) |
| 144 | +print("Time per sample (us): % 16.2f" % (time_per_sample * 1.e6)) |
| 145 | +print("sRI-MP2 correlation sample energy: % 16.10f" % e_srimp2) |
| 146 | + |
| 147 | +psi_mp2_energy = psi4.energy("MP2") |
| 148 | +mp2_correlation_energy = psi4.get_variable("MP2 CORRELATION ENERGY") |
| 149 | + |
| 150 | +print("\nRI-MP2 energy: % 16.10f" % mp2_correlation_energy) |
| 151 | +print("Sample error % 16.10f" % (e_srimp2 - mp2_correlation_energy)) |
0 commit comments