iqm-benchmarks 2.22__py3-none-any.whl → 2.23__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.py +5 -0
- iqm/benchmarks/benchmark_definition.py +14 -0
- iqm/benchmarks/compressive_gst/compressive_gst.py +80 -28
- iqm/benchmarks/compressive_gst/gst_analysis.py +20 -7
- iqm/benchmarks/entanglement/ghz.py +8 -49
- iqm/benchmarks/optimization/qscore.py +2 -1
- iqm/benchmarks/quantum_volume/clops.py +1 -0
- iqm/benchmarks/quantum_volume/quantum_volume.py +1 -5
- iqm/benchmarks/randomized_benchmarking/clifford_rb/clifford_rb.py +1 -0
- iqm/benchmarks/randomized_benchmarking/interleaved_rb/interleaved_rb.py +2 -0
- iqm/benchmarks/randomized_benchmarking/mirror_rb/mirror_rb.py +1 -0
- iqm/benchmarks/randomized_benchmarking/randomized_benchmarking_common.py +9 -1
- iqm/benchmarks/utils.py +248 -3
- {iqm_benchmarks-2.22.dist-info → iqm_benchmarks-2.23.dist-info}/METADATA +2 -2
- {iqm_benchmarks-2.22.dist-info → iqm_benchmarks-2.23.dist-info}/RECORD +22 -22
- mGST/algorithm.py +5 -7
- mGST/low_level_jit.py +38 -21
- mGST/qiskit_interface.py +1 -1
- mGST/reporting/reporting.py +1 -1
- {iqm_benchmarks-2.22.dist-info → iqm_benchmarks-2.23.dist-info}/LICENSE +0 -0
- {iqm_benchmarks-2.22.dist-info → iqm_benchmarks-2.23.dist-info}/WHEEL +0 -0
- {iqm_benchmarks-2.22.dist-info → iqm_benchmarks-2.23.dist-info}/top_level.txt +0 -0
iqm/benchmarks/benchmark.py
CHANGED
|
@@ -23,6 +23,7 @@ from typing import Dict, Literal, Optional, OrderedDict, Type
|
|
|
23
23
|
from matplotlib.figure import Figure
|
|
24
24
|
from pydantic import BaseModel
|
|
25
25
|
|
|
26
|
+
from iqm.iqm_client.models import DDStrategy
|
|
26
27
|
from iqm.qiskit_iqm.iqm_backend import IQMBackendBase
|
|
27
28
|
|
|
28
29
|
|
|
@@ -99,6 +100,8 @@ class BenchmarkConfigurationBase(BaseModel):
|
|
|
99
100
|
- "fixed": physical layout is constrained during transpilation to the selected initial physical qubits.
|
|
100
101
|
- "batching": physical layout is allowed to use any other physical qubits, and circuits are batched according to final measured qubits.
|
|
101
102
|
* Default for all benchmarks is "fixed".
|
|
103
|
+
use_DD (bool): Boolean flag determining if dynamical decoupling is enabled during circuit execution
|
|
104
|
+
* Default: False
|
|
102
105
|
"""
|
|
103
106
|
|
|
104
107
|
benchmark: Type[BenchmarkBase]
|
|
@@ -107,3 +110,5 @@ class BenchmarkConfigurationBase(BaseModel):
|
|
|
107
110
|
calset_id: Optional[str] = None
|
|
108
111
|
routing_method: Literal["basic", "lookahead", "stochastic", "sabre", "none"] = "sabre"
|
|
109
112
|
physical_layout: Literal["fixed", "batching"] = "fixed"
|
|
113
|
+
use_dd: Optional[bool] = False
|
|
114
|
+
dd_strategy: Optional[DDStrategy] = None
|
|
@@ -30,7 +30,9 @@ import xarray as xr
|
|
|
30
30
|
|
|
31
31
|
from iqm.benchmarks.benchmark import BenchmarkConfigurationBase
|
|
32
32
|
from iqm.benchmarks.circuit_containers import BenchmarkCircuit, Circuits
|
|
33
|
+
from iqm.benchmarks.logging_config import qcvv_logger
|
|
33
34
|
from iqm.benchmarks.utils import get_iqm_backend, timeit
|
|
35
|
+
from iqm.iqm_client.models import CircuitCompilationOptions, DDMode
|
|
34
36
|
from iqm.qiskit_iqm.iqm_backend import IQMBackendBase
|
|
35
37
|
from iqm.qiskit_iqm.iqm_provider import IQMBackend, IQMFacadeBackend
|
|
36
38
|
|
|
@@ -245,6 +247,18 @@ class Benchmark(ABC):
|
|
|
245
247
|
self.options.update(kwargs)
|
|
246
248
|
self.runs: list[BenchmarkRunResult] = []
|
|
247
249
|
|
|
250
|
+
# Circuit compilation options
|
|
251
|
+
if self.configuration.use_dd:
|
|
252
|
+
if self.name in ["compressive_GST", "mirror_rb", "interleaved_clifford_rb", "clifford_rb"]:
|
|
253
|
+
qcvv_logger.warning(
|
|
254
|
+
f"Beware that activating dynamical decoupling will change fidelities, error models and their interpretation."
|
|
255
|
+
)
|
|
256
|
+
self.circuit_compilation_options = CircuitCompilationOptions(
|
|
257
|
+
dd_mode=DDMode.ENABLED, dd_strategy=self.configuration.dd_strategy
|
|
258
|
+
)
|
|
259
|
+
else:
|
|
260
|
+
self.circuit_compilation_options = CircuitCompilationOptions(dd_mode=DDMode.DISABLED)
|
|
261
|
+
|
|
248
262
|
@classmethod
|
|
249
263
|
@abstractmethod
|
|
250
264
|
def name(cls):
|
|
@@ -102,10 +102,9 @@ class CompressiveGST(Benchmark):
|
|
|
102
102
|
|
|
103
103
|
# Circuit format used by mGST
|
|
104
104
|
self.J = np.empty((self.configuration.num_circuits, self.num_povm))
|
|
105
|
-
self.bootstrap_results = List[Tuple[np.ndarray]] # List of GST outcomes from bootstrapping
|
|
106
105
|
|
|
107
106
|
@timeit
|
|
108
|
-
def generate_meas_circuits(self) ->
|
|
107
|
+
def generate_meas_circuits(self) -> tuple[BenchmarkCircuit, BenchmarkCircuit]:
|
|
109
108
|
"""Generate random circuits from the gate set
|
|
110
109
|
|
|
111
110
|
The random circuits are distributed among different depths ranging from L_MIN
|
|
@@ -115,10 +114,17 @@ class CompressiveGST(Benchmark):
|
|
|
115
114
|
meaningful results
|
|
116
115
|
|
|
117
116
|
Returns:
|
|
117
|
+
transpiled_circuits: BenchmarkCircuit
|
|
118
|
+
The transpiled circuits
|
|
119
|
+
untranspiled_circuits: BenchmarkCircuit
|
|
120
|
+
The untranspiled circuits
|
|
118
121
|
circuit_gen_transp_time: float
|
|
119
122
|
The time it took to generate and transpile the circuits
|
|
120
|
-
|
|
121
123
|
"""
|
|
124
|
+
|
|
125
|
+
transpiled_circuits = BenchmarkCircuit(name="transpiled_circuits")
|
|
126
|
+
untranspiled_circuits = BenchmarkCircuit(name="untranspiled_circuits")
|
|
127
|
+
|
|
122
128
|
# Calculate number of short and long circuits
|
|
123
129
|
N_short = int(np.ceil(self.configuration.num_circuits / 2))
|
|
124
130
|
N_long = int(np.floor(self.configuration.num_circuits / 2))
|
|
@@ -142,25 +148,54 @@ class CompressiveGST(Benchmark):
|
|
|
142
148
|
f"Transpilation on star-architectures currently allows move gates to transit barriers, "
|
|
143
149
|
f"leading to context-dependent gates which GST can not accurately resolve."
|
|
144
150
|
)
|
|
145
|
-
for qubits in self.qubit_layouts:
|
|
146
|
-
coupling_map = set_coupling_map(qubits, self.backend, physical_layout="fixed")
|
|
147
151
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
+
# Perform transpilation to backend
|
|
153
|
+
qcvv_logger.info(
|
|
154
|
+
f"Will transpile all {self.configuration.num_circuits} circuits according to fixed physical layout"
|
|
155
|
+
)
|
|
156
|
+
if self.configuration.parallel_execution:
|
|
157
|
+
all_qubits = [qubit for layout in self.qubit_layouts for qubit in layout]
|
|
158
|
+
if len(all_qubits) != len(set(all_qubits)):
|
|
159
|
+
raise ValueError(
|
|
160
|
+
"Qubit layouts can't overlap when parallel_execution is enabled, please choose non-overlapping layouts."
|
|
161
|
+
)
|
|
162
|
+
raw_qc_list_parallel = []
|
|
163
|
+
for circ in raw_qc_list:
|
|
164
|
+
circ_parallel = QuantumCircuit(self.backend.num_qubits, len(set(all_qubits)))
|
|
165
|
+
clbits = np.arange(self.num_qubits)
|
|
166
|
+
for qubit_layout in self.qubit_layouts:
|
|
167
|
+
circ_parallel.compose(circ, qubits=qubit_layout, clbits=clbits, inplace=True)
|
|
168
|
+
clbits += self.num_qubits
|
|
169
|
+
raw_qc_list_parallel.append(circ_parallel)
|
|
152
170
|
transpiled_qc_list, _ = perform_backend_transpilation(
|
|
153
|
-
|
|
171
|
+
raw_qc_list_parallel,
|
|
154
172
|
self.backend,
|
|
155
|
-
qubits,
|
|
156
|
-
coupling_map=coupling_map,
|
|
173
|
+
qubits=np.arange(self.backend.num_qubits),
|
|
174
|
+
coupling_map=self.backend.coupling_map,
|
|
157
175
|
qiskit_optim_level=0,
|
|
158
176
|
optimize_sqg=False,
|
|
159
177
|
drop_final_rz=False,
|
|
160
178
|
)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
179
|
+
for qubits in self.qubit_layouts:
|
|
180
|
+
# Saving raw and transpiled circuits in a consistent format with other benchmarks
|
|
181
|
+
transpiled_circuits.circuit_groups.append(CircuitGroup(name=str(qubits), circuits=transpiled_qc_list))
|
|
182
|
+
untranspiled_circuits.circuit_groups.append(CircuitGroup(name=str(qubits), circuits=raw_qc_list))
|
|
183
|
+
else:
|
|
184
|
+
for qubits in self.qubit_layouts:
|
|
185
|
+
coupling_map = set_coupling_map(qubits, self.backend, physical_layout="fixed")
|
|
186
|
+
transpiled_qc_list, _ = perform_backend_transpilation(
|
|
187
|
+
raw_qc_list,
|
|
188
|
+
self.backend,
|
|
189
|
+
qubits,
|
|
190
|
+
coupling_map=coupling_map,
|
|
191
|
+
qiskit_optim_level=0,
|
|
192
|
+
optimize_sqg=False,
|
|
193
|
+
drop_final_rz=False,
|
|
194
|
+
)
|
|
195
|
+
# Saving raw and transpiled circuits in a consistent format with other benchmarks
|
|
196
|
+
transpiled_circuits.circuit_groups.append(CircuitGroup(name=str(qubits), circuits=transpiled_qc_list))
|
|
197
|
+
untranspiled_circuits.circuit_groups.append(CircuitGroup(name=str(qubits), circuits=raw_qc_list))
|
|
198
|
+
return transpiled_circuits, untranspiled_circuits
|
|
164
199
|
|
|
165
200
|
def add_configuration_to_dataset(self, dataset): # CHECK
|
|
166
201
|
"""
|
|
@@ -190,30 +225,45 @@ class CompressiveGST(Benchmark):
|
|
|
190
225
|
qcvv_logger.info(f"Now generating {self.configuration.num_circuits} random GST circuits...")
|
|
191
226
|
|
|
192
227
|
self.circuits = Circuits()
|
|
193
|
-
self.transpiled_circuits = BenchmarkCircuit(name="transpiled_circuits")
|
|
194
|
-
self.untranspiled_circuits = BenchmarkCircuit(name="untranspiled_circuits")
|
|
195
228
|
# Generate circuits
|
|
196
|
-
self.generate_meas_circuits()
|
|
229
|
+
(transpiled_circuits, untranspiled_circuits), _ = self.generate_meas_circuits()
|
|
197
230
|
|
|
198
231
|
# Submit all
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
232
|
+
if self.configuration.parallel_execution:
|
|
233
|
+
transpiled_circuit_dict = {
|
|
234
|
+
tuple(range(self.backend.num_qubits)): transpiled_circuits[str(self.qubit_layouts[0])].circuits
|
|
235
|
+
}
|
|
236
|
+
all_jobs_parallel, _ = submit_execute(
|
|
203
237
|
transpiled_circuit_dict,
|
|
204
238
|
backend,
|
|
205
239
|
self.configuration.shots,
|
|
206
240
|
self.calset_id,
|
|
207
241
|
max_gates_per_batch=self.configuration.max_gates_per_batch,
|
|
242
|
+
circuit_compilation_options=self.circuit_compilation_options,
|
|
208
243
|
)
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
244
|
+
# Retrieve
|
|
245
|
+
qcvv_logger.info(f"Now executing the corresponding circuit batch")
|
|
246
|
+
counts, _ = retrieve_all_counts(all_jobs_parallel)
|
|
247
|
+
dataset, _ = add_counts_to_dataset(counts, f"parallel_results", dataset)
|
|
248
|
+
else:
|
|
249
|
+
all_jobs: Dict = {}
|
|
250
|
+
for qubit_layout in self.qubit_layouts:
|
|
251
|
+
transpiled_circuit_dict = {tuple(qubit_layout): transpiled_circuits[str(qubit_layout)].circuits}
|
|
252
|
+
all_jobs[str(qubit_layout)], _ = submit_execute(
|
|
253
|
+
transpiled_circuit_dict,
|
|
254
|
+
backend,
|
|
255
|
+
self.configuration.shots,
|
|
256
|
+
self.calset_id,
|
|
257
|
+
max_gates_per_batch=self.configuration.max_gates_per_batch,
|
|
258
|
+
)
|
|
259
|
+
# Retrieve all
|
|
260
|
+
qcvv_logger.info(f"Now executing the corresponding circuit batch")
|
|
261
|
+
for qubit_layout in self.qubit_layouts:
|
|
262
|
+
counts, _ = retrieve_all_counts(all_jobs[str(qubit_layout)])
|
|
263
|
+
dataset, _ = add_counts_to_dataset(counts, str(qubit_layout), dataset)
|
|
214
264
|
|
|
265
|
+
self.circuits.benchmark_circuits = [transpiled_circuits, untranspiled_circuits]
|
|
215
266
|
self.add_configuration_to_dataset(dataset)
|
|
216
|
-
self.circuits.benchmark_circuits = [self.transpiled_circuits, self.untranspiled_circuits]
|
|
217
267
|
return dataset
|
|
218
268
|
|
|
219
269
|
|
|
@@ -270,6 +320,7 @@ class GSTConfiguration(BenchmarkConfigurationBase):
|
|
|
270
320
|
* Default: "auto"
|
|
271
321
|
bootstrap_samples (int): The number of times the optimization algorithm is repeated on fake data to estimate
|
|
272
322
|
the uncertainty via bootstrapping.
|
|
323
|
+
parallel_execution (bool): Whether to run the circuits for all layouts in parallel on the backend.
|
|
273
324
|
"""
|
|
274
325
|
|
|
275
326
|
benchmark: Type[Benchmark] = CompressiveGST
|
|
@@ -288,6 +339,7 @@ class GSTConfiguration(BenchmarkConfigurationBase):
|
|
|
288
339
|
batch_size: Union[str, int] = "auto"
|
|
289
340
|
bootstrap_samples: int = 0
|
|
290
341
|
testing: bool = False
|
|
342
|
+
parallel_execution: bool = False
|
|
291
343
|
|
|
292
344
|
|
|
293
345
|
def parse_layouts(qubit_layouts: Union[List[int], List[List[int]]]) -> List[List[int]]:
|
|
@@ -621,16 +621,29 @@ def dataset_counts_to_mgst_format(dataset: xr.Dataset, qubit_layout: List[int])
|
|
|
621
621
|
num_povm = dataset.attrs["num_povm"]
|
|
622
622
|
y_list = []
|
|
623
623
|
for run_index in range(dataset.attrs["num_circuits"]):
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
624
|
+
if dataset.attrs["parallel_execution"]:
|
|
625
|
+
result_da = dataset[f"parallel_results_counts_{run_index}"].copy()
|
|
626
|
+
bit_pos = dataset.attrs["qubit_layouts"].index(qubit_layout)
|
|
627
|
+
# Create a new coordinate of bits at the position given by the qubit layout and reverse order
|
|
628
|
+
new_coords = [
|
|
629
|
+
coord[::-1][bit_pos * num_qubits : (bit_pos + 1) * num_qubits]
|
|
630
|
+
for coord in result_da.coords[result_da.dims[0]].values
|
|
631
|
+
]
|
|
632
|
+
else:
|
|
633
|
+
result_da = dataset[f"{qubit_layout}_counts_{run_index}"].copy()
|
|
634
|
+
# Reverse order since counts are stored in qiskit order (bottom to top in circuit diagram)
|
|
635
|
+
new_coords = [coord[::-1] for coord in result_da.coords[result_da.dims[0]].values]
|
|
636
|
+
result_da.coords["new_coord"] = (result_da.dims[0], new_coords)
|
|
637
|
+
result_da = result_da.groupby("new_coord").sum()
|
|
638
|
+
|
|
639
|
+
coord_strings = list(result_da.coords[result_da.dims[0]].values)
|
|
640
|
+
# Translating from binary basis labels to integer POVM labels
|
|
641
|
+
basis_dict = {entry: int(entry, 2) for entry in coord_strings}
|
|
627
642
|
# Sort by index:
|
|
628
643
|
basis_dict = dict(sorted(basis_dict.items(), key=lambda item: item[1]))
|
|
629
644
|
|
|
630
|
-
counts_normalized = (
|
|
631
|
-
|
|
632
|
-
)
|
|
633
|
-
row = [counts_normalized.loc[key].data for key in basis_dict]
|
|
645
|
+
counts_normalized = result_da / result_da.sum()
|
|
646
|
+
row = [float(counts_normalized.loc[key].data) for key in basis_dict]
|
|
634
647
|
# row = [result[key] for key in basis_dict]
|
|
635
648
|
if len(row) < num_povm:
|
|
636
649
|
missing_entries = list(np.arange(num_povm))
|
|
@@ -16,9 +16,7 @@
|
|
|
16
16
|
GHZ state benchmark
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
|
-
from io import BytesIO
|
|
20
19
|
from itertools import chain
|
|
21
|
-
import json
|
|
22
20
|
from time import strftime
|
|
23
21
|
from typing import Any, Dict, List, Optional, Sequence, Tuple, Type, cast
|
|
24
22
|
|
|
@@ -27,7 +25,6 @@ import matplotlib.pyplot as plt
|
|
|
27
25
|
import networkx
|
|
28
26
|
from networkx import Graph, all_pairs_shortest_path, is_connected, minimum_spanning_tree
|
|
29
27
|
import numpy as np
|
|
30
|
-
import pycurl
|
|
31
28
|
from qiskit import QuantumRegister
|
|
32
29
|
from qiskit.quantum_info import random_clifford
|
|
33
30
|
from qiskit.transpiler import CouplingMap
|
|
@@ -48,6 +45,7 @@ from iqm.benchmarks.circuit_containers import BenchmarkCircuit, CircuitGroup, Ci
|
|
|
48
45
|
from iqm.benchmarks.logging_config import qcvv_logger
|
|
49
46
|
from iqm.benchmarks.readout_mitigation import apply_readout_error_mitigation
|
|
50
47
|
from iqm.benchmarks.utils import (
|
|
48
|
+
extract_fidelities,
|
|
51
49
|
perform_backend_transpilation,
|
|
52
50
|
reduce_to_active_qubits,
|
|
53
51
|
retrieve_all_counts,
|
|
@@ -387,47 +385,6 @@ def generate_ghz_spanning_tree(
|
|
|
387
385
|
return qc, list(participating_qubits)
|
|
388
386
|
|
|
389
387
|
|
|
390
|
-
def extract_fidelities(cal_url: str, qubit_layout: List[int]) -> Tuple[List[List[int]], List[float]]:
|
|
391
|
-
"""Returns couplings and CZ-fidelities from calibration data URL
|
|
392
|
-
|
|
393
|
-
Args:
|
|
394
|
-
cal_url: str
|
|
395
|
-
The url under which the calibration data for the backend can be found
|
|
396
|
-
qubit_layout: List[int]
|
|
397
|
-
The subset of system-qubits used in the protocol, indexed from 0
|
|
398
|
-
Returns:
|
|
399
|
-
list_couplings: List[List[int]]
|
|
400
|
-
A list of pairs, each of which is a qubit coupling for which the calibration
|
|
401
|
-
data contains a fidelity.
|
|
402
|
-
list_fids: List[float]
|
|
403
|
-
A list of CZ fidelities from the calibration url, ordered in the same way as list_couplings
|
|
404
|
-
"""
|
|
405
|
-
|
|
406
|
-
byteobj = BytesIO() # buffer creation
|
|
407
|
-
curlobj = pycurl.Curl() # pylint: disable=c-extension-no-member
|
|
408
|
-
curlobj.setopt(curlobj.URL, f"{cal_url}") # type: ignore
|
|
409
|
-
curlobj.setopt(curlobj.WRITEDATA, byteobj) # type: ignore
|
|
410
|
-
curlobj.perform() # perform file transfer
|
|
411
|
-
curlobj.close() # end of session
|
|
412
|
-
body = byteobj.getvalue()
|
|
413
|
-
res = json.loads(body.decode())
|
|
414
|
-
|
|
415
|
-
qubit_mapping = {qubit: idx for idx, qubit in enumerate(qubit_layout)}
|
|
416
|
-
list_couplings = []
|
|
417
|
-
list_fids = []
|
|
418
|
-
for key in res["metrics"]:
|
|
419
|
-
if "irb.cz" in key:
|
|
420
|
-
idx_1 = key.index(".QB")
|
|
421
|
-
idx_2 = key.index("__QB")
|
|
422
|
-
idx_3 = key.index(".fidelity")
|
|
423
|
-
qb1 = int(key[idx_1 + 3 : idx_2]) - 1
|
|
424
|
-
qb2 = int(key[idx_2 + 4 : idx_3]) - 1
|
|
425
|
-
if all([qb1 in qubit_layout, qb2 in qubit_layout]):
|
|
426
|
-
list_couplings.append([qubit_mapping[qb1], qubit_mapping[qb2]])
|
|
427
|
-
list_fids.append(float(res["metrics"][key]["value"]))
|
|
428
|
-
return list_couplings, list_fids
|
|
429
|
-
|
|
430
|
-
|
|
431
388
|
def get_edges(
|
|
432
389
|
coupling_map: CouplingMap,
|
|
433
390
|
qubit_layout: List[int],
|
|
@@ -454,16 +411,17 @@ def get_edges(
|
|
|
454
411
|
edges_patch = []
|
|
455
412
|
for idx, edge in enumerate(coupling_map):
|
|
456
413
|
if edge[0] in qubit_layout and edge[1] in qubit_layout:
|
|
457
|
-
|
|
414
|
+
if not set(edge) in edges_patch:
|
|
415
|
+
edges_patch.append(set(edge))
|
|
458
416
|
|
|
459
|
-
if fidelities_cal is not None:
|
|
417
|
+
if fidelities_cal is not None and edges_cal is not None:
|
|
460
418
|
fidelities_cal = list(
|
|
461
419
|
np.minimum(np.array(fidelities_cal), np.ones(len(fidelities_cal)))
|
|
462
420
|
) # get rid of > 1 fidelities
|
|
463
421
|
fidelities_patch = []
|
|
464
422
|
for edge in edges_patch:
|
|
465
|
-
for idx, edge_2 in enumerate(
|
|
466
|
-
if edge == edge_2:
|
|
423
|
+
for idx, edge_2 in enumerate(edges_cal):
|
|
424
|
+
if edge == set(edge_2):
|
|
467
425
|
fidelities_patch.append(fidelities_cal[idx])
|
|
468
426
|
weights = -np.log(np.array(fidelities_patch))
|
|
469
427
|
else:
|
|
@@ -666,7 +624,7 @@ class GHZBenchmark(Benchmark):
|
|
|
666
624
|
else:
|
|
667
625
|
effective_coupling_map = self.backend.coupling_map
|
|
668
626
|
if self.cal_url:
|
|
669
|
-
edges_cal, fidelities_cal = extract_fidelities(self.cal_url
|
|
627
|
+
edges_cal, fidelities_cal, _ = extract_fidelities(self.cal_url)
|
|
670
628
|
graph = get_edges(effective_coupling_map, qubit_layout, edges_cal, fidelities_cal)
|
|
671
629
|
else:
|
|
672
630
|
graph = get_edges(effective_coupling_map, qubit_layout)
|
|
@@ -893,6 +851,7 @@ class GHZBenchmark(Benchmark):
|
|
|
893
851
|
self.shots,
|
|
894
852
|
self.calset_id,
|
|
895
853
|
max_gates_per_batch=self.max_gates_per_batch,
|
|
854
|
+
circuit_compilation_options=self.circuit_compilation_options,
|
|
896
855
|
)
|
|
897
856
|
|
|
898
857
|
# Retrieve all
|
|
@@ -753,7 +753,7 @@ class QScoreBenchmark(Benchmark):
|
|
|
753
753
|
|
|
754
754
|
if self.custom_qubits_array is not None:
|
|
755
755
|
if self.use_virtual_node:
|
|
756
|
-
node_numbers = [len(qubit_layout)+1 for qubit_layout in self.custom_qubits_array]
|
|
756
|
+
node_numbers = [len(qubit_layout) + 1 for qubit_layout in self.custom_qubits_array]
|
|
757
757
|
else:
|
|
758
758
|
node_numbers = [len(qubit_layout) for qubit_layout in self.custom_qubits_array]
|
|
759
759
|
|
|
@@ -881,6 +881,7 @@ class QScoreBenchmark(Benchmark):
|
|
|
881
881
|
self.shots,
|
|
882
882
|
self.calset_id,
|
|
883
883
|
max_gates_per_batch=self.max_gates_per_batch,
|
|
884
|
+
circuit_compilation_options=self.circuit_compilation_options,
|
|
884
885
|
)
|
|
885
886
|
qc_transpiled_list.append(transpiled_qc)
|
|
886
887
|
qcvv_logger.setLevel(logging.INFO)
|
|
@@ -698,12 +698,8 @@ class QuantumVolumeBenchmark(Benchmark):
|
|
|
698
698
|
self.shots,
|
|
699
699
|
self.calset_id,
|
|
700
700
|
max_gates_per_batch=self.max_gates_per_batch,
|
|
701
|
+
circuit_compilation_options=self.circuit_compilation_options,
|
|
701
702
|
)
|
|
702
|
-
# else:
|
|
703
|
-
# DD IN DIQE VERSION PREVENTS SUBMITTING JOBS DYNAMICALLY:
|
|
704
|
-
# I.E., IT AUTOMATICALLY RETRIEVES COUNTS
|
|
705
|
-
# TODO: change this when job manager for DD in Pulla is updated! # pylint: disable=fixme
|
|
706
|
-
# raise ValueError("Dynamical decoupling is not yet enabled in the new base")
|
|
707
703
|
# qcvv_logger.info(
|
|
708
704
|
# f"Now executing {self.num_circuits} circuits with default strategy Dynamical Decoupling"
|
|
709
705
|
# )
|
|
@@ -513,6 +513,7 @@ class InterleavedRandomizedBenchmarking(Benchmark):
|
|
|
513
513
|
backend,
|
|
514
514
|
self.calset_id,
|
|
515
515
|
max_gates_per_batch=self.max_gates_per_batch,
|
|
516
|
+
circuit_compilation_options=self.circuit_compilation_options,
|
|
516
517
|
)
|
|
517
518
|
)
|
|
518
519
|
all_rb_jobs["interleaved"].extend(
|
|
@@ -523,6 +524,7 @@ class InterleavedRandomizedBenchmarking(Benchmark):
|
|
|
523
524
|
backend,
|
|
524
525
|
self.calset_id,
|
|
525
526
|
max_gates_per_batch=self.max_gates_per_batch,
|
|
527
|
+
circuit_compilation_options=self.circuit_compilation_options,
|
|
526
528
|
)
|
|
527
529
|
)
|
|
528
530
|
qcvv_logger.info(
|
|
@@ -36,6 +36,7 @@ import xarray as xr
|
|
|
36
36
|
from iqm.benchmarks.logging_config import qcvv_logger
|
|
37
37
|
from iqm.benchmarks.randomized_benchmarking.multi_lmfit import create_multi_dataset_params, multi_dataset_residual
|
|
38
38
|
from iqm.benchmarks.utils import get_iqm_backend, marginal_distribution, submit_execute, timeit
|
|
39
|
+
from iqm.iqm_client.models import CircuitCompilationOptions
|
|
39
40
|
from iqm.qiskit_iqm import IQMCircuit as QuantumCircuit
|
|
40
41
|
from iqm.qiskit_iqm import optimize_single_qubit_gates
|
|
41
42
|
from iqm.qiskit_iqm.iqm_backend import IQMBackendBase
|
|
@@ -473,6 +474,7 @@ def submit_sequential_rb_jobs(
|
|
|
473
474
|
backend_arg: str | IQMBackendBase,
|
|
474
475
|
calset_id: Optional[str] = None,
|
|
475
476
|
max_gates_per_batch: Optional[int] = None,
|
|
477
|
+
circuit_compilation_options: Optional[CircuitCompilationOptions] = None,
|
|
476
478
|
) -> List[Dict[str, Any]]:
|
|
477
479
|
"""Submit sequential RB jobs for execution in the specified IQMBackend
|
|
478
480
|
Args:
|
|
@@ -482,6 +484,7 @@ def submit_sequential_rb_jobs(
|
|
|
482
484
|
backend_arg (IQMBackendBase): the IQM backend to submit the job
|
|
483
485
|
calset_id (Optional[str]): the calibration identifier
|
|
484
486
|
max_gates_per_batch (Optional[int]): the maximum number of gates per batch
|
|
487
|
+
circuit_compilation_options (Optional[CircuitCompilationOptions]): Compilation options passed to submit_execute
|
|
485
488
|
Returns:
|
|
486
489
|
Dict with qubit layout, submitted job objects, type (vanilla/DD) and submission time
|
|
487
490
|
"""
|
|
@@ -496,7 +499,12 @@ def submit_sequential_rb_jobs(
|
|
|
496
499
|
# Submit - send to execute on backend
|
|
497
500
|
# pylint: disable=unbalanced-tuple-unpacking
|
|
498
501
|
execution_jobs, time_submit = submit_execute(
|
|
499
|
-
{tuple(qubits): transpiled_circuits[depth]},
|
|
502
|
+
{tuple(qubits): transpiled_circuits[depth]},
|
|
503
|
+
backend,
|
|
504
|
+
shots,
|
|
505
|
+
calset_id,
|
|
506
|
+
max_gates_per_batch,
|
|
507
|
+
circuit_compilation_options=circuit_compilation_options,
|
|
500
508
|
)
|
|
501
509
|
rb_submit_results[depth] = {
|
|
502
510
|
"qubits": qubits,
|
iqm/benchmarks/utils.py
CHANGED
|
@@ -17,11 +17,14 @@ General utility functions
|
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
19
|
from collections import defaultdict
|
|
20
|
+
from dataclasses import dataclass
|
|
20
21
|
from functools import wraps
|
|
21
22
|
from math import floor
|
|
23
|
+
import os
|
|
22
24
|
from time import time
|
|
23
25
|
from typing import Any, Dict, Iterable, List, Literal, Optional, Sequence, Tuple, Union, cast
|
|
24
26
|
|
|
27
|
+
import matplotlib.pyplot as plt
|
|
25
28
|
from more_itertools import chunked
|
|
26
29
|
from mthree.utils import final_measurement_mapping
|
|
27
30
|
import numpy as np
|
|
@@ -29,9 +32,12 @@ from numpy.random import Generator
|
|
|
29
32
|
from qiskit import ClassicalRegister, transpile
|
|
30
33
|
from qiskit.converters import circuit_to_dag
|
|
31
34
|
from qiskit.transpiler import CouplingMap
|
|
35
|
+
import requests
|
|
36
|
+
from rustworkx import PyGraph, spring_layout, visualization # pylint: disable=no-name-in-module
|
|
32
37
|
import xarray as xr
|
|
33
38
|
|
|
34
39
|
from iqm.benchmarks.logging_config import qcvv_logger
|
|
40
|
+
from iqm.iqm_client.models import CircuitCompilationOptions
|
|
35
41
|
from iqm.qiskit_iqm import IQMCircuit as QuantumCircuit
|
|
36
42
|
from iqm.qiskit_iqm import transpile_to_IQM
|
|
37
43
|
from iqm.qiskit_iqm.fake_backends.fake_adonis import IQMFakeAdonis
|
|
@@ -495,8 +501,9 @@ def submit_execute(
|
|
|
495
501
|
sorted_transpiled_qc_list: Dict[Tuple, List[QuantumCircuit]],
|
|
496
502
|
backend: IQMBackendBase,
|
|
497
503
|
shots: int,
|
|
498
|
-
calset_id: Optional[str],
|
|
499
|
-
max_gates_per_batch: Optional[int],
|
|
504
|
+
calset_id: Optional[str] = None,
|
|
505
|
+
max_gates_per_batch: Optional[int] = None,
|
|
506
|
+
circuit_compilation_options: Optional[CircuitCompilationOptions] = None,
|
|
500
507
|
) -> List[IQMJob]:
|
|
501
508
|
"""Submit for execute a list of quantum circuits on the specified Backend.
|
|
502
509
|
|
|
@@ -506,8 +513,11 @@ def submit_execute(
|
|
|
506
513
|
shots (int): the number of shots per circuit.
|
|
507
514
|
calset_id (Optional[str]): the calibration set ID, uses the latest one if None.
|
|
508
515
|
max_gates_per_batch (int): the maximum number of gates per batch sent to the backend, used to make manageable batches.
|
|
516
|
+
circuit_compilation_options (CircuitCompilationOptions): Ability to pass a compilation options object,
|
|
517
|
+
enabling execution with dynamical decoupling, among other options - see qiskit-iqm documentation.
|
|
509
518
|
Returns:
|
|
510
519
|
List[IQMJob]: the IQMJob objects of the executed circuits.
|
|
520
|
+
|
|
511
521
|
"""
|
|
512
522
|
final_jobs = []
|
|
513
523
|
for k in sorted(
|
|
@@ -538,7 +548,12 @@ def submit_execute(
|
|
|
538
548
|
qcvv_logger.info(
|
|
539
549
|
f"max_gates_per_batch restriction: submitting subbatch #{index+1} with {len(qc_batch)} circuits corresponding to qubits {list(k)}"
|
|
540
550
|
)
|
|
541
|
-
batch_jobs = backend.run(
|
|
551
|
+
batch_jobs = backend.run(
|
|
552
|
+
qc_batch,
|
|
553
|
+
shots=shots,
|
|
554
|
+
calibration_set_id=calset_id,
|
|
555
|
+
circuit_compilation_options=circuit_compilation_options,
|
|
556
|
+
)
|
|
542
557
|
final_batch_jobs.append(batch_jobs)
|
|
543
558
|
final_jobs.extend(final_batch_jobs)
|
|
544
559
|
|
|
@@ -559,3 +574,233 @@ def xrvariable_to_counts(dataset: xr.Dataset, identifier: str, counts_range: int
|
|
|
559
574
|
dict(zip(list(dataset[f"{identifier}_state_{u}"].data), dataset[f"{identifier}_counts_{u}"].data))
|
|
560
575
|
for u in range(counts_range)
|
|
561
576
|
]
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
@dataclass
|
|
580
|
+
class GraphPositions:
|
|
581
|
+
"""A class to store and generate graph positions for different chip layouts.
|
|
582
|
+
|
|
583
|
+
This class contains predefined node positions for various quantum chip topologies and
|
|
584
|
+
provides methods to generate positions for different layout types.
|
|
585
|
+
|
|
586
|
+
Attributes:
|
|
587
|
+
garnet_positions (Dict[int, Tuple[int, int]]): Mapping of node indices to (x,y) positions for Garnet chip.
|
|
588
|
+
deneb_positions (Dict[int, Tuple[int, int]]): Mapping of node indices to (x,y) positions for Deneb chip.
|
|
589
|
+
predefined_stations (Dict[str, Dict[int, Tuple[int, int]]]): Mapping of chip names to their position dictionaries.
|
|
590
|
+
"""
|
|
591
|
+
|
|
592
|
+
garnet_positions = {
|
|
593
|
+
0: (5.0, 7.0),
|
|
594
|
+
1: (6.0, 6.0),
|
|
595
|
+
2: (3.0, 7.0),
|
|
596
|
+
3: (4.0, 6.0),
|
|
597
|
+
4: (5.0, 5.0),
|
|
598
|
+
5: (6.0, 4.0),
|
|
599
|
+
6: (7.0, 3.0),
|
|
600
|
+
7: (2.0, 6.0),
|
|
601
|
+
8: (3.0, 5.0),
|
|
602
|
+
9: (4.0, 4.0),
|
|
603
|
+
10: (5.0, 3.0),
|
|
604
|
+
11: (6.0, 2.0),
|
|
605
|
+
12: (1.0, 5.0),
|
|
606
|
+
13: (2.0, 4.0),
|
|
607
|
+
14: (3.0, 3.0),
|
|
608
|
+
15: (4.0, 2.0),
|
|
609
|
+
16: (5.0, 1.0),
|
|
610
|
+
17: (1.0, 3.0),
|
|
611
|
+
18: (2.0, 2.0),
|
|
612
|
+
19: (3.0, 1.0),
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
deneb_positions = {
|
|
616
|
+
0: (2.0, 2.0),
|
|
617
|
+
1: (1.0, 1.0),
|
|
618
|
+
3: (2.0, 1.0),
|
|
619
|
+
5: (3.0, 1.0),
|
|
620
|
+
2: (1.0, 3.0),
|
|
621
|
+
4: (2.0, 3.0),
|
|
622
|
+
6: (3.0, 3.0),
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
predefined_stations = {
|
|
626
|
+
"Garnet": garnet_positions,
|
|
627
|
+
"Deneb": deneb_positions,
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
@staticmethod
|
|
631
|
+
def create_positions(graph: PyGraph, topology: Optional[str] = None) -> Dict[int, Tuple[float, float]]:
|
|
632
|
+
"""Generate node positions for a given graph and topology.
|
|
633
|
+
|
|
634
|
+
Args:
|
|
635
|
+
graph: The graph to generate positions for.
|
|
636
|
+
topology: The type of layout to generate. Must be either "star" or "crystal".
|
|
637
|
+
|
|
638
|
+
Returns:
|
|
639
|
+
A dictionary mapping node indices to (x,y) coordinates.
|
|
640
|
+
"""
|
|
641
|
+
n_nodes = len(graph.node_indices())
|
|
642
|
+
|
|
643
|
+
if topology == "star":
|
|
644
|
+
# Place center node at (0,0)
|
|
645
|
+
pos = {0: (0.0, 0.0)}
|
|
646
|
+
|
|
647
|
+
if n_nodes > 1:
|
|
648
|
+
# Place other nodes in a circle around the center
|
|
649
|
+
angles = np.linspace(0, 2 * np.pi, n_nodes - 1, endpoint=False)
|
|
650
|
+
radius = 1.0
|
|
651
|
+
|
|
652
|
+
for i, angle in enumerate(angles, start=1):
|
|
653
|
+
x = radius * np.cos(angle)
|
|
654
|
+
y = radius * np.sin(angle)
|
|
655
|
+
pos[i] = (x, y)
|
|
656
|
+
|
|
657
|
+
# Crystal and other topologies
|
|
658
|
+
else:
|
|
659
|
+
# Fix first node position in bottom right
|
|
660
|
+
fixed_pos = {0: (1.0, 1.0)} # For more consistent layouts
|
|
661
|
+
|
|
662
|
+
# Get spring layout with one fixed position
|
|
663
|
+
pos = {
|
|
664
|
+
int(k): (float(v[0]), float(v[1]))
|
|
665
|
+
for k, v in spring_layout(graph, scale=2, pos=fixed_pos, num_iter=300, fixed={0}).items()
|
|
666
|
+
}
|
|
667
|
+
return pos
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
def extract_fidelities(cal_url: str) -> tuple[list[list[int]], list[float], str]:
|
|
671
|
+
"""Returns couplings and CZ-fidelities from calibration data URL
|
|
672
|
+
|
|
673
|
+
Args:
|
|
674
|
+
cal_url: str
|
|
675
|
+
The url under which the calibration data for the backend can be found
|
|
676
|
+
Returns:
|
|
677
|
+
list_couplings: List[List[int]]
|
|
678
|
+
A list of pairs, each of which is a qubit coupling for which the calibration
|
|
679
|
+
data contains a fidelity.
|
|
680
|
+
list_fids: List[float]
|
|
681
|
+
A list of CZ fidelities from the calibration url, ordered in the same way as list_couplings
|
|
682
|
+
topology: str
|
|
683
|
+
Name of the chip topology layout, currently either "star" or "crystal"
|
|
684
|
+
"""
|
|
685
|
+
headers = {"Accept": "application/json", "Authorization": "Bearer " + os.environ["IQM_TOKEN"]}
|
|
686
|
+
r = requests.get(cal_url, headers=headers, timeout=60)
|
|
687
|
+
calibration = r.json()
|
|
688
|
+
cal_keys = {
|
|
689
|
+
el2["key"]: (i, j) for i, el1 in enumerate(calibration["calibrations"]) for j, el2 in enumerate(el1["metrics"])
|
|
690
|
+
}
|
|
691
|
+
list_couplings = []
|
|
692
|
+
list_fids = []
|
|
693
|
+
if "double_move_gate_fidelity" in cal_keys.keys():
|
|
694
|
+
i, j = cal_keys["double_move_gate_fidelity"]
|
|
695
|
+
topology = "star"
|
|
696
|
+
else:
|
|
697
|
+
i, j = cal_keys["cz_gate_fidelity"]
|
|
698
|
+
topology = "crystal"
|
|
699
|
+
for item in calibration["calibrations"][i]["metrics"][j]["metrics"]:
|
|
700
|
+
qb1 = int(item["locus"][0][2:]) if item["locus"][0] != "COMP_R" else 0
|
|
701
|
+
qb2 = int(item["locus"][1][2:]) if item["locus"][1] != "COMP_R" else 0
|
|
702
|
+
if topology == "star":
|
|
703
|
+
list_couplings.append([qb1, qb2])
|
|
704
|
+
else:
|
|
705
|
+
list_couplings.append([qb1 - 1, qb2 - 1])
|
|
706
|
+
list_fids.append(float(item["value"]))
|
|
707
|
+
calibrated_qubits = set(np.array(list_couplings).reshape(-1))
|
|
708
|
+
qubit_mapping = {qubit: idx for idx, qubit in enumerate(calibrated_qubits)}
|
|
709
|
+
list_couplings = [[qubit_mapping[edge[0]], qubit_mapping[edge[1]]] for edge in list_couplings]
|
|
710
|
+
|
|
711
|
+
return list_couplings, list_fids, topology
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
def plot_layout_fidelity_graph(
|
|
715
|
+
cal_url: str, qubit_layouts: Optional[list[list[int]]] = None, station: Optional[str] = None
|
|
716
|
+
):
|
|
717
|
+
"""Plot a graph showing the quantum chip layout with fidelity information.
|
|
718
|
+
|
|
719
|
+
Creates a visualization of the quantum chip topology where nodes represent qubits
|
|
720
|
+
and edges represent connections between qubits. Edge thickness indicates gate errors
|
|
721
|
+
(thinner edges mean better fidelity) and selected qubits are highlighted in orange.
|
|
722
|
+
|
|
723
|
+
Args:
|
|
724
|
+
cal_url: URL to retrieve calibration data from
|
|
725
|
+
qubit_layouts: List of qubit layouts where each layout is a list of qubit indices
|
|
726
|
+
station: Name of the quantum computing station to use predefined positions for.
|
|
727
|
+
If None, positions will be generated algorithmically.
|
|
728
|
+
|
|
729
|
+
Returns:
|
|
730
|
+
matplotlib.figure.Figure: The generated figure object containing the graph visualization
|
|
731
|
+
"""
|
|
732
|
+
edges_cal, fidelities_cal, topology = extract_fidelities(cal_url)
|
|
733
|
+
weights = -np.log(np.array(fidelities_cal))
|
|
734
|
+
edges_graph = [tuple(edge) + (weight,) for edge, weight in zip(edges_cal, weights)]
|
|
735
|
+
|
|
736
|
+
graph = PyGraph()
|
|
737
|
+
|
|
738
|
+
# Add nodes
|
|
739
|
+
nodes: set[int] = set()
|
|
740
|
+
for edge in edges_graph:
|
|
741
|
+
nodes.update(edge[:2])
|
|
742
|
+
graph.add_nodes_from(list(nodes))
|
|
743
|
+
|
|
744
|
+
# Add edges
|
|
745
|
+
graph.add_edges_from(edges_graph)
|
|
746
|
+
|
|
747
|
+
# Define qubit positions in plot
|
|
748
|
+
if station in GraphPositions.predefined_stations:
|
|
749
|
+
pos = GraphPositions.predefined_stations[station]
|
|
750
|
+
else:
|
|
751
|
+
pos = GraphPositions.create_positions(graph, topology)
|
|
752
|
+
|
|
753
|
+
# Define node colors
|
|
754
|
+
node_colors = ["lightgrey" for _ in range(len(nodes))]
|
|
755
|
+
if qubit_layouts is not None:
|
|
756
|
+
for qb in {qb for layout in qubit_layouts for qb in layout}:
|
|
757
|
+
node_colors[qb] = "orange"
|
|
758
|
+
|
|
759
|
+
# Ensuring weights are in correct order for the plot
|
|
760
|
+
edge_list = graph.edge_list()
|
|
761
|
+
weights_dict = {}
|
|
762
|
+
edge_pos = set()
|
|
763
|
+
|
|
764
|
+
# Create a mapping between edge positions as defined in rustworkx and their weights
|
|
765
|
+
for e, w in zip(edge_list, weights):
|
|
766
|
+
pos_tuple = (tuple(pos[e[0]]), tuple(pos[e[1]]))
|
|
767
|
+
weights_dict[pos_tuple] = w
|
|
768
|
+
edge_pos.add(pos_tuple)
|
|
769
|
+
|
|
770
|
+
# Get corresponding weights in the same order
|
|
771
|
+
weights_ordered = np.array([weights_dict[edge] for edge in list(edge_pos)])
|
|
772
|
+
|
|
773
|
+
plt.subplots(figsize=(6, 6))
|
|
774
|
+
|
|
775
|
+
# Draw the graph
|
|
776
|
+
visualization.mpl_draw(
|
|
777
|
+
graph,
|
|
778
|
+
with_labels=True,
|
|
779
|
+
node_color=node_colors,
|
|
780
|
+
pos=pos,
|
|
781
|
+
labels=lambda node: node,
|
|
782
|
+
width=7 * weights_ordered / np.max(weights_ordered),
|
|
783
|
+
) # type: ignore[call-arg]
|
|
784
|
+
|
|
785
|
+
# Add edge labels using matplotlib's annotate
|
|
786
|
+
for edge in edges_graph:
|
|
787
|
+
x1, y1 = pos[edge[0]]
|
|
788
|
+
x2, y2 = pos[edge[1]]
|
|
789
|
+
x = (x1 + x2) / 2
|
|
790
|
+
y = (y1 + y2) / 2
|
|
791
|
+
plt.annotate(
|
|
792
|
+
f"{edge[2]:.1e}",
|
|
793
|
+
xy=(x, y),
|
|
794
|
+
xytext=(0, 0),
|
|
795
|
+
textcoords="offset points",
|
|
796
|
+
ha="center",
|
|
797
|
+
va="center",
|
|
798
|
+
bbox={"boxstyle": "round,pad=0.2", "fc": "white", "ec": "none", "alpha": 0.6},
|
|
799
|
+
)
|
|
800
|
+
|
|
801
|
+
plt.gca().invert_yaxis()
|
|
802
|
+
plt.title(
|
|
803
|
+
"Chip layout with selected qubits in orange\n"
|
|
804
|
+
+ "and gate errors indicated by edge thickness (thinner is better)"
|
|
805
|
+
)
|
|
806
|
+
plt.show()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: iqm-benchmarks
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.23
|
|
4
4
|
Summary: A package for implementation of Quantum Characterization, Verification and Validation (QCVV) techniques on IQM's hardware at gate level abstraction
|
|
5
5
|
Author-email: IQM Finland Oy <developers@meetiqm.com>, Adrian Auer <adrian.auer@meetiqm.com>, Raphael Brieger <raphael.brieger@meetiqm.com>, Alessio Calzona <alessio.calzona@meetiqm.com>, Pedro Figueroa Romero <pedro.romero@meetiqm.com>, Amin Hosseinkhani <amin.hosseinkhani@meetiqm.com>, Miikka Koistinen <miikka@meetiqm.com>, Nadia Milazzo <nadia.milazzo@meetiqm.com>, Vicente Pina Canelles <vicente.pina@meetiqm.com>, Aniket Rath <aniket.rath@meetiqm.com>, Jami Rönkkö <jami@meetiqm.com>, Stefan Seegerer <stefan.seegerer@meetiqm.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/iqm-finland/iqm-benchmarks
|
|
@@ -24,7 +24,7 @@ Requires-Dist: tabulate<1.0.0,>=0.9.0
|
|
|
24
24
|
Requires-Dist: uncertainties<3.3.0,>=3.2.2
|
|
25
25
|
Requires-Dist: pycurl<8.0,>=7.45.3
|
|
26
26
|
Requires-Dist: xarray<2025.0.0,>=2024.6.0
|
|
27
|
-
Requires-Dist: types-
|
|
27
|
+
Requires-Dist: types-requests
|
|
28
28
|
Requires-Dist: myst-nb==1.1.0
|
|
29
29
|
Provides-Extra: cicd
|
|
30
30
|
Requires-Dist: build==1.0.3; extra == "cicd"
|
|
@@ -1,43 +1,43 @@
|
|
|
1
1
|
iqm/benchmarks/__init__.py,sha256=8SRHlADqZjlP8PtbKTMvu-ejpcEZkW8cPmgW8GVKJg8,2638
|
|
2
|
-
iqm/benchmarks/benchmark.py,sha256=
|
|
3
|
-
iqm/benchmarks/benchmark_definition.py,sha256=
|
|
2
|
+
iqm/benchmarks/benchmark.py,sha256=F0WdGFpYGVWMeyKmScfM7b7jvuS8P8UArB3_JwUKJrY,4694
|
|
3
|
+
iqm/benchmarks/benchmark_definition.py,sha256=e4xe0wlWKZqj48_6-zTglMaMeoiA9aGkHrrSgoCfPkM,11271
|
|
4
4
|
iqm/benchmarks/circuit_containers.py,sha256=anEtZEsodYqOX-34oZRmuKGeEpp_VfgG5045Mz4-4hI,7562
|
|
5
5
|
iqm/benchmarks/logging_config.py,sha256=U7olP5Kr75AcLJqNODf9VBhJLVqIvA4AYR6J39D5rww,1052
|
|
6
6
|
iqm/benchmarks/readout_mitigation.py,sha256=Q2SOGWTNgmklOYkNxepAaSaXlxSj0QQyymYY1bOkT8A,11756
|
|
7
|
-
iqm/benchmarks/utils.py,sha256=
|
|
7
|
+
iqm/benchmarks/utils.py,sha256=ZwAGYIhPe4Ij2Ds8drd_0eHwpdBj1L7rm1d33oqN4L8,31928
|
|
8
8
|
iqm/benchmarks/compressive_gst/__init__.py,sha256=LneifgYXtcwo2jcXo7GdUEHL6_peipukShhkrdaTRCA,929
|
|
9
|
-
iqm/benchmarks/compressive_gst/compressive_gst.py,sha256=
|
|
10
|
-
iqm/benchmarks/compressive_gst/gst_analysis.py,sha256=
|
|
9
|
+
iqm/benchmarks/compressive_gst/compressive_gst.py,sha256=D54hnaxxOIyD3ygUMKyxfRMwfUXfK4O5UdzdgscwkHA,25291
|
|
10
|
+
iqm/benchmarks/compressive_gst/gst_analysis.py,sha256=2qY46e-euAcdThcits_Wo7hGb0m69mDZY0d1Qv_wq4M,36104
|
|
11
11
|
iqm/benchmarks/entanglement/__init__.py,sha256=9T7prOwqMmFWdb4t6ETAHZXKK5o6FvU2DvVb6WhNi-U,682
|
|
12
|
-
iqm/benchmarks/entanglement/ghz.py,sha256=
|
|
12
|
+
iqm/benchmarks/entanglement/ghz.py,sha256=HqvdRaiwM879QUrFnULcprdZ-frr1-i4bQ1U8jKjIn0,40993
|
|
13
13
|
iqm/benchmarks/optimization/__init__.py,sha256=_ajW_OibYLCtzU5AUv5c2zuuVYn8ZNeZUcUUSIGt51M,747
|
|
14
|
-
iqm/benchmarks/optimization/qscore.py,sha256=
|
|
14
|
+
iqm/benchmarks/optimization/qscore.py,sha256=Ns3Sik90YQ_Cry1I5tY-QQWcLukpyipAfVb1Lmkkd7M,37957
|
|
15
15
|
iqm/benchmarks/quantum_volume/__init__.py,sha256=i-Q4SpDWELBw7frXnxm1j4wJRcxbIyrS5uEK_v06YHo,951
|
|
16
|
-
iqm/benchmarks/quantum_volume/clops.py,sha256=
|
|
17
|
-
iqm/benchmarks/quantum_volume/quantum_volume.py,sha256=
|
|
16
|
+
iqm/benchmarks/quantum_volume/clops.py,sha256=kMzofZEtB_SmbGWcB9LWGbG96I342Im3xIWDt9nTqUY,31393
|
|
17
|
+
iqm/benchmarks/quantum_volume/quantum_volume.py,sha256=2nNlZeR2VhtKkc3gpCkaR24NE23T0aQXNrepTlUL-8I,36549
|
|
18
18
|
iqm/benchmarks/randomized_benchmarking/__init__.py,sha256=IkKo-7zUChxZZd3my_csQCJfJfZNsV3-JTvdG8uqys4,734
|
|
19
19
|
iqm/benchmarks/randomized_benchmarking/clifford_1q.pkl,sha256=vvSd0pRWxtzyirohO9yf_58mjevkc2-pbuWIEb-4gaw,46928
|
|
20
20
|
iqm/benchmarks/randomized_benchmarking/clifford_2q.pkl,sha256=ZipqU3crPhz2T35qGFgB4GvMyoi_7pnu8NqW5ZP8NXg,90707258
|
|
21
21
|
iqm/benchmarks/randomized_benchmarking/multi_lmfit.py,sha256=Se1ygR4mXn_2_P82Ch31KBnCmY-g_A9NKzE9Ir8nEvw,3247
|
|
22
|
-
iqm/benchmarks/randomized_benchmarking/randomized_benchmarking_common.py,sha256=
|
|
22
|
+
iqm/benchmarks/randomized_benchmarking/randomized_benchmarking_common.py,sha256=SKpNukg_vOEVKpd_MAGj4xTFK5FpFJEwx2Zr_2uSUAQ,41867
|
|
23
23
|
iqm/benchmarks/randomized_benchmarking/clifford_rb/__init__.py,sha256=bTDA156LAl7OLGcMec--1nzDrV1XpPRVq3CquTmucgE,677
|
|
24
|
-
iqm/benchmarks/randomized_benchmarking/clifford_rb/clifford_rb.py,sha256=
|
|
24
|
+
iqm/benchmarks/randomized_benchmarking/clifford_rb/clifford_rb.py,sha256=H-nfuQaIUWcxYeW61iEc79eZwuAFbDvozTVT0xC0HRw,18403
|
|
25
25
|
iqm/benchmarks/randomized_benchmarking/interleaved_rb/__init__.py,sha256=sq6MgN_hwlpkOj10vyCU4e6eKSX-oLcF2L9na6W2Gt4,681
|
|
26
|
-
iqm/benchmarks/randomized_benchmarking/interleaved_rb/interleaved_rb.py,sha256=
|
|
26
|
+
iqm/benchmarks/randomized_benchmarking/interleaved_rb/interleaved_rb.py,sha256=CsnajadrpxQOqEvX8PpMGB5_9_g6W1IT4aaQRYa1Z08,28128
|
|
27
27
|
iqm/benchmarks/randomized_benchmarking/mirror_rb/__init__.py,sha256=ZekEqI_89nXzGO1vjM-b5Uwwicy59M4fYHXfA-f0MIg,674
|
|
28
|
-
iqm/benchmarks/randomized_benchmarking/mirror_rb/mirror_rb.py,sha256=
|
|
28
|
+
iqm/benchmarks/randomized_benchmarking/mirror_rb/mirror_rb.py,sha256=KNIfu3ucb-oxAwOIl5zarRUeWM1SPmiVEQFo6970oUU,34904
|
|
29
29
|
mGST/LICENSE,sha256=TtHNq55cUcbglb7uhVudeBLUh_qPdUoAEvU0BBwFz-k,1098
|
|
30
30
|
mGST/README.md,sha256=v_5kw253csHF4-RfE-44KqFmBXIsSMRmOtN0AUPrRxE,5050
|
|
31
31
|
mGST/additional_fns.py,sha256=_SEJ10FRNM7_CroysT8hCLZTfpm6ZhEIDCY5zPTnhjo,31390
|
|
32
|
-
mGST/algorithm.py,sha256=
|
|
32
|
+
mGST/algorithm.py,sha256=SRwC1sVFShRVxQDfH1bhUvnquvgwJFLSiZ70qo5SwxE,26314
|
|
33
33
|
mGST/compatibility.py,sha256=00DsPnNfOtrQcDTvxBDs-0aMhmuXmOIIxl_Ohy-Emkg,8920
|
|
34
|
-
mGST/low_level_jit.py,sha256=
|
|
34
|
+
mGST/low_level_jit.py,sha256=uE1D3v01FbPpsbP92C4220OQalzOfxgL1Ku89BNkxLY,27377
|
|
35
35
|
mGST/optimization.py,sha256=YHwkzIkYvsZOPjclR-BCQWh24jeqjuXp0BB0WX5Lwow,10559
|
|
36
|
-
mGST/qiskit_interface.py,sha256=
|
|
36
|
+
mGST/qiskit_interface.py,sha256=ajx6Zn5FnrX_T7tMP8xnBLyG4c2ddFRm0Fu2_3r1t30,10118
|
|
37
37
|
mGST/reporting/figure_gen.py,sha256=6Xd8vwfy09hLY1YbJY6TRevuMsQSU4MsWqemly3ZO0I,12970
|
|
38
|
-
mGST/reporting/reporting.py,sha256=
|
|
39
|
-
iqm_benchmarks-2.
|
|
40
|
-
iqm_benchmarks-2.
|
|
41
|
-
iqm_benchmarks-2.
|
|
42
|
-
iqm_benchmarks-2.
|
|
43
|
-
iqm_benchmarks-2.
|
|
38
|
+
mGST/reporting/reporting.py,sha256=B8NWfpZrrSmyH7lwZxd0EbZMYLsAGK1YsHRB4D5qXH4,26002
|
|
39
|
+
iqm_benchmarks-2.23.dist-info/LICENSE,sha256=2Ncb40-hqkTil78RPv3-YiJfKaJ8te9USJgliKqIdSY,11558
|
|
40
|
+
iqm_benchmarks-2.23.dist-info/METADATA,sha256=BvgUDcjk3LfQCM3ncFvwEILGrFt9oK471iLIM5P0KL8,10386
|
|
41
|
+
iqm_benchmarks-2.23.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
|
42
|
+
iqm_benchmarks-2.23.dist-info/top_level.txt,sha256=3G23Z-1LGf-IOzTCUl6QwWqiQ3USz25Zt90Ihq192to,9
|
|
43
|
+
iqm_benchmarks-2.23.dist-info/RECORD,,
|
mGST/algorithm.py
CHANGED
|
@@ -83,7 +83,7 @@ def A_SFN_riem_Hess(K, A, B, y, J, d, r, n_povm, lam=1e-3):
|
|
|
83
83
|
# derivative
|
|
84
84
|
Fy = dA_.reshape(n, pdim)
|
|
85
85
|
Y = A.reshape(n, pdim)
|
|
86
|
-
rGrad = Fy.conj() - Y @ Fy.T @ Y
|
|
86
|
+
rGrad = 2 * (Fy.conj() - Y @ Fy.T @ Y)
|
|
87
87
|
G = np.array([rGrad, rGrad.conj()]).reshape(-1)
|
|
88
88
|
|
|
89
89
|
P = np.eye(n) - Y @ Y.T.conj()
|
|
@@ -193,7 +193,7 @@ def B_SFN_riem_Hess(K, A, B, y, J, d, r, n_povm, lam=1e-3):
|
|
|
193
193
|
# derivative
|
|
194
194
|
Fy = dB_.reshape(n)
|
|
195
195
|
Y = B.reshape(n)
|
|
196
|
-
rGrad = Fy.conj() - Y * (Fy.T @ Y)
|
|
196
|
+
rGrad = 2 * (Fy.conj() - Y * (Fy.T @ Y))
|
|
197
197
|
G = np.array([rGrad, rGrad.conj()]).reshape(-1)
|
|
198
198
|
|
|
199
199
|
P = np.eye(n) - np.outer(Y, Y.T.conj())
|
|
@@ -297,10 +297,8 @@ def gd(K, E, rho, y, J, d, r, rK, fixed_gates, ls="COBYLA"):
|
|
|
297
297
|
Fy = dK_[k].reshape(n, pdim)
|
|
298
298
|
Y = K[k].reshape(n, pdim)
|
|
299
299
|
# Riem. gradient taken from conjugate derivative
|
|
300
|
-
rGrad = Fy.conj() - Y @ Fy.T @ Y
|
|
300
|
+
rGrad = 2 * (Fy.conj() - Y @ Fy.T @ Y)
|
|
301
301
|
Delta[k] = rGrad
|
|
302
|
-
|
|
303
|
-
Delta = tangent_proj(K, Delta, d, rK)
|
|
304
302
|
res = minimize(lineobjf_isom_geodesic, 1e-8, args=(Delta, K, E, rho, J, y), method=ls, options={"maxiter": 200})
|
|
305
303
|
a = res.x
|
|
306
304
|
K_new = update_K_geodesic(K, Delta, a)
|
|
@@ -368,7 +366,7 @@ def SFN_riem_Hess(K, E, rho, y, J, d, r, rK, lam=1e-3, ls="COBYLA", fixed_gates=
|
|
|
368
366
|
Fy = dK_[k].reshape(n, pdim)
|
|
369
367
|
Y = K[k].reshape(n, pdim)
|
|
370
368
|
# riemannian gradient, taken from conjugate derivative
|
|
371
|
-
rGrad = Fy.conj() - Y @ Fy.T @ Y
|
|
369
|
+
rGrad = 2 * (Fy.conj() - Y @ Fy.T @ Y)
|
|
372
370
|
G = np.array([rGrad, rGrad.conj()]).reshape(-1)
|
|
373
371
|
|
|
374
372
|
P = np.eye(n) - Y @ Y.T.conj()
|
|
@@ -471,7 +469,7 @@ def SFN_riem_Hess_full(K, E, rho, y, J, d, r, rK, lam=1e-3, ls="COBYLA"):
|
|
|
471
469
|
for k in range(d):
|
|
472
470
|
Fy = dK_[k].reshape((n, pdim))
|
|
473
471
|
Y = K[k].reshape((n, pdim))
|
|
474
|
-
rGrad = Fy.conj() - Y @ Fy.T @ Y
|
|
472
|
+
rGrad = 2 * (Fy.conj() - Y @ Fy.T @ Y)
|
|
475
473
|
|
|
476
474
|
G[0, k, :] = rGrad.reshape(-1)
|
|
477
475
|
G[1, k, :] = rGrad.conj().reshape(-1)
|
mGST/low_level_jit.py
CHANGED
|
@@ -104,7 +104,7 @@ def contract(X, j_vec):
|
|
|
104
104
|
return res
|
|
105
105
|
|
|
106
106
|
|
|
107
|
-
@njit(cache=True, fastmath=True)
|
|
107
|
+
@njit(cache=True, fastmath=True, parallel=True)
|
|
108
108
|
def objf(X, E, rho, J, y):
|
|
109
109
|
"""Calculate the objective function value for matrices, POVM elements, and target values.
|
|
110
110
|
|
|
@@ -132,12 +132,14 @@ def objf(X, E, rho, J, y):
|
|
|
132
132
|
"""
|
|
133
133
|
m = len(J)
|
|
134
134
|
n_povm = y.shape[0]
|
|
135
|
-
objf_ = 0
|
|
135
|
+
objf_: float = 0
|
|
136
136
|
for i in prange(m): # pylint: disable=not-an-iterable
|
|
137
137
|
j = J[i][J[i] >= 0]
|
|
138
|
-
|
|
138
|
+
state = rho
|
|
139
|
+
for ind in j[::-1]:
|
|
140
|
+
state = X[ind] @ state
|
|
139
141
|
for o in range(n_povm):
|
|
140
|
-
objf_ += abs(E[o].conj() @
|
|
142
|
+
objf_ += abs(E[o].conj() @ state - y[o, i]) ** 2
|
|
141
143
|
return objf_ / m / n_povm
|
|
142
144
|
|
|
143
145
|
|
|
@@ -240,7 +242,7 @@ def Mp_norm_lower(X_true, E_true, rho_true, X, E, rho, J, n_povm, p):
|
|
|
240
242
|
return dist ** (1 / p) / m / n_povm, max_dist ** (1 / p)
|
|
241
243
|
|
|
242
244
|
|
|
243
|
-
@njit(cache=True)
|
|
245
|
+
@njit(cache=True, parallel=True)
|
|
244
246
|
def dK(X, K, E, rho, J, y, d, r, rK):
|
|
245
247
|
"""Compute the derivative of the objective function with respect to the Kraus tensor K.
|
|
246
248
|
|
|
@@ -274,26 +276,32 @@ def dK(X, K, E, rho, J, y, d, r, rK):
|
|
|
274
276
|
The derivative objective function with respect to the Kraus tensor K,
|
|
275
277
|
reshaped to (d, rK, pdim, pdim), and scaled by 2/m/n_povm.
|
|
276
278
|
"""
|
|
279
|
+
# pylint: disable=too-many-nested-blocks
|
|
277
280
|
K = K.reshape(d, rK, -1)
|
|
278
281
|
pdim = int(np.sqrt(r))
|
|
279
282
|
n_povm = y.shape[0]
|
|
280
283
|
dK_ = np.zeros((d, rK, r))
|
|
281
284
|
dK_ = np.ascontiguousarray(dK_.astype(np.complex128))
|
|
282
285
|
m = len(J)
|
|
283
|
-
|
|
286
|
+
|
|
287
|
+
for k in prange(d): # pylint: disable=not-an-iterable
|
|
284
288
|
for n in range(m):
|
|
285
289
|
j = J[n][J[n] >= 0]
|
|
286
290
|
for i, j_curr in enumerate(j):
|
|
287
291
|
if j_curr == k:
|
|
292
|
+
R = rho.copy()
|
|
293
|
+
for ind in j[i + 1 :][::-1]:
|
|
294
|
+
R = X[ind] @ R
|
|
288
295
|
for o in range(n_povm):
|
|
289
|
-
L = E[o].conj()
|
|
290
|
-
|
|
296
|
+
L = E[o].conj()
|
|
297
|
+
for ind in j[:i]:
|
|
298
|
+
L = L @ X[ind]
|
|
291
299
|
D_ind = L @ X[k] @ R - y[o, n]
|
|
292
300
|
dK_[k] += D_ind * K[k].conj() @ np.kron(L.reshape(pdim, pdim).T, R.reshape(pdim, pdim).T)
|
|
293
301
|
return dK_.reshape(d, rK, pdim, pdim) * 2 / m / n_povm
|
|
294
302
|
|
|
295
303
|
|
|
296
|
-
@njit(cache=True)
|
|
304
|
+
@njit(cache=True, parallel=False)
|
|
297
305
|
def dK_dMdM(X, K, E, rho, J, y, d, r, rK):
|
|
298
306
|
"""Compute the derivatives of the objective function with respect to K and the
|
|
299
307
|
product of derivatives of the measurement map with respect to K.
|
|
@@ -397,7 +405,7 @@ def ddM(X, K, E, rho, J, y, d, r, rK):
|
|
|
397
405
|
ddK = np.zeros((d**2, rK**2, r, r))
|
|
398
406
|
ddK = np.ascontiguousarray(ddK.astype(np.complex128))
|
|
399
407
|
dconjdK = np.zeros((d**2, rK**2, r, r))
|
|
400
|
-
dconjdK = np.ascontiguousarray(
|
|
408
|
+
dconjdK = np.ascontiguousarray(dconjdK.astype(np.complex128))
|
|
401
409
|
m = len(J)
|
|
402
410
|
for k in range(d**2):
|
|
403
411
|
k1, k2 = local_basis(k, d, 2)
|
|
@@ -516,16 +524,17 @@ def dA(X, A, B, J, y, r, pdim, n_povm):
|
|
|
516
524
|
for k in range(n_povm):
|
|
517
525
|
E[k] = (A[k].T.conj() @ A[k]).reshape(-1)
|
|
518
526
|
rho = (B @ B.T.conj()).reshape(-1)
|
|
519
|
-
dA_ = np.zeros((n_povm, pdim, pdim))
|
|
520
|
-
dA_ = dA_.astype(np.complex128)
|
|
527
|
+
dA_ = np.zeros((n_povm, pdim, pdim)).astype(np.complex128)
|
|
521
528
|
m = len(J)
|
|
522
529
|
for n in prange(m): # pylint: disable=not-an-iterable
|
|
523
|
-
|
|
524
|
-
j = J[n][J[n] >= 0][1:]
|
|
530
|
+
j = J[n][J[n] >= 0]
|
|
525
531
|
inner_deriv = contract(X, j) @ rho
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
532
|
+
dA_step = np.zeros((n_povm, pdim, pdim)).astype(np.complex128)
|
|
533
|
+
for o in range(n_povm):
|
|
534
|
+
D_ind = E[o].conj() @ inner_deriv - y[o, n]
|
|
535
|
+
dA_step[o] += D_ind * A[o].conj() @ inner_deriv.reshape(pdim, pdim).T
|
|
536
|
+
dA_ += dA_step
|
|
537
|
+
return dA_ * 2 / m / n_povm
|
|
529
538
|
|
|
530
539
|
|
|
531
540
|
@njit(parallel=True, cache=True)
|
|
@@ -615,13 +624,21 @@ def ddA_derivs(X, A, B, J, y, r, pdim, n_povm):
|
|
|
615
624
|
for n in prange(m): # pylint: disable=not-an-iterable
|
|
616
625
|
j = J[n][J[n] >= 0]
|
|
617
626
|
R = contract(X, j) @ rho
|
|
627
|
+
dA_step = np.zeros((n_povm, pdim, pdim)).astype(np.complex128)
|
|
628
|
+
dMdM_step = np.zeros((n_povm, r, r)).astype(np.complex128)
|
|
629
|
+
dMconjdM_step = np.zeros((n_povm, r, r)).astype(np.complex128)
|
|
630
|
+
dconjdA_step = np.zeros((n_povm, r, r)).astype(np.complex128)
|
|
618
631
|
for o in range(n_povm):
|
|
619
632
|
D_ind = E[o].conj() @ R - y[o, n]
|
|
620
633
|
dM = A[o].conj() @ R.reshape(pdim, pdim).T
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
634
|
+
dMdM_step[o] += np.outer(dM, dM)
|
|
635
|
+
dMconjdM_step[o] += np.outer(dM.conj(), dM)
|
|
636
|
+
dA_step[o] += D_ind * dM
|
|
637
|
+
dconjdA_step[o] += D_ind * np.kron(np.eye(pdim).astype(np.complex128), R.reshape(pdim, pdim).T)
|
|
638
|
+
dA_ += dA_step
|
|
639
|
+
dMdM += dMdM_step
|
|
640
|
+
dMconjdM += dMconjdM_step
|
|
641
|
+
dconjdA += dconjdA_step
|
|
625
642
|
return dA_ * 2 / m / n_povm, dMdM * 2 / m / n_povm, dMconjdM * 2 / m / n_povm, dconjdA * 2 / m / n_povm
|
|
626
643
|
|
|
627
644
|
|
mGST/qiskit_interface.py
CHANGED
|
@@ -31,7 +31,7 @@ def qiskit_gate_to_operator(gate_set):
|
|
|
31
31
|
numpy.ndarray
|
|
32
32
|
An array of process matrices. Each element in the array is a 2D NumPy array.
|
|
33
33
|
"""
|
|
34
|
-
return np.array([[Operator(gate).to_matrix()] for gate in gate_set])
|
|
34
|
+
return np.array([[Operator(gate).reverse_qargs().to_matrix()] for gate in gate_set])
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
def add_idle_gates(gate_set, active_qubits, gate_qubits):
|
mGST/reporting/reporting.py
CHANGED
|
@@ -339,7 +339,7 @@ def compute_sparsest_Pauli_Hamiltonian(U_set):
|
|
|
339
339
|
pdim = U_set.shape[1]
|
|
340
340
|
pp_vecs = []
|
|
341
341
|
|
|
342
|
-
for
|
|
342
|
+
for _, U in enumerate(U_set):
|
|
343
343
|
# Schur decomposition finds the unitary diagonalization of a unitary matrix, which is not always returned by np.linalg.eig
|
|
344
344
|
T, evecs = schur(U)
|
|
345
345
|
evals = np.diag(T)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|