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
@@ -19,7 +19,7 @@ print("Test: Circuit und Hamiltonian mit Variablen (SPEX vs. Qulacs)")
|
|
19
19
|
a = tq.Variable("a")
|
20
20
|
b = tq.Variable("b")
|
21
21
|
c = tq.Variable("c")
|
22
|
-
variables = {"a": np.pi/3, "b": np.pi/4, "c": np.pi/6}
|
22
|
+
variables = {"a": np.pi / 3, "b": np.pi / 4, "c": np.pi / 6}
|
23
23
|
|
24
24
|
# --- Circuitaufbau ---
|
25
25
|
# Erzeuge einen Circuit, der auf 3 Qubits operiert:
|
@@ -27,9 +27,7 @@ variables = {"a": np.pi/3, "b": np.pi/4, "c": np.pi/6}
|
|
27
27
|
# - Eine Ry-Rotation auf Qubit 1 (Winkel "b")
|
28
28
|
# - Eine Rz-Rotation auf Qubit 2 (Winkel "c")
|
29
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,))
|
31
|
-
+ tq.gates.Ry(angle="b", target=(1,)) \
|
32
|
-
+ tq.gates.Rz(angle="c", target=(2,))
|
30
|
+
U = tq.gates.Rx(angle="a", target=(0,)) + tq.gates.Ry(angle="b", target=(1,)) + tq.gates.Rz(angle="c", target=(2,))
|
33
31
|
U += tq.gates.ExpPauli(angle="a", paulistring="X(0)Z(2)")
|
34
32
|
|
35
33
|
print("\nCircuit U:")
|
@@ -47,15 +45,15 @@ E = tq.ExpectationValue(U=U, H=H)
|
|
47
45
|
|
48
46
|
# --- Simulation mit SPEX ---
|
49
47
|
start = time.time()
|
50
|
-
wfn_spex = tq.simulate(U, variables, backend=
|
51
|
-
exp_spex = tq.simulate(E, variables, backend=
|
48
|
+
wfn_spex = tq.simulate(U, variables, backend="spex")
|
49
|
+
exp_spex = tq.simulate(E, variables, backend="spex")
|
52
50
|
end = time.time()
|
53
51
|
time_spex = end - start
|
54
52
|
|
55
53
|
# --- Simulation mit Qulacs ---
|
56
54
|
start = time.time()
|
57
|
-
wfn_qulacs = tq.simulate(U, variables, backend=
|
58
|
-
exp_qulacs = tq.simulate(E, variables, backend=
|
55
|
+
wfn_qulacs = tq.simulate(U, variables, backend="qulacs")
|
56
|
+
exp_qulacs = tq.simulate(E, variables, backend="qulacs")
|
59
57
|
end = time.time()
|
60
58
|
time_qulacs = end - start
|
61
59
|
|
@@ -70,11 +68,10 @@ print("\nErwartungswert (SPEX backend):", exp_spex, f"(Simulationszeit: {time_sp
|
|
70
68
|
print("Erwartungswert (Qulacs backend):", exp_qulacs, f"(Simulationszeit: {time_qulacs:.3f}s)")
|
71
69
|
|
72
70
|
# Optional: Vergleiche das innere Produkt der beiden Wavefunctions (Quadrat des Betrags)
|
73
|
-
inner_prod = np.abs(wfn_spex.inner(wfn_qulacs))**2
|
71
|
+
inner_prod = np.abs(wfn_spex.inner(wfn_qulacs)) ** 2
|
74
72
|
print("\nInneres Produkt (Quadrat) zwischen SPEX und Qulacs:", inner_prod)
|
75
73
|
|
76
74
|
|
77
|
-
|
78
75
|
"""
|
79
76
|
print("\nTest: 1")
|
80
77
|
U = tq.gates.ExpPauli(paulistring=PauliString({0: "X", 1: "Z"}), angle=np.pi / 2)
|
@@ -208,4 +205,4 @@ for n in range(1, 15):
|
|
208
205
|
|
209
206
|
E_qulacs = None
|
210
207
|
|
211
|
-
"""
|
208
|
+
"""
|
tequila/tools/convenience.py
CHANGED
@@ -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.
|
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 =
|
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) + (
|
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
|
9
|
-
|
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)
|
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,
|
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() ==
|
161
|
+
if gate.name.lower() == "rx":
|
159
162
|
gen = paulis.X(gate.target[0])
|
160
|
-
elif gate.name.lower() ==
|
163
|
+
elif gate.name.lower() == "ry":
|
161
164
|
gen = paulis.Y(gate.target[0])
|
162
|
-
elif gate.name.lower() ==
|
165
|
+
elif gate.name.lower() == "rz":
|
163
166
|
gen = paulis.Z(gate.target[0])
|
164
|
-
elif gate.name.lower() ==
|
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(
|
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(
|
173
|
-
|
174
|
-
|
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 = (
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
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.
|
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,
|
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(
|
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(
|
340
|
-
|
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(
|
368
|
-
|
369
|
-
|
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.
|
390
|
-
if hasattr(g._parameter,
|
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 {
|
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(
|
471
|
-
|
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(
|
476
|
-
|
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(
|
486
|
-
|
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(
|
497
|
-
|
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[
|
524
|
-
vec = c[
|
525
|
-
m = c[
|
526
|
-
pos = c[
|
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
|
@@ -4,8 +4,10 @@ from tequila.circuit.circuit import QCircuit
|
|
4
4
|
from tequila.hamiltonian.qubit_hamiltonian import QubitHamiltonian
|
5
5
|
from scipy.stats import unitary_group, ortho_group
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
|
8
|
+
def make_random_circuit(
|
9
|
+
n_qubits: int, rotation_gates: list = ["rx", "ry", "rz"], n_rotations: int = None, enable_controls: bool = None
|
10
|
+
) -> QCircuit:
|
9
11
|
"""Function that creates a circuit with random rotations or random control rotations.
|
10
12
|
|
11
13
|
Args:
|
@@ -15,37 +17,38 @@ def make_random_circuit(n_qubits: int, rotation_gates: list=['rx', 'ry', 'rz'],
|
|
15
17
|
enable_controls (bool): Boolean that switch on controls. Default to None.
|
16
18
|
|
17
19
|
Returns:
|
18
|
-
QCircuit: Random quantum circuit consiting of the given rotations gates
|
20
|
+
QCircuit: Random quantum circuit consiting of the given rotations gates
|
19
21
|
and their controlled versions
|
20
22
|
"""
|
21
23
|
if n_rotations is None:
|
22
|
-
n_rotations = np.random.randint(n_qubits, high=n_qubits*3)
|
24
|
+
n_rotations = np.random.randint(n_qubits, high=n_qubits * 3)
|
23
25
|
|
24
26
|
gates_list = [np.random.choice(rotation_gates) for i in range(n_rotations)]
|
25
|
-
|
26
|
-
angles = 2*np.pi * np.random.rand(n_rotations)
|
27
|
-
|
27
|
+
|
28
|
+
angles = 2 * np.pi * np.random.rand(n_rotations)
|
29
|
+
|
28
30
|
circ = QCircuit()
|
29
31
|
for i, angle in enumerate(angles):
|
30
|
-
target = i%n_qubits
|
32
|
+
target = i % n_qubits
|
31
33
|
if enable_controls:
|
32
34
|
controls = [i for i in circ.qubits if i != target]
|
33
35
|
control = np.random.choice(controls + [None])
|
34
|
-
else:
|
36
|
+
else:
|
35
37
|
control = None
|
36
38
|
|
37
|
-
if gates_list[i]==
|
39
|
+
if gates_list[i] == "rx":
|
38
40
|
circ += gates.Rx(angle=angle, target=target, control=control)
|
39
|
-
|
40
|
-
elif gates_list[i]==
|
41
|
+
|
42
|
+
elif gates_list[i] == "ry":
|
41
43
|
circ += gates.Ry(angle=angle, target=target, control=control)
|
42
|
-
|
43
|
-
elif gates_list[i]==
|
44
|
+
|
45
|
+
elif gates_list[i] == "rz":
|
44
46
|
circ += gates.Rz(angle=angle, target=target, control=control)
|
45
|
-
|
47
|
+
|
46
48
|
return circ
|
47
49
|
|
48
|
-
|
50
|
+
|
51
|
+
def make_random_hamiltonian(n_qubits: int, paulis: list = ["X", "Y", "Z"], n_ps: int = None) -> QubitHamiltonian:
|
49
52
|
"""Function that creates a random Hamiltonian, given the list
|
50
53
|
of Pauli ops. to use and the number of Pauli strings.
|
51
54
|
|
@@ -58,27 +61,28 @@ def make_random_hamiltonian(n_qubits: int , paulis: list=['X','Y','Z'], n_ps: in
|
|
58
61
|
tq.QubitHamiltonian: Random Hamiltonian
|
59
62
|
"""
|
60
63
|
if n_ps is None:
|
61
|
-
n_ps = np.random.randint(1, high=2*n_qubits+1)
|
62
|
-
|
63
|
-
ham =
|
64
|
+
n_ps = np.random.randint(1, high=2 * n_qubits + 1)
|
65
|
+
|
66
|
+
ham = ""
|
64
67
|
for ps in range(n_ps):
|
65
|
-
coeff =
|
66
|
-
pauli_str =
|
68
|
+
coeff = "{}*".format(round(np.random.sample(), 2))
|
69
|
+
pauli_str = ""
|
67
70
|
for qb in range(n_qubits):
|
68
|
-
pauli_str +=
|
69
|
-
|
70
|
-
if ps < n_ps-1:
|
71
|
-
pauli_str +=
|
72
|
-
|
73
|
-
ham += coeff+pauli_str
|
74
|
-
|
75
|
-
#print(ham)
|
76
|
-
|
71
|
+
pauli_str += "{}({})".format(np.random.choice(paulis), qb)
|
72
|
+
|
73
|
+
if ps < n_ps - 1:
|
74
|
+
pauli_str += "+"
|
75
|
+
|
76
|
+
ham += coeff + pauli_str
|
77
|
+
|
78
|
+
# print(ham)
|
79
|
+
|
77
80
|
H = QubitHamiltonian(ham)
|
78
81
|
return H
|
79
82
|
|
80
|
-
|
81
|
-
|
83
|
+
|
84
|
+
def generate_random_unitary(size, complex=False):
|
85
|
+
"""
|
82
86
|
Generates a random unitary (or furthermore orthogonal if complex is False) matrix of a specified size.
|
83
87
|
|
84
88
|
Parameters:
|
@@ -87,8 +91,8 @@ def generate_random_unitary(size, complex = False):
|
|
87
91
|
|
88
92
|
Returns:
|
89
93
|
- numpy.ndarray: A randomly generated unitary matrix.
|
90
|
-
|
94
|
+
"""
|
91
95
|
if complex:
|
92
96
|
return unitary_group.rvs(size)
|
93
97
|
else:
|
94
|
-
return ortho_group.rvs(size)
|
98
|
+
return ortho_group.rvs(size)
|
tequila/utils/bitstrings.py
CHANGED
@@ -43,14 +43,14 @@ class BitString:
|
|
43
43
|
@property
|
44
44
|
def binary(self):
|
45
45
|
if self.numbering is BitNumbering.MSB:
|
46
|
-
return format(self._value,
|
46
|
+
return format(self._value, "b").zfill(self.nbits)
|
47
47
|
else:
|
48
|
-
return format(self._value,
|
48
|
+
return format(self._value, "b").zfill(self.nbits)[::-1]
|
49
49
|
|
50
50
|
@binary.setter
|
51
51
|
def binary(self, other: str):
|
52
|
-
assert
|
53
|
-
if other.startswith(
|
52
|
+
assert isinstance(other, str)
|
53
|
+
if other.startswith("0b"):
|
54
54
|
other = other[2:]
|
55
55
|
if self.numbering == BitNumbering.LSB:
|
56
56
|
self._value = int(other[::-1], 2)
|
@@ -178,7 +178,6 @@ class BitString:
|
|
178
178
|
|
179
179
|
|
180
180
|
class BitStringLSB(BitString):
|
181
|
-
|
182
181
|
@property
|
183
182
|
def numbering(self) -> BitNumbering:
|
184
183
|
return BitNumbering.LSB
|
@@ -197,8 +196,9 @@ def reverse_int_bits(x: int, nbits: int) -> int:
|
|
197
196
|
return x >> (32 - nbits)
|
198
197
|
|
199
198
|
|
200
|
-
def initialize_bitstring(
|
201
|
-
|
199
|
+
def initialize_bitstring(
|
200
|
+
integer: int, nbits: int = None, numbering_in: BitNumbering = BitNumbering.MSB, numbering_out: BitNumbering = None
|
201
|
+
):
|
202
202
|
if numbering_out is None:
|
203
203
|
numbering_out = numbering_in
|
204
204
|
|
tequila/utils/exceptions.py
CHANGED
@@ -31,9 +31,16 @@ class TequilaParameterError(TequilaException):
|
|
31
31
|
"""
|
32
32
|
|
33
33
|
def __init__(self, parameter_name, parameter_class, parameter_value, called_from=""):
|
34
|
-
self.message =
|
35
|
-
|
36
|
-
|
34
|
+
self.message = (
|
35
|
+
"OpenVQE ParameterError:"
|
36
|
+
+ called_from
|
37
|
+
+ " unknown parameter value: "
|
38
|
+
+ str(parameter_name)
|
39
|
+
+ "="
|
40
|
+
+ str(parameter_value)
|
41
|
+
+ " for "
|
42
|
+
+ str(parameter_class)
|
43
|
+
)
|
37
44
|
|
38
45
|
|
39
46
|
class TequilaTypeError(TequilaException):
|
@@ -42,5 +49,12 @@ class TequilaTypeError(TequilaException):
|
|
42
49
|
"""
|
43
50
|
|
44
51
|
def __init__(self, attr, type, expected):
|
45
|
-
self.message =
|
46
|
-
|
52
|
+
self.message = (
|
53
|
+
"OpenVQE TypeError: "
|
54
|
+
+ "excpected type: "
|
55
|
+
+ str(expected)
|
56
|
+
+ " but got type "
|
57
|
+
+ str(type)
|
58
|
+
+ " for attribute "
|
59
|
+
+ str(attr)
|
60
|
+
)
|
@@ -1,12 +1,12 @@
|
|
1
1
|
class JoinedTransformation:
|
2
|
-
|
2
|
+
"""
|
3
3
|
class structure used to construct,track, and permit differentiation of the computation required
|
4
4
|
by mathematical operations on ExpectationValues,Variables and Objectives thereof.
|
5
5
|
JoinedTransformations allow operations to be combined.
|
6
|
-
|
6
|
+
"""
|
7
7
|
|
8
8
|
def __init__(self, left, right, split, op):
|
9
|
-
|
9
|
+
"""
|
10
10
|
:param left: Callable: the lefthand operation, one level down
|
11
11
|
:param right: Callable: the righthand operation, one level down
|
12
12
|
:param split: Int: the position in the call method, at which lefthand and righthand arguments split
|
@@ -15,21 +15,21 @@ class JoinedTransformation:
|
|
15
15
|
Example: split 2, left = numpy.add, right= numpy.subtract, op = numpy.multiply, call on list of length 4:
|
16
16
|
then the Joined Transform performs the following computation when called:
|
17
17
|
np.Multiply(np.add(arg_0,arg_1),np.subtract(arg_2,arg_3))
|
18
|
-
|
18
|
+
"""
|
19
19
|
self.split = split
|
20
20
|
self.left = left
|
21
21
|
self.right = right
|
22
22
|
self.op = op
|
23
23
|
|
24
24
|
def __call__(self, *args, **kwargs):
|
25
|
-
|
25
|
+
"""
|
26
26
|
|
27
27
|
:param args: iter: the arguments to the transformation.
|
28
28
|
:param kwargs: dict: keyword arguments to the transformation. Must be uniform.
|
29
29
|
:return: a number, the result of the calculation.
|
30
|
-
|
31
|
-
E_left = args[:self.split]
|
32
|
-
E_right = args[self.split:]
|
30
|
+
"""
|
31
|
+
E_left = args[: self.split]
|
32
|
+
E_right = args[self.split :]
|
33
33
|
if self.op is None:
|
34
34
|
if len(args) == 1:
|
35
35
|
return args[0]
|
@@ -39,13 +39,11 @@ class JoinedTransformation:
|
|
39
39
|
print(len(args))
|
40
40
|
return self.op(*args)
|
41
41
|
else:
|
42
|
-
|
43
42
|
return self.op(self.left(*E_left, **kwargs), *E_right)
|
44
43
|
if self.left is None:
|
45
44
|
if self.right is None:
|
46
45
|
return self.op(*args)
|
47
46
|
else:
|
48
|
-
|
49
47
|
return self.op(*E_left, self.right(*E_right, **kwargs))
|
50
48
|
else:
|
51
49
|
return self.op(self.left(*E_left, **kwargs), self.right(*E_right, **kwargs))
|