iqm-benchmarks 2.43__py3-none-any.whl → 2.45__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.
Potentially problematic release.
This version of iqm-benchmarks might be problematic. Click here for more details.
- iqm/benchmarks/benchmark_definition.py +1 -0
- iqm/benchmarks/compressive_gst/compressive_gst.py +100 -46
- iqm/benchmarks/compressive_gst/gst_analysis.py +480 -381
- iqm/benchmarks/entanglement/ghz.py +5 -5
- iqm/benchmarks/entanglement/graph_states.py +21 -21
- iqm/benchmarks/optimization/qscore.py +2 -2
- iqm/benchmarks/quantum_volume/clops.py +2 -3
- iqm/benchmarks/randomized_benchmarking/eplg/eplg.py +23 -24
- iqm/benchmarks/utils.py +72 -8
- iqm/benchmarks/utils_plots.py +309 -65
- {iqm_benchmarks-2.43.dist-info → iqm_benchmarks-2.45.dist-info}/METADATA +2 -2
- {iqm_benchmarks-2.43.dist-info → iqm_benchmarks-2.45.dist-info}/RECORD +22 -22
- mGST/additional_fns.py +35 -20
- mGST/algorithm.py +73 -57
- mGST/low_level_jit.py +122 -56
- mGST/optimization.py +12 -6
- mGST/qiskit_interface.py +64 -87
- mGST/reporting/figure_gen.py +390 -57
- mGST/reporting/reporting.py +209 -11
- {iqm_benchmarks-2.43.dist-info → iqm_benchmarks-2.45.dist-info}/WHEEL +0 -0
- {iqm_benchmarks-2.43.dist-info → iqm_benchmarks-2.45.dist-info}/licenses/LICENSE +0 -0
- {iqm_benchmarks-2.43.dist-info → iqm_benchmarks-2.45.dist-info}/top_level.txt +0 -0
mGST/optimization.py
CHANGED
|
@@ -95,7 +95,7 @@ def update_K_geodesic(K, H, a):
|
|
|
95
95
|
return K_new.reshape(d, rK, pdim, pdim)
|
|
96
96
|
|
|
97
97
|
|
|
98
|
-
def lineobjf_isom_geodesic(a, H, K, E, rho, J, y):
|
|
98
|
+
def lineobjf_isom_geodesic(a, H, K, E, rho, J, y, mle):
|
|
99
99
|
"""Compute objective function at position on geodesic
|
|
100
100
|
|
|
101
101
|
Parameters
|
|
@@ -115,6 +115,8 @@ def lineobjf_isom_geodesic(a, H, K, E, rho, J, y):
|
|
|
115
115
|
y : numpy array
|
|
116
116
|
2D array of measurement outcomes for sequences in J;
|
|
117
117
|
The columns contain the outcome probabilities for different povm elements
|
|
118
|
+
mle : bool
|
|
119
|
+
If True, the log-likelihood objective function is used, otherwise the least squares objective function is used
|
|
118
120
|
|
|
119
121
|
Returns
|
|
120
122
|
-------
|
|
@@ -126,7 +128,7 @@ def lineobjf_isom_geodesic(a, H, K, E, rho, J, y):
|
|
|
126
128
|
r = pdim**2
|
|
127
129
|
K_test = update_K_geodesic(K, H, a)
|
|
128
130
|
X_test = np.einsum("ijkl,ijnm -> iknlm", K_test, K_test.conj()).reshape((d, r, r))
|
|
129
|
-
return objf(X_test, E, rho, J, y)
|
|
131
|
+
return objf(X_test, E, rho, J, y, mle=mle)
|
|
130
132
|
|
|
131
133
|
|
|
132
134
|
def update_A_geodesic(A, H, a):
|
|
@@ -196,7 +198,7 @@ def update_B_geodesic(B, H, a):
|
|
|
196
198
|
return B_temp.reshape(pdim, pdim)
|
|
197
199
|
|
|
198
200
|
|
|
199
|
-
def lineobjf_A_geodesic(a, H, X, A, rho, J, y):
|
|
201
|
+
def lineobjf_A_geodesic(a, H, X, A, rho, J, y, mle=False):
|
|
200
202
|
"""Compute objective function at position on geodesic for POVM parametrization
|
|
201
203
|
|
|
202
204
|
Parameters
|
|
@@ -216,6 +218,8 @@ def lineobjf_A_geodesic(a, H, X, A, rho, J, y):
|
|
|
216
218
|
y : numpy array
|
|
217
219
|
2D array of measurement outcomes for sequences in J;
|
|
218
220
|
The columns contain the outcome probabilities for different povm elements
|
|
221
|
+
mle : bool
|
|
222
|
+
If True, the log-likelihood objective function is used, otherwise the least squares objective function is used
|
|
219
223
|
|
|
220
224
|
Returns
|
|
221
225
|
-------
|
|
@@ -225,10 +229,10 @@ def lineobjf_A_geodesic(a, H, X, A, rho, J, y):
|
|
|
225
229
|
n_povm = A.shape[0]
|
|
226
230
|
A_test = update_A_geodesic(A, H, a)
|
|
227
231
|
E_test = np.array([(A_test[i].T.conj() @ A_test[i]).reshape(-1) for i in range(n_povm)])
|
|
228
|
-
return objf(X, E_test, rho, J, y)
|
|
232
|
+
return objf(X, E_test, rho, J, y, mle=mle)
|
|
229
233
|
|
|
230
234
|
|
|
231
|
-
def lineobjf_B_geodesic(a, H, X, E, B, J, y):
|
|
235
|
+
def lineobjf_B_geodesic(a, H, X, E, B, J, y, mle=False):
|
|
232
236
|
"""Compute objective function at position on geodesic for the initial state parametrization
|
|
233
237
|
|
|
234
238
|
Parameters
|
|
@@ -248,6 +252,8 @@ def lineobjf_B_geodesic(a, H, X, E, B, J, y):
|
|
|
248
252
|
y : numpy array
|
|
249
253
|
2D array of measurement outcomes for sequences in J;
|
|
250
254
|
The columns contain the outcome probabilities for different povm elements
|
|
255
|
+
mle : bool
|
|
256
|
+
If True, the log-likelihood objective function is used, otherwise the least squares objective function is used
|
|
251
257
|
|
|
252
258
|
Returns
|
|
253
259
|
-------
|
|
@@ -256,7 +262,7 @@ def lineobjf_B_geodesic(a, H, X, E, B, J, y):
|
|
|
256
262
|
"""
|
|
257
263
|
B_test = update_B_geodesic(B, H, a)
|
|
258
264
|
rho_test = (B_test @ B_test.T.conj()).reshape(-1)
|
|
259
|
-
return objf(X, E, rho_test, J, y)
|
|
265
|
+
return objf(X, E, rho_test, J, y, mle=mle)
|
|
260
266
|
|
|
261
267
|
|
|
262
268
|
def lineobjf_A_B(a, v, delta_v, X, C, y, J, argument):
|
mGST/qiskit_interface.py
CHANGED
|
@@ -9,9 +9,7 @@ from qiskit.circuit.library import IGate
|
|
|
9
9
|
from qiskit.quantum_info import Operator
|
|
10
10
|
|
|
11
11
|
from iqm.qiskit_iqm import IQMCircuit as QuantumCircuit
|
|
12
|
-
from mGST import
|
|
13
|
-
from mGST.compatibility import arrays_to_pygsti_model
|
|
14
|
-
from mGST.reporting.reporting import gauge_opt, quick_report
|
|
12
|
+
from mGST import low_level_jit
|
|
15
13
|
|
|
16
14
|
|
|
17
15
|
def qiskit_gate_to_operator(gate_set):
|
|
@@ -131,6 +129,69 @@ def get_qiskit_circuits(gate_sequences, gate_set, n_qubits, active_qubits):
|
|
|
131
129
|
return qiskit_circuits
|
|
132
130
|
|
|
133
131
|
|
|
132
|
+
def get_composed_qiskit_circuits(gate_sequences, gate_set, n_qubits, qubit_layouts, gate_context=None, parallel=False):
|
|
133
|
+
"""Turn GST sequences into Qiskit circuits, adding context gates if provided.
|
|
134
|
+
|
|
135
|
+
For each GST sequence, either a single circuit is created for all qubit layouts if `parallel=True`, or a separate circuit if
|
|
136
|
+
`parallel=False`.
|
|
137
|
+
|
|
138
|
+
Parameters
|
|
139
|
+
----------
|
|
140
|
+
gate_sequences : list of list of int
|
|
141
|
+
Sequences of gate indices to apply. Each integer corresponds to a gate in the gate_set.
|
|
142
|
+
gate_set : list
|
|
143
|
+
The gate set defined as a list of Qiskit quantum circuits.
|
|
144
|
+
n_qubits : int
|
|
145
|
+
Total number of qubits in the system.
|
|
146
|
+
qubit_layouts : list of list of int
|
|
147
|
+
Lists of qubits on which the GST experiment is run.
|
|
148
|
+
gate_context : QuantumCircuit or list of QuantumCircuit, optional
|
|
149
|
+
Optional context circuit(s) to apply during each gate on qubits that are not measured for GST.
|
|
150
|
+
parallel : bool, optional
|
|
151
|
+
Whether GST for all qubits layouts is done in parallel on the backend.
|
|
152
|
+
If True, applies gates to all qubit layouts in a single circuit.
|
|
153
|
+
If False, creates separate circuits for each layout. Default is False.
|
|
154
|
+
|
|
155
|
+
Returns
|
|
156
|
+
-------
|
|
157
|
+
list or list of list of QuantumCircuit
|
|
158
|
+
If parallel=True: A list of QuantumCircuits, one for each gate sequence.
|
|
159
|
+
If parallel=False: A list of lists of QuantumCircuits, where the outer list corresponds
|
|
160
|
+
to qubit layouts and the inner list corresponds to gate sequences.
|
|
161
|
+
"""
|
|
162
|
+
if gate_context is not None and not isinstance(gate_context, list):
|
|
163
|
+
gate_context = [gate_context] * len(gate_set)
|
|
164
|
+
qiskit_circuits = []
|
|
165
|
+
if parallel:
|
|
166
|
+
all_qubits = [q for qubits in qubit_layouts for q in qubits]
|
|
167
|
+
all_clbits = [i for i, _ in enumerate(all_qubits)]
|
|
168
|
+
for gate_sequence in gate_sequences:
|
|
169
|
+
qc = QuantumCircuit(n_qubits, len(all_clbits))
|
|
170
|
+
for gate_num in gate_sequence:
|
|
171
|
+
if gate_context is not None:
|
|
172
|
+
qc.compose(gate_context[gate_num], inplace=True)
|
|
173
|
+
for qubits in qubit_layouts:
|
|
174
|
+
qc.compose(gate_set[gate_num], qubits, inplace=True)
|
|
175
|
+
qc.barrier()
|
|
176
|
+
qc.measure(all_qubits, all_clbits)
|
|
177
|
+
qiskit_circuits.append(qc)
|
|
178
|
+
else:
|
|
179
|
+
for qubits in qubit_layouts:
|
|
180
|
+
clbits = [i for i, _ in enumerate(qubits)]
|
|
181
|
+
layout_circuits = []
|
|
182
|
+
for gate_sequence in gate_sequences:
|
|
183
|
+
qc = QuantumCircuit(n_qubits, len(clbits))
|
|
184
|
+
for gate_num in gate_sequence:
|
|
185
|
+
if gate_context is not None:
|
|
186
|
+
qc.compose(gate_context[gate_num], inplace=True)
|
|
187
|
+
qc.compose(gate_set[gate_num], qubits, inplace=True)
|
|
188
|
+
qc.barrier()
|
|
189
|
+
qc.measure(qubits, clbits)
|
|
190
|
+
layout_circuits.append(qc)
|
|
191
|
+
qiskit_circuits.append(layout_circuits)
|
|
192
|
+
return qiskit_circuits
|
|
193
|
+
|
|
194
|
+
|
|
134
195
|
def get_gate_sequence(sequence_number, sequence_length, gate_set_length):
|
|
135
196
|
"""Generate a set of random gate sequences.
|
|
136
197
|
|
|
@@ -196,87 +257,3 @@ def job_counts_to_mgst_format(active_qubits, n_povm, result_dict):
|
|
|
196
257
|
y.append(row / np.sum(row))
|
|
197
258
|
y = np.array(y).T
|
|
198
259
|
return y
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
def get_gate_estimation(gate_set, gate_sequences, sequence_results, shots, rK=4):
|
|
202
|
-
"""Estimate quantum gates using a modified Gate Set Tomography (mGST) algorithm.
|
|
203
|
-
|
|
204
|
-
This function simulates quantum gates, applies noise, and then uses the mGST algorithm
|
|
205
|
-
to estimate the gates. It calculates and prints the Mean Variation Error (MVE) of the
|
|
206
|
-
estimation.
|
|
207
|
-
|
|
208
|
-
Parameters
|
|
209
|
-
----------
|
|
210
|
-
gate_set : array_like
|
|
211
|
-
The set of quantum gates to be estimated.
|
|
212
|
-
gate_sequences : array_like
|
|
213
|
-
The sequences of gates applied in the quantum circuit.
|
|
214
|
-
sequence_results : array_like
|
|
215
|
-
The results of executing the gate sequences.
|
|
216
|
-
shots : int
|
|
217
|
-
The number of shots (repetitions) for each measurement.
|
|
218
|
-
"""
|
|
219
|
-
|
|
220
|
-
K_target = qiskit_gate_to_operator(gate_set)
|
|
221
|
-
gate_set_length = len(gate_set)
|
|
222
|
-
pdim = K_target.shape[-1] # Physical dimension
|
|
223
|
-
r = pdim**2 # Matrix dimension of gate superoperators
|
|
224
|
-
n_povm = pdim # Number of POVM elements
|
|
225
|
-
sequence_length = gate_sequences.shape[0]
|
|
226
|
-
|
|
227
|
-
X_target = np.einsum("ijkl,ijnm -> iknlm", K_target, K_target.conj()).reshape(
|
|
228
|
-
(gate_set_length, pdim**2, pdim**2)
|
|
229
|
-
) # tensor of superoperators
|
|
230
|
-
|
|
231
|
-
# Initial state |0>
|
|
232
|
-
rho_target = (
|
|
233
|
-
np.kron(additional_fns.basis(pdim, 0).T.conj(), additional_fns.basis(pdim, 0)).reshape(-1).astype(np.complex128)
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
# Computational basis measurement:
|
|
237
|
-
E_target = np.array(
|
|
238
|
-
[
|
|
239
|
-
np.kron(additional_fns.basis(pdim, i).T.conj(), additional_fns.basis(pdim, i)).reshape(-1)
|
|
240
|
-
for i in range(pdim)
|
|
241
|
-
]
|
|
242
|
-
).astype(np.complex128)
|
|
243
|
-
target_mdl = arrays_to_pygsti_model(X_target, E_target, rho_target, basis="std")
|
|
244
|
-
|
|
245
|
-
K_init = additional_fns.perturbed_target_init(X_target, rK)
|
|
246
|
-
|
|
247
|
-
bsize = 30 * pdim # Batch size for optimization
|
|
248
|
-
_, X, E, rho, _ = algorithm.run_mGST(
|
|
249
|
-
sequence_results,
|
|
250
|
-
gate_sequences,
|
|
251
|
-
sequence_length,
|
|
252
|
-
gate_set_length,
|
|
253
|
-
r,
|
|
254
|
-
rK,
|
|
255
|
-
n_povm,
|
|
256
|
-
bsize,
|
|
257
|
-
shots,
|
|
258
|
-
method="SFN",
|
|
259
|
-
max_inits=10,
|
|
260
|
-
max_iter=100,
|
|
261
|
-
final_iter=50,
|
|
262
|
-
target_rel_prec=1e-4,
|
|
263
|
-
init=[K_init, E_target, rho_target],
|
|
264
|
-
)
|
|
265
|
-
|
|
266
|
-
# Output the final mean variation error
|
|
267
|
-
mean_var_error = additional_fns.MVE(
|
|
268
|
-
X_target, E_target, rho_target, X, E, rho, gate_set_length, sequence_length, n_povm
|
|
269
|
-
)[0]
|
|
270
|
-
print(f"Mean variation error:", mean_var_error)
|
|
271
|
-
print(f"Optimizing gauge...")
|
|
272
|
-
weights = dict({f"G%i" % i: 1 for i in range(gate_set_length)}, **{"spam": 1})
|
|
273
|
-
X_opt, E_opt, rho_opt = gauge_opt(X, E, rho, target_mdl, weights)
|
|
274
|
-
print("Compressive GST routine complete")
|
|
275
|
-
|
|
276
|
-
# Making sense of the outcomes
|
|
277
|
-
df_g, df_o = quick_report(X_opt, E_opt, rho_opt, gate_sequences, sequence_results, target_mdl)
|
|
278
|
-
print("First results:")
|
|
279
|
-
print(df_g.to_string())
|
|
280
|
-
print(df_o.T.to_string())
|
|
281
|
-
|
|
282
|
-
return X_opt, E_opt, rho_opt
|