tequila-basic 1.9.8__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 +177 -88
- 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 +91 -56
- 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 +394 -203
- tequila/quantumchemistry/encodings.py +121 -13
- tequila/quantumchemistry/madness_interface.py +170 -96
- tequila/quantumchemistry/orbital_optimizer.py +86 -40
- 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 +258 -106
- tequila/simulators/simulator_aqt.py +102 -0
- tequila/simulators/simulator_base.py +156 -55
- 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 +124 -114
- tequila/simulators/simulator_qlm.py +52 -26
- tequila/simulators/simulator_qulacs.py +85 -59
- tequila/simulators/simulator_spex.py +464 -0
- tequila/simulators/simulator_symbolic.py +6 -5
- tequila/simulators/test_spex_simulator.py +208 -0
- tequila/tools/convenience.py +4 -4
- tequila/tools/qng.py +72 -64
- tequila/tools/random_generators.py +38 -34
- tequila/utils/bitstrings.py +13 -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 +52 -30
- {tequila_basic-1.9.8.dist-info → tequila_basic-1.9.10.dist-info}/METADATA +23 -17
- tequila_basic-1.9.10.dist-info/RECORD +93 -0
- {tequila_basic-1.9.8.dist-info → tequila_basic-1.9.10.dist-info}/WHEEL +1 -1
- tequila_basic-1.9.8.dist-info/RECORD +0 -86
- {tequila_basic-1.9.8.dist-info → tequila_basic-1.9.10.dist-info/licenses}/LICENSE +0 -0
- {tequila_basic-1.9.8.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,37 +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
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"
|
559
|
+
for at in geom:
|
560
|
+
f += f"{at[0]} {at[1][0]} {at[1][1]} {at[1][2]}\n"
|
561
|
+
return f
|
562
|
+
|
446
563
|
|
447
564
|
@dataclass
|
448
565
|
class ClosedShellAmplitudes:
|
449
566
|
"""
|
450
|
-
Helper Class for
|
567
|
+
Helper Class for classical amplitudes
|
451
568
|
used internally
|
452
569
|
"""
|
570
|
+
|
453
571
|
tIjAb: numpy.ndarray = None
|
454
572
|
tIA: numpy.ndarray = None
|
455
573
|
|
456
|
-
def make_parameter_dictionary(self, threshold=1.
|
574
|
+
def make_parameter_dictionary(self, threshold=1.0e-8, screening=True):
|
457
575
|
"""
|
458
576
|
|
459
577
|
Parameters
|
@@ -469,13 +587,16 @@ class ClosedShellAmplitudes:
|
|
469
587
|
if self.tIjAb is not None:
|
470
588
|
nvirt = self.tIjAb.shape[2]
|
471
589
|
nocc = self.tIjAb.shape[0]
|
472
|
-
assert
|
590
|
+
assert self.tIjAb.shape[1] == nocc and self.tIjAb.shape[3] == nvirt
|
473
591
|
for (I, J, A, B), value in numpy.ndenumerate(self.tIjAb):
|
474
592
|
if not numpy.isclose(value, 0.0, atol=threshold) or not screening:
|
475
593
|
variables[(nocc + A, I, nocc + B, J)] = value
|
476
594
|
if self.tIA is not None:
|
477
595
|
nocc = self.tIA.shape[0]
|
478
|
-
for (
|
596
|
+
for (
|
597
|
+
(I, A),
|
598
|
+
value,
|
599
|
+
) in numpy.ndenumerate(self.tIA):
|
479
600
|
if not numpy.isclose(value, 0.0, atol=threshold) or not screening:
|
480
601
|
variables[(A + nocc, I)] = value
|
481
602
|
return dict(sorted(variables.items(), key=lambda x: numpy.abs(x[1]), reverse=True))
|
@@ -511,7 +632,7 @@ class Amplitudes:
|
|
511
632
|
-------
|
512
633
|
|
513
634
|
"""
|
514
|
-
tijab = cs.tIjAb - numpy.einsum("ijab -> ijba", cs.tIjAb, optimize=
|
635
|
+
tijab = cs.tIjAb - numpy.einsum("ijab -> ijba", cs.tIjAb, optimize="greedy")
|
515
636
|
return cls(tIjAb=cs.tIjAb, tIA=cs.tIA, tiJaB=cs.tIjAb, tia=cs.tIA, tijab=tijab, tIJAB=tijab)
|
516
637
|
|
517
638
|
tIjAb: numpy.ndarray = None
|
@@ -521,7 +642,7 @@ class Amplitudes:
|
|
521
642
|
tIJAB: numpy.ndarray = None
|
522
643
|
tia: numpy.ndarray = None
|
523
644
|
|
524
|
-
def make_parameter_dictionary(self, threshold=1.
|
645
|
+
def make_parameter_dictionary(self, threshold=1.0e-8):
|
525
646
|
"""
|
526
647
|
|
527
648
|
Parameters
|
@@ -539,7 +660,7 @@ class Amplitudes:
|
|
539
660
|
if self.tIjAb is not None:
|
540
661
|
nvirt = self.tIjAb.shape[2]
|
541
662
|
nocc = self.tIjAb.shape[0]
|
542
|
-
assert
|
663
|
+
assert self.tIjAb.shape[1] == nocc and self.tIjAb.shape[3] == nvirt
|
543
664
|
|
544
665
|
for (I, j, A, b), value in numpy.ndenumerate(self.tIjAb):
|
545
666
|
if not numpy.isclose(value, 0.0, atol=threshold):
|
@@ -556,11 +677,17 @@ class Amplitudes:
|
|
556
677
|
|
557
678
|
if self.tIA is not None:
|
558
679
|
nocc = self.tIjAb.shape[0]
|
559
|
-
assert
|
560
|
-
for (
|
680
|
+
assert self.tia.shape[0] == nocc
|
681
|
+
for (
|
682
|
+
(I, A),
|
683
|
+
value,
|
684
|
+
) in numpy.ndenumerate(self.tIA):
|
561
685
|
if not numpy.isclose(value, 0.0, atol=threshold):
|
562
686
|
variables[(2 * (A + nocc), 2 * I)] = value
|
563
|
-
for (
|
687
|
+
for (
|
688
|
+
(i, a),
|
689
|
+
value,
|
690
|
+
) in numpy.ndenumerate(self.tIA):
|
564
691
|
if not numpy.isclose(value, 0.0, atol=threshold):
|
565
692
|
variables[(2 * (a + nocc) + 1, 2 * i + 1)] = value
|
566
693
|
|
@@ -568,7 +695,7 @@ class Amplitudes:
|
|
568
695
|
|
569
696
|
|
570
697
|
class NBodyTensor:
|
571
|
-
"""
|
698
|
+
"""Convenience class for handling N-body tensors"""
|
572
699
|
|
573
700
|
class Ordering:
|
574
701
|
"""
|
@@ -599,7 +726,8 @@ class NBodyTensor:
|
|
599
726
|
return "of"
|
600
727
|
else:
|
601
728
|
raise TequilaException(
|
602
|
-
"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
|
+
)
|
603
731
|
|
604
732
|
def is_phys(self):
|
605
733
|
return self._scheme == "phys"
|
@@ -616,22 +744,25 @@ class NBodyTensor:
|
|
616
744
|
def identify_ordering(self, trials=25):
|
617
745
|
if len(self.shape) != 4:
|
618
746
|
return None
|
619
|
-
chem=False
|
620
|
-
phys=False
|
621
|
-
of=False
|
747
|
+
chem = False
|
748
|
+
phys = False
|
749
|
+
of = False
|
622
750
|
if self._verify_ordering_mulliken(trials=trials):
|
623
|
-
chem=self.Ordering(scheme="mulliken")
|
751
|
+
chem = self.Ordering(scheme="mulliken")
|
624
752
|
if self._verify_ordering_dirac(trials=trials):
|
625
|
-
phys=self.Ordering(scheme="dirac")
|
753
|
+
phys = self.Ordering(scheme="dirac")
|
626
754
|
if self._verify_ordering_of(trials=trials):
|
627
|
-
of=self.Ordering(scheme="openfermion")
|
628
|
-
|
629
|
-
uniqueness = (chem,phys,of)
|
630
|
-
if not uniqueness.count(False) == 2 and trials<100:
|
631
|
-
return self.identify_ordering(trials=trials*2)
|
632
|
-
if chem:
|
633
|
-
|
634
|
-
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")
|
635
766
|
else:
|
636
767
|
raise Exception("NBTensor ordering could not be identified")
|
637
768
|
|
@@ -643,10 +774,16 @@ class NBodyTensor:
|
|
643
774
|
elems = self.elems
|
644
775
|
n = self.shape[0]
|
645
776
|
for _ in range(trials):
|
646
|
-
idx = numpy.random.randint(0,n,4)
|
647
|
-
test1 = numpy.isclose(
|
648
|
-
|
649
|
-
|
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
|
+
)
|
650
787
|
if not (test1 and test2 and test3):
|
651
788
|
return False
|
652
789
|
|
@@ -659,10 +796,16 @@ class NBodyTensor:
|
|
659
796
|
elems = self.elems
|
660
797
|
n = self.shape[0]
|
661
798
|
for _ in range(trials):
|
662
|
-
idx = numpy.random.randint(0,n,4)
|
663
|
-
test1 = numpy.isclose(
|
664
|
-
|
665
|
-
|
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
|
+
)
|
666
809
|
if not (test1 and test2 and test3):
|
667
810
|
return False
|
668
811
|
|
@@ -675,17 +818,29 @@ class NBodyTensor:
|
|
675
818
|
elems = self.elems
|
676
819
|
n = self.shape[0]
|
677
820
|
for _ in range(trials):
|
678
|
-
idx = numpy.random.randint(0,n,4)
|
679
|
-
test1 = numpy.isclose(
|
680
|
-
|
681
|
-
|
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
|
+
)
|
682
831
|
if not (test1 and test2 and test3):
|
683
832
|
return False
|
684
833
|
|
685
834
|
return True
|
686
835
|
|
687
|
-
def __init__(
|
688
|
-
|
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
|
+
):
|
689
844
|
"""
|
690
845
|
Parameters
|
691
846
|
----------
|
@@ -728,10 +883,14 @@ class NBodyTensor:
|
|
728
883
|
if ordering is None:
|
729
884
|
ordering = self.identify_ordering()
|
730
885
|
elif verify:
|
731
|
-
try:
|
732
|
-
auto_ordering=self.identify_ordering()
|
886
|
+
try: # some RDMs are really sloppy (depends on backend)
|
887
|
+
auto_ordering = self.identify_ordering()
|
733
888
|
if auto_ordering is not ordering:
|
734
|
-
warnings.warn(
|
889
|
+
warnings.warn(
|
890
|
+
"Auto identified ordering of NBTensor does not match given ordering: {} vs {}".format(
|
891
|
+
auto_ordering, ordering
|
892
|
+
)
|
893
|
+
)
|
735
894
|
except Exception as E:
|
736
895
|
warnings.warn("could not verify odering {}".format(ordering))
|
737
896
|
self.ordering = self.Ordering(ordering)
|
@@ -764,8 +923,10 @@ class NBodyTensor:
|
|
764
923
|
"""
|
765
924
|
# Check if index list has correct size
|
766
925
|
if len(idx_lists) != self.order:
|
767
|
-
raise Exception(
|
768
|
-
|
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
|
+
)
|
769
930
|
|
770
931
|
# Perform slicing via numpy.take
|
771
932
|
out = self.elems
|
@@ -776,13 +937,12 @@ class NBodyTensor:
|
|
776
937
|
return out
|
777
938
|
|
778
939
|
def set_index_lists(self):
|
779
|
-
"""
|
940
|
+
"""Set passive and full index lists based on class inputs"""
|
780
941
|
tmp_size = self._size_full
|
781
942
|
if self._size_full is None:
|
782
943
|
tmp_size = self.elems.shape[0]
|
783
944
|
|
784
|
-
self._passive_indices = [i for i in range(tmp_size)
|
785
|
-
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]
|
786
946
|
self._full_indices = [i for i in range(tmp_size)]
|
787
947
|
|
788
948
|
def sub_str(self, name: str) -> numpy.ndarray:
|
@@ -821,11 +981,11 @@ class NBodyTensor:
|
|
821
981
|
idx_lists = []
|
822
982
|
# Parse name as string of space indices
|
823
983
|
for char in name:
|
824
|
-
if char.lower() ==
|
984
|
+
if char.lower() == "a":
|
825
985
|
idx_lists.append(self.active_indices)
|
826
|
-
elif char.lower() ==
|
986
|
+
elif char.lower() == "p":
|
827
987
|
idx_lists.append(self._passive_indices)
|
828
|
-
elif char.lower() ==
|
988
|
+
elif char.lower() == "f":
|
829
989
|
if self._size_full is None:
|
830
990
|
idx_lists.append(None)
|
831
991
|
else:
|
@@ -837,7 +997,7 @@ class NBodyTensor:
|
|
837
997
|
|
838
998
|
return out
|
839
999
|
|
840
|
-
def reorder(self, to: str =
|
1000
|
+
def reorder(self, to: str = "of"):
|
841
1001
|
"""
|
842
1002
|
Function to reorder tensors according to some convention.
|
843
1003
|
|
@@ -864,7 +1024,7 @@ class NBodyTensor:
|
|
864
1024
|
-------
|
865
1025
|
"""
|
866
1026
|
if self.order != 4:
|
867
|
-
warnings.warn(
|
1027
|
+
warnings.warn("Reordering currently only implemented for two-body tensors.")
|
868
1028
|
return self
|
869
1029
|
|
870
1030
|
to = self.Ordering(scheme=to)
|
@@ -873,21 +1033,21 @@ class NBodyTensor:
|
|
873
1033
|
return self
|
874
1034
|
elif self.ordering.is_chem():
|
875
1035
|
if to.is_of():
|
876
|
-
self.elems = numpy.einsum("psqr -> pqrs", self.elems, optimize=
|
1036
|
+
self.elems = numpy.einsum("psqr -> pqrs", self.elems, optimize="greedy")
|
877
1037
|
elif to.is_phys():
|
878
|
-
self.elems = numpy.einsum("prqs -> pqrs", self.elems, optimize=
|
1038
|
+
self.elems = numpy.einsum("prqs -> pqrs", self.elems, optimize="greedy")
|
879
1039
|
elif self.ordering.is_of():
|
880
1040
|
if to.is_chem():
|
881
|
-
self.elems = numpy.einsum("pqrs -> psqr", self.elems, optimize=
|
1041
|
+
self.elems = numpy.einsum("pqrs -> psqr", self.elems, optimize="greedy")
|
882
1042
|
elif to.is_phys():
|
883
|
-
self.elems = numpy.einsum("pqrs -> pqsr", self.elems, optimize=
|
1043
|
+
self.elems = numpy.einsum("pqrs -> pqsr", self.elems, optimize="greedy")
|
884
1044
|
elif self.ordering.is_phys():
|
885
1045
|
if to.is_chem():
|
886
|
-
self.elems = numpy.einsum("pqrs -> prqs", self.elems, optimize=
|
1046
|
+
self.elems = numpy.einsum("pqrs -> prqs", self.elems, optimize="greedy")
|
887
1047
|
elif to.is_of():
|
888
|
-
self.elems = numpy.einsum("pqsr -> pqrs", self.elems, optimize=
|
1048
|
+
self.elems = numpy.einsum("pqsr -> pqrs", self.elems, optimize="greedy")
|
889
1049
|
|
890
|
-
self.ordering=to
|
1050
|
+
self.ordering = to
|
891
1051
|
return self
|
892
1052
|
|
893
1053
|
|
@@ -908,7 +1068,14 @@ class OrbitalData:
|
|
908
1068
|
self.occ = 2.0 # mark as reference
|
909
1069
|
|
910
1070
|
def __str__(self):
|
911
|
-
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
|
+
|
912
1079
|
|
913
1080
|
class IntegralManager:
|
914
1081
|
"""
|
@@ -916,19 +1083,31 @@ class IntegralManager:
|
|
916
1083
|
All integrals are held in their original basis, the corresponding mo-coefficients have to be passed down
|
917
1084
|
and are usually held by the QuantumChemistryBaseClass
|
918
1085
|
"""
|
1086
|
+
|
919
1087
|
_overlap_integrals: numpy.ndarray = None
|
920
1088
|
_one_body_integrals: numpy.ndarray = None
|
921
1089
|
_two_body_integrals: NBodyTensor = None
|
922
1090
|
_constant_term: float = None
|
923
1091
|
_basis_name: str = "unknown"
|
924
|
-
_orbital_type: str = "unknown"
|
1092
|
+
_orbital_type: str = "unknown" # e.g. "HF", "PNO", "native"
|
925
1093
|
_orbital_coefficients: numpy.ndarray = None
|
926
1094
|
_active_space: ActiveSpaceData = None
|
927
1095
|
_orbitals: typing.List[OrbitalData] = None
|
928
1096
|
|
929
|
-
def __init__(
|
930
|
-
|
931
|
-
|
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
|
+
):
|
932
1111
|
self._one_body_integrals = one_body_integrals
|
933
1112
|
self._two_body_integrals = two_body_integrals
|
934
1113
|
self._constant_term = constant_term
|
@@ -942,7 +1121,9 @@ class IntegralManager:
|
|
942
1121
|
except Exception as E:
|
943
1122
|
raise TequilaException(
|
944
1123
|
"{}\ntwo_body_integrals given in wrong format. Needs to be a tq.chemistry.NBodyTensor in chem ordering.\n{} with ordering={}".format(
|
945
|
-
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
|
+
)
|
946
1127
|
|
947
1128
|
for i in range(4):
|
948
1129
|
assert self._one_body_integrals.shape[0] == self._two_body_integrals.elems.shape[i]
|
@@ -963,7 +1144,7 @@ class IntegralManager:
|
|
963
1144
|
|
964
1145
|
self._orbitals = orbitals
|
965
1146
|
self.active_space = active_space
|
966
|
-
|
1147
|
+
|
967
1148
|
def get_orthonormalized_orbital_coefficients(self):
|
968
1149
|
"""
|
969
1150
|
Computes orbitals in this basis that are orthonormal (through loewdin orthonormalization)
|
@@ -998,7 +1179,7 @@ class IntegralManager:
|
|
998
1179
|
self._active_space = other
|
999
1180
|
for x in self._orbitals:
|
1000
1181
|
x.idx = None
|
1001
|
-
for ii,i in enumerate(other.active_orbitals):
|
1182
|
+
for ii, i in enumerate(other.active_orbitals):
|
1002
1183
|
self._orbitals[i].idx = ii
|
1003
1184
|
|
1004
1185
|
@property
|
@@ -1007,7 +1188,9 @@ class IntegralManager:
|
|
1007
1188
|
|
1008
1189
|
@property
|
1009
1190
|
def active_reference_orbitals(self):
|
1010
|
-
return [
|
1191
|
+
return [
|
1192
|
+
self._orbitals[i] for i in self.active_space.active_orbitals if i in self.active_space.reference_orbitals
|
1193
|
+
]
|
1011
1194
|
|
1012
1195
|
@property
|
1013
1196
|
def overlap_integrals(self):
|
@@ -1060,23 +1243,26 @@ class IntegralManager:
|
|
1060
1243
|
def orbital_coefficients(self, other):
|
1061
1244
|
self.verify_orbital_coefficients(orbital_coefficients=other)
|
1062
1245
|
self._orbital_coefficients = other
|
1063
|
-
for i,x in enumerate(self._orbitals):
|
1246
|
+
for i, x in enumerate(self._orbitals):
|
1064
1247
|
y = OrbitalData(idx=x.idx, idx_total=x.idx_total)
|
1065
1248
|
self._orbitals[i] = y
|
1066
|
-
|
1249
|
+
|
1067
1250
|
def transform_to_native_orbitals(self):
|
1068
1251
|
"""
|
1069
1252
|
Transform orbitals to orthonormal functions closest to the native basis
|
1070
1253
|
"""
|
1071
1254
|
c = self.get_orthonormalized_orbital_coefficients()
|
1072
|
-
self.orbital_coefficients=c
|
1073
|
-
self._orbital_type="orthonormalized-{}-basis".format(self._basis_name)
|
1255
|
+
self.orbital_coefficients = c
|
1256
|
+
self._orbital_type = "orthonormalized-{}-basis".format(self._basis_name)
|
1074
1257
|
|
1075
1258
|
def is_unitary(self, U):
|
1076
|
-
if len(U.shape) != 2:
|
1077
|
-
|
1259
|
+
if len(U.shape) != 2:
|
1260
|
+
return False
|
1261
|
+
if U.shape[0] != U.shape[1]:
|
1262
|
+
return False
|
1078
1263
|
test = (U.conj().T).dot(U) - numpy.eye(U.shape[0])
|
1079
|
-
if not numpy.isclose(numpy.linalg.norm(test), 0.0):
|
1264
|
+
if not numpy.isclose(numpy.linalg.norm(test), 0.0):
|
1265
|
+
return False
|
1080
1266
|
return True
|
1081
1267
|
|
1082
1268
|
def transform_orbitals(self, U, name=None):
|
@@ -1097,7 +1283,9 @@ class IntegralManager:
|
|
1097
1283
|
else:
|
1098
1284
|
self._orbital_type = name
|
1099
1285
|
|
1100
|
-
def get_integrals(
|
1286
|
+
def get_integrals(
|
1287
|
+
self, orbital_coefficients=None, ordering="openfermion", ignore_active_space=False, *args, **kwargs
|
1288
|
+
):
|
1101
1289
|
"""
|
1102
1290
|
Get all molecular integrals in given orbital basis (determined by orbital_coefficients in self or the ones passed here)
|
1103
1291
|
active space is considered if not explicitly ignored
|
@@ -1118,12 +1306,14 @@ class IntegralManager:
|
|
1118
1306
|
h = self._get_transformed_one_body_integrals(orbital_coefficients=orbital_coefficients)
|
1119
1307
|
g = self._get_transformed_two_body_integrals(orbital_coefficients=orbital_coefficients, ordering=ordering)
|
1120
1308
|
if not ignore_active_space and self._active_space is not None:
|
1121
|
-
|
1122
1309
|
g = g.reorder(to="openfermion").elems
|
1123
1310
|
|
1124
|
-
active_integrals = get_active_space_integrals(
|
1125
|
-
|
1126
|
-
|
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
|
+
)
|
1127
1317
|
|
1128
1318
|
c = active_integrals[0] + c
|
1129
1319
|
|
@@ -1138,8 +1328,8 @@ class IntegralManager:
|
|
1138
1328
|
elif verify:
|
1139
1329
|
assert self.verify_orbital_coefficients(orbital_coefficients=orbital_coefficients)
|
1140
1330
|
h = self.one_body_integrals
|
1141
|
-
h = numpy.einsum("ix, xj -> ij", h, orbital_coefficients, optimize=
|
1142
|
-
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")
|
1143
1333
|
|
1144
1334
|
return h
|
1145
1335
|
|
@@ -1151,16 +1341,16 @@ class IntegralManager:
|
|
1151
1341
|
|
1152
1342
|
g = self.two_body_integrals
|
1153
1343
|
g = g.reorder("chem").elems
|
1154
|
-
g = numpy.einsum("ijkx, xl -> ijkl", g, orbital_coefficients, optimize=
|
1155
|
-
g = numpy.einsum("ijxl, xk -> ijkl", g, orbital_coefficients, optimize=
|
1156
|
-
g = numpy.einsum("ixkl, xj -> ijkl", g, orbital_coefficients, optimize=
|
1157
|
-
g = numpy.einsum("xjkl, xi -> ijkl", g, orbital_coefficients, optimize=
|
1158
|
-
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")
|
1159
1349
|
g = g.reorder(to=ordering)
|
1160
1350
|
|
1161
1351
|
return g
|
1162
1352
|
|
1163
|
-
def verify_orbital_coefficients(self, orbital_coefficients, tolerance=1.
|
1353
|
+
def verify_orbital_coefficients(self, orbital_coefficients, tolerance=1.0e-5):
|
1164
1354
|
"""
|
1165
1355
|
Verify if orbital coefficients are valid (i.e. if they define a orthonormal set of orbitals)
|
1166
1356
|
Parameters
|
@@ -1174,11 +1364,11 @@ class IntegralManager:
|
|
1174
1364
|
|
1175
1365
|
"""
|
1176
1366
|
S = self.overlap_integrals
|
1177
|
-
St = numpy.einsum("ix, xj -> ij", S, orbital_coefficients, optimize=
|
1178
|
-
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")
|
1179
1369
|
return numpy.linalg.norm(St - numpy.eye(S.shape[0])) < tolerance
|
1180
1370
|
|
1181
|
-
def basis_is_orthogonal(self, tolerance=1.
|
1371
|
+
def basis_is_orthogonal(self, tolerance=1.0e-5):
|
1182
1372
|
S = self.overlap_integrals
|
1183
1373
|
return numpy.linalg.norm(S - numpy.eye(S.shape[0])) < tolerance
|
1184
1374
|
|
@@ -1187,9 +1377,9 @@ class IntegralManager:
|
|
1187
1377
|
|
1188
1378
|
def __str__(self):
|
1189
1379
|
result = "\nIntegralManager:\n"
|
1190
|
-
result+= "ActiveSpace:\n"
|
1191
|
-
result+= str(self.active_space)
|
1192
|
-
result+= "Orbitals:\n"
|
1380
|
+
result += "ActiveSpace:\n"
|
1381
|
+
result += str(self.active_space)
|
1382
|
+
result += "Orbitals:\n"
|
1193
1383
|
for x in self.orbitals:
|
1194
1384
|
result += str(x) + "\n"
|
1195
1385
|
return result
|
@@ -1202,9 +1392,10 @@ class IntegralManager:
|
|
1202
1392
|
print("{:15} : {}".format("active orbitals", [o.idx_total for o in self.active_orbitals]), *args, **kwargs)
|
1203
1393
|
print("{:15} : {}".format("reference", [x.idx_total for x in self.reference_orbitals]), *args, **kwargs)
|
1204
1394
|
|
1205
|
-
if not print_coefficients:
|
1395
|
+
if not print_coefficients:
|
1396
|
+
return
|
1206
1397
|
|
1207
1398
|
print("Current Orbitals", *args, **kwargs)
|
1208
|
-
for i,x in enumerate(self.orbitals):
|
1399
|
+
for i, x in enumerate(self.orbitals):
|
1209
1400
|
print(x, *args, **kwargs)
|
1210
|
-
print("coefficients: ", self.orbital_coefficients[:,i], *args, **kwargs)
|
1401
|
+
print("coefficients: ", self.orbital_coefficients[:, i], *args, **kwargs)
|