-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Interleaved RB #864
Interleaved RB #864
Changes from 24 commits
a3e045e
b8ccf6b
67573ab
64ca47b
a87f7b5
67b9dc7
301df90
2d037af
d0b8a47
a636f88
31642bf
d363455
d6ba16f
2de76b3
933b51e
3c233f4
4224415
5ba40d9
29cc3dc
9c7d1b1
2a79e37
8846702
30468ee
962c951
c3af513
b7af56e
79bd4dd
fd141d8
a0c207d
0aa6469
c03b4a9
667e954
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
from dataclasses import dataclass | ||
|
||
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 _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) | ||
|
||
# FIXME: I can only get the data.fidelity if there is an acquisition step | ||
if data.fidelity is not None: | ||
fidelity_cz = {} | ||
for qubit in qubits: | ||
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] | ||
|
||
new_results = StandardRB2QInterResult( | ||
results.fidelity, | ||
results.pulse_fidelity, | ||
results.fit_parameters, | ||
results.fit_uncertainties, | ||
results.error_bars, | ||
fidelity_cz, | ||
) | ||
else: | ||
new_results = StandardRB2QInterResult( | ||
results.fidelity, | ||
results.pulse_fidelity, | ||
results.fit_parameters, | ||
results.fit_uncertainties, | ||
results.error_bars, | ||
) | ||
|
||
return new_results | ||
|
||
|
||
standard_rb_2q_inter = Routine(_acquisition, _fit, _plot) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ | |
from qibo.backends import GlobalBackend | ||
from qibo.config import raise_error | ||
from qibo.models import Circuit | ||
from qibolab.platform import Platform | ||
from qibolab.qubits import QubitId, QubitPairId | ||
|
||
from qibocal.auto.operation import Data, Parameters, Results | ||
|
@@ -121,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) | ||
|
@@ -306,6 +308,14 @@ def extract_probabilities(self, qubits): | |
return probs | ||
|
||
|
||
@dataclass | ||
class RB2QInterData(RB2QData): | ||
"""The output of the acquisition function.""" | ||
|
||
fidelity: dict[QubitPairId, list] = None | ||
"""The interleaved fidelity of this qubit.""" | ||
|
||
|
||
@dataclass | ||
class StandardRBResult(Results): | ||
"""Standard RB outputs.""" | ||
|
@@ -324,20 +334,24 @@ class StandardRBResult(Results): | |
|
||
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: | ||
|
@@ -351,6 +365,8 @@ def setup( | |
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 isinstance(cls, RB2QData) and interleave is not None: | ||
cls = RB2QInterData | ||
Jacfomg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
data = cls( | ||
depths=params.depths, | ||
uncertainties=params.uncertainties, | ||
|
@@ -404,6 +420,7 @@ def get_circuits( | |
add_inverse_layer, | ||
single_qubit, | ||
inv_file, | ||
interleave, | ||
) | ||
|
||
circuits.extend(circuits_depth) | ||
|
@@ -463,6 +480,7 @@ def execute_circuits(circuits, targets, params, backend, single_qubit=True): | |
|
||
def rb_acquisition( | ||
params: Parameters, | ||
platform: Platform, | ||
targets: list[QubitId], | ||
add_inverse_layer: bool = True, | ||
interleave: str = None, | ||
|
@@ -480,7 +498,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) | ||
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 | ||
) | ||
|
@@ -509,10 +527,11 @@ def rb_acquisition( | |
|
||
def twoq_rb_acquisition( | ||
params: Parameters, | ||
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. | ||
|
||
|
@@ -526,7 +545,7 @@ def twoq_rb_acquisition( | |
RB2QData: The acquired data for two qubit randomized benchmarking. | ||
""" | ||
|
||
data, noise_model, backend = setup(params, single_qubit=False) | ||
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 | ||
) | ||
|
@@ -559,13 +578,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. | ||
|
@@ -582,14 +604,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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Raise a not supported error instead of a FIXME There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 |
||
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") | ||
Comment on lines
+621
to
+624
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not just create the CZ with Qibo ? Since the 2qRB files are merely one possibility, let's make every effort to separate the Qibocal library from them. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed, here it should be better to create the CZ with Qibo |
||
|
||
if full_circuit is None: # instantiate in first loop | ||
full_circuit = Circuit(new_circuit.nqubits) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In which cases you don't have an acquisition step?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when you just want to plot becuase you ran a new 2qRB but not interleaved for example
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand the problem here. Either you run a 2qRb or a 2qInterleavedR, no?