Skip to content

Commit 4007e14

Browse files
Merge pull request #864 from qiboteam/2q_rb_interleaved
Interleaved RB
2 parents f348523 + 667e954 commit 4007e14

File tree

7 files changed

+149
-17
lines changed

7 files changed

+149
-17
lines changed

src/qibocal/protocols/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
from .randomized_benchmarking.filtered_rb import filtered_rb
4747
from .randomized_benchmarking.standard_rb import standard_rb
4848
from .randomized_benchmarking.standard_rb_2q import standard_rb_2q
49+
from .randomized_benchmarking.standard_rb_2q_inter import standard_rb_2q_inter
4950
from .readout_characterization import readout_characterization
5051
from .readout_mitigation_matrix import readout_mitigation_matrix
5152
from .readout_optimization.resonator_amplitude import resonator_amplitude
@@ -145,5 +146,6 @@
145146
"rabi_length_frequency",
146147
"rabi_length_frequency_signal",
147148
"standard_rb_2q",
149+
"standard_rb_2q_inter",
148150
"optimize_two_qubit_gate",
149151
]

src/qibocal/protocols/randomized_benchmarking/filtered_rb.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def _acquisition(
4242
RBData: The depths, samples and ground state probability of each experiment in the scan.
4343
"""
4444

45-
return rb_acquisition(params, targets, platform, add_inverse_layer=False)
45+
return rb_acquisition(params, platform, targets, add_inverse_layer=False)
4646

4747

4848
def _fit(data: RBData) -> FilteredRBResult:

src/qibocal/protocols/randomized_benchmarking/standard_rb.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def _acquisition(
8888
RBData: The depths, samples and ground state probability of each experiment in the scan.
8989
"""
9090

91-
return rb_acquisition(params, targets, platform)
91+
return rb_acquisition(params, platform, targets)
9292

9393

9494
def _fit(data: RBData) -> StandardRBResult:
@@ -120,7 +120,6 @@ def _plot(
120120
"""
121121
if isinstance(target, list):
122122
target = tuple(target)
123-
124123
qubit = target
125124
fig = go.Figure()
126125
fitting_report = ""

src/qibocal/protocols/randomized_benchmarking/standard_rb_2q.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def _acquisition(
3232
) -> RB2QData:
3333
"""Data acquisition for two qubit Standard Randomized Benchmarking."""
3434

35-
return twoq_rb_acquisition(params, targets, platform)
35+
return twoq_rb_acquisition(params, platform, targets)
3636

3737

3838
def _fit(data: RB2QData) -> StandardRBResult:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
from dataclasses import dataclass, fields
2+
3+
import numpy as np
4+
from qibolab.platform import Platform
5+
from qibolab.qubits import QubitPairId
6+
7+
from qibocal.auto.operation import Routine
8+
from qibocal.protocols.randomized_benchmarking.standard_rb import _plot
9+
from qibocal.protocols.randomized_benchmarking.standard_rb_2q import (
10+
StandardRB2QParameters,
11+
)
12+
13+
from .utils import RB2QInterData, StandardRBResult, fit, twoq_rb_acquisition
14+
15+
16+
@dataclass
17+
class StandardRB2QInterParameters(StandardRB2QParameters):
18+
"""Parameters for the standard 2q randomized benchmarking protocol."""
19+
20+
interleave: str = "CZ"
21+
"""Gate to interleave"""
22+
23+
24+
@dataclass
25+
class StandardRB2QInterResult(StandardRBResult):
26+
"""Standard RB outputs."""
27+
28+
fidelity_cz: dict[QubitPairId, list] = None
29+
"""The overall fidelity for the CZ gate and its uncertainty."""
30+
31+
def __contains__(self, value: QubitPairId):
32+
if isinstance(value, list):
33+
value = tuple(value)
34+
return all(
35+
value in getattr(self, field.name)
36+
for field in fields(self)
37+
if isinstance(getattr(self, field.name), dict)
38+
and field.name != "fidelity_cz"
39+
)
40+
41+
42+
def _acquisition(
43+
params: StandardRB2QInterParameters,
44+
platform: Platform,
45+
targets: list[QubitPairId],
46+
) -> RB2QInterData:
47+
"""Data acquisition for two qubit Interleaved Randomized Benchmarking."""
48+
49+
data = twoq_rb_acquisition(params, platform, targets, interleave=params.interleave)
50+
51+
fidelity = {}
52+
for target in targets:
53+
fidelity[target] = platform.pairs[target].gate_fidelity
54+
data.fidelity = fidelity
55+
56+
return data
57+
58+
59+
def _fit(data: RB2QInterData) -> StandardRB2QInterResult:
60+
"""Takes a data frame, extracts the depths and the signal and fits it with an
61+
exponential function y = Ap^x+B.
62+
63+
Args:
64+
data: Data from the data acquisition stage.
65+
66+
Returns:
67+
StandardRB2QInterResult: Aggregated and processed data.
68+
"""
69+
70+
qubits = data.pairs
71+
results = fit(qubits, data)
72+
73+
fidelity_cz = {}
74+
for qubit in qubits:
75+
if qubit in data.fidelity and data.fidelity[qubit] is not None:
76+
fid_cz = results.fidelity[qubit] / data.fidelity[qubit][0]
77+
uncertainty_cz = np.sqrt(
78+
1
79+
/ data.fidelity[qubit][0] ** 2
80+
* results.fit_uncertainties[qubit][1] ** 2
81+
+ (results.fidelity[qubit] / data.fidelity[qubit][0] ** 2) ** 2
82+
* data.fidelity[qubit][1] ** 2
83+
)
84+
fidelity_cz[qubit] = [fid_cz, uncertainty_cz]
85+
86+
return StandardRB2QInterResult(
87+
results.fidelity,
88+
results.pulse_fidelity,
89+
results.fit_parameters,
90+
results.fit_uncertainties,
91+
results.error_bars,
92+
fidelity_cz,
93+
)
94+
95+
96+
standard_rb_2q_inter = Routine(_acquisition, _fit, _plot)

src/qibocal/protocols/randomized_benchmarking/utils.py

+40-13
Original file line numberDiff line numberDiff line change
@@ -122,14 +122,15 @@ def random_circuits(
122122
inverse_layer=True,
123123
single_qubit=True,
124124
file_inv=pathlib.Path(),
125+
interleave=None,
125126
) -> Iterable:
126127
"""Returns random (self-inverting) Clifford circuits."""
127128

128129
circuits = []
129130
indexes = defaultdict(list)
130131
for _ in range(niter):
131132
for target in targets:
132-
circuit, random_index = layer_circuit(rb_gen, depth, target)
133+
circuit, random_index = layer_circuit(rb_gen, depth, target, interleave)
133134
if inverse_layer:
134135
add_inverse_layer(circuit, rb_gen, single_qubit, file_inv)
135136
add_measurement_layer(circuit)
@@ -307,6 +308,14 @@ def extract_probabilities(self, qubits):
307308
return probs
308309

309310

311+
@dataclass
312+
class RB2QInterData(RB2QData):
313+
"""The output of the acquisition function."""
314+
315+
fidelity: dict[QubitPairId, list] = field(default_factory=dict)
316+
"""The interleaved fidelity of this qubit."""
317+
318+
310319
@dataclass
311320
class StandardRBResult(Results):
312321
"""Standard RB outputs."""
@@ -319,26 +328,32 @@ class StandardRBResult(Results):
319328
"""Raw fitting parameters."""
320329
fit_uncertainties: dict[QubitId, list[float]]
321330
"""Fitting parameters uncertainties."""
322-
error_bars: dict[QubitId, Optional[Union[float, list[float]]]] = None
331+
error_bars: dict[QubitId, Optional[Union[float, list[float]]]] = field(
332+
default_factory=dict
333+
)
323334
"""Error bars for y."""
324335

325336

326337
def setup(
327338
params: Parameters,
339+
platform: Platform,
328340
single_qubit: bool = True,
341+
interleave: Optional[str] = None,
329342
):
330343
"""
331344
Set up the randomized benchmarking experiment backend, noise model and data class.
332345
333346
Args:
334347
params (Parameters): The parameters for the experiment.
335348
single_qubit (bool, optional): Flag indicating whether the experiment is for a single qubit or two qubits. Defaults to True.
349+
interleave: (str, optional): The type of interleaving to apply. Defaults to None.
336350
337351
Returns:
338352
tuple: A tuple containing the experiment data, noise model, and backend.
339353
"""
340354

341355
backend = GlobalBackend()
356+
backend.platform = platform
342357
# For simulations, a noise model can be added.
343358
noise_model = None
344359
if params.noise_model is not None:
@@ -351,7 +366,12 @@ def setup(
351366
noise_model = getattr(noisemodels, params.noise_model)(params.noise_params)
352367
params.noise_params = noise_model.params.tolist()
353368
# Set up the scan (here an iterator of circuits of random clifford gates with an inverse).
354-
cls = RBData if single_qubit else RB2QData
369+
if single_qubit:
370+
cls = RBData
371+
elif interleave is not None:
372+
cls = RB2QInterData
373+
else:
374+
cls = RB2QData
355375
data = cls(
356376
depths=params.depths,
357377
uncertainties=params.uncertainties,
@@ -405,6 +425,7 @@ def get_circuits(
405425
add_inverse_layer,
406426
single_qubit,
407427
inv_file,
428+
interleave,
408429
)
409430

410431
circuits.extend(circuits_depth)
@@ -464,8 +485,8 @@ def execute_circuits(circuits, targets, params, backend, single_qubit=True):
464485

465486
def rb_acquisition(
466487
params: Parameters,
467-
targets: list[QubitId],
468488
platform: Platform,
489+
targets: list[QubitId],
469490
add_inverse_layer: bool = True,
470491
interleave: str = None,
471492
) -> RBData:
@@ -482,8 +503,7 @@ def rb_acquisition(
482503
Returns:
483504
RBData: The depths, samples, and ground state probability of each experiment in the scan.
484505
"""
485-
data, noise_model, backend = setup(params, single_qubit=True)
486-
backend.platform = platform
506+
data, noise_model, backend = setup(params, platform, single_qubit=True)
487507
circuits, indexes, npulses_per_clifford = get_circuits(
488508
params, targets, add_inverse_layer, interleave, noise_model, single_qubit=True
489509
)
@@ -512,11 +532,11 @@ def rb_acquisition(
512532

513533
def twoq_rb_acquisition(
514534
params: Parameters,
515-
targets: list[QubitPairId],
516535
platform: Platform,
536+
targets: list[QubitPairId],
517537
add_inverse_layer: bool = True,
518538
interleave: str = None,
519-
) -> RB2QData:
539+
) -> Union[RB2QData, RB2QInterData]:
520540
"""
521541
The data acquisition stage of two qubit Standard Randomized Benchmarking.
522542
@@ -530,8 +550,7 @@ def twoq_rb_acquisition(
530550
RB2QData: The acquired data for two qubit randomized benchmarking.
531551
"""
532552

533-
data, noise_model, backend = setup(params, single_qubit=False)
534-
backend.platform = platform
553+
data, noise_model, backend = setup(params, platform, single_qubit=False)
535554
circuits, indexes, npulses_per_clifford = get_circuits(
536555
params, targets, add_inverse_layer, interleave, noise_model, single_qubit=False
537556
)
@@ -564,13 +583,16 @@ def twoq_rb_acquisition(
564583
return data
565584

566585

567-
def layer_circuit(rb_gen: Callable, depth: int, target) -> tuple[Circuit, dict]:
586+
def layer_circuit(
587+
rb_gen: Callable, depth: int, target, interleave: str = None
588+
) -> tuple[Circuit, dict]:
568589
"""Creates a circuit of `depth` layers from a generator `layer_gen` yielding `Circuit` or `Gate`
569590
and a dictionary with random indexes used to select the clifford gates.
570591
571592
Args:
572593
layer_gen (Callable): Should return gates or a full circuit specifying a layer.
573594
depth (int): Number of layers.
595+
interleave (str, optional): Interleaving pattern for the circuits. Defaults to None.
574596
575597
Returns:
576598
Circuit: with `depth` many layers.
@@ -587,14 +609,19 @@ def layer_circuit(rb_gen: Callable, depth: int, target) -> tuple[Circuit, dict]:
587609
for _ in range(depth):
588610
# Generate a layer.
589611
new_layer, random_index = rb_gen_layer
612+
random_indexes.append(random_index)
590613
new_circuit = Circuit(nqubits)
591614
if nqubits == 1:
592615
new_circuit.add(new_layer)
593616
elif nqubits == 2:
594617
for gate in new_layer:
595618
new_circuit.add(gate)
596-
597-
random_indexes.append(random_index)
619+
# FIXME: General interleave
620+
if interleave == "CZ":
621+
interleaved_clifford = rb_gen.two_qubit_cliffords["13"]
622+
interleaved_clifford_gate = clifford2gates(interleaved_clifford)
623+
new_circuit.add(interleaved_clifford_gate)
624+
random_indexes.append("13")
598625

599626
if full_circuit is None: # instantiate in first loop
600627
full_circuit = Circuit(new_circuit.nqubits)

tests/runcards/protocols.yml

+8
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,14 @@ actions:
701701
niter: 5
702702
nshots: 50
703703

704+
- id: standard rb 2q interleaved
705+
operation: standard_rb_2q_inter
706+
targets: [[0,2]]
707+
parameters:
708+
depths: [1, 2, 3, 5]
709+
niter: 5
710+
nshots: 50
711+
704712
- id: chevron cz
705713
operation: chevron
706714
targets: [[0, 2],[1,2]]

0 commit comments

Comments
 (0)