Skip to content

Commit fd87704

Browse files
sunqmbabbush
authored andcommitted
Update pyscf wrapper (#27)
* Update psycf interface * Efficient integral transformation function * Fixed n_orbitals * Extending MolecularData to hold pyscf objects * Bugfix for index convention of eri and 2-pdm * Renamed _molecular_data module and MolecularData class * Removing the DEBUG flag * Updated installation requirements and tests * Updated Author lists * More sanity checks in pyscf_molecular_data * Fix error in PySCFMolecularData.__init__ * Temporarily removed dependence to pyscf in requirements.txt * Name convention of test files. * Updated Author list * A pyscf-1.4.3 compatibility issue
1 parent 38081f3 commit fd87704

File tree

7 files changed

+463
-85
lines changed

7 files changed

+463
-85
lines changed

NOTICE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Nicolas Sawaya (Harvard)
2929
Kanav Setia (Dartmouth)
3030
Hannah Sim (Harvard)
3131
Mark Steudtner (Leiden University)
32+
Qiming Sun (Caltech)
3233
Wei Sun (Google)
3334
Fang Zhang (University of Michigan)
3435

@@ -40,3 +41,4 @@ Ian Kivlichan (Harvard)
4041
Damian Steiger (ETH Zurich)
4142
Thomas Haener (ETH Zurich)
4243
Dave Bacon (Google)
44+
Qiming Sun (Caltech)

README.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,9 @@ Authors
8383
`Kanav Setia <https://github.com/kanavsetia>`__ (Dartmouth),
8484
`Hannah Sim <https://github.com/hsim13372>`__ (Harvard),
8585
`Mark Steudtner <https://github.com/msteudtner>`__ (Leiden University),
86-
`Wei Sun <https://github.com/Spaceenter>`__ (Google) and
87-
`Fang Zhang <https://github.com/fangzh-umich>`__ (University of Michigan).
86+
`Qiming Sun <https://github.com/sunqm>`__ (Caltech).
87+
`Wei Sun <https://github.com/Spaceenter>`__ (Google),
88+
`Fang Zhang <https://github.com/fangzh-umich>`__ (University of Michigan) and
8889

8990
How to cite
9091
-----------

openfermionpyscf/__init__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@
1414
OpenFermion plugin to interface with PySCF.
1515
"""
1616

17-
from ._version import __version__
18-
1917
try:
20-
from ._run_pyscf import run_pyscf
18+
from ._pyscf_molecular_data import PyscfMolecularData
19+
from ._run_pyscf import prepare_pyscf_molecule, run_pyscf
2120
except ImportError:
2221
raise Exception("Please install PySCF.")
22+
23+
from ._version import __version__
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License");
2+
# you may not use this file except in compliance with the License.
3+
# You may obtain a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS,
9+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
# See the License for the specific language governing permissions and
11+
# limitations under the License.
12+
13+
"""Class to use pyscf program to access quantum chemistry data."""
14+
15+
import numpy
16+
from functools import reduce
17+
from pyscf import ao2mo
18+
from pyscf import scf
19+
from openfermion.hamiltonians import MolecularData
20+
21+
22+
class PyscfMolecularData(MolecularData):
23+
24+
"""A derived class from openfermion.hamiltonians.MolecularData. This class
25+
is created to store the PySCF method objects as well as molecule data from
26+
a fixed basis set at a fixed geometry that is obtained from PySCF
27+
electronic structure packages. This class provides an interface to access
28+
the PySCF Hartree-Fock, MP, CI, Coupled-Cluster methods and their energies,
29+
density matrices and wavefunctions.
30+
31+
Attributes:
32+
_pyscf_data(dict): To store PySCF method objects temporarily.
33+
"""
34+
def __init__(self, geometry=None, basis=None, multiplicity=None,
35+
charge=0, description="", filename="", data_directory=None):
36+
MolecularData.__init__(self, geometry, basis, multiplicity,
37+
charge, description, filename, data_directory)
38+
self._pyscf_data = {}
39+
40+
@property
41+
def canonical_orbitals(self):
42+
"""Hartree-Fock canonical orbital coefficients (represented on AO
43+
basis).
44+
"""
45+
if self._canonical_orbitals is None:
46+
scf = self._pyscf_data.get('scf', None)
47+
self._canonical_orbitals = scf.mo_coeff
48+
return self._canonical_orbitals
49+
50+
@property
51+
def overlap_integrals(self):
52+
"""Overlap integrals for AO basis."""
53+
if self._overlap_integrals is None:
54+
scf = self._pyscf_data.get('scf', None)
55+
self._overlap_integrals = scf.get_ovlp()
56+
return self._overlap_integrals
57+
58+
@property
59+
def one_body_integrals(self):
60+
"""A 2D array for one-body Hamiltonian (H_core) in the MO
61+
representation."""
62+
if self._one_body_integrals is None:
63+
scf = self._pyscf_data.get('scf', None)
64+
mo = self.canonical_orbitals
65+
h_core = scf.get_hcore()
66+
self._one_body_integrals = reduce(numpy.dot, (mo.T, h_core, mo))
67+
return self._one_body_integrals
68+
69+
@property
70+
def two_body_integrals(self):
71+
"""A 4-dimension array for electron repulsion integrals in the MO
72+
representation. The integrals are computed as
73+
h[p,q,r,s]=\int \phi_p(x)* \phi_q(y)* V_{elec-elec} \phi_r(y) \phi_s(x) dxdy
74+
"""
75+
if self._two_body_integrals is None:
76+
mol = self._pyscf_data.get('mol', None)
77+
mo = self.canonical_orbitals
78+
n_orbitals = mo.shape[1]
79+
80+
eri = ao2mo.kernel(mol, mo)
81+
eri = ao2mo.restore(1, # no permutation symmetry
82+
eri, n_orbitals)
83+
# See PQRS convention in OpenFermion.hamiltonians.molecular_data
84+
# h[p,q,r,s] = (ps|qr) = pyscf_eri[p,s,q,r]
85+
self._two_body_integrals = numpy.asarray(
86+
eri.transpose(0, 2, 3, 1), order='C')
87+
return self._two_body_integrals
88+
89+
@property
90+
def cisd_one_rdm(self):
91+
r"""A 2-dimension array for CISD one-particle density matrix in the MO
92+
representation. d[p,q] = < a^\dagger_p a_q >
93+
"""
94+
if self._cisd_one_rdm is None:
95+
cisd = self._pyscf_data.get('cisd', None)
96+
if cisd is None:
97+
return None
98+
99+
mf = self._pyscf_data.get('scf', None)
100+
if isinstance(mf, scf.uhf.UHF):
101+
raise ValueError('Spin trace for UCISD density matrix.')
102+
103+
rdm1 = cisd.make_rdm1()
104+
if isinstance(mf, scf.rohf.ROHF):
105+
rdm1 = rdm1[0] + rdm1[1]
106+
107+
# pyscf one_rdm is computed as dm1[p,q] = <a^\dagger_q a_p>
108+
self._cisd_one_rdm = rdm1.T
109+
return self._cisd_one_rdm
110+
111+
@property
112+
def cisd_two_rdm(self):
113+
r"""A 4-dimension array for CISD two-particle density matrix in the MO
114+
representation. D[p,q,r,s] = < a^\dagger_p a^\dagger_q a_r a_s >
115+
"""
116+
if self._cisd_two_rdm is None:
117+
cisd = self._pyscf_data.get('cisd', None)
118+
if cisd is None:
119+
return None
120+
121+
mf = self._pyscf_data.get('scf', None)
122+
if isinstance(mf, scf.uhf.UHF):
123+
raise ValueError('Spin trace for UCISD density matrix.')
124+
125+
rdm2 = cisd.make_rdm2()
126+
if isinstance(mf, scf.rohf.ROHF):
127+
aa, ab, bb = rdm2
128+
rdm2 = aa + bb + ab + ab.transpose(2, 3, 0, 1)
129+
130+
# pyscf.ci.cisd.make_rdm2 convention
131+
# dm2[p,s,q,r] = <a^\dagger_p a^\dagger_q a_r a_s>.
132+
# the two_body_tensor in openfermion.ops._interaction_rdm.InteractionRDM
133+
# tbt[p,q,r,s] = <a^\dagger_p a^\dagger_q a_r a_s>.
134+
self._cisd_two_rdm = rdm2.transpose(0, 2, 3, 1)
135+
return self._cisd_two_rdm
136+
137+
@property
138+
def ccsd_one_rdm(self):
139+
r"""A 2-dimension array for CCSD one-particle density matrix in the MO
140+
representation. d[p,q] = < a^\dagger_p a_q >
141+
"""
142+
ccsd = self._pyscf_data.get('ccsd', None)
143+
if ccsd is None:
144+
return None
145+
146+
mf = self._pyscf_data.get('scf', None)
147+
if isinstance(mf, scf.uhf.UHF):
148+
raise ValueError('Spin trace for UCCSD density matrix.')
149+
150+
rdm1 = ccsd.make_rdm1()
151+
if isinstance(mf, scf.rohf.ROHF):
152+
rdm1 = rdm1[0] + rdm1[1]
153+
return rdm1.T
154+
155+
@property
156+
def ccsd_two_rdm(self):
157+
r"""A 4-dimension array for CCSD two-particle density matrix in the MO
158+
representation. D[p,q,r,s] = < a^\dagger_p a^\dagger_q a_r a_s >
159+
"""
160+
ccsd = self._pyscf_data.get('ccsd', None)
161+
if ccsd is None:
162+
return None
163+
164+
mf = self._pyscf_data.get('scf', None)
165+
if isinstance(mf, scf.uhf.UHF):
166+
raise ValueError('Spin trace for UCCSD density matrix.')
167+
168+
rdm2 = ccsd.make_rdm2()
169+
if isinstance(mf, scf.rohf.ROHF):
170+
aa, ab, bb = rdm2
171+
rdm2 = aa + bb + ab + ab.transpose(2, 3, 0, 1)
172+
return rdm2.transpose(0, 2, 3, 1)
173+
174+
@property
175+
def mp2_one_rdm(self):
176+
r"""A 2-dimension array for MP2 one-particle density matrix in the MO
177+
representation. d[p,q] = < a^\dagger_p a_q >
178+
"""
179+
mp2 = self._pyscf_data.get('mp2', None)
180+
if mp2 is None:
181+
return None
182+
183+
mf = self._pyscf_data.get('scf', None)
184+
if isinstance(mf, scf.uhf.UHF):
185+
raise ValueError('Spin trace for UMP2 density matrix.')
186+
187+
rdm1 = mp2.make_rdm1()
188+
if isinstance(mf, scf.rohf.ROHF):
189+
rdm1 = rdm1[0] + rdm1[1]
190+
return rdm1.T
191+
192+
@property
193+
def mp2_two_rdm(self):
194+
r"""A 4-dimension array for MP2 two-particle density matrix in the MO
195+
representation. D[p,q,r,s] = < a^\dagger_p a^\dagger_q a_r a_s >
196+
"""
197+
mp2 = self._pyscf_data.get('mp2', None)
198+
if mp2 is None:
199+
return None
200+
201+
mf = self._pyscf_data.get('scf', None)
202+
if isinstance(mf, scf.uhf.UHF):
203+
raise ValueError('Spin trace for UMP2 density matrix.')
204+
205+
rdm2 = mp2.make_rdm2()
206+
if isinstance(mf, scf.rohf.ROHF):
207+
aa, ab, bb = rdm2
208+
rdm2 = aa + bb + ab + ab.transpose(2, 3, 0, 1)
209+
return rdm2.transpose(0, 2, 3, 1)
210+
211+
@property
212+
def fci_one_rdm(self):
213+
r"""A 2-dimension array for FCI one-particle density matrix in the MO
214+
representation. d[p,q] = < a^\dagger_p a_q >
215+
"""
216+
if self._fci_one_rdm is None:
217+
fci = self._pyscf_data.get('fci', None)
218+
if fci is None:
219+
return None
220+
221+
mf = self._pyscf_data.get('scf', None)
222+
if isinstance(mf, scf.uhf.UHF):
223+
raise ValueError('Spin trace for UHF-FCI density matrices.')
224+
225+
norb = self.canonical_orbitals.shape[1]
226+
nelec = self.n_electrons
227+
self._fci_one_rdm = fci.make_rdm1(fci.ci, norb, nelec).T
228+
return self._fci_one_rdm
229+
230+
@property
231+
def fci_two_rdm(self):
232+
r"""A 4-dimension array for FCI two-particle density matrix in the MO
233+
representation. D[p,q,r,s] = < a^\dagger_p a^\dagger_q a_r a_s >
234+
"""
235+
if self._fci_two_rdm is None:
236+
fci = self._pyscf_data.get('fci', None)
237+
if fci is None:
238+
return None
239+
240+
mf = self._pyscf_data.get('scf', None)
241+
if isinstance(mf, scf.uhf.UHF):
242+
raise ValueError('Spin trace for UHF-FCI density matrix.')
243+
244+
norb = self.canonical_orbitals.shape[1]
245+
nelec = self.n_electrons
246+
fci_rdm2 = fci.make_rdm2(fci.ci, norb, nelec)
247+
self._fci_two_rdm = fci_rdm2.transpose(0, 2, 3, 1)
248+
return self._fci_two_rdm
249+
250+
@property
251+
def ccsd_single_amps(self):
252+
r"""A 2-dimension array t[a,i] for RCCSD single excitation amplitudes
253+
where a is virtual index and i is occupied index.
254+
"""
255+
if self._ccsd_single_amps is None:
256+
ccsd = self._pyscf_data.get('ccsd', None)
257+
if ccsd is None:
258+
return None
259+
260+
mf = self._pyscf_data.get('scf', None)
261+
if isinstance(mf, (scf.rohf.ROHF, scf.uhf.UHF)):
262+
#raise ValueError('UCCSD t1 amplitudes not available.')
263+
return None
264+
265+
self._ccsd_single_amps = ccsd.t1.T
266+
return self._ccsd_single_amps
267+
268+
@property
269+
def ccsd_double_amps(self):
270+
r"""A 4-dimension array t[a,b,i,j] for RCCSD double excitation amplitudes
271+
where a, b are virtual indices and i, j are occupied indices.
272+
"""
273+
if self._ccsd_double_amps is None:
274+
ccsd = self._pyscf_data.get('ccsd', None)
275+
if ccsd is None:
276+
return None
277+
278+
mf = self._pyscf_data.get('scf', None)
279+
if isinstance(mf, (scf.rohf.ROHF, scf.uhf.UHF)):
280+
#raise ValueError('UCCSD t2 amplitudes not available.')
281+
return None
282+
283+
self._ccsd_double_amps = ccsd.t2.transpose(2, 3, 0, 1)
284+
return self._ccsd_double_amps

0 commit comments

Comments
 (0)