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.
- 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 +177 -88
- 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 +91 -56
- 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 +394 -203
- tequila/quantumchemistry/encodings.py +121 -13
- tequila/quantumchemistry/madness_interface.py +170 -96
- tequila/quantumchemistry/orbital_optimizer.py +86 -40
- 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 +258 -106
- tequila/simulators/simulator_aqt.py +102 -0
- tequila/simulators/simulator_base.py +156 -55
- 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 +124 -114
- tequila/simulators/simulator_qlm.py +52 -26
- tequila/simulators/simulator_qulacs.py +85 -59
- tequila/simulators/simulator_spex.py +464 -0
- tequila/simulators/simulator_symbolic.py +6 -5
- tequila/simulators/test_spex_simulator.py +208 -0
- tequila/tools/convenience.py +4 -4
- tequila/tools/qng.py +72 -64
- tequila/tools/random_generators.py +38 -34
- tequila/utils/bitstrings.py +13 -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 +52 -30
- {tequila_basic-1.9.8.dist-info → tequila_basic-1.9.10.dist-info}/METADATA +23 -17
- tequila_basic-1.9.10.dist-info/RECORD +93 -0
- {tequila_basic-1.9.8.dist-info → tequila_basic-1.9.10.dist-info}/WHEEL +1 -1
- tequila_basic-1.9.8.dist-info/RECORD +0 -86
- {tequila_basic-1.9.8.dist-info → tequila_basic-1.9.10.dist-info/licenses}/LICENSE +0 -0
- {tequila_basic-1.9.8.dist-info → tequila_basic-1.9.10.dist-info}/top_level.txt +0 -0
tequila/grouping/binary_rep.py
CHANGED
@@ -1,17 +1,27 @@
|
|
1
1
|
from tequila import TequilaException
|
2
2
|
from tequila.hamiltonian import QubitHamiltonian, PauliString
|
3
|
-
from tequila.grouping.binary_utils import
|
3
|
+
from tequila.grouping.binary_utils import (
|
4
|
+
get_lagrangian_subspace,
|
5
|
+
binary_symplectic_inner_product,
|
6
|
+
binary_solve,
|
7
|
+
binary_phase,
|
8
|
+
gen_single_qubit_term,
|
9
|
+
largest_first,
|
10
|
+
recursive_largest_first,
|
11
|
+
sorted_insertion_grouping,
|
12
|
+
)
|
4
13
|
from tequila.grouping.overlapping_methods import OverlappingGroups, OverlappingAuxiliary, get_opt_sample_size
|
5
14
|
import numpy as np
|
6
15
|
import tequila as tq
|
7
16
|
import numbers
|
8
17
|
from copy import deepcopy
|
9
18
|
|
19
|
+
|
10
20
|
class BinaryHamiltonian:
|
11
21
|
def __init__(self, binary_terms):
|
12
|
-
|
22
|
+
"""
|
13
23
|
Initiate from a list of Binary Pauli Strings
|
14
|
-
|
24
|
+
"""
|
15
25
|
self.binary_terms = binary_terms
|
16
26
|
|
17
27
|
self.n_qubit = binary_terms[0].get_n_qubit()
|
@@ -20,21 +30,18 @@ class BinaryHamiltonian:
|
|
20
30
|
|
21
31
|
@classmethod
|
22
32
|
def init_from_qubit_hamiltonian(cls, hamiltonian: QubitHamiltonian, n_qubits=None, ignore_const=False):
|
23
|
-
if ignore_const:
|
33
|
+
if ignore_const: # Ignore constant term.
|
24
34
|
Hof = hamiltonian.to_openfermion()
|
25
35
|
if () in Hof.terms:
|
26
36
|
del Hof.terms[()]
|
27
37
|
hamiltonian = QubitHamiltonian.from_openfermion(Hof)
|
28
38
|
if n_qubits is None:
|
29
|
-
n_qubits = max(hamiltonian.qubits)+1
|
39
|
+
n_qubits = max(hamiltonian.qubits) + 1
|
30
40
|
binary_terms = [
|
31
|
-
BinaryPauliString(
|
32
|
-
p.binary(n_qubits).binary,
|
33
|
-
p.binary(n_qubits).coeff) for p in hamiltonian.paulistrings
|
41
|
+
BinaryPauliString(p.binary(n_qubits).binary, p.binary(n_qubits).coeff) for p in hamiltonian.paulistrings
|
34
42
|
]
|
35
43
|
return BinaryHamiltonian(binary_terms)
|
36
44
|
|
37
|
-
|
38
45
|
def get_binary(self):
|
39
46
|
matrix = [p.get_binary() for p in self.binary_terms]
|
40
47
|
return matrix
|
@@ -44,29 +51,28 @@ class BinaryHamiltonian:
|
|
44
51
|
return coeff
|
45
52
|
|
46
53
|
def single_qubit_form(self):
|
47
|
-
|
54
|
+
"""
|
48
55
|
Returns
|
49
56
|
----------
|
50
57
|
hamiltonian : BinaryHamiltonian
|
51
58
|
The original hamiltonian in qubit-wise commuting form
|
52
|
-
lagrangian_basis : list of BinaryPauliStrings
|
59
|
+
lagrangian_basis : list of BinaryPauliStrings
|
53
60
|
Represents the basis of original Hamiltonian
|
54
|
-
new_basis : list of BinaryPauliStrings
|
61
|
+
new_basis : list of BinaryPauliStrings
|
55
62
|
Represents the basis of new Hamiltonian
|
56
|
-
|
63
|
+
"""
|
57
64
|
lagrangian_basis = get_lagrangian_subspace(self.get_binary())
|
58
65
|
new_basis = self.get_single_qubit_basis(lagrangian_basis)
|
59
66
|
lagrangian_basis = [BinaryPauliString(p) for p in lagrangian_basis]
|
60
67
|
new_basis = [BinaryPauliString(p) for p in new_basis]
|
61
|
-
qubit_wise_hamiltonian = self.basis_transform(lagrangian_basis,
|
62
|
-
new_basis)
|
68
|
+
qubit_wise_hamiltonian = self.basis_transform(lagrangian_basis, new_basis)
|
63
69
|
return qubit_wise_hamiltonian, lagrangian_basis, new_basis
|
64
70
|
|
65
71
|
def z_form(self):
|
66
|
-
|
72
|
+
"""
|
67
73
|
Parameters
|
68
74
|
----------
|
69
|
-
self : BinaryHamiltonian
|
75
|
+
self : BinaryHamiltonian
|
70
76
|
a qubit-wise commuting hamiltonian
|
71
77
|
|
72
78
|
Modifies
|
@@ -78,34 +84,34 @@ class BinaryHamiltonian:
|
|
78
84
|
----------
|
79
85
|
U : QCircuit
|
80
86
|
the single qubit transformation that rotates each term to z
|
81
|
-
|
87
|
+
"""
|
82
88
|
U = tq.QCircuit()
|
83
89
|
non_z = {}
|
84
90
|
for p in self.binary_terms:
|
85
91
|
for qub in range(self.n_qubit):
|
86
|
-
z_data = {qub:
|
92
|
+
z_data = {qub: "z"}
|
87
93
|
if p.has_x(qub):
|
88
94
|
p.set_z(qub)
|
89
|
-
non_z[qub] =
|
95
|
+
non_z[qub] = "x"
|
90
96
|
elif p.has_y(qub):
|
91
97
|
p.set_z(qub)
|
92
|
-
non_z[qub] =
|
93
|
-
|
98
|
+
non_z[qub] = "y"
|
99
|
+
|
94
100
|
for qub, term in non_z.items():
|
95
101
|
xy_data = {qub: term}
|
96
|
-
z_data = {qub:
|
97
|
-
U += tq.gates.ExpPauli(angle
|
98
|
-
U += tq.gates.ExpPauli(angle
|
99
|
-
U += tq.gates.ExpPauli(angle
|
102
|
+
z_data = {qub: "z"}
|
103
|
+
U += tq.gates.ExpPauli(angle=-tq.numpy.pi / 2, paulistring=tq.PauliString(z_data))
|
104
|
+
U += tq.gates.ExpPauli(angle=-tq.numpy.pi / 2, paulistring=tq.PauliString(xy_data))
|
105
|
+
U += tq.gates.ExpPauli(angle=-tq.numpy.pi / 2, paulistring=tq.PauliString(z_data))
|
100
106
|
|
101
107
|
return U
|
102
108
|
|
103
|
-
def get_qubit_wise(self, binary
|
104
|
-
|
109
|
+
def get_qubit_wise(self, binary=False):
|
110
|
+
"""
|
105
111
|
Return the qubit-wise form of the current binary hamiltonian.
|
106
|
-
And the unitary transformation U,
|
112
|
+
And the unitary transformation U,
|
107
113
|
where U = prod_i (1/2) ** (1/2) * (lagrangian_basis[i] + new_basis[i])
|
108
|
-
|
114
|
+
|
109
115
|
Parameters
|
110
116
|
----------
|
111
117
|
binary : determines whether the returned qwc hamiltonian is binary or qubit hamiltonian
|
@@ -117,14 +123,13 @@ class BinaryHamiltonian:
|
|
117
123
|
The original hamiltonian in qubit-wise commuting form
|
118
124
|
U : QCircuit
|
119
125
|
The unitary circuit that transforms the original hamiltonian into qwc form
|
120
|
-
|
126
|
+
"""
|
121
127
|
if not self.is_commuting():
|
122
|
-
raise TequilaException(
|
123
|
-
'Not all terms in the Hamiltonians are commuting.')
|
128
|
+
raise TequilaException("Not all terms in the Hamiltonians are commuting.")
|
124
129
|
if self.is_qubit_wise_commuting():
|
125
130
|
qubit_wise_hamiltonian = self
|
126
131
|
qwc_u = tq.QCircuit()
|
127
|
-
else:
|
132
|
+
else:
|
128
133
|
qubit_wise_hamiltonian, lagrangian_basis, new_basis = self.single_qubit_form()
|
129
134
|
|
130
135
|
# Constructing the unitary that rotates into qubit-wise parts
|
@@ -132,10 +137,10 @@ class BinaryHamiltonian:
|
|
132
137
|
for i in range(len(lagrangian_basis)):
|
133
138
|
sigma = lagrangian_basis[i].to_pauli_strings()
|
134
139
|
tau = new_basis[i].to_pauli_strings()
|
135
|
-
qwc_u += tq.gates.ExpPauli(angle=-tq.numpy.pi/2, paulistring=sigma)
|
136
|
-
qwc_u += tq.gates.ExpPauli(angle=-tq.numpy.pi/2, paulistring=tau)
|
137
|
-
qwc_u += tq.gates.ExpPauli(angle=-tq.numpy.pi/2, paulistring=sigma)
|
138
|
-
|
140
|
+
qwc_u += tq.gates.ExpPauli(angle=-tq.numpy.pi / 2, paulistring=sigma)
|
141
|
+
qwc_u += tq.gates.ExpPauli(angle=-tq.numpy.pi / 2, paulistring=tau)
|
142
|
+
qwc_u += tq.gates.ExpPauli(angle=-tq.numpy.pi / 2, paulistring=sigma)
|
143
|
+
|
139
144
|
single_qub_u = qubit_wise_hamiltonian.z_form()
|
140
145
|
# Return the basis in terms of Binary Hamiltonian
|
141
146
|
if binary:
|
@@ -144,10 +149,10 @@ class BinaryHamiltonian:
|
|
144
149
|
return qubit_wise_hamiltonian.to_qubit_hamiltonian(), qwc_u + single_qub_u
|
145
150
|
|
146
151
|
def get_single_qubit_basis(self, lagrangian_basis):
|
147
|
-
|
152
|
+
"""
|
148
153
|
Find the single_qubit_basis such that single_qubit_basis[i] anti-commutes
|
149
|
-
with lagrangian_basis[i], and commute for all other cases.
|
150
|
-
|
154
|
+
with lagrangian_basis[i], and commute for all other cases.
|
155
|
+
"""
|
151
156
|
dim = len(lagrangian_basis)
|
152
157
|
|
153
158
|
# Free Qubits
|
@@ -155,46 +160,42 @@ class BinaryHamiltonian:
|
|
155
160
|
pair = []
|
156
161
|
|
157
162
|
for i in range(dim):
|
158
|
-
cur_pair = self.find_single_qubit_pair(lagrangian_basis[i],
|
159
|
-
free_qub)
|
163
|
+
cur_pair = self.find_single_qubit_pair(lagrangian_basis[i], free_qub)
|
160
164
|
for j in range(dim):
|
161
165
|
if i != j:
|
162
|
-
if binary_symplectic_inner_product(
|
163
|
-
|
164
|
-
lagrangian_basis[j] = (lagrangian_basis[i] +
|
165
|
-
lagrangian_basis[j]) % 2
|
166
|
+
if binary_symplectic_inner_product(cur_pair, lagrangian_basis[j] == 1):
|
167
|
+
lagrangian_basis[j] = (lagrangian_basis[i] + lagrangian_basis[j]) % 2
|
166
168
|
pair.append(cur_pair)
|
167
169
|
return pair
|
168
170
|
|
169
171
|
def find_single_qubit_pair(self, cur_basis, free_qub):
|
170
|
-
|
171
|
-
Find the single qubit pair that anti-commute with cur_basis such that the single qubit is in free_qub
|
172
|
+
"""
|
173
|
+
Find the single qubit pair that anti-commute with cur_basis such that the single qubit is in free_qub
|
172
174
|
|
173
175
|
Return: Binary vectors representing the single qubit pair
|
174
176
|
Modify: Pops the qubit used from free_qub
|
175
|
-
|
177
|
+
"""
|
176
178
|
dim = len(cur_basis) // 2
|
177
179
|
for idx, qub in enumerate(free_qub):
|
178
180
|
for term in range(3):
|
179
181
|
pair = gen_single_qubit_term(dim, qub, term)
|
180
182
|
# if anticommute
|
181
|
-
if
|
183
|
+
if binary_symplectic_inner_product(pair, cur_basis) == 1:
|
182
184
|
free_qub.pop(idx)
|
183
185
|
return pair
|
184
186
|
|
185
187
|
def basis_transform(self, old, new):
|
186
|
-
|
188
|
+
"""
|
187
189
|
Transforms the given hamiltonian from the old basis to the new basis.
|
188
|
-
|
190
|
+
|
189
191
|
Return: The transformed Binary hamiltonian
|
190
|
-
|
191
|
-
return BinaryHamiltonian(
|
192
|
-
[p.basis_transform(old, new) for p in self.binary_terms])
|
192
|
+
"""
|
193
|
+
return BinaryHamiltonian([p.basis_transform(old, new) for p in self.binary_terms])
|
193
194
|
|
194
195
|
def is_commuting(self):
|
195
|
-
|
196
|
+
"""
|
196
197
|
Return whether all terms in the Hamiltonian are commuting
|
197
|
-
|
198
|
+
"""
|
198
199
|
for i in range(self.n_term):
|
199
200
|
for j in range(i + 1, self.n_term):
|
200
201
|
if not self.binary_terms[i].commute(self.binary_terms[j]):
|
@@ -202,15 +203,15 @@ class BinaryHamiltonian:
|
|
202
203
|
return True
|
203
204
|
|
204
205
|
def is_qubit_wise_commuting(self):
|
205
|
-
|
206
|
-
Return whether all terms in the Hamiltonian are qubit-wise commuting
|
207
|
-
|
206
|
+
"""
|
207
|
+
Return whether all terms in the Hamiltonian are qubit-wise commuting
|
208
|
+
"""
|
208
209
|
# Keep a dictionary of qubit-wise term found
|
209
210
|
qubit_term = {}
|
210
211
|
for i in range(self.n_term):
|
211
212
|
for qub in range(self.n_qubit):
|
212
213
|
cur_binary_term = self.binary_terms[i].get_binary()
|
213
|
-
cur_qub_term = (cur_binary_term[qub], cur_binary_term[qub+self.n_qubit])
|
214
|
+
cur_qub_term = (cur_binary_term[qub], cur_binary_term[qub + self.n_qubit])
|
214
215
|
if cur_qub_term != (0, 0):
|
215
216
|
if qub not in qubit_term:
|
216
217
|
qubit_term[qub] = cur_qub_term
|
@@ -218,13 +219,11 @@ class BinaryHamiltonian:
|
|
218
219
|
if not qubit_term[qub] == cur_qub_term:
|
219
220
|
return False
|
220
221
|
return True
|
221
|
-
|
222
222
|
|
223
223
|
def to_qubit_hamiltonian(self):
|
224
224
|
qub_ham = QubitHamiltonian()
|
225
225
|
for p in self.binary_terms:
|
226
|
-
qub_ham += QubitHamiltonian.from_paulistrings(
|
227
|
-
p.to_pauli_strings())
|
226
|
+
qub_ham += QubitHamiltonian.from_paulistrings(p.to_pauli_strings())
|
228
227
|
return qub_ham
|
229
228
|
|
230
229
|
def anti_commutativity_matrix(self):
|
@@ -234,7 +233,7 @@ class BinaryHamiltonian:
|
|
234
233
|
"""
|
235
234
|
n = self.n_qubit
|
236
235
|
matrix = np.array(self.get_binary())
|
237
|
-
gram = np.block([[np.zeros((n,n)), np.eye(n)], [np.eye(n), np.zeros((n,n))]])
|
236
|
+
gram = np.block([[np.zeros((n, n)), np.eye(n)], [np.eye(n), np.zeros((n, n))]])
|
238
237
|
return matrix @ gram @ matrix.T % 2
|
239
238
|
|
240
239
|
def commuting_groups(self, options=None):
|
@@ -246,11 +245,11 @@ class BinaryHamiltonian:
|
|
246
245
|
Parameters
|
247
246
|
----------
|
248
247
|
options: dictionary: Dictionary containing user-defined parameters:
|
249
|
-
key: method, val: 'lf' [largest first J. Chem. Phys. 152, 124114 (2020)], 'rlf' [recursive largest first J. Chem. Phys. 152, 124114 (2020)],
|
248
|
+
key: method, val: 'lf' [largest first J. Chem. Phys. 152, 124114 (2020)], 'rlf' [recursive largest first J. Chem. Phys. 152, 124114 (2020)],
|
250
249
|
'si' [sorted insertion Quantum 5, 385 (2021)], 'ics' [iterative coefficient splitting npj Quantum Inf. 9, 14 (2023)]
|
251
250
|
key: condition, val: 'fc' (fully commuting Pauli products are measured together)
|
252
251
|
'qwc' (qubit-wise commuting Pauli products are measured together)
|
253
|
-
key: cov_dict, val: Dictionary containing {(binary_tuple of pw1, binary_tuple of pw2) : Cov (pw1, pw2)}.
|
252
|
+
key: cov_dict, val: Dictionary containing {(binary_tuple of pw1, binary_tuple of pw2) : Cov (pw1, pw2)}.
|
254
253
|
Only covariances for [pw1,pw2]=0 are necessary. This dictionary is necessary for ics.
|
255
254
|
For other methods, if cov_dict is given, the optimal allocation of samples will be returned.
|
256
255
|
key: n_iter, val: integer number of iterations in ics.
|
@@ -259,18 +258,21 @@ class BinaryHamiltonian:
|
|
259
258
|
----------
|
260
259
|
List of BinaryHamiltonian's
|
261
260
|
"""
|
261
|
+
|
262
262
|
def process_options(options):
|
263
|
-
method =
|
264
|
-
condition =
|
265
|
-
sample_suggestion = False
|
266
|
-
overlap_aux = None
|
263
|
+
method = "rlf" # Method used for Hamiltonian partitioning.
|
264
|
+
condition = "fc" # Commutativitiy condition within a group; either fully commuting (fc) or qubit-wise commuting (qwc).
|
265
|
+
sample_suggestion = False # Whether to return suggested ratio of samples between groups.
|
266
|
+
overlap_aux = None # Help variable for overlapping methods.
|
267
267
|
if options is not None:
|
268
|
-
if "method" in options:
|
269
|
-
|
268
|
+
if "method" in options:
|
269
|
+
method = options["method"]
|
270
|
+
if "condition" in options:
|
271
|
+
condition = options["condition"]
|
270
272
|
|
271
273
|
# If cov_dict is given in options, use the user-defined covariance dictionary.
|
272
|
-
if "cov_dict" in options:
|
273
|
-
sample_suggestion = True
|
274
|
+
if "cov_dict" in options:
|
275
|
+
sample_suggestion = True # Suggested sample size can be given whenever covariances are present.
|
274
276
|
if "n_iter" in options:
|
275
277
|
overlap_aux = OverlappingAuxiliary(options["cov_dict"], options["n_iter"])
|
276
278
|
else:
|
@@ -279,17 +281,20 @@ class BinaryHamiltonian:
|
|
279
281
|
# (TODO) Compute default HF/CISD covariances if cov_dict is not given.
|
280
282
|
overlap_aux = None
|
281
283
|
return method, condition, overlap_aux, sample_suggestion
|
282
|
-
|
284
|
+
|
283
285
|
def method_class(method, condition):
|
284
286
|
"""
|
285
287
|
Return the class that the method belongs to: One from Minimum clique cover (mcc)
|
286
|
-
and Greedy grouping algorithms.
|
288
|
+
and Greedy grouping algorithms.
|
287
289
|
"""
|
288
|
-
if
|
289
|
-
mc =
|
290
|
-
if condition != "fc":
|
291
|
-
|
292
|
-
|
290
|
+
if method == "lf" or method == "rlf":
|
291
|
+
mc = "mcc"
|
292
|
+
if condition != "fc":
|
293
|
+
raise TequilaException(
|
294
|
+
f"Combination of options={{method:{method},condition:{condition}}} is not valid. E.g., lf and rlf can only return fully commuting fragments, i.e., condition=fc is necessary."
|
295
|
+
)
|
296
|
+
elif method == "si" or method == "ics":
|
297
|
+
mc = "greedy"
|
293
298
|
else:
|
294
299
|
raise TequilaException(f"There is not options={{method:{method}}}")
|
295
300
|
return mc
|
@@ -298,33 +303,38 @@ class BinaryHamiltonian:
|
|
298
303
|
n = self.n_term
|
299
304
|
|
300
305
|
method, condition, overlap_aux, sample_suggestion = process_options(options)
|
301
|
-
if method_class(method, condition) ==
|
306
|
+
if method_class(method, condition) == "mcc":
|
302
307
|
cg = self.anti_commutativity_matrix()
|
303
|
-
if method ==
|
308
|
+
if method == "lf":
|
304
309
|
colors = largest_first(terms, n, cg)
|
305
|
-
elif method ==
|
310
|
+
elif method == "rlf":
|
306
311
|
colors = recursive_largest_first(terms, n, cg)
|
307
312
|
groups = [value for key, value in colors.items()]
|
308
313
|
result = [BinaryHamiltonian(value) for key, value in colors.items()]
|
309
|
-
elif method_class(method, condition) ==
|
310
|
-
if method ==
|
311
|
-
|
312
|
-
|
314
|
+
elif method_class(method, condition) == "greedy":
|
315
|
+
if method == "si":
|
316
|
+
groups = sorted_insertion_grouping(terms, condition)
|
317
|
+
if method == "ics":
|
318
|
+
if overlap_aux is None:
|
319
|
+
raise TequilaException(
|
320
|
+
"Overlapping SI grouping requires a dictionary of covariances, call with options={cov_dict:X}, where X is the dictionary."
|
321
|
+
)
|
313
322
|
o_groups = OverlappingGroups.init_from_binary_terms(terms, condition)
|
314
323
|
groups = o_groups.optimal_overlapping_groups(overlap_aux)
|
315
324
|
result = [BinaryHamiltonian(group) for group in groups]
|
316
|
-
|
325
|
+
|
317
326
|
if sample_suggestion:
|
318
327
|
suggested_sample_size = get_opt_sample_size(groups, options["cov_dict"])
|
319
328
|
else:
|
320
329
|
suggested_sample_size = [None] * len(groups)
|
321
330
|
return result, suggested_sample_size
|
322
331
|
|
332
|
+
|
323
333
|
class BinaryPauliString:
|
324
334
|
def __init__(self, binary_vector=np.array([0, 0]), coeff=1.0):
|
325
|
-
|
326
|
-
Stores a list of binary vectors and a list of corresponding coefficients.
|
327
|
-
|
335
|
+
"""
|
336
|
+
Stores a list of binary vectors and a list of corresponding coefficients.
|
337
|
+
"""
|
328
338
|
self.binary = np.array(binary_vector)
|
329
339
|
self.coeff = coeff
|
330
340
|
self.n_qubit = len(binary_vector) // 2
|
@@ -332,85 +342,78 @@ class BinaryPauliString:
|
|
332
342
|
self.is_coeff()
|
333
343
|
|
334
344
|
def __eq__(self, other):
|
335
|
-
|
336
|
-
Check if two BinaryPauliStrings are equivalent.
|
337
|
-
The size of small is chosen arbitrarily.
|
338
|
-
|
345
|
+
"""
|
346
|
+
Check if two BinaryPauliStrings are equivalent.
|
347
|
+
The size of small is chosen arbitrarily.
|
348
|
+
"""
|
339
349
|
small = 1e-10
|
340
350
|
return all(self.binary == other.binary) and (np.abs(self.coeff - other.coeff) <= small)
|
341
351
|
|
342
352
|
def binary_tuple(self):
|
343
|
-
|
353
|
+
"""
|
344
354
|
Return binary vector as a tuple. Useful for cov_dict (see overlapping_methods).
|
345
|
-
|
355
|
+
"""
|
346
356
|
return tuple(self.binary)
|
347
357
|
|
348
358
|
def is_binary(self):
|
349
359
|
if not isinstance(self.binary, np.ndarray):
|
350
360
|
raise TequilaException(
|
351
|
-
|
352
|
-
|
361
|
+
"Unknown representation of binary vector. Got " + str(self.binary) + " with type " + type(self.binary)
|
362
|
+
)
|
353
363
|
if not all([x == 1 or x == 0 for x in self.binary]):
|
354
|
-
raise TequilaException(
|
355
|
-
'Not all number in the binary vector is 0 or 1. Got ' +
|
356
|
-
str(self.binary))
|
364
|
+
raise TequilaException("Not all number in the binary vector is 0 or 1. Got " + str(self.binary))
|
357
365
|
|
358
366
|
def is_coeff(self):
|
359
367
|
if not isinstance(self.coeff, numbers.Number):
|
360
|
-
raise TequilaException(
|
361
|
-
str(self.coeff))
|
368
|
+
raise TequilaException("Unknown coefficients. Got " + str(self.coeff))
|
362
369
|
|
363
370
|
def qubit_wise_commute(self, other):
|
364
|
-
|
365
|
-
Determine whether the corresponding pauli-strings of
|
366
|
-
the two binary vectors are qubit-wise commuting.
|
367
|
-
|
368
|
-
qubit_term = {}
|
371
|
+
"""
|
372
|
+
Determine whether the corresponding pauli-strings of
|
373
|
+
the two binary vectors are qubit-wise commuting.
|
374
|
+
"""
|
375
|
+
qubit_term = {} # Dictionary of qubit terms in self.
|
369
376
|
for qub in range(self.n_qubit):
|
370
|
-
cur_qub_term = (self.binary[qub], self.binary[qub + self.n_qubit])
|
377
|
+
cur_qub_term = (self.binary[qub], self.binary[qub + self.n_qubit])
|
371
378
|
if cur_qub_term != (0, 0):
|
372
379
|
qubit_term[qub] = cur_qub_term
|
373
380
|
|
374
381
|
for qub in range(other.n_qubit):
|
375
|
-
cur_qub_term = (other.binary[qub], other.binary[qub + self.n_qubit])
|
382
|
+
cur_qub_term = (other.binary[qub], other.binary[qub + self.n_qubit])
|
376
383
|
if cur_qub_term != (0, 0):
|
377
384
|
if qub in qubit_term:
|
378
|
-
if qubit_term[qub] != cur_qub_term:
|
385
|
+
if qubit_term[qub] != cur_qub_term:
|
386
|
+
return False
|
379
387
|
return True
|
380
388
|
|
381
389
|
def commute(self, other):
|
382
|
-
|
383
|
-
Determine whether the corresponding pauli-strings of
|
384
|
-
the two binary vectors commute.
|
385
|
-
|
386
|
-
inner_product = binary_symplectic_inner_product(
|
387
|
-
self.binary, other.binary)
|
390
|
+
"""
|
391
|
+
Determine whether the corresponding pauli-strings of
|
392
|
+
the two binary vectors commute.
|
393
|
+
"""
|
394
|
+
inner_product = binary_symplectic_inner_product(self.binary, other.binary)
|
388
395
|
|
389
396
|
if inner_product == 0:
|
390
397
|
return True
|
391
398
|
elif inner_product == 1:
|
392
399
|
return False
|
393
400
|
else:
|
394
|
-
raise TequilaException(
|
395
|
-
str(inner_product))
|
401
|
+
raise TequilaException("Computed unexpected inner product. Got " + str(inner_product))
|
396
402
|
|
397
403
|
def basis_transform(self, old, new):
|
398
|
-
|
404
|
+
"""
|
399
405
|
Transform the pauli string from old to new binary basis.
|
400
406
|
|
401
407
|
Return: Pauli string in the new basis.
|
402
|
-
|
403
|
-
old_basis_coeff = binary_solve([p.get_binary() for p in old],
|
404
|
-
self.binary)
|
408
|
+
"""
|
409
|
+
old_basis_coeff = binary_solve([p.get_binary() for p in old], self.binary)
|
405
410
|
original_pauli_vec = np.zeros(self.n_qubit * 2)
|
406
411
|
new_pauli_vec = np.zeros(self.n_qubit * 2)
|
407
412
|
phase = 1
|
408
413
|
for i, i_coeff in enumerate(old_basis_coeff):
|
409
414
|
if i_coeff == 1:
|
410
|
-
phase *= binary_phase(original_pauli_vec, old[i].get_binary(),
|
411
|
-
|
412
|
-
original_pauli_vec = (original_pauli_vec +
|
413
|
-
old[i].get_binary()) % 2
|
415
|
+
phase *= binary_phase(original_pauli_vec, old[i].get_binary(), self.n_qubit)
|
416
|
+
original_pauli_vec = (original_pauli_vec + old[i].get_binary()) % 2
|
414
417
|
new_pauli_vec = (new_pauli_vec + new[i].get_binary()) % 2
|
415
418
|
|
416
419
|
new_pauli_str = BinaryPauliString(new_pauli_vec)
|
@@ -431,37 +434,37 @@ class BinaryPauliString:
|
|
431
434
|
|
432
435
|
def has_z(self, i):
|
433
436
|
return self.binary[i] == 0 and self.binary[i + self.n_qubit] == 1
|
434
|
-
|
437
|
+
|
435
438
|
def set_x(self, i):
|
436
|
-
|
439
|
+
"""
|
437
440
|
Set the ith qubit to having x
|
438
|
-
|
441
|
+
"""
|
439
442
|
self.binary[i] = 1
|
440
443
|
self.binary[i + self.n_qubit] = 0
|
441
|
-
|
444
|
+
|
442
445
|
def set_y(self, i):
|
443
|
-
|
446
|
+
"""
|
444
447
|
Set the ith qubit to having y
|
445
|
-
|
448
|
+
"""
|
446
449
|
self.binary[i] = 1
|
447
450
|
self.binary[i + self.n_qubit] = 1
|
448
451
|
|
449
452
|
def set_z(self, i):
|
450
|
-
|
453
|
+
"""
|
451
454
|
Set the ith qubit to having z
|
452
|
-
|
455
|
+
"""
|
453
456
|
self.binary[i] = 0
|
454
457
|
self.binary[i + self.n_qubit] = 1
|
455
|
-
|
458
|
+
|
456
459
|
def to_pauli_strings(self):
|
457
460
|
data = {}
|
458
461
|
for i in range(self.n_qubit):
|
459
462
|
if self.has_x(i):
|
460
|
-
data[i] =
|
463
|
+
data[i] = "X"
|
461
464
|
elif self.has_y(i):
|
462
|
-
data[i] =
|
465
|
+
data[i] = "Y"
|
463
466
|
elif self.has_z(i):
|
464
|
-
data[i] =
|
467
|
+
data[i] = "Z"
|
465
468
|
return PauliString(data, self.coeff)
|
466
469
|
|
467
470
|
def get_coeff(self):
|
@@ -477,10 +480,9 @@ class BinaryPauliString:
|
|
477
480
|
return self.n_qubit
|
478
481
|
|
479
482
|
def term_w_coeff(self, new_coeff):
|
480
|
-
|
483
|
+
"""
|
481
484
|
Return BinaryPauliString with a new coefficient.
|
482
|
-
|
485
|
+
"""
|
483
486
|
new_term = deepcopy(self)
|
484
487
|
new_term.set_coeff(new_coeff)
|
485
488
|
return new_term
|
486
|
-
|