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
@@ -8,7 +8,9 @@ The whole thing is currently not very stable
8
8
 
9
9
  from tequila.circuit import QCircuit
10
10
  from tequila import BitString
11
- import typing, numpy, copy
11
+ import typing
12
+ import numpy
13
+ import copy
12
14
  from tequila import TequilaException
13
15
  from tequila.apps._unary_state_prep_impl import UnaryStatePrepImpl, sympy
14
16
  from tequila.simulators.simulator_symbolic import BackendCircuitSymbolic
@@ -26,8 +28,8 @@ class TequilaUnaryStateException(TequilaException):
26
28
 
27
29
  pass
28
30
 
29
- class UnaryStatePrep:
30
31
 
32
+ class UnaryStatePrep:
31
33
  @property
32
34
  def n_qubits(self) -> int:
33
35
  return self._n_qubits
@@ -40,8 +42,9 @@ class UnaryStatePrep:
40
42
  def circuit(self) -> QCircuit:
41
43
  return self._abstract_circuit
42
44
 
43
- def __init__(self, target_space: typing.List[BitString], max_repeat: int = 100,
44
- use_symbolic_solution: bool = False):
45
+ def __init__(
46
+ self, target_space: typing.List[BitString], max_repeat: int = 100, use_symbolic_solution: bool = False
47
+ ):
45
48
  """
46
49
  :param target_space: Give the target space by its basis functions
47
50
  e.g. target_space = ['00', '11']
@@ -70,7 +73,7 @@ class UnaryStatePrep:
70
73
  count = 0
71
74
  success = False
72
75
 
73
- while (not success and count < self.max_repeat):
76
+ while not success and count < self.max_repeat:
74
77
  try:
75
78
  count += 1
76
79
  IMPL = UnaryStatePrepImpl()
@@ -81,13 +84,13 @@ class UnaryStatePrep:
81
84
  except NotImplementedError:
82
85
  numpy.random.shuffle(target_space)
83
86
 
84
- if not success: raise TequilaUnaryStateException(
85
- "Could not disentangle the given state after " + str(count) + " restarts")
87
+ if not success:
88
+ raise TequilaUnaryStateException("Could not disentangle the given state after " + str(count) + " restarts")
86
89
 
87
90
  # get the equations to determine the angles
88
91
  simulator = BackendCircuitSymbolic(abstract_circuit=self._abstract_circuit, variables={})
89
92
  simulator.convert_to_numpy = False
90
- variables = None # {k:k.name.evalf() for k in self._abstract_circuit.extract_variables()}
93
+ variables = None # {k:k.name.evalf() for k in self._abstract_circuit.extract_variables()}
91
94
  wfn = simulator.simulate(initial_state=BitString.from_int(0, nbits=self.n_qubits), variables=variables)
92
95
  equations = []
93
96
  for k in target_space:
@@ -95,11 +98,11 @@ class UnaryStatePrep:
95
98
 
96
99
  normeq = -sympy.Integer(1)
97
100
  for c in abstract_coefficients.values():
98
- normeq += c ** 2
101
+ normeq += c**2
99
102
  equations.append(normeq)
100
103
 
101
104
  # this gives back multiple solutions and some of them sometimes have sign errors ...
102
- if (self._use_symbolic_solver):
105
+ if self._use_symbolic_solver:
103
106
  solutions = sympy.solve(equations, *tuple(abstract_angles), check=True, dict=True)[1]
104
107
  if len(abstract_angles) != len(solutions):
105
108
  raise TequilaUnaryStateException("Could definetely not solve for the angles in UnaryStatePrep!")
@@ -137,7 +140,7 @@ class UnaryStatePrep:
137
140
  raise TequilaException("UnaryStatePrep currently only possible for real coefficients")
138
141
  subs[ac] = sympy.Float(float(coeff.real))
139
142
  result = dict()
140
- if (self._use_symbolic_solver):
143
+ if self._use_symbolic_solver:
141
144
  # fails a lot of times
142
145
  # better don't use
143
146
  # get the angle variables from the symbolic equations for the angles
@@ -148,7 +151,7 @@ class UnaryStatePrep:
148
151
  # integrated repeat loop for the case that the randomly generated guess is especially bad
149
152
  count = 0
150
153
  solutions = []
151
- while (count < self.max_repeat and len(solutions) == 0):
154
+ while count < self.max_repeat and len(solutions) == 0:
152
155
  try:
153
156
  # same substitution map as before, but initialized as list of tuples
154
157
  subsx = [x for x in subs.items()]
@@ -156,10 +159,10 @@ class UnaryStatePrep:
156
159
  guess = numpy.random.uniform(0.1, 0.9 * 2 * numpy.pi, len(self._abstract_angles))
157
160
  solutions = sympy.nsolve(equations, self._abstract_angles, guess)
158
161
  count += 1
159
- except:
162
+ except Exception:
160
163
  count += 1
161
164
 
162
- if (len(solutions) == 0):
165
+ if len(solutions) == 0:
163
166
  raise TequilaUnaryStateException("Failed to numerically solve for angles")
164
167
 
165
168
  for i, symbol in enumerate(self._abstract_angles):
@@ -176,14 +179,15 @@ class UnaryStatePrep:
176
179
  assert wfn.length() == len(self._target_space)
177
180
  for key in wfn.keys():
178
181
  try:
179
- assert (key in self._target_space)
182
+ assert key in self._target_space
180
183
  except AssertionError:
181
184
  print("key=", key.binary, " not found in target space")
182
185
  except AssertionError:
183
- raise TequilaException("UnaryStatePrep was not initialized for the basis states in your wavefunction\n"
184
- "You gave:\n" + str(wfn) + "\n"
185
- "But the target_space is " + str(
186
- [k.binary for k in self._target_space]) + "\n")
186
+ raise TequilaException(
187
+ "UnaryStatePrep was not initialized for the basis states in your wavefunction\n"
188
+ "You gave:\n" + str(wfn) + "\n"
189
+ "But the target_space is " + str([k.binary for k in self._target_space]) + "\n"
190
+ )
187
191
 
188
192
  angles = self._evaluate_angles(wfn=wfn)
189
193
 
@@ -194,7 +198,9 @@ class UnaryStatePrep:
194
198
  if hasattr(g, "parameter"):
195
199
  symbol = g.parameter
196
200
  # the module needs repairing ....
197
- g2._parameter = assign_variable(-angles[-symbol()]) # the minus follows mahas convention since the circuits are daggered in the end
201
+ g2._parameter = assign_variable(
202
+ -angles[-symbol()]
203
+ ) # the minus follows mahas convention since the circuits are daggered in the end
198
204
  result += g2
199
205
 
200
206
  return result
@@ -208,7 +214,7 @@ class UnaryStatePrep:
208
214
  g2 = copy.deepcopy(g)
209
215
  if hasattr(g, "parameter"):
210
216
  symbol = g.parameter
211
- name = str(-symbol) # kill the minus from the dagger
217
+ name = str(-symbol) # kill the minus from the dagger
212
218
  g2._parameter = assign_variable(name)
213
219
  result += g2
214
220
 
@@ -216,5 +222,5 @@ class UnaryStatePrep:
216
222
 
217
223
  def angles(self, wfn: QubitWaveFunction) -> typing.Dict[typing.Hashable, float]:
218
224
  sympy_angles = self._evaluate_angles(wfn=wfn)
219
- angles = {assign_variable(str(key)):value for key, value in sympy_angles.items()}
220
- return angles
225
+ angles = {assign_variable(str(key)): value for key, value in sympy_angles.items()}
226
+ return angles
@@ -6,9 +6,10 @@ __AUTOGRAD__BACKEND__ = None
6
6
  try:
7
7
  import jax
8
8
  from jax import numpy
9
- jax.config.update('jax_platform_name', 'cpu')
9
+
10
+ jax.config.update("jax_platform_name", "cpu")
10
11
  __AUTOGRAD__BACKEND__ = "jax"
11
- except:
12
+ except Exception:
12
13
  # will pick autograd if jax is not installed
13
14
  # or if there are errors on import (like on M2 chips or when jax/jaxlib are not matching
14
15
  try:
@@ -19,18 +20,19 @@ except:
19
20
  except ImportError:
20
21
  raise TequilaException("Neither jax nor autograd found on your system")
21
22
 
22
- def change_classical_differentiation_backend(name:str):
23
+
24
+ def change_classical_differentiation_backend(name: str):
23
25
  if name.lower() == "jax":
24
26
  try:
25
27
  import jax
26
28
  from jax import numpy
27
- except:
29
+ except Exception:
28
30
  raise TequilaException("failed to load jax as classical differentiation backend")
29
31
  elif name.lower() == "autograd":
30
32
  try:
31
33
  import autograd as jax
32
34
  from autograd import numpy
33
- except:
35
+ except Exception:
34
36
  raise TequilaException("failed to load autograd as classical differentiation backend")
35
37
 
36
38
  else:
@@ -38,6 +40,7 @@ def change_classical_differentiation_backend(name:str):
38
40
 
39
41
  return True
40
42
 
43
+
41
44
  def status():
42
45
  print("currently loaded autodiff numpy: {}, {}".format(numpy.__name__, numpy))
43
46
  print("currently loaded autodiff library: {}, {}".format(jax.__name__, jax))
@@ -3,7 +3,8 @@ from .noise import NoiseModel
3
3
  from .qpic import export_to
4
4
  from .compiler import CircuitCompiler as CircuitCompiler
5
5
 
6
+
6
7
  def compile_circuit(U, *args, **kwargs):
7
8
  # see CircuitCompiler documentation
8
9
  c = CircuitCompiler.standard_gate_set(*args, **kwargs)
9
- return c(U)
10
+ return c(U)
@@ -16,7 +16,7 @@ UnionParam = typing.Union[Variable, FixedVariable]
16
16
 
17
17
 
18
18
  class QGateImpl:
19
- """"
19
+ """ "
20
20
  BaseClass for internal gate representation
21
21
  All other gate classes should derive from here
22
22
  """
@@ -47,12 +47,12 @@ class QGateImpl:
47
47
  return self.compute_max_qubit()
48
48
 
49
49
  def extract_variables(self):
50
- if self.is_parametrized() and hasattr(self.parameter, "extract_variables"):
50
+ if self.is_parameterized() and hasattr(self.parameter, "extract_variables"):
51
51
  return self.parameter.extract_variables()
52
52
  else:
53
53
  return []
54
54
 
55
- def is_parametrized(self) -> bool:
55
+ def is_parameterized(self) -> bool:
56
56
  return hasattr(self, "parameter")
57
57
 
58
58
  def make_generator(self, include_controls=False):
@@ -62,9 +62,8 @@ class QGateImpl:
62
62
  return self.generator
63
63
 
64
64
  def map_variables(self, variables):
65
-
66
- if self.is_parametrized():
67
- self.parameter=self.parameter.map_variables(variables)
65
+ if self.is_parameterized():
66
+ self.parameter = self.parameter.map_variables(variables)
68
67
 
69
68
  return self
70
69
 
@@ -82,7 +81,7 @@ class QGateImpl:
82
81
  """
83
82
  :return: return the hermitian conjugate of the gate.
84
83
  """
85
- result=copy.deepcopy(self)
84
+ result = copy.deepcopy(self)
86
85
  result.generator *= -1.0
87
86
  return result
88
87
 
@@ -103,22 +102,25 @@ class QGateImpl:
103
102
  return (not self.control) and (len(self.target) == 1)
104
103
 
105
104
  def finalize(self):
106
- if not self.target:
107
- raise Exception('Received no targets upon initialization')
105
+ if not self.target and not isinstance(self, GlobalPhaseGateImpl):
106
+ raise Exception("Received no targets upon initialization")
108
107
  if self.is_controlled():
109
108
  for c in self.target:
110
109
  if c in self.control:
111
110
  raise Exception("control and target are the same qubit: " + self.__str__())
112
- if hasattr(self,"generator") and self.generator:
111
+ if hasattr(self, "generator") and self.generator:
113
112
  if set(list(self.generator.qubits)) != set(list(self.target)):
114
- raise Exception("qubits of generator and targets don't agree -- mapping error?\n gate = {}".format(self.__str__()))
113
+ raise Exception(
114
+ "qubits of generator and targets don't agree -- mapping error?\n gate = {}".format(self.__str__())
115
+ )
115
116
  if hasattr(self, "generators"):
116
117
  genq = []
117
118
  for generator in self.generators:
118
119
  genq += generator.qubits
119
120
  if set(list(genq)) != set(list(self.target)):
120
- raise Exception("qubits of generator and targets don't agree -- mapping error?\n gate = {}".format(self.__str__()))
121
-
121
+ raise Exception(
122
+ "qubits of generator and targets don't agree -- mapping error?\n gate = {}".format(self.__str__())
123
+ )
122
124
 
123
125
  def __str__(self):
124
126
  result = str(self.name) + "(target=" + str(self.target)
@@ -138,9 +140,9 @@ class QGateImpl:
138
140
  :return: highest qubit index used by this gate
139
141
  """
140
142
  if self.control is None:
141
- return max(self.target)
143
+ return max(self.target, default=0)
142
144
  else:
143
- return max(self.target + self.control)
145
+ return max(self.target + self.control, default=0)
144
146
 
145
147
  def __eq__(self, other):
146
148
  if self.name != other.name:
@@ -165,10 +167,11 @@ class QGateImpl:
165
167
  mapped.generator = self.generator.map_qubits(qubit_map=qubit_map)
166
168
  return mapped
167
169
 
170
+
168
171
  class ParametrizedGateImpl(QGateImpl, ABC):
169
- '''
172
+ """
170
173
  the base class from which all parametrized gates inherit. User defined gates, when implemented, are liable to be members of this class directly.
171
- '''
174
+ """
172
175
 
173
176
  @property
174
177
  def parameter(self):
@@ -178,12 +181,24 @@ class ParametrizedGateImpl(QGateImpl, ABC):
178
181
  def parameter(self, other):
179
182
  self._parameter = assign_variable(variable=other)
180
183
 
181
- def __init__(self, name, parameter: UnionParam, target: UnionList, control: UnionList = None,
182
- generator: QubitHamiltonian = None):
184
+ def __init__(
185
+ self,
186
+ name,
187
+ parameter: UnionParam,
188
+ target: UnionList,
189
+ control: UnionList = None,
190
+ generator: QubitHamiltonian = None,
191
+ ):
183
192
  # failsafe
184
- if hasattr(parameter, "shape") and parameter.shape not in [tuple()]: # take care of new numpy conventions where scalars have shape ()
185
- self._parameter=None
186
- raise TequilaException("parameter has to be a scalar. Received {}\n{}\n{}".format(repr(parameter), type(parameter), str(parameter)))
193
+ if hasattr(parameter, "shape") and parameter.shape not in [
194
+ tuple()
195
+ ]: # take care of new numpy conventions where scalars have shape ()
196
+ self._parameter = None
197
+ raise TequilaException(
198
+ "parameter has to be a scalar. Received {}\n{}\n{}".format(
199
+ repr(parameter), type(parameter), str(parameter)
200
+ )
201
+ )
187
202
  self._parameter = assign_variable(variable=parameter)
188
203
  super().__init__(name=name, target=target, control=control, generator=generator)
189
204
 
@@ -209,16 +224,16 @@ class ParametrizedGateImpl(QGateImpl, ABC):
209
224
  result._parameter = assign_variable(-self.parameter)
210
225
  return result
211
226
 
212
- class DifferentiableGateImpl(ParametrizedGateImpl):
213
227
 
228
+ class DifferentiableGateImpl(ParametrizedGateImpl):
214
229
  @property
215
230
  def eigenvalues_magnitude(self):
216
231
  return self._eigenvalues_magnitude
217
232
 
218
- def __init__(self,eigenvalues_magnitude=None, assume_real=False, *args, **kwargs):
219
- self._eigenvalues_magnitude=eigenvalues_magnitude
233
+ def __init__(self, eigenvalues_magnitude=None, assume_real=False, *args, **kwargs):
234
+ self._eigenvalues_magnitude = eigenvalues_magnitude
220
235
  super().__init__(*args, **kwargs)
221
- self.assume_real=assume_real
236
+ self.assume_real = assume_real
222
237
 
223
238
  def shifted_gates(self, r=None):
224
239
  """
@@ -234,12 +249,12 @@ class DifferentiableGateImpl(ParametrizedGateImpl):
234
249
  if r is None:
235
250
  r = self.eigenvalues_magnitude
236
251
 
237
- s = np.pi / (4 * r)
252
+ s = np.pi / (4 * r)
238
253
  if self.is_controlled() and not self.assume_real:
239
254
  # following https://arxiv.org/abs/2104.05695
240
255
  shifts = [s, -s, 3 * s, -3 * s]
241
- coeff1 = (np.sqrt(2) + 1)/np.sqrt(8) * r
242
- coeff2 = (np.sqrt(2) - 1)/np.sqrt(8) * r
256
+ coeff1 = (np.sqrt(2) + 1) / np.sqrt(8) * r
257
+ coeff2 = (np.sqrt(2) - 1) / np.sqrt(8) * r
243
258
  coefficients = [coeff1, -coeff1, -coeff2, coeff2]
244
259
  circuits = []
245
260
  for i, shift in enumerate(shifts):
@@ -257,16 +272,17 @@ class DifferentiableGateImpl(ParametrizedGateImpl):
257
272
 
258
273
  if self.is_controlled():
259
274
  # following https://doi.org/10.1039/D0SC06627C
260
- p0 = paulis.Qp(self.control) # Qp = |0><0|
261
- right2 = GeneralizedRotationImpl(angle=s, generator=p0, eigenvalues_magnitude=r/2) # controls are in p0
262
- left2 = GeneralizedRotationImpl(angle=-s, generator=p0, eigenvalues_magnitude=r/2) # controls are in p0
263
- return [(r, [right, right2]), (-r, [left , left2])]
275
+ p0 = paulis.Qp(self.control) # Qp = |0><0|
276
+ right2 = GeneralizedRotationImpl(angle=s, generator=p0, eigenvalues_magnitude=r / 2) # controls are in p0
277
+ left2 = GeneralizedRotationImpl(angle=-s, generator=p0, eigenvalues_magnitude=r / 2) # controls are in p0
278
+ return [(r, [right, right2]), (-r, [left, left2])]
264
279
  else:
265
- return [ (r, right), (-r, left) ]
280
+ return [(r, right), (-r, left)]
266
281
 
267
282
  def finalize(self):
268
283
  super().finalize()
269
284
 
285
+
270
286
  class RotationGateImpl(DifferentiableGateImpl):
271
287
  axis_to_string = {0: "x", 1: "y", 2: "z"}
272
288
  string_to_axis = {"x": 0, "y": 1, "z": 2}
@@ -294,8 +310,15 @@ class RotationGateImpl(DifferentiableGateImpl):
294
310
  return result
295
311
 
296
312
  def __init__(self, axis, angle, target: list, control: list = None, assume_real=False):
297
- assert (angle is not None)
298
- super().__init__(eigenvalues_magnitude=0.5, assume_real=assume_real, name=self.get_name(axis=axis), parameter=angle, target=target, control=control)
313
+ assert angle is not None
314
+ super().__init__(
315
+ eigenvalues_magnitude=0.5,
316
+ assume_real=assume_real,
317
+ name=self.get_name(axis=axis),
318
+ parameter=angle,
319
+ target=target,
320
+ control=control,
321
+ )
299
322
  self._axis = self.assign_axis(axis)
300
323
  self.generator = self.assign_generator(self.axis, self.target)
301
324
 
@@ -306,7 +329,7 @@ class RotationGateImpl(DifferentiableGateImpl):
306
329
  elif hasattr(axis, "lower") and axis.lower() in RotationGateImpl.string_to_axis:
307
330
  return RotationGateImpl.string_to_axis[axis.lower()]
308
331
  else:
309
- assert (axis in [0, 1, 2])
332
+ assert axis in [0, 1, 2]
310
333
  return axis
311
334
 
312
335
  @staticmethod
@@ -319,11 +342,21 @@ class RotationGateImpl(DifferentiableGateImpl):
319
342
  return sum(paulis.Z(q) for q in qubits)
320
343
 
321
344
 
322
- class PhaseGateImpl(DifferentiableGateImpl):
345
+ class GlobalPhaseGateImpl(DifferentiableGateImpl):
346
+ def __init__(self, phase):
347
+ super().__init__(
348
+ name="GlobalPhase",
349
+ parameter=phase,
350
+ target=(), # Global phases don't act on any particular qubit
351
+ generator=paulis.I(),
352
+ eigenvalues_magnitude=0.5,
353
+ )
354
+
323
355
 
356
+ class PhaseGateImpl(DifferentiableGateImpl):
324
357
  def __init__(self, phase, target: list, control: list = None):
325
- assert (phase is not None)
326
- super().__init__(eigenvalues_magnitude=0.5, name='Phase', parameter=phase, target=target, control=control)
358
+ assert phase is not None
359
+ super().__init__(eigenvalues_magnitude=0.5, name="Phase", parameter=phase, target=target, control=control)
327
360
  self.generator = paulis.Z(target) - paulis.I(target)
328
361
 
329
362
  def __pow__(self, power, modulo=None):
@@ -331,6 +364,7 @@ class PhaseGateImpl(DifferentiableGateImpl):
331
364
  result.parameter *= power
332
365
  return result
333
366
 
367
+
334
368
  class PowerGateImpl(ParametrizedGateImpl):
335
369
  """
336
370
  Attributes
@@ -344,9 +378,9 @@ class PowerGateImpl(ParametrizedGateImpl):
344
378
 
345
379
  @property
346
380
  def power(self):
347
- return self.parameter/np.pi
381
+ return self.parameter / np.pi
348
382
 
349
- def __init__(self, name, generator: QubitHamiltonian, target: list, power, control: list = None):
383
+ def __init__(self, name, generator: QubitHamiltonian, target: list, power, control: list = None):
350
384
  if generator is None:
351
385
  assert name is not None and name.upper() in ["X", "Y", "Z"]
352
386
  generator = QubitHamiltonian.from_string("{}({})".format(name.upper(), target))
@@ -376,24 +410,43 @@ class GeneralizedRotationImpl(DifferentiableGateImpl):
376
410
  targets += [k for k in ps.keys()]
377
411
  return tuple(set(targets))
378
412
 
379
- def __init__(self, angle, generator, p0=None, control=None, target=None, eigenvalues_magnitude=0.5, steps=1, name="GenRot", assume_real=False):
380
- if target == None:
413
+ def __init__(
414
+ self,
415
+ angle,
416
+ generator,
417
+ p0=None,
418
+ control=None,
419
+ target=None,
420
+ eigenvalues_magnitude=0.5,
421
+ steps=1,
422
+ name="GenRot",
423
+ assume_real=False,
424
+ ):
425
+ if target is None:
381
426
  target = self.extract_targets(generator)
382
- super().__init__(eigenvalues_magnitude=eigenvalues_magnitude, generator=generator, assume_real=assume_real, name=name, parameter=angle, target=target, control=control)
427
+ super().__init__(
428
+ eigenvalues_magnitude=eigenvalues_magnitude,
429
+ generator=generator,
430
+ assume_real=assume_real,
431
+ name=name,
432
+ parameter=angle,
433
+ target=target,
434
+ control=control,
435
+ )
383
436
  self.steps = steps
384
437
  if control is None and p0 is not None:
385
438
  # augment p0 for control qubits
386
439
  # Qp = 1/2(1+Z) = |0><0|
387
- p0 = p0*paulis.Qp(control)
440
+ p0 = p0 * paulis.Qp(control)
388
441
  self.p0 = p0
389
-
442
+
390
443
  def shifted_gates(self):
391
444
  if not self.assume_real:
392
445
  # following https://arxiv.org/abs/2104.05695
393
446
  s = 0.5 * np.pi
394
447
  shifts = [s, -s, 3 * s, -3 * s]
395
- coeff1 = 0.25 * (np.sqrt(2) + 1)/np.sqrt(2)
396
- coeff2 = 0.25 * (np.sqrt(2) - 1)/np.sqrt(2)
448
+ coeff1 = 0.25 * (np.sqrt(2) + 1) / np.sqrt(2)
449
+ coeff2 = 0.25 * (np.sqrt(2) - 1) / np.sqrt(2)
397
450
  coefficients = [coeff1, -coeff1, -coeff2, coeff2]
398
451
  circuits = []
399
452
  for i, shift in enumerate(shifts):
@@ -403,17 +456,18 @@ class GeneralizedRotationImpl(DifferentiableGateImpl):
403
456
  return circuits
404
457
 
405
458
  r = 0.25
406
- s = 0.5*np.pi
407
-
459
+ s = 0.5 * np.pi
460
+
408
461
  Up1 = copy.deepcopy(self)
409
- Up1._parameter = self.parameter+s
410
- Up2 = GeneralizedRotationImpl(angle=s, generator=self.p0, eigenvalues_magnitude=r) # controls are in p0
462
+ Up1._parameter = self.parameter + s
463
+ Up2 = GeneralizedRotationImpl(angle=s, generator=self.p0, eigenvalues_magnitude=r) # controls are in p0
411
464
  Um1 = copy.deepcopy(self)
412
- Um1._parameter = self.parameter-s
413
- Um2 = GeneralizedRotationImpl(angle=-s, generator=self.p0, eigenvalues_magnitude=r) # controls are in p0
465
+ Um1._parameter = self.parameter - s
466
+ Um2 = GeneralizedRotationImpl(angle=-s, generator=self.p0, eigenvalues_magnitude=r) # controls are in p0
467
+
468
+ return [(2.0 * r, [Up1, Up2]), (-2.0 * r, [Um1, Um2])]
469
+
414
470
 
415
- return [(2.0 * r, [Up1, Up2]), (-2.0 * r, [Um1, Um2])]
416
-
417
471
  class ExponentialPauliGateImpl(DifferentiableGateImpl):
418
472
  """
419
473
  Same convention as for rotation gates:
@@ -421,7 +475,13 @@ class ExponentialPauliGateImpl(DifferentiableGateImpl):
421
475
  """
422
476
 
423
477
  def __init__(self, paulistring: PauliString, angle: float, control: typing.List[int] = None):
424
- super().__init__(eigenvalues_magnitude=0.5, name="Exp-Pauli", target=tuple(t for t in paulistring.keys()), control=control, parameter=angle)
478
+ super().__init__(
479
+ eigenvalues_magnitude=0.5,
480
+ name="Exp-Pauli",
481
+ target=tuple(t for t in paulistring.keys()),
482
+ control=control,
483
+ parameter=angle,
484
+ )
425
485
  self.paulistring = paulistring
426
486
  self.generator = QubitHamiltonian.from_paulistrings(paulistring)
427
487
  self.finalize()
@@ -443,13 +503,16 @@ class ExponentialPauliGateImpl(DifferentiableGateImpl):
443
503
 
444
504
 
445
505
  class TrotterizedGateImpl(ParametrizedGateImpl):
446
-
447
- def __init__(self, generator: QubitHamiltonian,
448
- angle: typing.Union[numbers.Real, Variable],
449
- steps: int = 1,
450
- control: typing.Union[list, int] = None,
451
- threshold: numbers.Real = 0.0,
452
- randomize: bool = True, **kwargs):
506
+ def __init__(
507
+ self,
508
+ generator: QubitHamiltonian,
509
+ angle: typing.Union[numbers.Real, Variable],
510
+ steps: int = 1,
511
+ control: typing.Union[list, int] = None,
512
+ threshold: numbers.Real = 0.0,
513
+ randomize: bool = True,
514
+ **kwargs,
515
+ ):
453
516
  """
454
517
  :param generators: list of generators
455
518
  :param angles: coefficients for each generator
@@ -463,7 +526,13 @@ class TrotterizedGateImpl(ParametrizedGateImpl):
463
526
  assert angle is not None
464
527
  assert generator is not None
465
528
 
466
- super().__init__(name="Trotterized", target=self.extract_targets(generator), control=control, generator=generator, parameter=angle)
529
+ super().__init__(
530
+ name="Trotterized",
531
+ target=self.extract_targets(generator),
532
+ control=control,
533
+ generator=generator,
534
+ parameter=angle,
535
+ )
467
536
  self._parameter = angle
468
537
  self.steps = steps
469
538
  self.threshold = threshold
@@ -486,4 +555,3 @@ class TrotterizedGateImpl(ParametrizedGateImpl):
486
555
  for ps in generator.paulistrings:
487
556
  targets += [k for k in ps.keys()]
488
557
  return tuple(set(targets))
489
-