cirq-core 1.3.0.dev20231201141002__py3-none-any.whl → 1.4.0__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.
- cirq/__init__.py +4 -0
- cirq/_compat.py +9 -11
- cirq/_compat_test.py +45 -56
- cirq/_version.py +31 -1
- cirq/_version_test.py +1 -1
- cirq/circuits/circuit.py +13 -8
- cirq/circuits/circuit_operation.py +2 -1
- cirq/circuits/circuit_test.py +2 -2
- cirq/circuits/frozen_circuit.py +3 -2
- cirq/circuits/moment.py +12 -10
- cirq/circuits/qasm_output.py +5 -1
- cirq/circuits/qasm_output_test.py +25 -10
- cirq/contrib/qcircuit/qcircuit_diagram_info.py +9 -7
- cirq/contrib/quimb/mps_simulator_test.py +1 -1
- cirq/contrib/quimb/state_vector.py +9 -2
- cirq/contrib/svg/svg.py +2 -1
- cirq/contrib/svg/svg_test.py +1 -0
- cirq/devices/grid_qubit.py +85 -32
- cirq/devices/grid_qubit_test.py +22 -4
- cirq/devices/line_qubit.py +74 -26
- cirq/devices/line_qubit_test.py +19 -0
- cirq/devices/noise_utils.py +33 -31
- cirq/devices/noise_utils_test.py +1 -84
- cirq/devices/superconducting_qubits_noise_properties.py +7 -6
- cirq/experiments/__init__.py +8 -0
- cirq/experiments/qubit_characterizations.py +288 -44
- cirq/experiments/qubit_characterizations_test.py +61 -7
- cirq/experiments/random_quantum_circuit_generation.py +1 -1
- cirq/experiments/single_qubit_readout_calibration.py +132 -6
- cirq/experiments/single_qubit_readout_calibration_test.py +3 -1
- cirq/experiments/t1_decay_experiment.py +14 -7
- cirq/experiments/t1_decay_experiment_test.py +14 -26
- cirq/experiments/two_qubit_xeb.py +483 -0
- cirq/experiments/two_qubit_xeb_test.py +304 -0
- cirq/json_resolver_cache.py +2 -0
- cirq/linalg/decompositions.py +11 -13
- cirq/linalg/decompositions_test.py +1 -3
- cirq/linalg/diagonalize.py +5 -4
- cirq/linalg/predicates.py +8 -6
- cirq/linalg/transformations.py +2 -1
- cirq/linalg/transformations_test.py +1 -1
- cirq/ops/__init__.py +2 -0
- cirq/ops/clifford_gate.py +59 -16
- cirq/ops/common_gates_test.py +1 -2
- cirq/ops/control_values.py +4 -3
- cirq/ops/controlled_gate_test.py +1 -3
- cirq/ops/gate_operation.py +10 -1
- cirq/ops/named_qubit.py +74 -28
- cirq/ops/named_qubit_test.py +19 -0
- cirq/ops/parity_gates.py +5 -0
- cirq/ops/parity_gates_test.py +2 -10
- cirq/ops/pauli_gates.py +5 -2
- cirq/ops/pauli_string.py +2 -2
- cirq/ops/permutation_gate.py +16 -18
- cirq/ops/phased_iswap_gate_test.py +1 -3
- cirq/ops/phased_x_gate.py +1 -1
- cirq/ops/phased_x_z_gate.py +17 -1
- cirq/ops/phased_x_z_gate_test.py +24 -0
- cirq/ops/qid_util.py +4 -8
- cirq/ops/qubit_manager.py +7 -4
- cirq/ops/qubit_manager_test.py +20 -0
- cirq/ops/raw_types.py +5 -2
- cirq/ops/raw_types_test.py +14 -15
- cirq/ops/uniform_superposition_gate.py +123 -0
- cirq/ops/uniform_superposition_gate_test.py +94 -0
- cirq/protocols/approximate_equality_protocol_test.py +2 -2
- cirq/protocols/circuit_diagram_info_protocol.py +6 -4
- cirq/protocols/commutes_protocol.py +2 -4
- cirq/protocols/decompose_protocol.py +7 -12
- cirq/protocols/decompose_protocol_test.py +7 -3
- cirq/protocols/has_stabilizer_effect_protocol.py +1 -5
- cirq/protocols/has_stabilizer_effect_protocol_test.py +13 -4
- cirq/protocols/json_serialization.py +51 -181
- cirq/protocols/json_serialization_test.py +13 -47
- cirq/protocols/json_test_data/CircuitOperation.json +131 -148
- cirq/protocols/json_test_data/CircuitOperation.json_inward +55 -0
- cirq/protocols/json_test_data/CircuitOperation.repr_inward +6 -0
- cirq/protocols/json_test_data/FrozenCircuit.json +196 -210
- cirq/protocols/json_test_data/FrozenCircuit.json_inward +35 -0
- cirq/protocols/json_test_data/FrozenCircuit.repr_inward +4 -0
- cirq/protocols/json_test_data/UniformSuperpositionGate.json +5 -0
- cirq/protocols/json_test_data/UniformSuperpositionGate.repr +1 -0
- cirq/protocols/json_test_data/cirq.MSGate.json +4 -0
- cirq/protocols/json_test_data/cirq.MSGate.repr +1 -0
- cirq/protocols/json_test_data/spec.py +2 -0
- cirq/protocols/pow_protocol_test.py +1 -3
- cirq/protocols/resolve_parameters.py +4 -2
- cirq/qis/__init__.py +10 -0
- cirq/qis/clifford_tableau.py +8 -2
- cirq/qis/noise_utils.py +123 -0
- cirq/qis/noise_utils_test.py +97 -0
- cirq/sim/classical_simulator.py +227 -87
- cirq/sim/classical_simulator_test.py +135 -0
- cirq/sim/clifford/clifford_simulator_test.py +4 -2
- cirq/sim/mux.py +5 -3
- cirq/sim/simulation_product_state.py +15 -10
- cirq/sim/simulation_state.py +1 -1
- cirq/sim/simulation_state_test.py +2 -2
- cirq/sim/simulator_base.py +3 -3
- cirq/sim/state_vector_simulation_state.py +4 -4
- cirq/sim/state_vector_simulator.py +17 -2
- cirq/study/__init__.py +1 -0
- cirq/study/result.py +14 -0
- cirq/study/result_test.py +6 -0
- cirq/study/sweeps.py +4 -2
- cirq/study/sweeps_test.py +8 -0
- cirq/testing/__init__.py +6 -1
- cirq/testing/_compat_test_data/__init__.py +3 -3
- cirq/testing/_compat_test_data/module_a/__init__.py +2 -2
- cirq/testing/circuit_compare.py +1 -1
- cirq/testing/consistent_qasm.py +6 -0
- cirq/testing/gate_features.py +10 -0
- cirq/testing/lin_alg_utils.py +5 -3
- cirq/transformers/__init__.py +15 -0
- cirq/transformers/analytical_decompositions/controlled_gate_decomposition.py +3 -1
- cirq/transformers/analytical_decompositions/two_qubit_to_cz.py +24 -0
- cirq/transformers/analytical_decompositions/two_qubit_to_cz_test.py +17 -0
- cirq/transformers/dynamical_decoupling.py +122 -0
- cirq/transformers/dynamical_decoupling_test.py +123 -0
- cirq/transformers/gauge_compiling/__init__.py +26 -0
- cirq/transformers/gauge_compiling/cz_gauge.py +46 -0
- cirq/transformers/gauge_compiling/cz_gauge_test.py +23 -0
- cirq/transformers/gauge_compiling/gauge_compiling.py +214 -0
- cirq/transformers/gauge_compiling/gauge_compiling_test.py +41 -0
- cirq/transformers/gauge_compiling/gauge_compiling_test_utils.py +83 -0
- cirq/transformers/gauge_compiling/gauge_compiling_test_utils_test.py +52 -0
- cirq/transformers/gauge_compiling/iswap_gauge.py +105 -0
- cirq/transformers/gauge_compiling/iswap_gauge_test.py +23 -0
- cirq/transformers/gauge_compiling/spin_inversion_gauge.py +33 -0
- cirq/transformers/gauge_compiling/spin_inversion_gauge_test.py +37 -0
- cirq/transformers/gauge_compiling/sqrt_cz_gauge.py +64 -0
- cirq/transformers/gauge_compiling/sqrt_cz_gauge_test.py +27 -0
- cirq/transformers/gauge_compiling/sqrt_iswap_gauge.py +94 -0
- cirq/transformers/gauge_compiling/sqrt_iswap_gauge_test.py +22 -0
- cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation.py +1 -0
- cirq/transformers/merge_k_qubit_gates_test.py +23 -23
- cirq/transformers/merge_single_qubit_gates_test.py +14 -14
- cirq/transformers/optimize_for_target_gateset.py +39 -17
- cirq/transformers/optimize_for_target_gateset_test.py +189 -39
- cirq/transformers/qubit_management_transformers.py +1 -1
- cirq/transformers/routing/visualize_routed_circuit_test.py +17 -17
- cirq/transformers/stratify_test.py +13 -13
- cirq/transformers/target_gatesets/compilation_target_gateset.py +26 -2
- cirq/transformers/target_gatesets/compilation_target_gateset_test.py +16 -16
- cirq/transformers/target_gatesets/cz_gateset.py +4 -0
- cirq/transformers/transformer_api.py +1 -2
- cirq/transformers/transformer_primitives.py +15 -14
- cirq/transformers/transformer_primitives_test.py +99 -72
- cirq/value/classical_data.py +6 -6
- cirq/value/value_equality_attr.py +4 -0
- cirq/work/sampler.py +3 -4
- cirq/work/sampler_test.py +25 -0
- {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/METADATA +10 -19
- {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/RECORD +157 -130
- {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/WHEEL +1 -1
- {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/LICENSE +0 -0
- {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/top_level.txt +0 -0
|
@@ -17,9 +17,10 @@
|
|
|
17
17
|
|
|
18
18
|
import abc
|
|
19
19
|
from dataclasses import dataclass, field
|
|
20
|
+
from functools import cached_property
|
|
20
21
|
from typing import Dict, TYPE_CHECKING, List, Set, Type
|
|
21
22
|
|
|
22
|
-
from cirq import
|
|
23
|
+
from cirq import ops, devices, qis
|
|
23
24
|
from cirq.devices import noise_utils
|
|
24
25
|
|
|
25
26
|
if TYPE_CHECKING:
|
|
@@ -128,10 +129,10 @@ class SuperconductingQubitsNoiseProperties(devices.NoiseProperties, abc.ABC):
|
|
|
128
129
|
def _get_pauli_error(self, p_error: float, op_id: noise_utils.OpIdentifier):
|
|
129
130
|
time_ns = float(self.gate_times_ns[op_id.gate_type])
|
|
130
131
|
for q in op_id.qubits:
|
|
131
|
-
p_error -=
|
|
132
|
+
p_error -= qis.decoherence_pauli_error(self.t1_ns[q], self.tphi_ns[q], time_ns)
|
|
132
133
|
return p_error
|
|
133
134
|
|
|
134
|
-
@
|
|
135
|
+
@cached_property
|
|
135
136
|
def _depolarizing_error(self) -> Dict[noise_utils.OpIdentifier, float]:
|
|
136
137
|
"""Returns the portion of Pauli error from depolarization."""
|
|
137
138
|
depol_errors = {}
|
|
@@ -179,9 +180,9 @@ class SuperconductingQubitsNoiseProperties(devices.NoiseProperties, abc.ABC):
|
|
|
179
180
|
p_00, p_11 = self.readout_errors[qubit]
|
|
180
181
|
p = p_11 / (p_00 + p_11)
|
|
181
182
|
gamma = p_11 / p
|
|
182
|
-
added_measure_errors[
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
added_measure_errors[noise_utils.OpIdentifier(ops.MeasurementGate, qubit)] = (
|
|
184
|
+
ops.generalized_amplitude_damp(p, gamma).on(qubit)
|
|
185
|
+
)
|
|
185
186
|
|
|
186
187
|
noise_models.append(
|
|
187
188
|
devices.InsertionNoiseModel(ops_added=added_measure_errors, prepend=True)
|
cirq/experiments/__init__.py
CHANGED
|
@@ -20,6 +20,7 @@ from cirq.experiments.qubit_characterizations import (
|
|
|
20
20
|
TomographyResult,
|
|
21
21
|
two_qubit_randomized_benchmarking,
|
|
22
22
|
two_qubit_state_tomography,
|
|
23
|
+
parallel_single_qubit_randomized_benchmarking,
|
|
23
24
|
)
|
|
24
25
|
|
|
25
26
|
from cirq.experiments.fidelity_estimation import (
|
|
@@ -63,3 +64,10 @@ from cirq.experiments.t1_decay_experiment import t1_decay, T1DecayResult
|
|
|
63
64
|
from cirq.experiments.t2_decay_experiment import t2_decay, T2DecayResult
|
|
64
65
|
|
|
65
66
|
from cirq.experiments.xeb_fitting import XEBPhasedFSimCharacterizationOptions
|
|
67
|
+
|
|
68
|
+
from cirq.experiments.two_qubit_xeb import (
|
|
69
|
+
InferredXEBResult,
|
|
70
|
+
TwoQubitXEBResult,
|
|
71
|
+
parallel_two_qubit_xeb,
|
|
72
|
+
run_rb_and_xeb,
|
|
73
|
+
)
|
|
@@ -14,15 +14,32 @@
|
|
|
14
14
|
|
|
15
15
|
import dataclasses
|
|
16
16
|
import itertools
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
import functools
|
|
18
|
+
|
|
19
|
+
from typing import (
|
|
20
|
+
Any,
|
|
21
|
+
cast,
|
|
22
|
+
Iterator,
|
|
23
|
+
List,
|
|
24
|
+
Optional,
|
|
25
|
+
Sequence,
|
|
26
|
+
Tuple,
|
|
27
|
+
TYPE_CHECKING,
|
|
28
|
+
Mapping,
|
|
29
|
+
Dict,
|
|
30
|
+
)
|
|
19
31
|
import numpy as np
|
|
32
|
+
from scipy.optimize import curve_fit
|
|
20
33
|
|
|
21
34
|
from matplotlib import pyplot as plt
|
|
35
|
+
import cirq.vis.heatmap as cirq_heatmap
|
|
36
|
+
import cirq.vis.histogram as cirq_histogram
|
|
22
37
|
|
|
23
38
|
# this is for older systems with matplotlib <3.2 otherwise 3d projections fail
|
|
24
39
|
from mpl_toolkits import mplot3d
|
|
25
40
|
from cirq import circuits, ops, protocols
|
|
41
|
+
from cirq.devices import grid_qubit
|
|
42
|
+
|
|
26
43
|
|
|
27
44
|
if TYPE_CHECKING:
|
|
28
45
|
import cirq
|
|
@@ -46,11 +63,11 @@ class Cliffords:
|
|
|
46
63
|
s1_y
|
|
47
64
|
"""
|
|
48
65
|
|
|
49
|
-
c1_in_xy: List[List[ops.
|
|
50
|
-
c1_in_xz: List[List[ops.
|
|
51
|
-
s1: List[List[ops.
|
|
52
|
-
s1_x: List[List[ops.
|
|
53
|
-
s1_y: List[List[ops.
|
|
66
|
+
c1_in_xy: List[List[ops.SingleQubitCliffordGate]]
|
|
67
|
+
c1_in_xz: List[List[ops.SingleQubitCliffordGate]]
|
|
68
|
+
s1: List[List[ops.SingleQubitCliffordGate]]
|
|
69
|
+
s1_x: List[List[ops.SingleQubitCliffordGate]]
|
|
70
|
+
s1_y: List[List[ops.SingleQubitCliffordGate]]
|
|
54
71
|
|
|
55
72
|
|
|
56
73
|
class RandomizedBenchMarkResult:
|
|
@@ -92,13 +109,165 @@ class RandomizedBenchMarkResult:
|
|
|
92
109
|
fig, ax = plt.subplots(1, 1, figsize=(8, 8)) # pragma: no cover
|
|
93
110
|
ax = cast(plt.Axes, ax) # pragma: no cover
|
|
94
111
|
ax.set_ylim((0.0, 1.0)) # pragma: no cover
|
|
95
|
-
ax.plot(self._num_cfds_seq, self._gnd_state_probs, 'ro
|
|
112
|
+
ax.plot(self._num_cfds_seq, self._gnd_state_probs, 'ro', label='data', **plot_kwargs)
|
|
113
|
+
x = np.linspace(self._num_cfds_seq[0], self._num_cfds_seq[-1], 100)
|
|
114
|
+
opt_params, _ = self._fit_exponential()
|
|
115
|
+
ax.plot(x, opt_params[0] * opt_params[2] ** x + opt_params[1], '--k', label='fit')
|
|
116
|
+
ax.legend(loc='upper right')
|
|
96
117
|
ax.set_xlabel(r"Number of Cliffords")
|
|
97
118
|
ax.set_ylabel('Ground State Probability')
|
|
98
119
|
if show_plot:
|
|
99
120
|
fig.show()
|
|
100
121
|
return ax
|
|
101
122
|
|
|
123
|
+
def pauli_error(self) -> float:
|
|
124
|
+
r"""Returns the Pauli error inferred from randomized benchmarking.
|
|
125
|
+
|
|
126
|
+
If sequence fidelity $F$ decays with number of gates $m$ as
|
|
127
|
+
|
|
128
|
+
$$F = A p^m + B,$$
|
|
129
|
+
|
|
130
|
+
where $0 < p < 1$, then the Pauli error $r_p$ is given by
|
|
131
|
+
|
|
132
|
+
$$r_p = (1 - 1/d^2) * (1 - p),$$
|
|
133
|
+
|
|
134
|
+
where $d = 2^N_Q$ is the Hilbert space dimension and $N_Q$ is the number of qubits.
|
|
135
|
+
"""
|
|
136
|
+
opt_params, _ = self._fit_exponential()
|
|
137
|
+
p = opt_params[2]
|
|
138
|
+
return (1.0 - 1.0 / 4.0) * (1.0 - p)
|
|
139
|
+
|
|
140
|
+
def _fit_exponential(self) -> Tuple[np.ndarray, np.ndarray]:
|
|
141
|
+
exp_fit = lambda x, A, B, p: A * p**x + B
|
|
142
|
+
return curve_fit(
|
|
143
|
+
f=exp_fit,
|
|
144
|
+
xdata=self._num_cfds_seq,
|
|
145
|
+
ydata=self._gnd_state_probs,
|
|
146
|
+
p0=[0.5, 0.5, 1.0 - 1e-3],
|
|
147
|
+
bounds=([0, 0.25, 0], [0.5, 0.75, 1]),
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@dataclasses.dataclass(frozen=True)
|
|
152
|
+
class ParallelRandomizedBenchmarkingResult:
|
|
153
|
+
"""Results from a parallel randomized benchmarking experiment."""
|
|
154
|
+
|
|
155
|
+
results_dictionary: Mapping['cirq.Qid', 'RandomizedBenchMarkResult']
|
|
156
|
+
|
|
157
|
+
def plot_single_qubit(
|
|
158
|
+
self, qubit: 'cirq.Qid', ax: Optional[plt.Axes] = None, **plot_kwargs: Any
|
|
159
|
+
) -> plt.Axes:
|
|
160
|
+
"""Plot the raw data for the specified qubit.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
qubit: Plot data for this qubit.
|
|
164
|
+
ax: the plt.Axes to plot on. If not given, a new figure is created,
|
|
165
|
+
plotted on, and shown.
|
|
166
|
+
**plot_kwargs: Arguments to be passed to 'plt.Axes.plot'.
|
|
167
|
+
Returns:
|
|
168
|
+
The plt.Axes containing the plot.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
return self.results_dictionary[qubit].plot(ax, **plot_kwargs)
|
|
172
|
+
|
|
173
|
+
def pauli_error(self) -> Mapping['cirq.Qid', float]:
|
|
174
|
+
"""Return a dictionary of Pauli errors.
|
|
175
|
+
Returns:
|
|
176
|
+
A dictionary containing the Pauli errors for all qubits.
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
qubit: self.results_dictionary[qubit].pauli_error() for qubit in self.results_dictionary
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
def plot_heatmap(
|
|
184
|
+
self,
|
|
185
|
+
ax: Optional[plt.Axes] = None,
|
|
186
|
+
annotation_format: str = '0.1%',
|
|
187
|
+
title: str = 'Single-qubit Pauli error',
|
|
188
|
+
**plot_kwargs: Any,
|
|
189
|
+
) -> plt.Axes:
|
|
190
|
+
"""Plot a heatmap of the Pauli errors. If qubits are not cirq.GridQubits, throws an error.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
ax: the plt.Axes to plot on. If not given, a new figure is created,
|
|
194
|
+
plotted on, and shown.
|
|
195
|
+
annotation_format: The format string for the numbers in the heatmap.
|
|
196
|
+
title: The title printed above the heatmap.
|
|
197
|
+
**plot_kwargs: Arguments to be passed to 'cirq.Heatmap.plot()'.
|
|
198
|
+
Returns:
|
|
199
|
+
The plt.Axes containing the plot.
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
pauli_errors = self.pauli_error()
|
|
203
|
+
pauli_errors_with_grid_qubit_keys = {}
|
|
204
|
+
for qubit in pauli_errors:
|
|
205
|
+
assert type(qubit) == grid_qubit.GridQubit, "qubits must be cirq.GridQubits"
|
|
206
|
+
pauli_errors_with_grid_qubit_keys[qubit] = pauli_errors[qubit] # just for typecheck
|
|
207
|
+
|
|
208
|
+
if ax is None:
|
|
209
|
+
_, ax = plt.subplots(dpi=200, facecolor='white')
|
|
210
|
+
|
|
211
|
+
ax, _ = cirq_heatmap.Heatmap(pauli_errors_with_grid_qubit_keys).plot(
|
|
212
|
+
ax, annotation_format=annotation_format, title=title, **plot_kwargs
|
|
213
|
+
)
|
|
214
|
+
return ax
|
|
215
|
+
|
|
216
|
+
def plot_integrated_histogram(
|
|
217
|
+
self,
|
|
218
|
+
ax: Optional[plt.Axes] = None,
|
|
219
|
+
cdf_on_x: bool = False,
|
|
220
|
+
axis_label: str = 'Pauli error',
|
|
221
|
+
semilog: bool = True,
|
|
222
|
+
median_line: bool = True,
|
|
223
|
+
median_label: Optional[str] = 'median',
|
|
224
|
+
mean_line: bool = False,
|
|
225
|
+
mean_label: Optional[str] = 'mean',
|
|
226
|
+
show_zero: bool = False,
|
|
227
|
+
title: Optional[str] = None,
|
|
228
|
+
**kwargs,
|
|
229
|
+
) -> plt.Axes:
|
|
230
|
+
"""Plot the Pauli errors using cirq.integrated_histogram().
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
ax: The axis to plot on. If None, we generate one.
|
|
234
|
+
cdf_on_x: If True, flip the axes compared the above example.
|
|
235
|
+
axis_label: Label for x axis (y-axis if cdf_on_x is True).
|
|
236
|
+
semilog: If True, force the x-axis to be logarithmic.
|
|
237
|
+
median_line: If True, draw a vertical line on the median value.
|
|
238
|
+
median_label: If drawing median line, optional label for it.
|
|
239
|
+
mean_line: If True, draw a vertical line on the mean value.
|
|
240
|
+
mean_label: If drawing mean line, optional label for it.
|
|
241
|
+
title: Title of the plot. If None, we assign "N={len(data)}".
|
|
242
|
+
show_zero: If True, moves the step plot up by one unit by prepending 0
|
|
243
|
+
to the data.
|
|
244
|
+
**kwargs: Kwargs to forward to `ax.step()`. Some examples are
|
|
245
|
+
color: Color of the line.
|
|
246
|
+
linestyle: Linestyle to use for the plot.
|
|
247
|
+
lw: linewidth for integrated histogram.
|
|
248
|
+
ms: marker size for a histogram trace.
|
|
249
|
+
label: An optional label which can be used in a legend.
|
|
250
|
+
Returns:
|
|
251
|
+
The axis that was plotted on.
|
|
252
|
+
"""
|
|
253
|
+
|
|
254
|
+
ax = cirq_histogram.integrated_histogram(
|
|
255
|
+
data=self.pauli_error(),
|
|
256
|
+
ax=ax,
|
|
257
|
+
cdf_on_x=cdf_on_x,
|
|
258
|
+
axis_label=axis_label,
|
|
259
|
+
semilog=semilog,
|
|
260
|
+
median_line=median_line,
|
|
261
|
+
median_label=median_label,
|
|
262
|
+
mean_line=mean_line,
|
|
263
|
+
mean_label=mean_label,
|
|
264
|
+
show_zero=show_zero,
|
|
265
|
+
title=title,
|
|
266
|
+
**kwargs,
|
|
267
|
+
)
|
|
268
|
+
ax.set_ylabel('Percentile')
|
|
269
|
+
return ax
|
|
270
|
+
|
|
102
271
|
|
|
103
272
|
class TomographyResult:
|
|
104
273
|
"""Results from a state tomography experiment."""
|
|
@@ -175,9 +344,9 @@ def single_qubit_randomized_benchmarking(
|
|
|
175
344
|
qubit: 'cirq.Qid',
|
|
176
345
|
use_xy_basis: bool = True,
|
|
177
346
|
*,
|
|
178
|
-
num_clifford_range: Sequence[int] =
|
|
179
|
-
num_circuits: int =
|
|
180
|
-
repetitions: int =
|
|
347
|
+
num_clifford_range: Sequence[int] = tuple(np.logspace(np.log10(5), 3, 5, dtype=int)),
|
|
348
|
+
num_circuits: int = 10,
|
|
349
|
+
repetitions: int = 600,
|
|
181
350
|
) -> RandomizedBenchMarkResult:
|
|
182
351
|
"""Clifford-based randomized benchmarking (RB) of a single qubit.
|
|
183
352
|
|
|
@@ -213,21 +382,73 @@ def single_qubit_randomized_benchmarking(
|
|
|
213
382
|
A RandomizedBenchMarkResult object that stores and plots the result.
|
|
214
383
|
"""
|
|
215
384
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
385
|
+
result = parallel_single_qubit_randomized_benchmarking(
|
|
386
|
+
sampler,
|
|
387
|
+
(qubit,),
|
|
388
|
+
use_xy_basis,
|
|
389
|
+
num_clifford_range=num_clifford_range,
|
|
390
|
+
num_circuits=num_circuits,
|
|
391
|
+
repetitions=repetitions,
|
|
392
|
+
)
|
|
393
|
+
return result.results_dictionary[qubit]
|
|
219
394
|
|
|
220
|
-
gnd_probs = []
|
|
221
|
-
for num_cfds in num_clifford_range:
|
|
222
|
-
excited_probs_l = []
|
|
223
|
-
for _ in range(num_circuits):
|
|
224
|
-
circuit = _random_single_q_clifford(qubit, num_cfds, c1, cfd_mats)
|
|
225
|
-
circuit.append(ops.measure(qubit, key='z'))
|
|
226
|
-
results = sampler.run(circuit, repetitions=repetitions)
|
|
227
|
-
excited_probs_l.append(np.mean(results.measurements['z']))
|
|
228
|
-
gnd_probs.append(1.0 - np.mean(excited_probs_l))
|
|
229
395
|
|
|
230
|
-
|
|
396
|
+
def parallel_single_qubit_randomized_benchmarking(
|
|
397
|
+
sampler: 'cirq.Sampler',
|
|
398
|
+
qubits: Sequence['cirq.Qid'],
|
|
399
|
+
use_xy_basis: bool = True,
|
|
400
|
+
*,
|
|
401
|
+
num_clifford_range: Sequence[int] = tuple(
|
|
402
|
+
np.logspace(np.log10(5), np.log10(1000), 5, dtype=int)
|
|
403
|
+
),
|
|
404
|
+
num_circuits: int = 10,
|
|
405
|
+
repetitions: int = 1000,
|
|
406
|
+
) -> 'ParallelRandomizedBenchmarkingResult':
|
|
407
|
+
"""Clifford-based randomized benchmarking (RB) single qubits in parallel.
|
|
408
|
+
|
|
409
|
+
This is the same as `single_qubit_randomized_benchmarking` except on all
|
|
410
|
+
of the specified qubits in parallel, i.e. with the individual randomized
|
|
411
|
+
benchmarking circuits zipped together.
|
|
412
|
+
|
|
413
|
+
Args:
|
|
414
|
+
sampler: The quantum engine or simulator to run the circuits.
|
|
415
|
+
use_xy_basis: Determines if the Clifford gates are built with x and y
|
|
416
|
+
rotations (True) or x and z rotations (False).
|
|
417
|
+
qubits: The qubits to benchmark.
|
|
418
|
+
num_clifford_range: The different numbers of Cliffords in the RB study.
|
|
419
|
+
num_circuits: The number of random circuits generated for each
|
|
420
|
+
number of Cliffords.
|
|
421
|
+
repetitions: The number of repetitions of each circuit.
|
|
422
|
+
|
|
423
|
+
Returns:
|
|
424
|
+
A dictionary from qubits to RandomizedBenchMarkResult objects.
|
|
425
|
+
"""
|
|
426
|
+
|
|
427
|
+
clifford_group = _single_qubit_cliffords()
|
|
428
|
+
c1 = clifford_group.c1_in_xy if use_xy_basis else clifford_group.c1_in_xz
|
|
429
|
+
|
|
430
|
+
# create circuits
|
|
431
|
+
circuits_all: List['cirq.AbstractCircuit'] = []
|
|
432
|
+
for num_cliffords in num_clifford_range:
|
|
433
|
+
for _ in range(num_circuits):
|
|
434
|
+
circuits_all.append(_create_parallel_rb_circuit(qubits, num_cliffords, c1))
|
|
435
|
+
|
|
436
|
+
# run circuits
|
|
437
|
+
results = sampler.run_batch(circuits_all, repetitions=repetitions)
|
|
438
|
+
gnd_probs: dict = {q: [] for q in qubits}
|
|
439
|
+
idx = 0
|
|
440
|
+
for num_cliffords in num_clifford_range:
|
|
441
|
+
excited_probs: Dict['cirq.Qid', List[float]] = {q: [] for q in qubits}
|
|
442
|
+
for _ in range(num_circuits):
|
|
443
|
+
result = results[idx][0]
|
|
444
|
+
for qubit in qubits:
|
|
445
|
+
excited_probs[qubit].append(np.mean(result.measurements[str(qubit)]))
|
|
446
|
+
idx += 1
|
|
447
|
+
for qubit in qubits:
|
|
448
|
+
gnd_probs[qubit].append(1.0 - np.mean(excited_probs[qubit]))
|
|
449
|
+
return ParallelRandomizedBenchmarkingResult(
|
|
450
|
+
{q: RandomizedBenchMarkResult(num_clifford_range, gnd_probs[q]) for q in qubits}
|
|
451
|
+
)
|
|
231
452
|
|
|
232
453
|
|
|
233
454
|
def two_qubit_randomized_benchmarking(
|
|
@@ -464,6 +685,21 @@ def two_qubit_state_tomography(
|
|
|
464
685
|
return TomographyResult(rho)
|
|
465
686
|
|
|
466
687
|
|
|
688
|
+
def _create_parallel_rb_circuit(
|
|
689
|
+
qubits: Sequence['cirq.Qid'], num_cliffords: int, c1: list
|
|
690
|
+
) -> 'cirq.Circuit':
|
|
691
|
+
sequences_to_zip = [_random_single_q_clifford(qubit, num_cliffords, c1) for qubit in qubits]
|
|
692
|
+
# Ensure each sequence has the same number of moments.
|
|
693
|
+
num_moments = max(len(sequence) for sequence in sequences_to_zip)
|
|
694
|
+
for q, sequence in zip(qubits, sequences_to_zip):
|
|
695
|
+
if (n := len(sequence)) < num_moments:
|
|
696
|
+
sequence.extend(
|
|
697
|
+
[ops.SingleQubitCliffordGate.I.to_phased_xz_gate()(q)] * (num_moments - n)
|
|
698
|
+
)
|
|
699
|
+
moments = zip(*sequences_to_zip)
|
|
700
|
+
return circuits.Circuit.from_moments(*moments, ops.measure_each(*qubits))
|
|
701
|
+
|
|
702
|
+
|
|
467
703
|
def _indices_after_basis_rot(i: int, j: int) -> Tuple[int, Sequence[int], Sequence[int]]:
|
|
468
704
|
mat_idx = 3 * (3 * i + j)
|
|
469
705
|
q_0_i = 3 - i
|
|
@@ -505,18 +741,15 @@ def _two_qubit_clifford_matrices(
|
|
|
505
741
|
|
|
506
742
|
|
|
507
743
|
def _random_single_q_clifford(
|
|
508
|
-
qubit: 'cirq.Qid',
|
|
509
|
-
|
|
510
|
-
cfds: Sequence[Sequence['cirq.Gate']],
|
|
511
|
-
cfd_matrices: np.ndarray,
|
|
512
|
-
) -> 'cirq.Circuit':
|
|
744
|
+
qubit: 'cirq.Qid', num_cfds: int, cfds: Sequence[Sequence['cirq.ops.SingleQubitCliffordGate']]
|
|
745
|
+
) -> List['cirq.Operation']:
|
|
513
746
|
clifford_group_size = 24
|
|
747
|
+
operations = [[gate.to_phased_xz_gate()(qubit) for gate in gates] for gates in cfds]
|
|
514
748
|
gate_ids = list(np.random.choice(clifford_group_size, num_cfds))
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
return circuit
|
|
749
|
+
adjoint = _reduce_gate_seq([gate for gate_id in gate_ids for gate in cfds[gate_id]]) ** -1
|
|
750
|
+
return [op for gate_id in gate_ids for op in operations[gate_id]] + [
|
|
751
|
+
adjoint.to_phased_xz_gate()(qubit)
|
|
752
|
+
]
|
|
520
753
|
|
|
521
754
|
|
|
522
755
|
def _random_two_q_clifford(
|
|
@@ -574,11 +807,13 @@ def _matrix_bar_plot(
|
|
|
574
807
|
ax.set_title(title)
|
|
575
808
|
|
|
576
809
|
|
|
577
|
-
def
|
|
578
|
-
|
|
810
|
+
def _reduce_gate_seq(
|
|
811
|
+
gate_seq: Sequence[ops.SingleQubitCliffordGate],
|
|
812
|
+
) -> ops.SingleQubitCliffordGate:
|
|
813
|
+
cur = gate_seq[0]
|
|
579
814
|
for gate in gate_seq[1:]:
|
|
580
|
-
|
|
581
|
-
return
|
|
815
|
+
cur = cur.merged_with(gate)
|
|
816
|
+
return cur
|
|
582
817
|
|
|
583
818
|
|
|
584
819
|
def _two_qubit_clifford(
|
|
@@ -686,11 +921,16 @@ def _single_qubit_gates(
|
|
|
686
921
|
yield gate(qubit)
|
|
687
922
|
|
|
688
923
|
|
|
924
|
+
@functools.cache
|
|
689
925
|
def _single_qubit_cliffords() -> Cliffords:
|
|
690
|
-
X, Y, Z =
|
|
926
|
+
X, Y, Z = (
|
|
927
|
+
ops.SingleQubitCliffordGate.X,
|
|
928
|
+
ops.SingleQubitCliffordGate.Y,
|
|
929
|
+
ops.SingleQubitCliffordGate.Z,
|
|
930
|
+
)
|
|
691
931
|
|
|
692
|
-
c1_in_xy: List[List[
|
|
693
|
-
c1_in_xz: List[List[
|
|
932
|
+
c1_in_xy: List[List[ops.SingleQubitCliffordGate]] = []
|
|
933
|
+
c1_in_xz: List[List[ops.SingleQubitCliffordGate]] = []
|
|
694
934
|
|
|
695
935
|
for phi_0, phi_1 in itertools.product([1.0, 0.5, -0.5], [0.0, 0.5, -0.5]):
|
|
696
936
|
c1_in_xy.append([X**phi_0, Y**phi_1])
|
|
@@ -713,8 +953,12 @@ def _single_qubit_cliffords() -> Cliffords:
|
|
|
713
953
|
for z0, x, z1 in phi_xz:
|
|
714
954
|
c1_in_xz.append([Z**z0, X**x, Z**z1])
|
|
715
955
|
|
|
716
|
-
s1: List[List[
|
|
717
|
-
s1_x: List[List[
|
|
718
|
-
s1_y: List[List[
|
|
956
|
+
s1: List[List[ops.SingleQubitCliffordGate]] = [[X**0.0], [Y**0.5, X**0.5], [X**-0.5, Y**-0.5]]
|
|
957
|
+
s1_x: List[List[ops.SingleQubitCliffordGate]] = [[X**0.5], [X**0.5, Y**0.5, X**0.5], [Y**-0.5]]
|
|
958
|
+
s1_y: List[List[ops.SingleQubitCliffordGate]] = [
|
|
959
|
+
[Y**0.5],
|
|
960
|
+
[X**-0.5, Y**-0.5, X**0.5],
|
|
961
|
+
[Y, X**0.5],
|
|
962
|
+
]
|
|
719
963
|
|
|
720
964
|
return Cliffords(c1_in_xy, c1_in_xz, s1, s1_x, s1_y)
|
|
@@ -26,6 +26,7 @@ from cirq.experiments import (
|
|
|
26
26
|
two_qubit_randomized_benchmarking,
|
|
27
27
|
single_qubit_state_tomography,
|
|
28
28
|
two_qubit_state_tomography,
|
|
29
|
+
parallel_single_qubit_randomized_benchmarking,
|
|
29
30
|
)
|
|
30
31
|
|
|
31
32
|
|
|
@@ -74,9 +75,32 @@ def test_single_qubit_cliffords():
|
|
|
74
75
|
|
|
75
76
|
# Check that XZ decomposition has at most one X gate per clifford.
|
|
76
77
|
for gates in cliffords.c1_in_xz:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
num_i = len([gate for gate in gates if gate == cirq.ops.SingleQubitCliffordGate.I])
|
|
79
|
+
num_x = len(
|
|
80
|
+
[
|
|
81
|
+
gate
|
|
82
|
+
for gate in gates
|
|
83
|
+
if gate
|
|
84
|
+
in (
|
|
85
|
+
cirq.ops.SingleQubitCliffordGate.X,
|
|
86
|
+
cirq.ops.SingleQubitCliffordGate.X_sqrt,
|
|
87
|
+
cirq.ops.SingleQubitCliffordGate.X_nsqrt,
|
|
88
|
+
)
|
|
89
|
+
]
|
|
90
|
+
)
|
|
91
|
+
num_z = len(
|
|
92
|
+
[
|
|
93
|
+
gate
|
|
94
|
+
for gate in gates
|
|
95
|
+
if gate
|
|
96
|
+
in (
|
|
97
|
+
cirq.ops.SingleQubitCliffordGate.Z,
|
|
98
|
+
cirq.ops.SingleQubitCliffordGate.Z_sqrt,
|
|
99
|
+
cirq.ops.SingleQubitCliffordGate.Z_nsqrt,
|
|
100
|
+
)
|
|
101
|
+
]
|
|
102
|
+
)
|
|
103
|
+
assert num_x + num_z + num_i == len(gates)
|
|
80
104
|
assert num_x <= 1
|
|
81
105
|
|
|
82
106
|
|
|
@@ -85,12 +109,30 @@ def test_single_qubit_randomized_benchmarking():
|
|
|
85
109
|
# sequences is always unity.
|
|
86
110
|
simulator = sim.Simulator()
|
|
87
111
|
qubit = GridQubit(0, 0)
|
|
88
|
-
num_cfds =
|
|
89
|
-
results = single_qubit_randomized_benchmarking(
|
|
90
|
-
simulator, qubit, num_clifford_range=num_cfds, repetitions=100
|
|
91
|
-
)
|
|
112
|
+
num_cfds = tuple(np.logspace(np.log10(5), 3, 5, dtype=int))
|
|
113
|
+
results = single_qubit_randomized_benchmarking(simulator, qubit, num_clifford_range=num_cfds)
|
|
92
114
|
g_pops = np.asarray(results.data)[:, 1]
|
|
93
115
|
assert np.isclose(np.mean(g_pops), 1.0)
|
|
116
|
+
assert np.isclose(results.pauli_error(), 0.0, atol=1e-7) # warning is expected
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_parallel_single_qubit_randomized_benchmarking():
|
|
120
|
+
# Check that the ground state population at the end of the Clifford
|
|
121
|
+
# sequences is always unity.
|
|
122
|
+
simulator = sim.Simulator()
|
|
123
|
+
qubits = (GridQubit(0, 0), GridQubit(0, 1))
|
|
124
|
+
num_cfds = range(5, 20, 5)
|
|
125
|
+
results = parallel_single_qubit_randomized_benchmarking(
|
|
126
|
+
simulator, num_clifford_range=num_cfds, repetitions=100, qubits=qubits
|
|
127
|
+
)
|
|
128
|
+
for qubit in qubits:
|
|
129
|
+
g_pops = np.asarray(results.results_dictionary[qubit].data)[:, 1]
|
|
130
|
+
assert np.isclose(np.mean(g_pops), 1.0)
|
|
131
|
+
_ = results.plot_single_qubit(qubit)
|
|
132
|
+
pauli_errors = results.pauli_error()
|
|
133
|
+
assert len(pauli_errors) == len(qubits)
|
|
134
|
+
_ = results.plot_heatmap()
|
|
135
|
+
_ = results.plot_integrated_histogram()
|
|
94
136
|
|
|
95
137
|
|
|
96
138
|
def test_two_qubit_randomized_benchmarking():
|
|
@@ -185,3 +227,15 @@ def test_tomography_plot_raises_for_incorrect_number_of_axes():
|
|
|
185
227
|
with pytest.raises(ValueError):
|
|
186
228
|
_, axes = plt.subplots(1, 3)
|
|
187
229
|
result.plot(axes)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def test_single_qubit_cliffords_gateset():
|
|
233
|
+
qubits = [GridQubit(0, i) for i in range(4)]
|
|
234
|
+
clifford_group = cirq.experiments.qubit_characterizations._single_qubit_cliffords()
|
|
235
|
+
c = cirq.experiments.qubit_characterizations._create_parallel_rb_circuit(
|
|
236
|
+
qubits, 5, clifford_group.c1_in_xy
|
|
237
|
+
)
|
|
238
|
+
device = cirq.testing.ValidatingTestDevice(
|
|
239
|
+
qubits=qubits, allowed_gates=(cirq.ops.PhasedXZGate, cirq.MeasurementGate)
|
|
240
|
+
)
|
|
241
|
+
device.validate_circuit(c)
|