tequila-basic 1.9.9__py3-none-any.whl → 1.9.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tequila/__init__.py +29 -14
- tequila/apps/__init__.py +14 -5
- tequila/apps/_unary_state_prep_impl.py +145 -112
- tequila/apps/adapt/__init__.py +9 -1
- tequila/apps/adapt/adapt.py +154 -113
- tequila/apps/krylov/__init__.py +1 -1
- tequila/apps/krylov/krylov.py +23 -21
- tequila/apps/robustness/helpers.py +10 -6
- tequila/apps/robustness/interval.py +238 -156
- tequila/apps/unary_state_prep.py +29 -23
- tequila/autograd_imports.py +8 -5
- tequila/circuit/__init__.py +2 -1
- tequila/circuit/_gates_impl.py +135 -67
- tequila/circuit/circuit.py +163 -79
- tequila/circuit/compiler.py +114 -105
- tequila/circuit/gates.py +288 -120
- tequila/circuit/gradient.py +35 -23
- tequila/circuit/noise.py +83 -74
- tequila/circuit/postselection.py +120 -0
- tequila/circuit/pyzx.py +10 -6
- tequila/circuit/qasm.py +201 -83
- tequila/circuit/qpic.py +63 -61
- tequila/grouping/binary_rep.py +148 -146
- tequila/grouping/binary_utils.py +84 -75
- tequila/grouping/compile_groups.py +334 -230
- tequila/grouping/ev_utils.py +77 -41
- tequila/grouping/fermionic_functions.py +383 -308
- tequila/grouping/fermionic_methods.py +170 -123
- tequila/grouping/overlapping_methods.py +69 -52
- tequila/hamiltonian/paulis.py +12 -13
- tequila/hamiltonian/paulistring.py +1 -1
- tequila/hamiltonian/qubit_hamiltonian.py +45 -35
- tequila/ml/__init__.py +1 -0
- tequila/ml/interface_torch.py +19 -16
- tequila/ml/ml_api.py +11 -10
- tequila/ml/utils_ml.py +12 -11
- tequila/objective/__init__.py +8 -3
- tequila/objective/braket.py +55 -47
- tequila/objective/objective.py +87 -55
- tequila/objective/qtensor.py +36 -27
- tequila/optimizers/__init__.py +31 -23
- tequila/optimizers/_containers.py +11 -7
- tequila/optimizers/optimizer_base.py +111 -83
- tequila/optimizers/optimizer_gd.py +258 -231
- tequila/optimizers/optimizer_gpyopt.py +56 -42
- tequila/optimizers/optimizer_scipy.py +157 -112
- tequila/quantumchemistry/__init__.py +66 -38
- tequila/quantumchemistry/chemistry_tools.py +393 -209
- tequila/quantumchemistry/encodings.py +121 -13
- tequila/quantumchemistry/madness_interface.py +170 -96
- tequila/quantumchemistry/orbital_optimizer.py +86 -41
- tequila/quantumchemistry/psi4_interface.py +166 -97
- tequila/quantumchemistry/pyscf_interface.py +70 -23
- tequila/quantumchemistry/qc_base.py +866 -414
- tequila/simulators/__init__.py +0 -3
- tequila/simulators/simulator_api.py +247 -105
- tequila/simulators/simulator_aqt.py +102 -0
- tequila/simulators/simulator_base.py +147 -53
- tequila/simulators/simulator_cirq.py +58 -42
- tequila/simulators/simulator_cudaq.py +600 -0
- tequila/simulators/simulator_ddsim.py +390 -0
- tequila/simulators/simulator_mqp.py +30 -0
- tequila/simulators/simulator_pyquil.py +190 -171
- tequila/simulators/simulator_qibo.py +95 -87
- tequila/simulators/simulator_qiskit.py +119 -107
- tequila/simulators/simulator_qlm.py +52 -26
- tequila/simulators/simulator_qulacs.py +74 -52
- tequila/simulators/simulator_spex.py +95 -60
- tequila/simulators/simulator_symbolic.py +6 -5
- tequila/simulators/test_spex_simulator.py +8 -11
- tequila/tools/convenience.py +4 -4
- tequila/tools/qng.py +72 -64
- tequila/tools/random_generators.py +38 -34
- tequila/utils/bitstrings.py +7 -7
- tequila/utils/exceptions.py +19 -5
- tequila/utils/joined_transformation.py +8 -10
- tequila/utils/keymap.py +0 -5
- tequila/utils/misc.py +6 -4
- tequila/version.py +1 -1
- tequila/wavefunction/qubit_wavefunction.py +47 -28
- {tequila_basic-1.9.9.dist-info → tequila_basic-1.9.10.dist-info}/METADATA +13 -16
- tequila_basic-1.9.10.dist-info/RECORD +93 -0
- {tequila_basic-1.9.9.dist-info → tequila_basic-1.9.10.dist-info}/WHEEL +1 -1
- tequila_basic-1.9.9.dist-info/RECORD +0 -88
- {tequila_basic-1.9.9.dist-info → tequila_basic-1.9.10.dist-info}/licenses/LICENSE +0 -0
- {tequila_basic-1.9.9.dist-info → tequila_basic-1.9.10.dist-info}/top_level.txt +0 -0
@@ -5,29 +5,34 @@ from dataclasses import dataclass
|
|
5
5
|
from copy import deepcopy
|
6
6
|
from numbers import Real
|
7
7
|
import numpy
|
8
|
+
from scipy.constants import physical_constants
|
8
9
|
|
9
|
-
from tequila import BitString, QCircuit, TequilaException,Variable,compile_circuit
|
10
|
+
from tequila import BitString, QCircuit, TequilaException, TequilaWarning, Variable, compile_circuit
|
10
11
|
from tequila.circuit import gates
|
12
|
+
|
11
13
|
try:
|
12
14
|
from openfermion.ops.representations import get_active_space_integrals # needs openfermion 1.3
|
13
15
|
except ImportError as E:
|
14
16
|
raise TequilaException("{}\nplease update openfermion to version 1.3 or higher".format(str(E)))
|
15
17
|
|
18
|
+
|
16
19
|
@dataclass
|
17
20
|
class ActiveSpaceData:
|
18
21
|
"""
|
19
22
|
Small dataclass to keep the overview in active spaces
|
20
23
|
Class is used internally
|
21
24
|
"""
|
22
|
-
|
25
|
+
|
26
|
+
active_orbitals: list = None # active orbitals (spatial, c1)
|
23
27
|
reference_orbitals: list = None # reference orbitals (spatial, c1)
|
24
28
|
|
25
29
|
def __str__(self):
|
26
30
|
result = "Active Space Data:\n"
|
27
31
|
result += "{key:15} : {value:15} \n".format(key="active_orbitals", value=str(self.active_orbitals))
|
28
32
|
result += "{key:15} : {value:15} \n".format(key="reference_orbitals", value=str(self.reference_orbitals))
|
29
|
-
result += "{key:15} : {value:15} \n".format(
|
30
|
-
|
33
|
+
result += "{key:15} : {value:15} \n".format(
|
34
|
+
key="active_reference_orbitals", value=str(self.active_reference_orbitals)
|
35
|
+
)
|
31
36
|
return result
|
32
37
|
|
33
38
|
@property
|
@@ -50,10 +55,11 @@ class FermionicGateImpl(gates.QubitExcitationImpl):
|
|
50
55
|
self._name = "FermionicExcitation"
|
51
56
|
self.transformation = transformation
|
52
57
|
self.indices = indices
|
53
|
-
if not hasattr(indices[0],"__len__"):
|
54
|
-
self.indices = [(indices[2 * i], indices[2 * i+1]) for i in range(len(indices) // 2)]
|
58
|
+
if not hasattr(indices[0], "__len__"):
|
59
|
+
self.indices = [(indices[2 * i], indices[2 * i + 1]) for i in range(len(indices) // 2)]
|
55
60
|
self.sign = self.format_excitation_variables(self.indices)
|
56
61
|
self.indices = self.format_excitation_indices(self.indices)
|
62
|
+
|
57
63
|
def compile(self, *args, **kwargs):
|
58
64
|
if self.is_convertable_to_qubit_excitation():
|
59
65
|
target = []
|
@@ -63,9 +69,12 @@ class FermionicGateImpl(gates.QubitExcitationImpl):
|
|
63
69
|
return gates.QubitExcitation(target=target, angle=self.parameter, control=self.control)
|
64
70
|
else:
|
65
71
|
if self.transformation.lower().strip("_") == "jordanwigner":
|
66
|
-
return self.fermionic_excitation(
|
72
|
+
return self.fermionic_excitation(
|
73
|
+
angle=self.sign * self.parameter, indices=self.indices, control=self.control, opt=False
|
74
|
+
)
|
67
75
|
else:
|
68
76
|
return gates.Trotterized(generator=self.generator, control=self.control, angle=self.parameter, steps=1)
|
77
|
+
|
69
78
|
def format_excitation_indices(self, idx):
|
70
79
|
"""
|
71
80
|
Consistent formatting of excitation indices
|
@@ -78,6 +87,7 @@ class FermionicGateImpl(gates.QubitExcitationImpl):
|
|
78
87
|
idx = [tuple(sorted(x)) for x in idx]
|
79
88
|
idx = sorted(idx, key=lambda x: x[0])
|
80
89
|
return list(idx)
|
90
|
+
|
81
91
|
def format_excitation_variables(self, idx):
|
82
92
|
"""
|
83
93
|
Consistent formatting of excitation variable
|
@@ -88,21 +98,28 @@ class FermionicGateImpl(gates.QubitExcitationImpl):
|
|
88
98
|
"""
|
89
99
|
sig = 1
|
90
100
|
for pair in idx:
|
91
|
-
if pair[1]>pair[0]:
|
101
|
+
if pair[1] > pair[0]:
|
92
102
|
sig *= -1
|
93
|
-
for pair in range(len(idx)-1):
|
94
|
-
if idx[pair+1][0]>idx[pair][0]:
|
103
|
+
for pair in range(len(idx) - 1):
|
104
|
+
if idx[pair + 1][0] > idx[pair][0]:
|
95
105
|
sig *= -1
|
96
106
|
return sig
|
97
|
-
|
98
|
-
|
99
|
-
|
107
|
+
|
108
|
+
def cCRy(
|
109
|
+
self,
|
110
|
+
target: int,
|
111
|
+
dcontrol: typing.Union[list, int],
|
112
|
+
control: typing.Union[list, int],
|
113
|
+
angle: typing.Union[Real, Variable, typing.Hashable],
|
114
|
+
case: int = 1,
|
115
|
+
) -> QCircuit:
|
116
|
+
"""
|
100
117
|
Compilation of CRy as on https://doi.org/10.1103/PhysRevA.102.062612
|
101
118
|
If not control passed, Ry returned
|
102
119
|
Parameters
|
103
120
|
----------
|
104
121
|
case: if 1 employs eq. 12 from the paper, if 0 eq. 13
|
105
|
-
|
122
|
+
"""
|
106
123
|
if control is not None and not len(control):
|
107
124
|
control = None
|
108
125
|
if isinstance(dcontrol, int):
|
@@ -117,26 +134,43 @@ class FermionicGateImpl(gates.QubitExcitationImpl):
|
|
117
134
|
ctr = deepcopy(dcontrol)
|
118
135
|
ctr.pop(0)
|
119
136
|
if case:
|
120
|
-
U +=
|
121
|
-
|
122
|
-
|
123
|
-
|
137
|
+
U += (
|
138
|
+
self.cCRy(target=target, dcontrol=ctr, angle=angle / 2, case=1, control=control)
|
139
|
+
+ gates.H(aux)
|
140
|
+
+ gates.CNOT(target, aux)
|
141
|
+
)
|
142
|
+
U += (
|
143
|
+
self.cCRy(target=target, dcontrol=ctr, angle=-angle / 2, case=0, control=control)
|
144
|
+
+ gates.CNOT(target, aux)
|
145
|
+
+ gates.H(aux)
|
146
|
+
)
|
124
147
|
else:
|
125
|
-
U +=
|
126
|
-
|
127
|
-
|
128
|
-
|
148
|
+
U += (
|
149
|
+
gates.H(aux)
|
150
|
+
+ gates.CNOT(target, aux)
|
151
|
+
+ self.cCRy(target=target, dcontrol=ctr, angle=-angle / 2, case=0, control=control)
|
152
|
+
)
|
153
|
+
U += (
|
154
|
+
gates.CNOT(target, aux)
|
155
|
+
+ gates.H(aux)
|
156
|
+
+ self.cCRy(target=target, dcontrol=ctr, angle=angle / 2, case=1, control=control)
|
157
|
+
)
|
129
158
|
return U
|
130
159
|
|
131
|
-
def fermionic_excitation(
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
160
|
+
def fermionic_excitation(
|
161
|
+
self,
|
162
|
+
angle: typing.Union[Real, Variable, typing.Hashable],
|
163
|
+
indices: typing.List,
|
164
|
+
control: typing.Union[int, typing.List] = None,
|
165
|
+
opt: bool = True,
|
166
|
+
) -> QCircuit:
|
167
|
+
"""
|
168
|
+
Excitation [(i,j),(k,l)],... compiled following https://doi.org/10.1103/PhysRevA.102.062612
|
169
|
+
opt: whether to optimized CNOT H CNOT --> Rz Rz CNOT Rz
|
170
|
+
"""
|
137
171
|
lto = []
|
138
172
|
lfrom = []
|
139
|
-
if isinstance(indices,tuple) and not hasattr(indices[0],"__len__"):
|
173
|
+
if isinstance(indices, tuple) and not hasattr(indices[0], "__len__"):
|
140
174
|
indices = [(indices[2 * i], indices[2 * i + 1]) for i in range(len(indices) // 2)]
|
141
175
|
for pair in indices:
|
142
176
|
lfrom.append(pair[0])
|
@@ -165,7 +199,7 @@ class FermionicGateImpl(gates.QubitExcitationImpl):
|
|
165
199
|
if isinstance(control, int):
|
166
200
|
crt.append(control)
|
167
201
|
else:
|
168
|
-
crt = crt + control
|
202
|
+
crt = crt + [*control]
|
169
203
|
control = []
|
170
204
|
Ur = self.cCRy(target=lto[-1], dcontrol=crt, angle=angle, control=control)
|
171
205
|
Upair2 = Upair.dagger()
|
@@ -195,12 +229,32 @@ class FermionicGateImpl(gates.QubitExcitationImpl):
|
|
195
229
|
|
196
230
|
"""
|
197
231
|
return False
|
198
|
-
if not self.transformation.lower().strip("_") == "jordanwigner":
|
199
|
-
|
200
|
-
if not self.indices
|
201
|
-
|
232
|
+
if not self.transformation.lower().strip("_") == "jordanwigner":
|
233
|
+
return False
|
234
|
+
if not len(self.indices) == 2:
|
235
|
+
return False
|
236
|
+
if not self.indices[0][0] // 2 == self.indices[1][0] // 2:
|
237
|
+
return False
|
238
|
+
if not self.indices[0][1] // 2 == self.indices[1][1] // 2:
|
239
|
+
return False
|
202
240
|
return True
|
203
241
|
|
242
|
+
def map_qubits(self, qubit_map: dict):
|
243
|
+
mapped = deepcopy(self)
|
244
|
+
mapped._target = tuple([qubit_map[i] for i in self.target])
|
245
|
+
if self.control is not None:
|
246
|
+
mapped._control = tuple([qubit_map[i] for i in self.control])
|
247
|
+
if hasattr(self, "generator") and self.generator:
|
248
|
+
mapped.generator = self.generator.map_qubits(qubit_map=qubit_map)
|
249
|
+
if hasattr(self, "generators"):
|
250
|
+
mapped.generators = [i.map_qubits(qubit_map=qubit_map) for i in self.generators]
|
251
|
+
mapped.finalize()
|
252
|
+
if hasattr(self, "generator"):
|
253
|
+
mapped.generator = self.generator.map_qubits(qubit_map=qubit_map)
|
254
|
+
if hasattr(self, "indices"):
|
255
|
+
mapped.indices = [(qubit_map[t[0]], qubit_map[t[1]]) for t in self.indices]
|
256
|
+
return mapped
|
257
|
+
|
204
258
|
|
205
259
|
def prepare_product_state(state: BitString) -> QCircuit:
|
206
260
|
"""Small convenience function
|
@@ -228,64 +282,102 @@ def prepare_product_state(state: BitString) -> QCircuit:
|
|
228
282
|
@dataclass
|
229
283
|
class ParametersQC:
|
230
284
|
"""Specialization of ParametersHamiltonian"""
|
285
|
+
|
231
286
|
basis_set: str = None # Quantum chemistry basis set
|
232
|
-
geometry: str = None # geometry of the underlying molecule
|
287
|
+
geometry: str = None # geometry of the underlying molecule,
|
233
288
|
# this can be a filename leading to an .xyz file or the geometry given as a string
|
289
|
+
units: str = None # units of the geometry, if nothing is passed it will be set to Angstrom!
|
234
290
|
description: str = ""
|
235
291
|
multiplicity: int = 1
|
236
292
|
charge: int = 0
|
237
293
|
name: str = None
|
238
294
|
frozen_core: bool = True
|
239
|
-
|
295
|
+
|
240
296
|
def get_number_of_core_electrons(self):
|
241
297
|
result = 0
|
242
298
|
for atom in self.get_atoms():
|
243
|
-
n=self.get_atom_number(atom)
|
244
|
-
if n>2:
|
245
|
-
result += 2
|
246
|
-
if n>10:
|
247
|
-
result +=
|
248
|
-
if n>18:
|
249
|
-
result += 18
|
250
|
-
if n>36:
|
251
|
-
result += 36
|
252
|
-
if n>
|
253
|
-
result += 54
|
254
|
-
if n>86:
|
255
|
-
result += 86
|
299
|
+
n = self.get_atom_number(atom)
|
300
|
+
if n > 2:
|
301
|
+
result += 2
|
302
|
+
if n > 10:
|
303
|
+
result += 10 - 2
|
304
|
+
if n > 18:
|
305
|
+
result += 18 - 10 - 2
|
306
|
+
if n > 36:
|
307
|
+
result += 36 - 18 - 10 - 2
|
308
|
+
if n > 54:
|
309
|
+
result += 54 - 36 - 18 - 10 - 2
|
310
|
+
if n > 86:
|
311
|
+
result += 86 - 54 - 36 - 18 - 10 - 2
|
256
312
|
return result
|
257
313
|
|
258
314
|
@property
|
259
|
-
def
|
315
|
+
def total_n_electrons(self, *args, **kwargs):
|
260
316
|
return self.get_nuc_charge() - self.charge
|
261
317
|
|
262
318
|
def get_nuc_charge(self):
|
263
319
|
return sum(self.get_atom_number(name=atom) for atom in self.get_atoms())
|
264
320
|
|
265
321
|
def get_atom_number(self, name):
|
266
|
-
atom_numbers = {
|
267
|
-
|
322
|
+
atom_numbers = {
|
323
|
+
"h": 1,
|
324
|
+
"he": 2,
|
325
|
+
"li": 3,
|
326
|
+
"be": 4,
|
327
|
+
"b": 5,
|
328
|
+
"c": 6,
|
329
|
+
"n": 7,
|
330
|
+
"o": 8,
|
331
|
+
"f": 9,
|
332
|
+
"ne": 10,
|
333
|
+
"na": 11,
|
334
|
+
"mg": 12,
|
335
|
+
"al": 13,
|
336
|
+
"si": 14,
|
337
|
+
"ph": 15,
|
338
|
+
"s": 16,
|
339
|
+
"cl": 17,
|
340
|
+
"ar": 18,
|
341
|
+
}
|
268
342
|
if name.lower() in atom_numbers:
|
269
343
|
return atom_numbers[name.lower()]
|
270
344
|
try:
|
271
345
|
import periodictable as pt
|
346
|
+
|
272
347
|
atom = list(name.lower())
|
273
348
|
atom[0] = atom[0].upper()
|
274
|
-
atom =
|
349
|
+
atom = "".join(atom)
|
275
350
|
element = pt.elements.symbol(atom)
|
276
351
|
return element.number
|
277
|
-
except:
|
352
|
+
except Exception:
|
278
353
|
raise TequilaException(
|
279
|
-
"can not assign atomic number to element {}\npip install periodictable will fix it".format(atom)
|
354
|
+
"can not assign atomic number to element {}\npip install periodictable will fix it".format(atom)
|
355
|
+
)
|
280
356
|
|
281
357
|
def get_atoms(self):
|
282
358
|
return [x[0] for x in self.get_geometry()]
|
283
359
|
|
284
360
|
def __post_init__(self, *args, **kwargs):
|
285
|
-
|
286
361
|
if self.name is None and self.geometry is None:
|
287
362
|
raise TequilaException(
|
288
|
-
"no geometry or name given to molecule\nprovide geometry=filename.xyz or geometry=`h 0.0 0.0 0.0\\n...`\nor name=whatever with file whatever.xyz being present"
|
363
|
+
"no geometry or name given to molecule\nprovide geometry=filename.xyz or geometry=`h 0.0 0.0 0.0\\n...`\nor name=whatever with file whatever.xyz being present"
|
364
|
+
)
|
365
|
+
# determine units, default Angstrom
|
366
|
+
if self.units is None:
|
367
|
+
warnings.warn("Warning: No units passed with geometry, assuming units are angstrom.", TequilaWarning)
|
368
|
+
self.units = "angstrom"
|
369
|
+
else:
|
370
|
+
self.units = self.units.lower()
|
371
|
+
if self.units in ["angstrom", "ang", "a", "å"]:
|
372
|
+
self.units = "angstrom"
|
373
|
+
elif self.units in ["bohr", "atomic units", "au", "a.u."]:
|
374
|
+
self.units = "bohr"
|
375
|
+
else:
|
376
|
+
warnings.warn(
|
377
|
+
"Warning: Units passed with geometry not recognized (available units are angstrom or bohr), assuming units are angstrom.",
|
378
|
+
TequilaWarning,
|
379
|
+
)
|
380
|
+
self.units = "angstrom"
|
289
381
|
# auto naming
|
290
382
|
if self.name is None:
|
291
383
|
if ".xyz" in self.geometry:
|
@@ -315,9 +407,14 @@ class ParametersQC:
|
|
315
407
|
@property
|
316
408
|
def molecular_data_param(self) -> dict:
|
317
409
|
""":return: Give back all parameters for the MolecularData format from openfermion as dictionary"""
|
318
|
-
return {
|
319
|
-
|
320
|
-
|
410
|
+
return {
|
411
|
+
"basis": self.basis_set,
|
412
|
+
"geometry": self.get_geometry(desired_units="angstrom"),
|
413
|
+
"description": self.description,
|
414
|
+
"charge": self.charge,
|
415
|
+
"multiplicity": self.multiplicity,
|
416
|
+
"filename": self.filename,
|
417
|
+
}
|
321
418
|
|
322
419
|
@staticmethod
|
323
420
|
def format_element_name(string):
|
@@ -335,26 +432,30 @@ class ParametersQC:
|
|
335
432
|
-------
|
336
433
|
|
337
434
|
"""
|
338
|
-
assert
|
339
|
-
assert
|
435
|
+
assert len(string) > 0
|
436
|
+
assert isinstance(string, str)
|
340
437
|
fstring = string[0].upper() + string[1:].lower()
|
341
438
|
return fstring
|
342
439
|
|
343
440
|
@staticmethod
|
344
|
-
def convert_to_list(geometry):
|
441
|
+
def convert_to_list(geometry, initial_units, desired_units):
|
345
442
|
"""Convert a molecular structure given as a string into a list suitable for openfermion
|
346
443
|
|
347
444
|
Parameters
|
348
445
|
----------
|
349
446
|
geometry :
|
350
|
-
a string specifying a mol. structure. E.g. geometry="h 0.0 0.0 0.0\n h 0.0 0.0 1.0"
|
351
|
-
|
447
|
+
a string specifying a mol. structure. E.g. geometry="h 0.0 0.0 0.0\n h 0.0 0.0 1.0",
|
448
|
+
initial_units :
|
449
|
+
the units of the input geometry
|
450
|
+
desired_units :
|
451
|
+
the units of the output coordinates
|
352
452
|
Returns
|
353
453
|
-------
|
354
454
|
type
|
355
|
-
A list with the correct format for openfermion E.g return [
|
356
|
-
|
455
|
+
A list with the correct format for openfermion E.g return [ ('h',(0.0,0.0,0.0)), (..)], coordinates are in "desired_units",
|
456
|
+
note that openfermion requires coordinates in angstrom
|
357
457
|
"""
|
458
|
+
c_bohrtoang = physical_constants["Bohr radius"][0] * 10**10
|
358
459
|
result = []
|
359
460
|
# Remove blank lines
|
360
461
|
lines = [l for l in geometry.split("\n") if l]
|
@@ -367,55 +468,65 @@ class ParametersQC:
|
|
367
468
|
words += [0.0] * (4 - len(words))
|
368
469
|
|
369
470
|
try:
|
370
|
-
|
371
|
-
|
471
|
+
if initial_units == desired_units:
|
472
|
+
tmp = (
|
473
|
+
ParametersQC.format_element_name(words[0]),
|
474
|
+
(float(words[1]), float(words[2]), float(words[3])),
|
475
|
+
)
|
476
|
+
elif initial_units == "angstrom":
|
477
|
+
tmp = (
|
478
|
+
ParametersQC.format_element_name(words[0]),
|
479
|
+
(float(words[1]) / c_bohrtoang, float(words[2]) / c_bohrtoang, float(words[3]) / c_bohrtoang),
|
480
|
+
)
|
481
|
+
elif initial_units == "bohr":
|
482
|
+
tmp = (
|
483
|
+
ParametersQC.format_element_name(words[0]),
|
484
|
+
(float(words[1]) * c_bohrtoang, float(words[2]) * c_bohrtoang, float(words[3]) * c_bohrtoang),
|
485
|
+
)
|
372
486
|
result.append(tmp)
|
373
487
|
except ValueError:
|
374
488
|
print("get_geometry list unknown line:\n ", line, "\n proceed with caution!")
|
375
489
|
return result
|
376
490
|
|
377
|
-
def get_geometry_string(self) -> str:
|
491
|
+
def get_geometry_string(self, desired_units="angstrom") -> str:
|
378
492
|
"""returns the geometry as a string
|
379
|
-
:return: geometry string
|
380
|
-
|
381
|
-
Parameters
|
382
|
-
----------
|
383
|
-
|
384
|
-
Returns
|
385
|
-
-------
|
493
|
+
:return: geometry string, if desired_units is not equal to self.units, the coordinates will be transformed to "desired_units"
|
386
494
|
|
387
495
|
"""
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
else:
|
394
|
-
return self.geometry
|
496
|
+
geom = self.get_geometry(desired_units=desired_units)
|
497
|
+
f = ""
|
498
|
+
for at in geom:
|
499
|
+
f += f"{at[0]} {at[1][0]} {at[1][1]} {at[1][2]}\n"
|
500
|
+
return f
|
395
501
|
|
396
|
-
def get_geometry(self):
|
502
|
+
def get_geometry(self, desired_units="angstrom"):
|
397
503
|
"""Returns the geometry
|
398
504
|
If a xyz filename was given the file is read out
|
399
505
|
otherwise it is assumed that the geometry was given as string
|
400
506
|
which is then reformatted as a list usable as input for openfermion
|
401
507
|
:return: geometry as list
|
402
508
|
e.g. [(h,(0.0,0.0,0.35)),(h,(0.0,0.0,-0.35))]
|
403
|
-
|
404
|
-
|
405
|
-
Parameters
|
406
|
-
----------
|
407
|
-
|
408
|
-
Returns
|
409
|
-
-------
|
410
|
-
|
509
|
+
Coordinates are transformed into the "desired_units", note that openfermion requires coordinates in Angstrom!
|
411
510
|
"""
|
412
|
-
|
511
|
+
desired_units = desired_units.lower()
|
512
|
+
if desired_units in ["angstrom", "ang", "a", "å"]:
|
513
|
+
desired_units = "angstrom"
|
514
|
+
elif desired_units in ["bohr", "atomic units", "au", "a.u."]:
|
515
|
+
desired_units = "bohr"
|
516
|
+
else:
|
517
|
+
warnings.warn(
|
518
|
+
"Warning: Desired units not recognized (available units are angstrom or bohr), defaulting to angstrom.",
|
519
|
+
TequilaWarning,
|
520
|
+
)
|
521
|
+
desired_units = "angstrom"
|
522
|
+
|
523
|
+
if self.geometry.split(".")[-1] == "xyz":
|
413
524
|
geomstring, comment = self.read_xyz_from_file(self.geometry)
|
414
|
-
if self.description ==
|
525
|
+
if self.description == "":
|
415
526
|
self.description = comment
|
416
|
-
return self.convert_to_list(geomstring)
|
527
|
+
return self.convert_to_list(geomstring, initial_units=self.units, desired_units=desired_units)
|
417
528
|
elif self.geometry is not None:
|
418
|
-
return self.convert_to_list(self.geometry)
|
529
|
+
return self.convert_to_list(self.geometry, initial_units=self.units, desired_units=desired_units)
|
419
530
|
else:
|
420
531
|
raise Exception("Parameters.qc.geometry is None")
|
421
532
|
|
@@ -423,44 +534,44 @@ class ParametersQC:
|
|
423
534
|
def read_xyz_from_file(filename):
|
424
535
|
"""Read XYZ filetype for molecular structures
|
425
536
|
https://en.wikipedia.org/wiki/XYZ_file_format
|
426
|
-
Units
|
537
|
+
Units of the geometry in the file have to be equal to self.units, default for self.units is angstrom.
|
427
538
|
|
428
539
|
Parameters
|
429
540
|
----------
|
430
|
-
filename
|
431
|
-
return:
|
432
|
-
|
433
|
-
Returns
|
434
|
-
-------
|
541
|
+
filename of the .xyz file
|
435
542
|
|
436
543
|
"""
|
437
|
-
with open(filename,
|
544
|
+
with open(filename, "r") as file:
|
438
545
|
content = file.readlines()
|
439
546
|
natoms = int(content[0])
|
440
|
-
comment = str(content[1]).strip(
|
441
|
-
coord =
|
547
|
+
comment = str(content[1]).strip("\n")
|
548
|
+
coord = ""
|
442
549
|
for i in range(natoms):
|
443
550
|
coord += content[2 + i]
|
444
551
|
return coord, comment
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
f
|
552
|
+
|
553
|
+
def get_xyz(self, desired_units="angstrom") -> str:
|
554
|
+
"""Returns string for a .xyz file with the coordinates in the "desired_units" """
|
555
|
+
geom = self.get_geometry(desired_units=desired_units)
|
556
|
+
f = ""
|
557
|
+
f += f"{len(geom)}\n"
|
558
|
+
f += f"{self.name}\n"
|
450
559
|
for at in geom:
|
451
|
-
f += f
|
560
|
+
f += f"{at[0]} {at[1][0]} {at[1][1]} {at[1][2]}\n"
|
452
561
|
return f
|
453
562
|
|
563
|
+
|
454
564
|
@dataclass
|
455
565
|
class ClosedShellAmplitudes:
|
456
566
|
"""
|
457
|
-
Helper Class for
|
567
|
+
Helper Class for classical amplitudes
|
458
568
|
used internally
|
459
569
|
"""
|
570
|
+
|
460
571
|
tIjAb: numpy.ndarray = None
|
461
572
|
tIA: numpy.ndarray = None
|
462
573
|
|
463
|
-
def make_parameter_dictionary(self, threshold=1.
|
574
|
+
def make_parameter_dictionary(self, threshold=1.0e-8, screening=True):
|
464
575
|
"""
|
465
576
|
|
466
577
|
Parameters
|
@@ -476,13 +587,16 @@ class ClosedShellAmplitudes:
|
|
476
587
|
if self.tIjAb is not None:
|
477
588
|
nvirt = self.tIjAb.shape[2]
|
478
589
|
nocc = self.tIjAb.shape[0]
|
479
|
-
assert
|
590
|
+
assert self.tIjAb.shape[1] == nocc and self.tIjAb.shape[3] == nvirt
|
480
591
|
for (I, J, A, B), value in numpy.ndenumerate(self.tIjAb):
|
481
592
|
if not numpy.isclose(value, 0.0, atol=threshold) or not screening:
|
482
593
|
variables[(nocc + A, I, nocc + B, J)] = value
|
483
594
|
if self.tIA is not None:
|
484
595
|
nocc = self.tIA.shape[0]
|
485
|
-
for (
|
596
|
+
for (
|
597
|
+
(I, A),
|
598
|
+
value,
|
599
|
+
) in numpy.ndenumerate(self.tIA):
|
486
600
|
if not numpy.isclose(value, 0.0, atol=threshold) or not screening:
|
487
601
|
variables[(A + nocc, I)] = value
|
488
602
|
return dict(sorted(variables.items(), key=lambda x: numpy.abs(x[1]), reverse=True))
|
@@ -518,7 +632,7 @@ class Amplitudes:
|
|
518
632
|
-------
|
519
633
|
|
520
634
|
"""
|
521
|
-
tijab = cs.tIjAb - numpy.einsum("ijab -> ijba", cs.tIjAb, optimize=
|
635
|
+
tijab = cs.tIjAb - numpy.einsum("ijab -> ijba", cs.tIjAb, optimize="greedy")
|
522
636
|
return cls(tIjAb=cs.tIjAb, tIA=cs.tIA, tiJaB=cs.tIjAb, tia=cs.tIA, tijab=tijab, tIJAB=tijab)
|
523
637
|
|
524
638
|
tIjAb: numpy.ndarray = None
|
@@ -528,7 +642,7 @@ class Amplitudes:
|
|
528
642
|
tIJAB: numpy.ndarray = None
|
529
643
|
tia: numpy.ndarray = None
|
530
644
|
|
531
|
-
def make_parameter_dictionary(self, threshold=1.
|
645
|
+
def make_parameter_dictionary(self, threshold=1.0e-8):
|
532
646
|
"""
|
533
647
|
|
534
648
|
Parameters
|
@@ -546,7 +660,7 @@ class Amplitudes:
|
|
546
660
|
if self.tIjAb is not None:
|
547
661
|
nvirt = self.tIjAb.shape[2]
|
548
662
|
nocc = self.tIjAb.shape[0]
|
549
|
-
assert
|
663
|
+
assert self.tIjAb.shape[1] == nocc and self.tIjAb.shape[3] == nvirt
|
550
664
|
|
551
665
|
for (I, j, A, b), value in numpy.ndenumerate(self.tIjAb):
|
552
666
|
if not numpy.isclose(value, 0.0, atol=threshold):
|
@@ -563,11 +677,17 @@ class Amplitudes:
|
|
563
677
|
|
564
678
|
if self.tIA is not None:
|
565
679
|
nocc = self.tIjAb.shape[0]
|
566
|
-
assert
|
567
|
-
for (
|
680
|
+
assert self.tia.shape[0] == nocc
|
681
|
+
for (
|
682
|
+
(I, A),
|
683
|
+
value,
|
684
|
+
) in numpy.ndenumerate(self.tIA):
|
568
685
|
if not numpy.isclose(value, 0.0, atol=threshold):
|
569
686
|
variables[(2 * (A + nocc), 2 * I)] = value
|
570
|
-
for (
|
687
|
+
for (
|
688
|
+
(i, a),
|
689
|
+
value,
|
690
|
+
) in numpy.ndenumerate(self.tIA):
|
571
691
|
if not numpy.isclose(value, 0.0, atol=threshold):
|
572
692
|
variables[(2 * (a + nocc) + 1, 2 * i + 1)] = value
|
573
693
|
|
@@ -575,7 +695,7 @@ class Amplitudes:
|
|
575
695
|
|
576
696
|
|
577
697
|
class NBodyTensor:
|
578
|
-
"""
|
698
|
+
"""Convenience class for handling N-body tensors"""
|
579
699
|
|
580
700
|
class Ordering:
|
581
701
|
"""
|
@@ -606,7 +726,8 @@ class NBodyTensor:
|
|
606
726
|
return "of"
|
607
727
|
else:
|
608
728
|
raise TequilaException(
|
609
|
-
"Unknown two-body tensor scheme {}. Supported are dirac, mulliken, and openfermion".format(scheme)
|
729
|
+
"Unknown two-body tensor scheme {}. Supported are dirac, mulliken, and openfermion".format(scheme)
|
730
|
+
)
|
610
731
|
|
611
732
|
def is_phys(self):
|
612
733
|
return self._scheme == "phys"
|
@@ -623,22 +744,25 @@ class NBodyTensor:
|
|
623
744
|
def identify_ordering(self, trials=25):
|
624
745
|
if len(self.shape) != 4:
|
625
746
|
return None
|
626
|
-
chem=False
|
627
|
-
phys=False
|
628
|
-
of=False
|
747
|
+
chem = False
|
748
|
+
phys = False
|
749
|
+
of = False
|
629
750
|
if self._verify_ordering_mulliken(trials=trials):
|
630
|
-
chem=self.Ordering(scheme="mulliken")
|
751
|
+
chem = self.Ordering(scheme="mulliken")
|
631
752
|
if self._verify_ordering_dirac(trials=trials):
|
632
|
-
phys=self.Ordering(scheme="dirac")
|
753
|
+
phys = self.Ordering(scheme="dirac")
|
633
754
|
if self._verify_ordering_of(trials=trials):
|
634
|
-
of=self.Ordering(scheme="openfermion")
|
635
|
-
|
636
|
-
uniqueness = (chem,phys,of)
|
637
|
-
if not uniqueness.count(False) == 2 and trials<100:
|
638
|
-
return self.identify_ordering(trials=trials*2)
|
639
|
-
if chem:
|
640
|
-
|
641
|
-
elif
|
755
|
+
of = self.Ordering(scheme="openfermion")
|
756
|
+
|
757
|
+
uniqueness = (chem, phys, of)
|
758
|
+
if not uniqueness.count(False) == 2 and trials < 100:
|
759
|
+
return self.identify_ordering(trials=trials * 2)
|
760
|
+
if chem:
|
761
|
+
return self.Ordering(scheme="chem")
|
762
|
+
elif phys:
|
763
|
+
return self.Ordering(scheme="phys")
|
764
|
+
elif of:
|
765
|
+
return self.Ordering(scheme="openfermion")
|
642
766
|
else:
|
643
767
|
raise Exception("NBTensor ordering could not be identified")
|
644
768
|
|
@@ -650,10 +774,16 @@ class NBodyTensor:
|
|
650
774
|
elems = self.elems
|
651
775
|
n = self.shape[0]
|
652
776
|
for _ in range(trials):
|
653
|
-
idx = numpy.random.randint(0,n,4)
|
654
|
-
test1 = numpy.isclose(
|
655
|
-
|
656
|
-
|
777
|
+
idx = numpy.random.randint(0, n, 4)
|
778
|
+
test1 = numpy.isclose(
|
779
|
+
elems[idx[0], idx[1], idx[2], idx[3]], elems[idx[2], idx[1], idx[0], idx[3]], atol=1.0e-4
|
780
|
+
)
|
781
|
+
test2 = numpy.isclose(
|
782
|
+
elems[idx[0], idx[1], idx[2], idx[3]], elems[idx[0], idx[3], idx[2], idx[1]], atol=1.0e-4
|
783
|
+
)
|
784
|
+
test3 = numpy.isclose(
|
785
|
+
elems[idx[0], idx[1], idx[2], idx[3]], elems[idx[2], idx[3], idx[0], idx[1]], atol=1.0e-4
|
786
|
+
)
|
657
787
|
if not (test1 and test2 and test3):
|
658
788
|
return False
|
659
789
|
|
@@ -666,10 +796,16 @@ class NBodyTensor:
|
|
666
796
|
elems = self.elems
|
667
797
|
n = self.shape[0]
|
668
798
|
for _ in range(trials):
|
669
|
-
idx = numpy.random.randint(0,n,4)
|
670
|
-
test1 = numpy.isclose(
|
671
|
-
|
672
|
-
|
799
|
+
idx = numpy.random.randint(0, n, 4)
|
800
|
+
test1 = numpy.isclose(
|
801
|
+
elems[idx[0], idx[1], idx[2], idx[3]], elems[idx[1], idx[0], idx[2], idx[3]], atol=1.0e-4
|
802
|
+
)
|
803
|
+
test2 = numpy.isclose(
|
804
|
+
elems[idx[0], idx[1], idx[2], idx[3]], elems[idx[0], idx[1], idx[3], idx[2]], atol=1.0e-4
|
805
|
+
)
|
806
|
+
test3 = numpy.isclose(
|
807
|
+
elems[idx[0], idx[1], idx[2], idx[3]], elems[idx[1], idx[0], idx[3], idx[2]], atol=1.0e-4
|
808
|
+
)
|
673
809
|
if not (test1 and test2 and test3):
|
674
810
|
return False
|
675
811
|
|
@@ -682,17 +818,29 @@ class NBodyTensor:
|
|
682
818
|
elems = self.elems
|
683
819
|
n = self.shape[0]
|
684
820
|
for _ in range(trials):
|
685
|
-
idx = numpy.random.randint(0,n,4)
|
686
|
-
test1 = numpy.isclose(
|
687
|
-
|
688
|
-
|
821
|
+
idx = numpy.random.randint(0, n, 4)
|
822
|
+
test1 = numpy.isclose(
|
823
|
+
elems[idx[0], idx[1], idx[2], idx[3]], elems[idx[3], idx[1], idx[2], idx[0]], atol=1.0e-4
|
824
|
+
)
|
825
|
+
test2 = numpy.isclose(
|
826
|
+
elems[idx[0], idx[1], idx[2], idx[3]], elems[idx[0], idx[2], idx[1], idx[3]], atol=1.0e-4
|
827
|
+
)
|
828
|
+
test3 = numpy.isclose(
|
829
|
+
elems[idx[0], idx[1], idx[2], idx[3]], elems[idx[3], idx[2], idx[1], idx[0]], atol=1.0e-4
|
830
|
+
)
|
689
831
|
if not (test1 and test2 and test3):
|
690
832
|
return False
|
691
833
|
|
692
834
|
return True
|
693
835
|
|
694
|
-
def __init__(
|
695
|
-
|
836
|
+
def __init__(
|
837
|
+
self,
|
838
|
+
elems: numpy.ndarray = None,
|
839
|
+
active_indices: list = None,
|
840
|
+
ordering: str = None,
|
841
|
+
size_full: int = None,
|
842
|
+
verify=False,
|
843
|
+
):
|
696
844
|
"""
|
697
845
|
Parameters
|
698
846
|
----------
|
@@ -735,10 +883,14 @@ class NBodyTensor:
|
|
735
883
|
if ordering is None:
|
736
884
|
ordering = self.identify_ordering()
|
737
885
|
elif verify:
|
738
|
-
try:
|
739
|
-
auto_ordering=self.identify_ordering()
|
886
|
+
try: # some RDMs are really sloppy (depends on backend)
|
887
|
+
auto_ordering = self.identify_ordering()
|
740
888
|
if auto_ordering is not ordering:
|
741
|
-
warnings.warn(
|
889
|
+
warnings.warn(
|
890
|
+
"Auto identified ordering of NBTensor does not match given ordering: {} vs {}".format(
|
891
|
+
auto_ordering, ordering
|
892
|
+
)
|
893
|
+
)
|
742
894
|
except Exception as E:
|
743
895
|
warnings.warn("could not verify odering {}".format(ordering))
|
744
896
|
self.ordering = self.Ordering(ordering)
|
@@ -771,8 +923,10 @@ class NBodyTensor:
|
|
771
923
|
"""
|
772
924
|
# Check if index list has correct size
|
773
925
|
if len(idx_lists) != self.order:
|
774
|
-
raise Exception(
|
775
|
-
|
926
|
+
raise Exception(
|
927
|
+
"Need to pass an index list for each dimension!"
|
928
|
+
+ " Length of idx_lists needs to match order of tensor."
|
929
|
+
)
|
776
930
|
|
777
931
|
# Perform slicing via numpy.take
|
778
932
|
out = self.elems
|
@@ -783,13 +937,12 @@ class NBodyTensor:
|
|
783
937
|
return out
|
784
938
|
|
785
939
|
def set_index_lists(self):
|
786
|
-
"""
|
940
|
+
"""Set passive and full index lists based on class inputs"""
|
787
941
|
tmp_size = self._size_full
|
788
942
|
if self._size_full is None:
|
789
943
|
tmp_size = self.elems.shape[0]
|
790
944
|
|
791
|
-
self._passive_indices = [i for i in range(tmp_size)
|
792
|
-
if i not in self.active_indices]
|
945
|
+
self._passive_indices = [i for i in range(tmp_size) if i not in self.active_indices]
|
793
946
|
self._full_indices = [i for i in range(tmp_size)]
|
794
947
|
|
795
948
|
def sub_str(self, name: str) -> numpy.ndarray:
|
@@ -828,11 +981,11 @@ class NBodyTensor:
|
|
828
981
|
idx_lists = []
|
829
982
|
# Parse name as string of space indices
|
830
983
|
for char in name:
|
831
|
-
if char.lower() ==
|
984
|
+
if char.lower() == "a":
|
832
985
|
idx_lists.append(self.active_indices)
|
833
|
-
elif char.lower() ==
|
986
|
+
elif char.lower() == "p":
|
834
987
|
idx_lists.append(self._passive_indices)
|
835
|
-
elif char.lower() ==
|
988
|
+
elif char.lower() == "f":
|
836
989
|
if self._size_full is None:
|
837
990
|
idx_lists.append(None)
|
838
991
|
else:
|
@@ -844,7 +997,7 @@ class NBodyTensor:
|
|
844
997
|
|
845
998
|
return out
|
846
999
|
|
847
|
-
def reorder(self, to: str =
|
1000
|
+
def reorder(self, to: str = "of"):
|
848
1001
|
"""
|
849
1002
|
Function to reorder tensors according to some convention.
|
850
1003
|
|
@@ -871,7 +1024,7 @@ class NBodyTensor:
|
|
871
1024
|
-------
|
872
1025
|
"""
|
873
1026
|
if self.order != 4:
|
874
|
-
warnings.warn(
|
1027
|
+
warnings.warn("Reordering currently only implemented for two-body tensors.")
|
875
1028
|
return self
|
876
1029
|
|
877
1030
|
to = self.Ordering(scheme=to)
|
@@ -880,21 +1033,21 @@ class NBodyTensor:
|
|
880
1033
|
return self
|
881
1034
|
elif self.ordering.is_chem():
|
882
1035
|
if to.is_of():
|
883
|
-
self.elems = numpy.einsum("psqr -> pqrs", self.elems, optimize=
|
1036
|
+
self.elems = numpy.einsum("psqr -> pqrs", self.elems, optimize="greedy")
|
884
1037
|
elif to.is_phys():
|
885
|
-
self.elems = numpy.einsum("prqs -> pqrs", self.elems, optimize=
|
1038
|
+
self.elems = numpy.einsum("prqs -> pqrs", self.elems, optimize="greedy")
|
886
1039
|
elif self.ordering.is_of():
|
887
1040
|
if to.is_chem():
|
888
|
-
self.elems = numpy.einsum("pqrs -> psqr", self.elems, optimize=
|
1041
|
+
self.elems = numpy.einsum("pqrs -> psqr", self.elems, optimize="greedy")
|
889
1042
|
elif to.is_phys():
|
890
|
-
self.elems = numpy.einsum("pqrs -> pqsr", self.elems, optimize=
|
1043
|
+
self.elems = numpy.einsum("pqrs -> pqsr", self.elems, optimize="greedy")
|
891
1044
|
elif self.ordering.is_phys():
|
892
1045
|
if to.is_chem():
|
893
|
-
self.elems = numpy.einsum("pqrs -> prqs", self.elems, optimize=
|
1046
|
+
self.elems = numpy.einsum("pqrs -> prqs", self.elems, optimize="greedy")
|
894
1047
|
elif to.is_of():
|
895
|
-
self.elems = numpy.einsum("pqsr -> pqrs", self.elems, optimize=
|
1048
|
+
self.elems = numpy.einsum("pqsr -> pqrs", self.elems, optimize="greedy")
|
896
1049
|
|
897
|
-
self.ordering=to
|
1050
|
+
self.ordering = to
|
898
1051
|
return self
|
899
1052
|
|
900
1053
|
|
@@ -915,7 +1068,14 @@ class OrbitalData:
|
|
915
1068
|
self.occ = 2.0 # mark as reference
|
916
1069
|
|
917
1070
|
def __str__(self):
|
918
|
-
return
|
1071
|
+
return (
|
1072
|
+
"{"
|
1073
|
+
+ "{}".format("".join(["{}:{}, ".format(k, v) for k, v in self.__dict__.items() if v is not None]))
|
1074
|
+
.rstrip()
|
1075
|
+
.rstrip(",")
|
1076
|
+
+ "}"
|
1077
|
+
)
|
1078
|
+
|
919
1079
|
|
920
1080
|
class IntegralManager:
|
921
1081
|
"""
|
@@ -923,19 +1083,31 @@ class IntegralManager:
|
|
923
1083
|
All integrals are held in their original basis, the corresponding mo-coefficients have to be passed down
|
924
1084
|
and are usually held by the QuantumChemistryBaseClass
|
925
1085
|
"""
|
1086
|
+
|
926
1087
|
_overlap_integrals: numpy.ndarray = None
|
927
1088
|
_one_body_integrals: numpy.ndarray = None
|
928
1089
|
_two_body_integrals: NBodyTensor = None
|
929
1090
|
_constant_term: float = None
|
930
1091
|
_basis_name: str = "unknown"
|
931
|
-
_orbital_type: str = "unknown"
|
1092
|
+
_orbital_type: str = "unknown" # e.g. "HF", "PNO", "native"
|
932
1093
|
_orbital_coefficients: numpy.ndarray = None
|
933
1094
|
_active_space: ActiveSpaceData = None
|
934
1095
|
_orbitals: typing.List[OrbitalData] = None
|
935
1096
|
|
936
|
-
def __init__(
|
937
|
-
|
938
|
-
|
1097
|
+
def __init__(
|
1098
|
+
self,
|
1099
|
+
one_body_integrals,
|
1100
|
+
two_body_integrals,
|
1101
|
+
basis_name="unknown",
|
1102
|
+
orbital_type="unknown",
|
1103
|
+
constant_term=0.0,
|
1104
|
+
orbital_coefficients=None,
|
1105
|
+
active_space=None,
|
1106
|
+
overlap_integrals=None,
|
1107
|
+
orbitals=None,
|
1108
|
+
*args,
|
1109
|
+
**kwargs,
|
1110
|
+
):
|
939
1111
|
self._one_body_integrals = one_body_integrals
|
940
1112
|
self._two_body_integrals = two_body_integrals
|
941
1113
|
self._constant_term = constant_term
|
@@ -949,7 +1121,9 @@ class IntegralManager:
|
|
949
1121
|
except Exception as E:
|
950
1122
|
raise TequilaException(
|
951
1123
|
"{}\ntwo_body_integrals given in wrong format. Needs to be a tq.chemistry.NBodyTensor in chem ordering.\n{} with ordering={}".format(
|
952
|
-
str(E), str(type(two_body_integrals)), str(two_body_integrals.ordering)
|
1124
|
+
str(E), str(type(two_body_integrals)), str(two_body_integrals.ordering)
|
1125
|
+
)
|
1126
|
+
)
|
953
1127
|
|
954
1128
|
for i in range(4):
|
955
1129
|
assert self._one_body_integrals.shape[0] == self._two_body_integrals.elems.shape[i]
|
@@ -970,7 +1144,7 @@ class IntegralManager:
|
|
970
1144
|
|
971
1145
|
self._orbitals = orbitals
|
972
1146
|
self.active_space = active_space
|
973
|
-
|
1147
|
+
|
974
1148
|
def get_orthonormalized_orbital_coefficients(self):
|
975
1149
|
"""
|
976
1150
|
Computes orbitals in this basis that are orthonormal (through loewdin orthonormalization)
|
@@ -1005,7 +1179,7 @@ class IntegralManager:
|
|
1005
1179
|
self._active_space = other
|
1006
1180
|
for x in self._orbitals:
|
1007
1181
|
x.idx = None
|
1008
|
-
for ii,i in enumerate(other.active_orbitals):
|
1182
|
+
for ii, i in enumerate(other.active_orbitals):
|
1009
1183
|
self._orbitals[i].idx = ii
|
1010
1184
|
|
1011
1185
|
@property
|
@@ -1014,7 +1188,9 @@ class IntegralManager:
|
|
1014
1188
|
|
1015
1189
|
@property
|
1016
1190
|
def active_reference_orbitals(self):
|
1017
|
-
return [
|
1191
|
+
return [
|
1192
|
+
self._orbitals[i] for i in self.active_space.active_orbitals if i in self.active_space.reference_orbitals
|
1193
|
+
]
|
1018
1194
|
|
1019
1195
|
@property
|
1020
1196
|
def overlap_integrals(self):
|
@@ -1067,23 +1243,26 @@ class IntegralManager:
|
|
1067
1243
|
def orbital_coefficients(self, other):
|
1068
1244
|
self.verify_orbital_coefficients(orbital_coefficients=other)
|
1069
1245
|
self._orbital_coefficients = other
|
1070
|
-
for i,x in enumerate(self._orbitals):
|
1246
|
+
for i, x in enumerate(self._orbitals):
|
1071
1247
|
y = OrbitalData(idx=x.idx, idx_total=x.idx_total)
|
1072
1248
|
self._orbitals[i] = y
|
1073
|
-
|
1249
|
+
|
1074
1250
|
def transform_to_native_orbitals(self):
|
1075
1251
|
"""
|
1076
1252
|
Transform orbitals to orthonormal functions closest to the native basis
|
1077
1253
|
"""
|
1078
1254
|
c = self.get_orthonormalized_orbital_coefficients()
|
1079
|
-
self.orbital_coefficients=c
|
1080
|
-
self._orbital_type="orthonormalized-{}-basis".format(self._basis_name)
|
1255
|
+
self.orbital_coefficients = c
|
1256
|
+
self._orbital_type = "orthonormalized-{}-basis".format(self._basis_name)
|
1081
1257
|
|
1082
1258
|
def is_unitary(self, U):
|
1083
|
-
if len(U.shape) != 2:
|
1084
|
-
|
1259
|
+
if len(U.shape) != 2:
|
1260
|
+
return False
|
1261
|
+
if U.shape[0] != U.shape[1]:
|
1262
|
+
return False
|
1085
1263
|
test = (U.conj().T).dot(U) - numpy.eye(U.shape[0])
|
1086
|
-
if not numpy.isclose(numpy.linalg.norm(test), 0.0):
|
1264
|
+
if not numpy.isclose(numpy.linalg.norm(test), 0.0):
|
1265
|
+
return False
|
1087
1266
|
return True
|
1088
1267
|
|
1089
1268
|
def transform_orbitals(self, U, name=None):
|
@@ -1104,7 +1283,9 @@ class IntegralManager:
|
|
1104
1283
|
else:
|
1105
1284
|
self._orbital_type = name
|
1106
1285
|
|
1107
|
-
def get_integrals(
|
1286
|
+
def get_integrals(
|
1287
|
+
self, orbital_coefficients=None, ordering="openfermion", ignore_active_space=False, *args, **kwargs
|
1288
|
+
):
|
1108
1289
|
"""
|
1109
1290
|
Get all molecular integrals in given orbital basis (determined by orbital_coefficients in self or the ones passed here)
|
1110
1291
|
active space is considered if not explicitly ignored
|
@@ -1125,12 +1306,14 @@ class IntegralManager:
|
|
1125
1306
|
h = self._get_transformed_one_body_integrals(orbital_coefficients=orbital_coefficients)
|
1126
1307
|
g = self._get_transformed_two_body_integrals(orbital_coefficients=orbital_coefficients, ordering=ordering)
|
1127
1308
|
if not ignore_active_space and self._active_space is not None:
|
1128
|
-
|
1129
1309
|
g = g.reorder(to="openfermion").elems
|
1130
1310
|
|
1131
|
-
active_integrals = get_active_space_integrals(
|
1132
|
-
|
1133
|
-
|
1311
|
+
active_integrals = get_active_space_integrals(
|
1312
|
+
one_body_integrals=h,
|
1313
|
+
two_body_integrals=g,
|
1314
|
+
occupied_indices=self._active_space.frozen_reference_orbitals,
|
1315
|
+
active_indices=self._active_space.active_orbitals,
|
1316
|
+
)
|
1134
1317
|
|
1135
1318
|
c = active_integrals[0] + c
|
1136
1319
|
|
@@ -1145,8 +1328,8 @@ class IntegralManager:
|
|
1145
1328
|
elif verify:
|
1146
1329
|
assert self.verify_orbital_coefficients(orbital_coefficients=orbital_coefficients)
|
1147
1330
|
h = self.one_body_integrals
|
1148
|
-
h = numpy.einsum("ix, xj -> ij", h, orbital_coefficients, optimize=
|
1149
|
-
h = numpy.einsum("xj, xi -> ij", h, orbital_coefficients, optimize=
|
1331
|
+
h = numpy.einsum("ix, xj -> ij", h, orbital_coefficients, optimize="greedy")
|
1332
|
+
h = numpy.einsum("xj, xi -> ij", h, orbital_coefficients, optimize="greedy")
|
1150
1333
|
|
1151
1334
|
return h
|
1152
1335
|
|
@@ -1158,16 +1341,16 @@ class IntegralManager:
|
|
1158
1341
|
|
1159
1342
|
g = self.two_body_integrals
|
1160
1343
|
g = g.reorder("chem").elems
|
1161
|
-
g = numpy.einsum("ijkx, xl -> ijkl", g, orbital_coefficients, optimize=
|
1162
|
-
g = numpy.einsum("ijxl, xk -> ijkl", g, orbital_coefficients, optimize=
|
1163
|
-
g = numpy.einsum("ixkl, xj -> ijkl", g, orbital_coefficients, optimize=
|
1164
|
-
g = numpy.einsum("xjkl, xi -> ijkl", g, orbital_coefficients, optimize=
|
1165
|
-
g = NBodyTensor(elems=numpy.asarray(g), ordering=
|
1344
|
+
g = numpy.einsum("ijkx, xl -> ijkl", g, orbital_coefficients, optimize="greedy")
|
1345
|
+
g = numpy.einsum("ijxl, xk -> ijkl", g, orbital_coefficients, optimize="greedy")
|
1346
|
+
g = numpy.einsum("ixkl, xj -> ijkl", g, orbital_coefficients, optimize="greedy")
|
1347
|
+
g = numpy.einsum("xjkl, xi -> ijkl", g, orbital_coefficients, optimize="greedy")
|
1348
|
+
g = NBodyTensor(elems=numpy.asarray(g), ordering="chem")
|
1166
1349
|
g = g.reorder(to=ordering)
|
1167
1350
|
|
1168
1351
|
return g
|
1169
1352
|
|
1170
|
-
def verify_orbital_coefficients(self, orbital_coefficients, tolerance=1.
|
1353
|
+
def verify_orbital_coefficients(self, orbital_coefficients, tolerance=1.0e-5):
|
1171
1354
|
"""
|
1172
1355
|
Verify if orbital coefficients are valid (i.e. if they define a orthonormal set of orbitals)
|
1173
1356
|
Parameters
|
@@ -1181,11 +1364,11 @@ class IntegralManager:
|
|
1181
1364
|
|
1182
1365
|
"""
|
1183
1366
|
S = self.overlap_integrals
|
1184
|
-
St = numpy.einsum("ix, xj -> ij", S, orbital_coefficients, optimize=
|
1185
|
-
St = numpy.einsum("xj, xi -> ij", St, orbital_coefficients, optimize=
|
1367
|
+
St = numpy.einsum("ix, xj -> ij", S, orbital_coefficients, optimize="greedy")
|
1368
|
+
St = numpy.einsum("xj, xi -> ij", St, orbital_coefficients, optimize="greedy")
|
1186
1369
|
return numpy.linalg.norm(St - numpy.eye(S.shape[0])) < tolerance
|
1187
1370
|
|
1188
|
-
def basis_is_orthogonal(self, tolerance=1.
|
1371
|
+
def basis_is_orthogonal(self, tolerance=1.0e-5):
|
1189
1372
|
S = self.overlap_integrals
|
1190
1373
|
return numpy.linalg.norm(S - numpy.eye(S.shape[0])) < tolerance
|
1191
1374
|
|
@@ -1194,9 +1377,9 @@ class IntegralManager:
|
|
1194
1377
|
|
1195
1378
|
def __str__(self):
|
1196
1379
|
result = "\nIntegralManager:\n"
|
1197
|
-
result+= "ActiveSpace:\n"
|
1198
|
-
result+= str(self.active_space)
|
1199
|
-
result+= "Orbitals:\n"
|
1380
|
+
result += "ActiveSpace:\n"
|
1381
|
+
result += str(self.active_space)
|
1382
|
+
result += "Orbitals:\n"
|
1200
1383
|
for x in self.orbitals:
|
1201
1384
|
result += str(x) + "\n"
|
1202
1385
|
return result
|
@@ -1209,9 +1392,10 @@ class IntegralManager:
|
|
1209
1392
|
print("{:15} : {}".format("active orbitals", [o.idx_total for o in self.active_orbitals]), *args, **kwargs)
|
1210
1393
|
print("{:15} : {}".format("reference", [x.idx_total for x in self.reference_orbitals]), *args, **kwargs)
|
1211
1394
|
|
1212
|
-
if not print_coefficients:
|
1395
|
+
if not print_coefficients:
|
1396
|
+
return
|
1213
1397
|
|
1214
1398
|
print("Current Orbitals", *args, **kwargs)
|
1215
|
-
for i,x in enumerate(self.orbitals):
|
1399
|
+
for i, x in enumerate(self.orbitals):
|
1216
1400
|
print(x, *args, **kwargs)
|
1217
|
-
print("coefficients: ", self.orbital_coefficients[:,i], *args, **kwargs)
|
1401
|
+
print("coefficients: ", self.orbital_coefficients[:, i], *args, **kwargs)
|