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.
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 +177 -88
  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 +91 -56
  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 +394 -203
  49. tequila/quantumchemistry/encodings.py +121 -13
  50. tequila/quantumchemistry/madness_interface.py +170 -96
  51. tequila/quantumchemistry/orbital_optimizer.py +86 -40
  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 +258 -106
  57. tequila/simulators/simulator_aqt.py +102 -0
  58. tequila/simulators/simulator_base.py +156 -55
  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 +124 -114
  66. tequila/simulators/simulator_qlm.py +52 -26
  67. tequila/simulators/simulator_qulacs.py +85 -59
  68. tequila/simulators/simulator_spex.py +464 -0
  69. tequila/simulators/simulator_symbolic.py +6 -5
  70. tequila/simulators/test_spex_simulator.py +208 -0
  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 +13 -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 +52 -30
  81. {tequila_basic-1.9.8.dist-info → tequila_basic-1.9.10.dist-info}/METADATA +23 -17
  82. tequila_basic-1.9.10.dist-info/RECORD +93 -0
  83. {tequila_basic-1.9.8.dist-info → tequila_basic-1.9.10.dist-info}/WHEEL +1 -1
  84. tequila_basic-1.9.8.dist-info/RECORD +0 -86
  85. {tequila_basic-1.9.8.dist-info → tequila_basic-1.9.10.dist-info/licenses}/LICENSE +0 -0
  86. {tequila_basic-1.9.8.dist-info → tequila_basic-1.9.10.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,208 @@
1
+ import tequila as tq
2
+ import numpy as np
3
+ import time
4
+ import os
5
+ import csv
6
+ from tequila.hamiltonian import PauliString
7
+ import pytest
8
+
9
+
10
+ #!/usr/bin/env python
11
+ import tequila as tq
12
+ import numpy as np
13
+ import time
14
+
15
+ print("Test: Circuit und Hamiltonian mit Variablen (SPEX vs. Qulacs)")
16
+
17
+ # --- Parameterdefinition ---
18
+ # Definiere mehrere Variablen für die Rotation
19
+ a = tq.Variable("a")
20
+ b = tq.Variable("b")
21
+ c = tq.Variable("c")
22
+ variables = {"a": np.pi / 3, "b": np.pi / 4, "c": np.pi / 6}
23
+
24
+ # --- Circuitaufbau ---
25
+ # Erzeuge einen Circuit, der auf 3 Qubits operiert:
26
+ # - Eine Rx-Rotation auf Qubit 0 (Winkel "a")
27
+ # - Eine Ry-Rotation auf Qubit 1 (Winkel "b")
28
+ # - Eine Rz-Rotation auf Qubit 2 (Winkel "c")
29
+ # - Zusätzlich eine parametrische exponentielle Pauli-Rotation (ExpPauli) auf Qubit 0 und 2 (Pauli-String "X(0)Z(2)")
30
+ U = tq.gates.Rx(angle="a", target=(0,)) + tq.gates.Ry(angle="b", target=(1,)) + tq.gates.Rz(angle="c", target=(2,))
31
+ U += tq.gates.ExpPauli(angle="a", paulistring="X(0)Z(2)")
32
+
33
+ print("\nCircuit U:")
34
+ print(U)
35
+
36
+ # --- Hamiltonianaufbau ---
37
+ # Erstelle einen zusammengesetzten Hamiltonian auf 3 Qubits,
38
+ # z.B.: H = Z(0) + X(1)Y(2) + Z(0)Z(1)
39
+ H = tq.QubitHamiltonian("Z(0) + X(1)Y(2) + Z(0)Z(1)")
40
+ print("\nHamiltonian H:")
41
+ print(H)
42
+
43
+ # Erzeuge ein Erwartungswertobjekt
44
+ E = tq.ExpectationValue(U=U, H=H)
45
+
46
+ # --- Simulation mit SPEX ---
47
+ start = time.time()
48
+ wfn_spex = tq.simulate(U, variables, backend="spex")
49
+ exp_spex = tq.simulate(E, variables, backend="spex")
50
+ end = time.time()
51
+ time_spex = end - start
52
+
53
+ # --- Simulation mit Qulacs ---
54
+ start = time.time()
55
+ wfn_qulacs = tq.simulate(U, variables, backend="qulacs")
56
+ exp_qulacs = tq.simulate(E, variables, backend="qulacs")
57
+ end = time.time()
58
+ time_qulacs = end - start
59
+
60
+ # --- Ergebnisse ausgeben ---
61
+ print("\nSimulationsergebnisse:")
62
+ print("Wellenfunktion (SPEX backend):")
63
+ print(wfn_spex)
64
+ print("\nWellenfunktion (Qulacs backend):")
65
+ print(wfn_qulacs)
66
+
67
+ print("\nErwartungswert (SPEX backend):", exp_spex, f"(Simulationszeit: {time_spex:.3f}s)")
68
+ print("Erwartungswert (Qulacs backend):", exp_qulacs, f"(Simulationszeit: {time_qulacs:.3f}s)")
69
+
70
+ # Optional: Vergleiche das innere Produkt der beiden Wavefunctions (Quadrat des Betrags)
71
+ inner_prod = np.abs(wfn_spex.inner(wfn_qulacs)) ** 2
72
+ print("\nInneres Produkt (Quadrat) zwischen SPEX und Qulacs:", inner_prod)
73
+
74
+
75
+ """
76
+ print("\nTest: 1")
77
+ U = tq.gates.ExpPauli(paulistring=PauliString({0: "X", 1: "Z"}), angle=np.pi / 2)
78
+ H = tq.QubitHamiltonian("Z(0)")
79
+ E = tq.ExpectationValue(U=U, H=H)
80
+
81
+ print(U)
82
+ print("spex-U:", tq.simulate(U, backend='spex'))
83
+ print("qulacs-U:", tq.simulate(U, backend='qulacs'))
84
+ print("spex:", tq.simulate(E, backend='spex'))
85
+ print("qulacs:", tq.simulate(E, backend='qulacs'))
86
+
87
+
88
+ print("\nTest: 2")
89
+ H = tq.QubitHamiltonian("Z(0)X(1)")
90
+
91
+ U = tq.gates.Rx(angle=np.pi / 4, target=(0,))
92
+ U += tq.gates.Ry(angle=np.pi / 3, target=(1,))
93
+ E = tq.ExpectationValue(U=U, H=H)
94
+
95
+ print(U)
96
+ print("spex-U:", tq.simulate(U, backend='spex'))
97
+ print("qulacs-U:", tq.simulate(U, backend='qulacs'))
98
+ print("spex:", tq.simulate(E, backend='spex'))
99
+ print("qulacs:", tq.simulate(E, backend='qulacs'))
100
+
101
+
102
+ print("\nTest: 3")
103
+ H = tq.QubitHamiltonian("Z(0)")
104
+
105
+ U = tq.gates.X(target=(0,))
106
+ U += tq.gates.Y(target=(1,))
107
+ E = tq.ExpectationValue(U=U, H=H)
108
+
109
+ print(U)
110
+ print("spex-U:", tq.simulate(U, backend='spex'))
111
+ print("qulacs-U:", tq.simulate(U, backend='qulacs'))
112
+ print("spex:", tq.simulate(E, backend='spex'))
113
+ print("qulacs:", tq.simulate(E, backend='qulacs'))
114
+
115
+
116
+ print("\nTest: 4")
117
+ U = (
118
+ tq.gates.Rx(angle=np.pi / 2, target=(0,)) +
119
+ tq.gates.Rz(angle=np.pi / 2, target=(1,)) +
120
+ tq.gates.Ry(angle=np.pi / 3, target=(2,)) +
121
+ tq.gates.ExpPauli(paulistring=PauliString({0: "X", 1: "X", 2: "X"}), angle=np.pi / 4)
122
+ )
123
+
124
+ H = tq.QubitHamiltonian("X(0)X(1)X(2) + Z(0) + Z(1) + Z(2)")
125
+ E = tq.ExpectationValue(U=U, H=H)
126
+
127
+ print(U)
128
+ print("spex-U:", tq.simulate(U, backend='spex'))
129
+ print("qulacs-U:", tq.simulate(U, backend='qulacs'))
130
+ print("spex:", tq.simulate(E, backend='spex'))
131
+ print("qulacs:", tq.simulate(E, backend='qulacs'))
132
+
133
+ print("\nTest: 5")
134
+ os.environ["OMP_NUM_THREADS"] = "6"
135
+
136
+ results = []
137
+
138
+ for n in range(1, 15):
139
+ # 1) Geometrie aufbauen
140
+ geom = ""
141
+ for k in range(2*n):
142
+ geom += f"h 0.0 0.0 {1.5*k}\n"
143
+
144
+ # 2) Molekül + Hamiltonian
145
+ mol = tq.Molecule(geometry=geom, basis_set="sto-3g")
146
+ H = mol.make_hardcore_boson_hamiltonian()
147
+
148
+ # 3) Ansatz U
149
+ edges = [(2*i, 2*i+1) for i in range(n)]
150
+ U = mol.make_ansatz(name="HCB-SPA", edges=edges)
151
+ # Alle im Ansatz auftretenden Variablen auf 1.0 setzen
152
+ U = U.map_variables({var: 1.0 for var in U.extract_variables()})
153
+
154
+ # 4) Erwartungswert-Objekt
155
+ E_obj = tq.ExpectationValue(H=H, U=U)
156
+
157
+ # -- SPEX-Berechnung --
158
+ start_spex = time.time()
159
+ E_spex = tq.simulate(E_obj, backend='spex', num_threads=6)
160
+ end_spex = time.time()
161
+ time_spex = end_spex - start_spex
162
+
163
+ # -- Qulacs-Berechnung --
164
+ if n <= 10:
165
+ start_qulacs = time.time()
166
+ E_qulacs = tq.simulate(E_obj, backend='qulacs')
167
+ end_qulacs = time.time()
168
+ time_qulacs = end_qulacs - start_qulacs
169
+
170
+ total_measurements = E_obj.count_measurements()
171
+
172
+ # Speichern der Daten
173
+ results.append({
174
+ 'n': n,
175
+ 'total_measurements' : total_measurements,
176
+ 'E_spex': E_spex,
177
+ 'time_spex': time_spex,
178
+ 'E_qulacs': E_qulacs,
179
+ 'time_qulacs': time_qulacs
180
+ })
181
+
182
+ if E_qulacs is not None:
183
+ print(f"n={n:2d} | total_measurements={total_measurements} | "
184
+ f"E_spex={E_spex:.6f} (dt={time_spex:.2f}s) | "
185
+ f"E_qulacs={E_qulacs:.6f} (dt={time_qulacs:.2f}s)")
186
+ else:
187
+ print(f"n={n:2d} | total_measurements={total_measurements} | "
188
+ f"E_spex={E_spex:.6f} (dt={time_spex:.2f}s) | "
189
+ f"E_qulacs=--- (dt=---) (für n>13 nicht berechnet)")
190
+
191
+ with open("spex_qulacs_comparison.csv", mode="w", newline="") as f:
192
+ writer = csv.writer(f)
193
+ # Kopfzeile
194
+ writer.writerow(["n", "total_measurements", "E_spex", "time_spex", "E_qulacs", "time_qulacs"])
195
+ # Datenzeilen
196
+ for entry in results:
197
+ writer.writerow([
198
+ entry["n"],
199
+ entry["total_measurements"],
200
+ entry["E_spex"],
201
+ entry["time_spex"],
202
+ entry["E_qulacs"] if entry["E_qulacs"] is not None else "NA",
203
+ entry["time_qulacs"] if entry["time_qulacs"] is not None else "NA"
204
+ ])
205
+
206
+ E_qulacs = None
207
+
208
+ """
@@ -12,7 +12,7 @@ def list_assignment(o):
12
12
  """
13
13
  if o is None:
14
14
  return []
15
- elif isinstance(o,tuple):
15
+ elif isinstance(o, tuple):
16
16
  return o
17
17
  elif hasattr(o, "__get_item__"):
18
18
  return list(o)
@@ -22,14 +22,14 @@ def list_assignment(o):
22
22
  return [o]
23
23
 
24
24
 
25
- def number_to_string(number: complex, precision: int = 4, threshold: float = 1.e-6) -> str:
25
+ def number_to_string(number: complex, precision: int = 4, threshold: float = 1.0e-6) -> str:
26
26
  if not isinstance(number, numbers.Number):
27
27
  return str(number)
28
28
 
29
29
  number = complex(number)
30
30
  real = number.real
31
31
  imag = number.imag
32
- prec = '{:+.' + str(precision) + 'f}'
32
+ prec = "{:+." + str(precision) + "f}"
33
33
 
34
34
  if isclose(real, 0.0, atol=threshold):
35
35
  return prec.format(imag) + "i"
@@ -37,7 +37,7 @@ def number_to_string(number: complex, precision: int = 4, threshold: float = 1.e
37
37
  return prec.format(real)
38
38
  else:
39
39
  r, theta = polar(number)
40
- return prec.format(r) + ('e^(' + prec).format(theta / pi) + 'πi)'
40
+ return prec.format(r) + ("e^(" + prec).format(theta / pi) + "πi)"
41
41
 
42
42
 
43
43
  if __name__ == "__main__":
tequila/tools/qng.py CHANGED
@@ -5,15 +5,19 @@ from tequila.circuit.circuit import QCircuit
5
5
  from tequila.simulators.simulator_api import compile_objective
6
6
  from tequila.circuit.gradient import __grad_inner
7
7
  from tequila.autograd_imports import jax
8
- from tequila.circuit.compiler import compile_controlled_rotation, compile_power_gate, \
9
- compile_trotterized_gate, compile_controlled_phase, compile_multitarget
8
+ from tequila.circuit.compiler import (
9
+ compile_controlled_rotation,
10
+ compile_power_gate,
11
+ compile_trotterized_gate,
12
+ compile_controlled_phase,
13
+ compile_multitarget,
14
+ )
10
15
  import typing
11
16
  import numpy
12
17
  import copy
13
18
 
14
19
 
15
20
  class QngMatrix:
16
-
17
21
  """
18
22
  A callable class which is meant to be used for calculating the inverse qgt of an expectationvalue.
19
23
 
@@ -40,18 +44,18 @@ class QngMatrix:
40
44
  d = 0
41
45
  for block in self.blocks:
42
46
  d += len(block)
43
- return (d,d)
47
+ return (d, d)
44
48
 
45
- def __init__(self,blocks):
49
+ def __init__(self, blocks):
46
50
  """
47
51
  Parameters
48
52
  ----------
49
53
  blocks: list:
50
54
  list of list of lists. the blocks of the qgt.
51
55
  """
52
- self.blocks= blocks
56
+ self.blocks = blocks
53
57
 
54
- def __call__(self, variables,samples=None) -> numpy.ndarray:
58
+ def __call__(self, variables, samples=None) -> numpy.ndarray:
55
59
  """
56
60
  from the blocks provided, evaluate all qgt terms, then calculate the pseudo-inverse, and return it.
57
61
  Parameters
@@ -71,22 +75,21 @@ class QngMatrix:
71
75
  # displace our running index of position, as we enumerate through a block
72
76
  # d_v does this. If you only provide one block (the whole QGT), this won't matter
73
77
  for block in self.blocks:
74
-
75
- d_v_temp = 0 # how much to increment d_v by when done with the block at hand.
78
+ d_v_temp = 0 # how much to increment d_v by when done with the block at hand.
76
79
  for i, row in enumerate(block):
77
80
  for j, term in enumerate(row):
78
81
  if i <= j:
79
82
  # if its an objective, call it. Else, it is a float.
80
83
  try:
81
- output[i + d_v][j + d_v] = term(variables=variables,samples=samples)
82
- except:
84
+ output[i + d_v][j + d_v] = term(variables=variables, samples=samples)
85
+ except Exception:
83
86
  output[i + d_v][j + d_v] = term
84
87
  else:
85
88
  output[i + d_v][j + d_v] = output[j + d_v][i + d_v]
86
89
  d_v_temp += 1
87
90
  d_v += d_v_temp
88
91
 
89
- back = numpy.linalg.pinv(output) # return the pseudo_inverse of the matrix!
92
+ back = numpy.linalg.pinv(output) # return the pseudo_inverse of the matrix!
90
93
  return back
91
94
 
92
95
 
@@ -104,7 +107,7 @@ class CallableVector:
104
107
  def dim(self):
105
108
  return (len(self._vector),)
106
109
 
107
- def __init__(self,vector):
110
+ def __init__(self, vector):
108
111
  """
109
112
  init.
110
113
  Parameters
@@ -133,7 +136,7 @@ class CallableVector:
133
136
 
134
137
  output = numpy.empty(self.dim)
135
138
  for i, entry in enumerate(self._vector):
136
- if hasattr(entry, '__call__'):
139
+ if hasattr(entry, "__call__"):
137
140
  output[i] = entry(variables, samples=samples)
138
141
  else:
139
142
  output[i] = entry
@@ -155,24 +158,23 @@ def get_generator(gate) -> paulis.QubitHamiltonian:
155
158
 
156
159
  """
157
160
 
158
- if gate.name.lower() == 'rx':
161
+ if gate.name.lower() == "rx":
159
162
  gen = paulis.X(gate.target[0])
160
- elif gate.name.lower() == 'ry':
163
+ elif gate.name.lower() == "ry":
161
164
  gen = paulis.Y(gate.target[0])
162
- elif gate.name.lower() == 'rz':
165
+ elif gate.name.lower() == "rz":
163
166
  gen = paulis.Z(gate.target[0])
164
- elif gate.name.lower() == 'phase':
167
+ elif gate.name.lower() == "phase":
165
168
  gen = paulis.Qm(gate.target[0])
166
169
  else:
167
170
  print(gate.name.lower())
168
- raise TequilaException('cant get the generator of a non Gaussian gate, you fool!')
171
+ raise TequilaException("cant get the generator of a non Gaussian gate, you fool!")
169
172
  return gen
170
173
 
171
174
 
172
- def stokes_block(expectation, initial_values=None, samples=None, device=None,
173
- backend=None,
174
- noise=None) -> typing.List[typing.List[typing.List[typing.Union[float,Objective]]]]:
175
-
175
+ def stokes_block(
176
+ expectation, initial_values=None, samples=None, device=None, backend=None, noise=None
177
+ ) -> typing.List[typing.List[typing.List[typing.Union[float, Objective]]]]:
176
178
  """
177
179
  returns the blocks of the layerwise block-diagonal approximation to the qgt.
178
180
  The default for all qng-based optimizations, as a method for obtaining the qgt.
@@ -207,7 +209,7 @@ def stokes_block(expectation, initial_values=None, samples=None, device=None,
207
209
  # rebuild the sub circuits used in the expectation values that populate the QGT
208
210
  sub = [QCircuit.from_moments(moments[:i]) for i in range(1, len(moments), 2)]
209
211
  # this is the list of just the moments which are parametrized.
210
- parametric_moms = [moments[i] for i in range(1, len(moments)+1, 2)]
212
+ parametric_moms = [moments[i] for i in range(1, len(moments) + 1, 2)]
211
213
  generators = []
212
214
  # generators is a list of lists, ultimately, where each sublist is all the generators in order
213
215
  # for a given parametric layer (if said layer is
@@ -238,13 +240,15 @@ def stokes_block(expectation, initial_values=None, samples=None, device=None,
238
240
  for q, gen2 in enumerate(g_set):
239
241
  ### make sure you compile the objectives! otherwise this bad boy will not run
240
242
  if k == q:
241
- arg = (ExpectationValue(U=sub[i], H=gen1 * gen1) - ExpectationValue(U=sub[i], H=gen1)**2)/4
243
+ arg = (ExpectationValue(U=sub[i], H=gen1 * gen1) - ExpectationValue(U=sub[i], H=gen1) ** 2) / 4
242
244
  else:
243
- arg = (ExpectationValue(U=sub[i], H=gen1 * gen2) - ExpectationValue(U=sub[i], H=gen1) *
244
- ExpectationValue(U=sub[i], H=gen2)) / 4
245
- block[k][q] = compile_objective(arg, variables=initial_values, samples=samples,
246
- backend=backend, device=device,
247
- noise=noise)
245
+ arg = (
246
+ ExpectationValue(U=sub[i], H=gen1 * gen2)
247
+ - ExpectationValue(U=sub[i], H=gen1) * ExpectationValue(U=sub[i], H=gen2)
248
+ ) / 4
249
+ block[k][q] = compile_objective(
250
+ arg, variables=initial_values, samples=samples, backend=backend, device=device, noise=noise
251
+ )
248
252
  blocks.append(block)
249
253
  return blocks
250
254
 
@@ -270,18 +274,18 @@ def qng_circuit_grad(E: ExpectationValueImpl) -> typing.List[Objective]:
270
274
  unitary = E.U
271
275
 
272
276
  # fast return if possible
273
- out=[]
277
+ out = []
274
278
  for i, g in enumerate(unitary.gates):
275
- if g.is_parametrized():
279
+ if g.is_parameterized():
276
280
  if g.is_controlled():
277
281
  raise TequilaException("controlled gate in qng circuit gradient: Compiler was not called")
278
282
  if hasattr(g, "eigenvalues_magnitude"):
279
- if hasattr(g._parameter,'extract_variables'):
283
+ if hasattr(g._parameter, "extract_variables"):
280
284
  shifter = qng_grad_gaussian(unitary, g, i, hamiltonian)
281
285
  out.append(shifter)
282
286
  else:
283
287
  print(g, type(g))
284
- raise TequilaException('No shift found for gate {}'.format(g))
288
+ raise TequilaException("No shift found for gate {}".format(g))
285
289
  if out is None:
286
290
  raise TequilaException("caught a dead circuit in qng gradient")
287
291
  return out
@@ -336,8 +340,9 @@ def qng_grad_gaussian(unitary, g, i, hamiltonian) -> Objective:
336
340
  return dOinc
337
341
 
338
342
 
339
- def subvector_procedure(e_val, initial_values=None, samples=None, device=None,
340
- backend=None, noise=None) -> CallableVector:
343
+ def subvector_procedure(
344
+ e_val, initial_values=None, samples=None, device=None, backend=None, noise=None
345
+ ) -> CallableVector:
341
346
  """
342
347
  take an expectation value and return its (qng style) gradient as a CallableVector.
343
348
 
@@ -364,9 +369,11 @@ def subvector_procedure(e_val, initial_values=None, samples=None, device=None,
364
369
  vect = qng_circuit_grad(e_val)
365
370
  out = []
366
371
  for entry in vect:
367
- out.append(compile_objective(entry, variables=initial_values, samples=samples, device=device,
368
- backend=backend,
369
- noise=noise))
372
+ out.append(
373
+ compile_objective(
374
+ entry, variables=initial_values, samples=samples, device=device, backend=backend, noise=noise
375
+ )
376
+ )
370
377
  return CallableVector(out)
371
378
 
372
379
 
@@ -384,10 +391,10 @@ def get_self_pars(U) -> typing.List:
384
391
  list eg. of Objectives and Variables; the self-parameters of a circuit.
385
392
  """
386
393
 
387
- out=[]
394
+ out = []
388
395
  for g in U.gates:
389
- if g.is_parametrized():
390
- if hasattr(g._parameter,'extract_variables'):
396
+ if g.is_parameterized():
397
+ if hasattr(g._parameter, "extract_variables"):
391
398
  out.append(g._parameter)
392
399
  return out
393
400
 
@@ -416,13 +423,12 @@ def qng_dict(argument, matrix, subvector, mapping, positional) -> typing.Dict:
416
423
  dict containing information used to obtain the qng of some argument of an objective.
417
424
 
418
425
  """
419
- return {'arg': argument, 'matrix': matrix, 'vector': subvector, 'mapping': mapping, 'positional': positional}
420
-
426
+ return {"arg": argument, "matrix": matrix, "vector": subvector, "mapping": mapping, "positional": positional}
421
427
 
422
- def get_qng_combos(objective, func=stokes_block,
423
- initial_values=None, samples=None,
424
- backend=None, device=None, noise=None) -> typing.List[typing.Dict]:
425
428
 
429
+ def get_qng_combos(
430
+ objective, func=stokes_block, initial_values=None, samples=None, backend=None, device=None, noise=None
431
+ ) -> typing.List[typing.Dict]:
426
432
  """
427
433
  get all the objects needed to evaluate the qng for some objective; return them in a list of dictionaries.
428
434
 
@@ -459,7 +465,7 @@ def get_qng_combos(objective, func=stokes_block,
459
465
  compiled = compile_power_gate(gate=compiled)
460
466
  compiled = compile_controlled_phase(gate=compiled)
461
467
  compiled = compile_controlled_rotation(gate=compiled)
462
- for i,arg in enumerate(compiled.args):
468
+ for i, arg in enumerate(compiled.args):
463
469
  if not isinstance(arg, ExpectationValueImpl):
464
470
  # this is a variable, no QNG involved
465
471
  mat = QngMatrix([[[1]]])
@@ -467,13 +473,15 @@ def get_qng_combos(objective, func=stokes_block,
467
473
  mapping = {0: {v: __grad_inner(arg, v) for v in var_list}}
468
474
  else:
469
475
  # if the arg is an expectationvalue, we need to build some qngs and mappings!
470
- blocks = func(arg, initial_values=initial_values, samples=samples, device=device,
471
- backend=backend, noise=noise)
476
+ blocks = func(
477
+ arg, initial_values=initial_values, samples=samples, device=device, backend=backend, noise=noise
478
+ )
472
479
 
473
480
  mat = QngMatrix(blocks)
474
481
 
475
- vec = subvector_procedure(arg, initial_values=initial_values, samples=samples, device=device,
476
- backend=backend, noise=noise)
482
+ vec = subvector_procedure(
483
+ arg, initial_values=initial_values, samples=samples, device=device, backend=backend, noise=noise
484
+ )
477
485
 
478
486
  mapping = {}
479
487
  self_pars = get_self_pars(arg.U)
@@ -482,25 +490,25 @@ def get_qng_combos(objective, func=stokes_block,
482
490
  for v in p.extract_variables():
483
491
  gi = __grad_inner(p, v)
484
492
  if isinstance(gi, Objective):
485
- g = compile_objective(gi, variables=initial_values, samples=samples, device=device,
486
- backend=backend, noise=noise)
493
+ g = compile_objective(
494
+ gi, variables=initial_values, samples=samples, device=device, backend=backend, noise=noise
495
+ )
487
496
  else:
488
497
  g = gi
489
498
  indict[v] = g
490
499
  mapping[j] = indict
491
500
 
492
-
493
501
  pos_arg = jax.grad(compiled.transformation, i)
494
502
  p = Objective(compiled.args, transformation=pos_arg)
495
503
 
496
- pos = compile_objective(p, variables=initial_values, samples=samples, device=device,
497
- backend=backend, noise=noise)
504
+ pos = compile_objective(
505
+ p, variables=initial_values, samples=samples, device=device, backend=backend, noise=noise
506
+ )
498
507
  combos.append(qng_dict(arg, mat, vec, mapping, pos))
499
508
  return combos
500
509
 
501
510
 
502
511
  def evaluate_qng(combos, variables, samples=None) -> list:
503
-
504
512
  """
505
513
  actually evaluate the terms of a qng.
506
514
  Parameters
@@ -520,17 +528,17 @@ def evaluate_qng(combos, variables, samples=None) -> list:
520
528
  """
521
529
  gd = {v: 0 for v in variables.keys()}
522
530
  for c in combos:
523
- qgt = c['matrix']
524
- vec = c['vector']
525
- m = c['mapping']
526
- pos = c['positional']
531
+ qgt = c["matrix"]
532
+ vec = c["vector"]
533
+ m = c["mapping"]
534
+ pos = c["positional"]
527
535
  marco = qgt(variables, samples=samples)
528
536
  polo = vec(variables, samples=samples)
529
537
  ev = numpy.dot(marco, polo)
530
538
  for i, val in enumerate(ev):
531
539
  maps = m[i]
532
540
  for k in maps.keys():
533
- gd[k] += (val*maps[k]*pos)(variables=variables, samples=samples)
541
+ gd[k] += (val * maps[k] * pos)(variables=variables, samples=samples)
534
542
 
535
543
  out = [v for v in gd.values()]
536
544
  return out
@@ -547,7 +555,7 @@ class QNGVector:
547
555
 
548
556
  """
549
557
 
550
- def __init__(self,combos):
558
+ def __init__(self, combos):
551
559
  """
552
560
  init.
553
561
  Parameters