tequila-basic 1.9.9__py3-none-any.whl → 1.9.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tequila/__init__.py +29 -14
- tequila/apps/__init__.py +14 -5
- tequila/apps/_unary_state_prep_impl.py +145 -112
- tequila/apps/adapt/__init__.py +9 -1
- tequila/apps/adapt/adapt.py +154 -113
- tequila/apps/krylov/__init__.py +1 -1
- tequila/apps/krylov/krylov.py +23 -21
- tequila/apps/robustness/helpers.py +10 -6
- tequila/apps/robustness/interval.py +238 -156
- tequila/apps/unary_state_prep.py +29 -23
- tequila/autograd_imports.py +8 -5
- tequila/circuit/__init__.py +2 -1
- tequila/circuit/_gates_impl.py +135 -67
- tequila/circuit/circuit.py +163 -79
- tequila/circuit/compiler.py +114 -105
- tequila/circuit/gates.py +288 -120
- tequila/circuit/gradient.py +35 -23
- tequila/circuit/noise.py +83 -74
- tequila/circuit/postselection.py +120 -0
- tequila/circuit/pyzx.py +10 -6
- tequila/circuit/qasm.py +201 -83
- tequila/circuit/qpic.py +63 -61
- tequila/grouping/binary_rep.py +148 -146
- tequila/grouping/binary_utils.py +84 -75
- tequila/grouping/compile_groups.py +334 -230
- tequila/grouping/ev_utils.py +77 -41
- tequila/grouping/fermionic_functions.py +383 -308
- tequila/grouping/fermionic_methods.py +170 -123
- tequila/grouping/overlapping_methods.py +69 -52
- tequila/hamiltonian/paulis.py +12 -13
- tequila/hamiltonian/paulistring.py +1 -1
- tequila/hamiltonian/qubit_hamiltonian.py +45 -35
- tequila/ml/__init__.py +1 -0
- tequila/ml/interface_torch.py +19 -16
- tequila/ml/ml_api.py +11 -10
- tequila/ml/utils_ml.py +12 -11
- tequila/objective/__init__.py +8 -3
- tequila/objective/braket.py +55 -47
- tequila/objective/objective.py +87 -55
- tequila/objective/qtensor.py +36 -27
- tequila/optimizers/__init__.py +31 -23
- tequila/optimizers/_containers.py +11 -7
- tequila/optimizers/optimizer_base.py +111 -83
- tequila/optimizers/optimizer_gd.py +258 -231
- tequila/optimizers/optimizer_gpyopt.py +56 -42
- tequila/optimizers/optimizer_scipy.py +157 -112
- tequila/quantumchemistry/__init__.py +66 -38
- tequila/quantumchemistry/chemistry_tools.py +393 -209
- tequila/quantumchemistry/encodings.py +121 -13
- tequila/quantumchemistry/madness_interface.py +170 -96
- tequila/quantumchemistry/orbital_optimizer.py +86 -41
- tequila/quantumchemistry/psi4_interface.py +166 -97
- tequila/quantumchemistry/pyscf_interface.py +70 -23
- tequila/quantumchemistry/qc_base.py +866 -414
- tequila/simulators/__init__.py +0 -3
- tequila/simulators/simulator_api.py +247 -105
- tequila/simulators/simulator_aqt.py +102 -0
- tequila/simulators/simulator_base.py +147 -53
- tequila/simulators/simulator_cirq.py +58 -42
- tequila/simulators/simulator_cudaq.py +600 -0
- tequila/simulators/simulator_ddsim.py +390 -0
- tequila/simulators/simulator_mqp.py +30 -0
- tequila/simulators/simulator_pyquil.py +190 -171
- tequila/simulators/simulator_qibo.py +95 -87
- tequila/simulators/simulator_qiskit.py +119 -107
- tequila/simulators/simulator_qlm.py +52 -26
- tequila/simulators/simulator_qulacs.py +74 -52
- tequila/simulators/simulator_spex.py +95 -60
- tequila/simulators/simulator_symbolic.py +6 -5
- tequila/simulators/test_spex_simulator.py +8 -11
- tequila/tools/convenience.py +4 -4
- tequila/tools/qng.py +72 -64
- tequila/tools/random_generators.py +38 -34
- tequila/utils/bitstrings.py +7 -7
- tequila/utils/exceptions.py +19 -5
- tequila/utils/joined_transformation.py +8 -10
- tequila/utils/keymap.py +0 -5
- tequila/utils/misc.py +6 -4
- tequila/version.py +1 -1
- tequila/wavefunction/qubit_wavefunction.py +47 -28
- {tequila_basic-1.9.9.dist-info → tequila_basic-1.9.10.dist-info}/METADATA +13 -16
- tequila_basic-1.9.10.dist-info/RECORD +93 -0
- {tequila_basic-1.9.9.dist-info → tequila_basic-1.9.10.dist-info}/WHEEL +1 -1
- tequila_basic-1.9.9.dist-info/RECORD +0 -88
- {tequila_basic-1.9.9.dist-info → tequila_basic-1.9.10.dist-info}/licenses/LICENSE +0 -0
- {tequila_basic-1.9.9.dist-info → tequila_basic-1.9.10.dist-info}/top_level.txt +0 -0
tequila/apps/unary_state_prep.py
CHANGED
@@ -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
|
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__(
|
44
|
-
|
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
|
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:
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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(
|
184
|
-
|
185
|
-
|
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(
|
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)
|
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
|
tequila/autograd_imports.py
CHANGED
@@ -6,9 +6,10 @@ __AUTOGRAD__BACKEND__ = None
|
|
6
6
|
try:
|
7
7
|
import jax
|
8
8
|
from jax import numpy
|
9
|
-
|
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
|
-
|
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))
|
tequila/circuit/__init__.py
CHANGED
@@ -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)
|
tequila/circuit/_gates_impl.py
CHANGED
@@ -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.
|
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
|
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
|
-
|
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(
|
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(
|
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(
|
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__(
|
182
|
-
|
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 [
|
185
|
-
|
186
|
-
|
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 =
|
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)
|
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
|
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 [
|
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
|
298
|
-
super().__init__(
|
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
|
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
|
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
|
326
|
-
super().__init__(eigenvalues_magnitude=0.5, name=
|
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,
|
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__(
|
380
|
-
|
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__(
|
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)
|
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)
|
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__(
|
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
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
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__(
|
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
|
-
|