Skip to content

Commit

Permalink
Merge pull request #864 from qiboteam/2q_rb_interleaved
Browse files Browse the repository at this point in the history
Interleaved RB
  • Loading branch information
andrea-pasquale authored Aug 14, 2024
2 parents f348523 + 667e954 commit 4007e14
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 17 deletions.
2 changes: 2 additions & 0 deletions src/qibocal/protocols/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
from .randomized_benchmarking.filtered_rb import filtered_rb
from .randomized_benchmarking.standard_rb import standard_rb
from .randomized_benchmarking.standard_rb_2q import standard_rb_2q
from .randomized_benchmarking.standard_rb_2q_inter import standard_rb_2q_inter
from .readout_characterization import readout_characterization
from .readout_mitigation_matrix import readout_mitigation_matrix
from .readout_optimization.resonator_amplitude import resonator_amplitude
Expand Down Expand Up @@ -145,5 +146,6 @@
"rabi_length_frequency",
"rabi_length_frequency_signal",
"standard_rb_2q",
"standard_rb_2q_inter",
"optimize_two_qubit_gate",
]
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def _acquisition(
RBData: The depths, samples and ground state probability of each experiment in the scan.
"""

return rb_acquisition(params, targets, platform, add_inverse_layer=False)
return rb_acquisition(params, platform, targets, add_inverse_layer=False)


def _fit(data: RBData) -> FilteredRBResult:
Expand Down
3 changes: 1 addition & 2 deletions src/qibocal/protocols/randomized_benchmarking/standard_rb.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def _acquisition(
RBData: The depths, samples and ground state probability of each experiment in the scan.
"""

return rb_acquisition(params, targets, platform)
return rb_acquisition(params, platform, targets)


def _fit(data: RBData) -> StandardRBResult:
Expand Down Expand Up @@ -120,7 +120,6 @@ def _plot(
"""
if isinstance(target, list):
target = tuple(target)

qubit = target
fig = go.Figure()
fitting_report = ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def _acquisition(
) -> RB2QData:
"""Data acquisition for two qubit Standard Randomized Benchmarking."""

return twoq_rb_acquisition(params, targets, platform)
return twoq_rb_acquisition(params, platform, targets)


def _fit(data: RB2QData) -> StandardRBResult:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from dataclasses import dataclass, fields

import numpy as np
from qibolab.platform import Platform
from qibolab.qubits import QubitPairId

from qibocal.auto.operation import Routine
from qibocal.protocols.randomized_benchmarking.standard_rb import _plot
from qibocal.protocols.randomized_benchmarking.standard_rb_2q import (
StandardRB2QParameters,
)

from .utils import RB2QInterData, StandardRBResult, fit, twoq_rb_acquisition


@dataclass
class StandardRB2QInterParameters(StandardRB2QParameters):
"""Parameters for the standard 2q randomized benchmarking protocol."""

interleave: str = "CZ"
"""Gate to interleave"""


@dataclass
class StandardRB2QInterResult(StandardRBResult):
"""Standard RB outputs."""

fidelity_cz: dict[QubitPairId, list] = None
"""The overall fidelity for the CZ gate and its uncertainty."""

def __contains__(self, value: QubitPairId):
if isinstance(value, list):
value = tuple(value)
return all(
value in getattr(self, field.name)
for field in fields(self)
if isinstance(getattr(self, field.name), dict)
and field.name != "fidelity_cz"
)


def _acquisition(
params: StandardRB2QInterParameters,
platform: Platform,
targets: list[QubitPairId],
) -> RB2QInterData:
"""Data acquisition for two qubit Interleaved Randomized Benchmarking."""

data = twoq_rb_acquisition(params, platform, targets, interleave=params.interleave)

fidelity = {}
for target in targets:
fidelity[target] = platform.pairs[target].gate_fidelity
data.fidelity = fidelity

return data


def _fit(data: RB2QInterData) -> StandardRB2QInterResult:
"""Takes a data frame, extracts the depths and the signal and fits it with an
exponential function y = Ap^x+B.
Args:
data: Data from the data acquisition stage.
Returns:
StandardRB2QInterResult: Aggregated and processed data.
"""

qubits = data.pairs
results = fit(qubits, data)

fidelity_cz = {}
for qubit in qubits:
if qubit in data.fidelity and data.fidelity[qubit] is not None:
fid_cz = results.fidelity[qubit] / data.fidelity[qubit][0]
uncertainty_cz = np.sqrt(
1
/ data.fidelity[qubit][0] ** 2
* results.fit_uncertainties[qubit][1] ** 2
+ (results.fidelity[qubit] / data.fidelity[qubit][0] ** 2) ** 2
* data.fidelity[qubit][1] ** 2
)
fidelity_cz[qubit] = [fid_cz, uncertainty_cz]

return StandardRB2QInterResult(
results.fidelity,
results.pulse_fidelity,
results.fit_parameters,
results.fit_uncertainties,
results.error_bars,
fidelity_cz,
)


standard_rb_2q_inter = Routine(_acquisition, _fit, _plot)
53 changes: 40 additions & 13 deletions src/qibocal/protocols/randomized_benchmarking/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,15 @@ def random_circuits(
inverse_layer=True,
single_qubit=True,
file_inv=pathlib.Path(),
interleave=None,
) -> Iterable:
"""Returns random (self-inverting) Clifford circuits."""

circuits = []
indexes = defaultdict(list)
for _ in range(niter):
for target in targets:
circuit, random_index = layer_circuit(rb_gen, depth, target)
circuit, random_index = layer_circuit(rb_gen, depth, target, interleave)
if inverse_layer:
add_inverse_layer(circuit, rb_gen, single_qubit, file_inv)
add_measurement_layer(circuit)
Expand Down Expand Up @@ -307,6 +308,14 @@ def extract_probabilities(self, qubits):
return probs


@dataclass
class RB2QInterData(RB2QData):
"""The output of the acquisition function."""

fidelity: dict[QubitPairId, list] = field(default_factory=dict)
"""The interleaved fidelity of this qubit."""


@dataclass
class StandardRBResult(Results):
"""Standard RB outputs."""
Expand All @@ -319,26 +328,32 @@ class StandardRBResult(Results):
"""Raw fitting parameters."""
fit_uncertainties: dict[QubitId, list[float]]
"""Fitting parameters uncertainties."""
error_bars: dict[QubitId, Optional[Union[float, list[float]]]] = None
error_bars: dict[QubitId, Optional[Union[float, list[float]]]] = field(
default_factory=dict
)
"""Error bars for y."""


def setup(
params: Parameters,
platform: Platform,
single_qubit: bool = True,
interleave: Optional[str] = None,
):
"""
Set up the randomized benchmarking experiment backend, noise model and data class.
Args:
params (Parameters): The parameters for the experiment.
single_qubit (bool, optional): Flag indicating whether the experiment is for a single qubit or two qubits. Defaults to True.
interleave: (str, optional): The type of interleaving to apply. Defaults to None.
Returns:
tuple: A tuple containing the experiment data, noise model, and backend.
"""

backend = GlobalBackend()
backend.platform = platform
# For simulations, a noise model can be added.
noise_model = None
if params.noise_model is not None:
Expand All @@ -351,7 +366,12 @@ def setup(
noise_model = getattr(noisemodels, params.noise_model)(params.noise_params)
params.noise_params = noise_model.params.tolist()
# Set up the scan (here an iterator of circuits of random clifford gates with an inverse).
cls = RBData if single_qubit else RB2QData
if single_qubit:
cls = RBData
elif interleave is not None:
cls = RB2QInterData
else:
cls = RB2QData
data = cls(
depths=params.depths,
uncertainties=params.uncertainties,
Expand Down Expand Up @@ -405,6 +425,7 @@ def get_circuits(
add_inverse_layer,
single_qubit,
inv_file,
interleave,
)

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

def rb_acquisition(
params: Parameters,
targets: list[QubitId],
platform: Platform,
targets: list[QubitId],
add_inverse_layer: bool = True,
interleave: str = None,
) -> RBData:
Expand All @@ -482,8 +503,7 @@ def rb_acquisition(
Returns:
RBData: The depths, samples, and ground state probability of each experiment in the scan.
"""
data, noise_model, backend = setup(params, single_qubit=True)
backend.platform = platform
data, noise_model, backend = setup(params, platform, single_qubit=True)
circuits, indexes, npulses_per_clifford = get_circuits(
params, targets, add_inverse_layer, interleave, noise_model, single_qubit=True
)
Expand Down Expand Up @@ -512,11 +532,11 @@ def rb_acquisition(

def twoq_rb_acquisition(
params: Parameters,
targets: list[QubitPairId],
platform: Platform,
targets: list[QubitPairId],
add_inverse_layer: bool = True,
interleave: str = None,
) -> RB2QData:
) -> Union[RB2QData, RB2QInterData]:
"""
The data acquisition stage of two qubit Standard Randomized Benchmarking.
Expand All @@ -530,8 +550,7 @@ def twoq_rb_acquisition(
RB2QData: The acquired data for two qubit randomized benchmarking.
"""

data, noise_model, backend = setup(params, single_qubit=False)
backend.platform = platform
data, noise_model, backend = setup(params, platform, single_qubit=False)
circuits, indexes, npulses_per_clifford = get_circuits(
params, targets, add_inverse_layer, interleave, noise_model, single_qubit=False
)
Expand Down Expand Up @@ -564,13 +583,16 @@ def twoq_rb_acquisition(
return data


def layer_circuit(rb_gen: Callable, depth: int, target) -> tuple[Circuit, dict]:
def layer_circuit(
rb_gen: Callable, depth: int, target, interleave: str = None
) -> tuple[Circuit, dict]:
"""Creates a circuit of `depth` layers from a generator `layer_gen` yielding `Circuit` or `Gate`
and a dictionary with random indexes used to select the clifford gates.
Args:
layer_gen (Callable): Should return gates or a full circuit specifying a layer.
depth (int): Number of layers.
interleave (str, optional): Interleaving pattern for the circuits. Defaults to None.
Returns:
Circuit: with `depth` many layers.
Expand All @@ -587,14 +609,19 @@ def layer_circuit(rb_gen: Callable, depth: int, target) -> tuple[Circuit, dict]:
for _ in range(depth):
# Generate a layer.
new_layer, random_index = rb_gen_layer
random_indexes.append(random_index)
new_circuit = Circuit(nqubits)
if nqubits == 1:
new_circuit.add(new_layer)
elif nqubits == 2:
for gate in new_layer:
new_circuit.add(gate)

random_indexes.append(random_index)
# FIXME: General interleave
if interleave == "CZ":
interleaved_clifford = rb_gen.two_qubit_cliffords["13"]
interleaved_clifford_gate = clifford2gates(interleaved_clifford)
new_circuit.add(interleaved_clifford_gate)
random_indexes.append("13")

if full_circuit is None: # instantiate in first loop
full_circuit = Circuit(new_circuit.nqubits)
Expand Down
8 changes: 8 additions & 0 deletions tests/runcards/protocols.yml
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,14 @@ actions:
niter: 5
nshots: 50

- id: standard rb 2q interleaved
operation: standard_rb_2q_inter
targets: [[0,2]]
parameters:
depths: [1, 2, 3, 5]
niter: 5
nshots: 50

- id: chevron cz
operation: chevron
targets: [[0, 2],[1,2]]
Expand Down

0 comments on commit 4007e14

Please sign in to comment.