32
32
from scipy .optimize import curve_fit
33
33
34
34
from matplotlib import pyplot as plt
35
+ import cirq .vis .heatmap as cirq_heatmap
36
+ import cirq .vis .histogram as cirq_histogram
35
37
36
38
# this is for older systems with matplotlib <3.2 otherwise 3d projections fail
37
39
from mpl_toolkits import mplot3d
38
40
from cirq import circuits , ops , protocols
41
+ from cirq .devices import grid_qubit
42
+
39
43
40
44
if TYPE_CHECKING :
41
45
import cirq
@@ -144,6 +148,127 @@ def _fit_exponential(self) -> Tuple[np.ndarray, np.ndarray]:
144
148
)
145
149
146
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
+
271
+
147
272
class TomographyResult :
148
273
"""Results from a state tomography experiment."""
149
274
@@ -265,7 +390,7 @@ def single_qubit_randomized_benchmarking(
265
390
num_circuits = num_circuits ,
266
391
repetitions = repetitions ,
267
392
)
268
- return result [qubit ]
393
+ return result . results_dictionary [qubit ]
269
394
270
395
271
396
def parallel_single_qubit_randomized_benchmarking (
@@ -278,7 +403,7 @@ def parallel_single_qubit_randomized_benchmarking(
278
403
),
279
404
num_circuits : int = 10 ,
280
405
repetitions : int = 1000 ,
281
- ) -> Mapping [ 'cirq.Qid' , 'RandomizedBenchMarkResult' ] :
406
+ ) -> 'ParallelRandomizedBenchmarkingResult' :
282
407
"""Clifford-based randomized benchmarking (RB) single qubits in parallel.
283
408
284
409
This is the same as `single_qubit_randomized_benchmarking` except on all
@@ -321,7 +446,9 @@ def parallel_single_qubit_randomized_benchmarking(
321
446
idx += 1
322
447
for qubit in qubits :
323
448
gnd_probs [qubit ].append (1.0 - np .mean (excited_probs [qubit ]))
324
- return {q : RandomizedBenchMarkResult (num_clifford_range , gnd_probs [q ]) for q in qubits }
449
+ return ParallelRandomizedBenchmarkingResult (
450
+ {q : RandomizedBenchMarkResult (num_clifford_range , gnd_probs [q ]) for q in qubits }
451
+ )
325
452
326
453
327
454
def two_qubit_randomized_benchmarking (
0 commit comments