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.
Files changed (86) hide show
  1. tequila/__init__.py +29 -14
  2. tequila/apps/__init__.py +14 -5
  3. tequila/apps/_unary_state_prep_impl.py +145 -112
  4. tequila/apps/adapt/__init__.py +9 -1
  5. tequila/apps/adapt/adapt.py +154 -113
  6. tequila/apps/krylov/__init__.py +1 -1
  7. tequila/apps/krylov/krylov.py +23 -21
  8. tequila/apps/robustness/helpers.py +10 -6
  9. tequila/apps/robustness/interval.py +238 -156
  10. tequila/apps/unary_state_prep.py +29 -23
  11. tequila/autograd_imports.py +8 -5
  12. tequila/circuit/__init__.py +2 -1
  13. tequila/circuit/_gates_impl.py +135 -67
  14. tequila/circuit/circuit.py +163 -79
  15. tequila/circuit/compiler.py +114 -105
  16. tequila/circuit/gates.py +288 -120
  17. tequila/circuit/gradient.py +35 -23
  18. tequila/circuit/noise.py +83 -74
  19. tequila/circuit/postselection.py +120 -0
  20. tequila/circuit/pyzx.py +10 -6
  21. tequila/circuit/qasm.py +201 -83
  22. tequila/circuit/qpic.py +63 -61
  23. tequila/grouping/binary_rep.py +148 -146
  24. tequila/grouping/binary_utils.py +84 -75
  25. tequila/grouping/compile_groups.py +334 -230
  26. tequila/grouping/ev_utils.py +77 -41
  27. tequila/grouping/fermionic_functions.py +383 -308
  28. tequila/grouping/fermionic_methods.py +170 -123
  29. tequila/grouping/overlapping_methods.py +69 -52
  30. tequila/hamiltonian/paulis.py +12 -13
  31. tequila/hamiltonian/paulistring.py +1 -1
  32. tequila/hamiltonian/qubit_hamiltonian.py +45 -35
  33. tequila/ml/__init__.py +1 -0
  34. tequila/ml/interface_torch.py +19 -16
  35. tequila/ml/ml_api.py +11 -10
  36. tequila/ml/utils_ml.py +12 -11
  37. tequila/objective/__init__.py +8 -3
  38. tequila/objective/braket.py +55 -47
  39. tequila/objective/objective.py +87 -55
  40. tequila/objective/qtensor.py +36 -27
  41. tequila/optimizers/__init__.py +31 -23
  42. tequila/optimizers/_containers.py +11 -7
  43. tequila/optimizers/optimizer_base.py +111 -83
  44. tequila/optimizers/optimizer_gd.py +258 -231
  45. tequila/optimizers/optimizer_gpyopt.py +56 -42
  46. tequila/optimizers/optimizer_scipy.py +157 -112
  47. tequila/quantumchemistry/__init__.py +66 -38
  48. tequila/quantumchemistry/chemistry_tools.py +393 -209
  49. tequila/quantumchemistry/encodings.py +121 -13
  50. tequila/quantumchemistry/madness_interface.py +170 -96
  51. tequila/quantumchemistry/orbital_optimizer.py +86 -41
  52. tequila/quantumchemistry/psi4_interface.py +166 -97
  53. tequila/quantumchemistry/pyscf_interface.py +70 -23
  54. tequila/quantumchemistry/qc_base.py +866 -414
  55. tequila/simulators/__init__.py +0 -3
  56. tequila/simulators/simulator_api.py +247 -105
  57. tequila/simulators/simulator_aqt.py +102 -0
  58. tequila/simulators/simulator_base.py +147 -53
  59. tequila/simulators/simulator_cirq.py +58 -42
  60. tequila/simulators/simulator_cudaq.py +600 -0
  61. tequila/simulators/simulator_ddsim.py +390 -0
  62. tequila/simulators/simulator_mqp.py +30 -0
  63. tequila/simulators/simulator_pyquil.py +190 -171
  64. tequila/simulators/simulator_qibo.py +95 -87
  65. tequila/simulators/simulator_qiskit.py +119 -107
  66. tequila/simulators/simulator_qlm.py +52 -26
  67. tequila/simulators/simulator_qulacs.py +74 -52
  68. tequila/simulators/simulator_spex.py +95 -60
  69. tequila/simulators/simulator_symbolic.py +6 -5
  70. tequila/simulators/test_spex_simulator.py +8 -11
  71. tequila/tools/convenience.py +4 -4
  72. tequila/tools/qng.py +72 -64
  73. tequila/tools/random_generators.py +38 -34
  74. tequila/utils/bitstrings.py +7 -7
  75. tequila/utils/exceptions.py +19 -5
  76. tequila/utils/joined_transformation.py +8 -10
  77. tequila/utils/keymap.py +0 -5
  78. tequila/utils/misc.py +6 -4
  79. tequila/version.py +1 -1
  80. tequila/wavefunction/qubit_wavefunction.py +47 -28
  81. {tequila_basic-1.9.9.dist-info → tequila_basic-1.9.10.dist-info}/METADATA +13 -16
  82. tequila_basic-1.9.10.dist-info/RECORD +93 -0
  83. {tequila_basic-1.9.9.dist-info → tequila_basic-1.9.10.dist-info}/WHEEL +1 -1
  84. tequila_basic-1.9.9.dist-info/RECORD +0 -88
  85. {tequila_basic-1.9.9.dist-info → tequila_basic-1.9.10.dist-info}/licenses/LICENSE +0 -0
  86. {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
- active_orbitals: list = None # active orbitals (spatial, c1)
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(key="active_reference_orbitals",
30
- value=str(self.active_reference_orbitals))
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(angle=self.sign*self.parameter, indices=self.indices, control=self.control,opt=False)
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
- def cCRy(self, target: int, dcontrol: typing.Union[list, int], control: typing.Union[list, int],
98
- angle: typing.Union[Real, Variable, typing.Hashable], case: int = 1) -> QCircuit:
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 += self.cCRy(target=target, dcontrol=ctr, angle=angle / 2, case=1, control=control) + gates.H(
121
- aux) + gates.CNOT(target, aux)
122
- U += self.cCRy(target=target, dcontrol=ctr, angle=-angle / 2, case=0, control=control) + gates.CNOT(
123
- target, aux) + gates.H(aux)
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 += gates.H(aux) + gates.CNOT(target, aux) + self.cCRy(target=target, dcontrol=ctr, angle=-angle / 2,
126
- case=0, control=control)
127
- U += gates.CNOT(target, aux) + gates.H(aux) + self.cCRy(target=target, dcontrol=ctr, angle=angle / 2,
128
- case=1, control=control)
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(self, angle: typing.Union[Real, Variable, typing.Hashable], indices: typing.List,
132
- control: typing.Union[int, typing.List] = None, opt: bool = True) -> QCircuit:
133
- '''
134
- Excitation [(i,j),(k,l)],... compiled following https://doi.org/10.1103/PhysRevA.102.062612
135
- opt: whether to optimized CNOT H CNOT --> Rz Rz CNOT Rz
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": return False
199
- if not len(self.indices) == 2: return False
200
- if not self.indices[0][0] // 2 == self.indices[1][0] // 2: return False
201
- if not self.indices[0][1] // 2 == self.indices[1][1] // 2: return False
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 (units: Angstrom!),
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 += 8
248
- if n>18:
249
- result += 18
250
- if n>36:
251
- result += 36
252
- if n>45:
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 n_electrons(self, *args, **kwargs):
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 = {"h": 1, "he": 2, "li": 3, "be": 4, "b": 5, "c": 6, "n": 7, "o": 8, "f": 9, "ne": 10, "na": 11,
267
- "mg": 12, "al": 13, "si": 14, "ph": 15, "s": 16, "cl": 17, "ar": 18}
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 = ''.join(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 {'basis': self.basis_set, 'geometry': self.get_geometry(), 'description': self.description,
319
- 'charge': self.charge, 'multiplicity': self.multiplicity, 'filename': self.filename
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 (len(string) > 0)
339
- assert (isinstance(string, str))
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 [ ['h',[0.0,0.0,0.0], [..]]
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
- tmp = (ParametersQC.format_element_name(words[0]),
371
- (float(words[1]), float(words[2]), float(words[3])))
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
- if self.geometry.split('.')[-1] == 'xyz':
389
- geomstring, comment = self.read_xyz_from_file(self.geometry)
390
- if comment is not None:
391
- self.description = comment
392
- return geomstring
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
- Units: Angstrom!
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
- if self.geometry.split('.')[-1] == 'xyz':
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: Angstrom!
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, 'r') as file:
544
+ with open(filename, "r") as file:
438
545
  content = file.readlines()
439
546
  natoms = int(content[0])
440
- comment = str(content[1]).strip('\n')
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
- def get_xyz(self)->str:
446
- geom = self.parameters.get_geometry()
447
- f = ''
448
- f += f'{len(geom)}\n'
449
- f += f'{self.parameters.name}\n'
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'{at[0]} {at[1][0]} {at[1][1]} {at[1][2]}\n'
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 clasical amplitudes
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.e-8, screening=True):
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 (self.tIjAb.shape[1] == nocc and self.tIjAb.shape[3] == nvirt)
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 (I, A), value, in numpy.ndenumerate(self.tIA):
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='greedy')
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.e-8):
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 (self.tIjAb.shape[1] == nocc and self.tIjAb.shape[3] == nvirt)
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 (self.tia.shape[0] == nocc)
567
- for (I, A), value, in numpy.ndenumerate(self.tIA):
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 (i, a), value, in numpy.ndenumerate(self.tIA):
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
- """ Convenience class for handling N-body tensors """
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: return self.Ordering(scheme="chem")
640
- elif phys: return self.Ordering(scheme="phys")
641
- elif of: return self.Ordering(scheme="openfermion")
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(elems[idx[0],idx[1],idx[2],idx[3]],elems[idx[2],idx[1],idx[0],idx[3]], atol=1.e-4)
655
- test2 = numpy.isclose(elems[idx[0],idx[1],idx[2],idx[3]],elems[idx[0],idx[3],idx[2],idx[1]], atol=1.e-4)
656
- test3 = numpy.isclose(elems[idx[0],idx[1],idx[2],idx[3]],elems[idx[2],idx[3],idx[0],idx[1]], atol=1.e-4)
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(elems[idx[0],idx[1],idx[2],idx[3]],elems[idx[1],idx[0],idx[2],idx[3]], atol=1.e-4)
671
- test2 = numpy.isclose(elems[idx[0],idx[1],idx[2],idx[3]],elems[idx[0],idx[1],idx[3],idx[2]], atol=1.e-4)
672
- test3 = numpy.isclose(elems[idx[0],idx[1],idx[2],idx[3]],elems[idx[1],idx[0],idx[3],idx[2]], atol=1.e-4)
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(elems[idx[0],idx[1],idx[2],idx[3]],elems[idx[3],idx[1],idx[2],idx[0]], atol=1.e-4)
687
- test2 = numpy.isclose(elems[idx[0],idx[1],idx[2],idx[3]],elems[idx[0],idx[2],idx[1],idx[3]], atol=1.e-4)
688
- test3 = numpy.isclose(elems[idx[0],idx[1],idx[2],idx[3]],elems[idx[3],idx[2],idx[1],idx[0]], atol=1.e-4)
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__(self, elems: numpy.ndarray = None, active_indices: list = None, ordering: str = None,
695
- size_full: int = None, verify=False):
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: # some RDMs are really sloppy (depends on backend)
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("Auto identified ordering of NBTensor does not match given ordering: {} vs {}".format(auto_ordering, ordering))
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("Need to pass an index list for each dimension!" +
775
- " Length of idx_lists needs to match order of tensor.")
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
- """ Set passive and full index lists based on class inputs """
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() == 'a':
984
+ if char.lower() == "a":
832
985
  idx_lists.append(self.active_indices)
833
- elif char.lower() == 'p':
986
+ elif char.lower() == "p":
834
987
  idx_lists.append(self._passive_indices)
835
- elif char.lower() == 'f':
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 = 'of'):
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('Reordering currently only implemented for two-body tensors.')
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='greedy')
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='greedy')
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='greedy')
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='greedy')
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='greedy')
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='greedy')
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 "{"+"{}".format("".join(["{}:{}, ".format(k,v) for k,v in self.__dict__.items() if v is not None])).rstrip().rstrip(",")+"}"
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" # e.g. "HF", "PNO", "native"
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__(self, one_body_integrals, two_body_integrals,
937
- basis_name="unknown", orbital_type="unknown",
938
- constant_term=0.0, orbital_coefficients=None, active_space=None, overlap_integrals=None, orbitals=None, *args, **kwargs):
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 [self._orbitals[i] for i in self.active_space.active_orbitals if i in self.active_space.reference_orbitals]
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: return False
1084
- if U.shape[0] != U.shape[1]: return False
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): return False
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(self, orbital_coefficients=None, ordering="openfermion", ignore_active_space=False, *args, **kwargs):
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(one_body_integrals=h, two_body_integrals=g,
1132
- occupied_indices=self._active_space.frozen_reference_orbitals,
1133
- active_indices=self._active_space.active_orbitals)
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='greedy')
1149
- h = numpy.einsum("xj, xi -> ij", h, orbital_coefficients, optimize='greedy')
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='greedy')
1162
- g = numpy.einsum("ijxl, xk -> ijkl", g, orbital_coefficients, optimize='greedy')
1163
- g = numpy.einsum("ixkl, xj -> ijkl", g, orbital_coefficients, optimize='greedy')
1164
- g = numpy.einsum("xjkl, xi -> ijkl", g, orbital_coefficients, optimize='greedy')
1165
- g = NBodyTensor(elems=numpy.asarray(g), ordering='chem')
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.e-5):
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='greedy')
1185
- St = numpy.einsum("xj, xi -> ij", St, orbital_coefficients, optimize='greedy')
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.e-5):
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: return
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)