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.

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 additional_fns, algorithm, low_level_jit
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