From a3e045e0f829cc22793063267e668b70937391b7 Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Tue, 21 May 2024 18:03:34 +0400 Subject: [PATCH 01/22] feat: 1st draft for the interleaved --- src/qibocal/protocols/__init__.py | 2 + .../standard_rb_2q_inter.py | 401 ++++++++++++++++++ .../randomized_benchmarking/utils.py | 34 ++ 3 files changed, 437 insertions(+) create mode 100644 src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py diff --git a/src/qibocal/protocols/__init__.py b/src/qibocal/protocols/__init__.py index e65f18404..5e96d4773 100644 --- a/src/qibocal/protocols/__init__.py +++ b/src/qibocal/protocols/__init__.py @@ -43,6 +43,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 @@ -135,3 +136,4 @@ class Operation(Enum): calibrate_state_discrimination = calibrate_state_discrimination state_tomography = state_tomography standard_rb_2q = standard_rb_2q + standard_rb_2q_inter = standard_rb_2q_inter diff --git a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py new file mode 100644 index 000000000..93f66563d --- /dev/null +++ b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py @@ -0,0 +1,401 @@ +from collections import defaultdict +from dataclasses import dataclass +from typing import Iterable + +import numpy as np +import plotly.graph_objects as go +from qibo.backends import GlobalBackend +from qibolab.platform import Platform +from qibolab.qubits import QubitPairId + +from qibocal.auto.operation import Routine +from qibocal.auto.transpile import ( + dummy_transpiler, + execute_transpiled_circuit, + execute_transpiled_circuits, +) +from qibocal.config import raise_error +from qibocal.protocols.randomized_benchmarking import noisemodels +from qibocal.protocols.randomized_benchmarking.standard_rb import StandardRBParameters +from qibocal.protocols.randomized_benchmarking.standard_rb_2q import ( + RB2Q_Generator, + RB2QData, + StandardRB2QResult, +) + +from ..utils import table_dict, table_html +from .fitting import exp1B_func, fit_exp1B_func +from .utils import ( + RBType, + add_inverse_2q_layer, + add_measurement_layer, + data_uncertainties, + layer_2q_interleaved_circuit, + number_to_str, +) + + +@dataclass +class RB2QInterData(RB2QData): + """The output of the acquisition function.""" + + fidelity: dict[QubitPairId, list] = None + """Number of pulses for an average clifford.""" + + +@dataclass +class StandardRB2QParameters(StandardRBParameters): + """Parameters for the standard 2q randomized benchmarking protocol.""" + + interleaved: str = "CZ" + """Gate to interleave""" + file: str = "2qubitCliffs.json" + """File with the cliffords to be used.""" + file_inv: str = "2qubitCliffsInv.npz" + """File with the cliffords to be used in an inverted dict.""" + + +@dataclass +class StandardRB2QInterResult(StandardRB2QResult): + """Standard RB outputs.""" + + fidelity_cz: dict[QubitPairId, list] = None + """The overall fidelity for the CZ gate and its uncertainty.""" + + +def random_circuits( + depth: int, + targets: list[QubitPairId], + niter, + rb_gen, + file_inv, + noise_model=None, + interleaved=None, +) -> Iterable: + """Returns single-qubit random self-inverting Clifford circuits. + + Args: + params (StandardRBParameters): Parameters of the RB protocol. + targets (list[QubitId]): + list of qubits the circuit is executed on. + nqubits (int, optional): Number of qubits of the resulting circuits. + If ``None``, sets ``len(qubits)``. Defaults to ``None``. + + Returns: + Iterable: The iterator of circuits. + """ + + circuits = [] + indexes = defaultdict(list) + for _ in range(niter): + for target in targets: + circuit, random_index = layer_2q_interleaved_circuit( + rb_gen, depth, target, interleaved + ) + add_inverse_2q_layer(circuit, rb_gen.two_qubit_cliffords, file_inv) + add_measurement_layer(circuit) + if noise_model is not None: + circuit = noise_model.apply(circuit) + circuits.append(circuit) + indexes[target].append(random_index) + + return circuits, indexes + + +def _acquisition( + params: StandardRB2QParameters, + platform: Platform, + targets: list[QubitPairId], +) -> RB2QData: + """The data acquisition stage of Standard Randomized Benchmarking. + + 1. Set up the scan + 2. Execute the scan + 3. Post process the data and initialize a standard rb data object with it. + + Args: + params (StandardRBParameters): All parameters in one object. + platform (Platform): Platform the experiment is executed on. + qubits (dict[int, Union[str, int]] or list[Union[str, int]]): list of qubits the experiment is executed on. + + Returns: + RBData: The depths, samples and ground state probability of each experiment in the scan. + """ + + backend = GlobalBackend() + # For simulations, a noise model can be added. + noise_model = None + if params.noise_model is not None: + if backend.name == "qibolab": + raise_error( + ValueError, + "Backend qibolab (%s) does not perform noise models simulation. ", + ) + + noise_model = getattr(noisemodels, params.noise_model)(params.noise_params) + params.noise_params = noise_model.params.tolist() + # 1. Set up the scan (here an iterator of circuits of random clifford gates with an inverse). + nqubits = len(targets) + data = RB2QInterData( + depths=params.depths, + uncertainties=params.uncertainties, + seed=params.seed, + nshots=params.nshots, + niter=params.niter, + ) + + circuits = [] + indexes = {} + samples = [] + qubits_ids = targets + rb_gen = RB2Q_Generator(params.file, params.seed) + for depth in params.depths: + # TODO: This does not generate multi qubit circuits + circuits_depth, random_indexes = random_circuits( + depth, + qubits_ids, + params.niter, + rb_gen, + params.file_inv, + noise_model, + interleaved=params.interleaved, + ) + circuits.extend(circuits_depth) + for qubit in random_indexes.keys(): + indexes[(qubit[0], qubit[1], depth)] = random_indexes[qubit] + + backend = GlobalBackend() + transpiler = dummy_transpiler(backend) + qubit_maps = [list(i) for i in targets] * (len(params.depths) * params.niter) + + # Execute the circuits + if params.unrolling: + _, executed_circuits = execute_transpiled_circuits( + circuits, + qubit_maps=qubit_maps, + backend=backend, + nshots=params.nshots, + transpiler=transpiler, + ) + else: + executed_circuits = [ + execute_transpiled_circuit( + circuit, + qubit_map=qubit_map, + backend=backend, + nshots=params.nshots, + transpiler=transpiler, + )[1] + for circuit, qubit_map in zip(circuits, qubit_maps) + ] + + zero_array = np.array([0, 0]) + for circ in executed_circuits: + # Post process [0,0] to 0 and [1,0], [0,1], [1,1] to 1 + converted_samples = [] + for sample in circ.samples(): + if np.all(sample == zero_array): + converted_samples.append(np.array(0, dtype=np.int32)) + else: + converted_samples.append(np.array(1, dtype=np.int32)) + samples.extend(converted_samples) + + samples = np.reshape(samples, (-1, nqubits, params.nshots)) + + for i, depth in enumerate(params.depths): + index = (i * params.niter, (i + 1) * params.niter) + for nqubit, qubit_id in enumerate(targets): + data.register_qubit( + RBType, + (qubit_id[0], qubit_id[1], depth), + dict( + samples=samples[index[0] : index[1]][:, nqubit], + ), + ) + data.circuits = indexes + data.npulses_per_clifford = rb_gen.calculate_average_pulses() + + 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 (RBData): Data from the data acquisition stage. + + Returns: + StandardRBResult: Aggregated and processed data. + """ + qubits = data.pairs + + fidelity, fidelity_cz, pulse_fidelity = {}, {}, {} + popts, perrs = {}, {} + error_barss = {} + for qubit in qubits: + # Extract depths and probabilities + x = data.depths + probs = data.extract_probabilities(qubit) + samples_mean = np.mean(probs, axis=1) + # TODO: Should we use the median or the mean? + median = np.median(probs, axis=1) + + error_bars = data_uncertainties( + probs, + method=data.uncertainties, + data_median=median, + ) + + sigma = ( + np.max(error_bars, axis=0) if data.uncertainties is not None else error_bars + ) + + popt, perr = fit_exp1B_func(x, samples_mean, sigma=sigma, bounds=[0, 1]) + # Compute the fidelities + infidelity = (1 - popt[1]) / 2 + fidelity[qubit] = 1 - infidelity + + fid_cz = fidelity[qubit] / data.fidelity[qubit][0] + uncertainty_cz = data.fidelity[qubit][1] + + fidelity_cz[qubit] = [fid_cz, uncertainty_cz] + + pulse_fidelity[qubit] = 1 - infidelity / data.npulses_per_clifford + + # conversion from np.array to list/tuple + error_bars = error_bars.tolist() + error_barss[qubit] = error_bars + perrs[qubit] = perr + popts[qubit] = popt + + return StandardRB2QInterResult( + fidelity, pulse_fidelity, popts, perrs, error_barss, fidelity_cz + ) + + +def _plot( + data: RB2QData, fit: StandardRB2QInterResult, target: QubitPairId +) -> tuple[list[go.Figure], str]: + """Builds the table for the qq pipe, calls the plot function of the result object + and returns the figure es list. + + Args: + data (RBData): Data object used for the table. + fit (StandardRBResult): Is called for the plot. + target (_type_): Not used yet. + + Returns: + tuple[list[go.Figure], str]: + """ + + qubits = target + fig = go.Figure() + fitting_report = "" + x = data.depths + raw_data = data.extract_probabilities(qubits) + y = np.mean(raw_data, axis=1) + raw_depths = [[depth] * data.niter for depth in data.depths] + + fig.add_trace( + go.Scatter( + x=np.hstack(raw_depths), + y=np.hstack(raw_data), + line=dict(color="#6597aa"), + mode="markers", + marker={"opacity": 0.2, "symbol": "square"}, + name="iterations", + ) + ) + + fig.add_trace( + go.Scatter( + x=x, + y=y, + line=dict(color="#aa6464"), + mode="markers", + name="average", + ) + ) + # Create a dictionary for the error bars + error_y_dict = None + if fit is not None: + popt, perr = fit.fit_parameters[qubits], fit.fit_uncertainties[qubits] + label = "Fit: y=Ap^x
A: {}
p: {}
B: {}".format( + number_to_str(popt[0], perr[0]), + number_to_str(popt[1], perr[1]), + number_to_str(popt[2], perr[2]), + ) + x_fit = np.linspace(min(x), max(x), len(x) * 20) + y_fit = exp1B_func(x_fit, *popt) + fig.add_trace( + go.Scatter( + x=x_fit, + y=y_fit, + name=label, + line=go.scatter.Line(dash="dot", color="#00cc96"), + ) + ) + if fit.error_bars is not None: + error_bars = fit.error_bars[qubits] + # Constant error bars + if isinstance(error_bars, Iterable) is False: + error_y_dict = {"type": "constant", "value": error_bars} + # Symmetric error bars + elif isinstance(error_bars[0], Iterable) is False: + error_y_dict = {"type": "data", "array": error_bars} + # Asymmetric error bars + else: + error_y_dict = { + "type": "data", + "symmetric": False, + "array": error_bars[1], + "arrayminus": error_bars[0], + } + fig.add_trace( + go.Scatter( + x=x, + y=y, + error_y=error_y_dict, + line={"color": "#aa6464"}, + mode="markers", + name="error bars", + ) + ) + if fit is not None: + fitting_report = table_html( + table_dict( + str(qubits), + ["niter", "nshots", "uncertainties", "fidelity", "pulse_fidelity"], + [ + data.niter, + data.nshots, + data.uncertainties, + number_to_str( + fit.fidelity[qubits], + np.array(fit.fit_uncertainties[qubits][1]) / 2, + ), + number_to_str( + fit.pulse_fidelity[qubits], + np.array(fit.fit_uncertainties[qubits][1]) + / (2 * data.npulses_per_clifford), + ), + ], + ) + ) + + fig.update_layout( + showlegend=True, + xaxis_title="Circuit depth", + yaxis_title="Survival Probability", + ) + + return [fig], fitting_report + + +standard_rb_2q_inter = Routine(_acquisition, _fit, _plot) diff --git a/src/qibocal/protocols/randomized_benchmarking/utils.py b/src/qibocal/protocols/randomized_benchmarking/utils.py index e318a6db3..9cfecefcb 100644 --- a/src/qibocal/protocols/randomized_benchmarking/utils.py +++ b/src/qibocal/protocols/randomized_benchmarking/utils.py @@ -669,6 +669,40 @@ def layer_2q_circuit(rb_gen: Callable, depth: int, qubits) -> tuple[Circuit, dic return full_circuit, random_indexes +def layer_2q_interleaved_circuit( + rb_gen: Callable, depth: int, qubits, interleaved: str +) -> 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. + + Returns: + Circuit: with `depth` many layers. + """ + + full_circuit = None + random_indexes = [] + # Build each layer, there will be depth many in the final circuit. + for _ in range(depth): + # Generate a layer. + new_layer, random_index = rb_gen.layer_gen() + new_circuit = Circuit(2) + for gate in new_layer: + new_circuit.add(gate) + random_indexes.append(random_index) + + if interleaved == "CZ": + new_circuit.add(gates.CZ(0, 1)) + + if full_circuit is None: # instantiate in first loop + full_circuit = Circuit(new_circuit.nqubits) + full_circuit = full_circuit + new_circuit + return full_circuit, random_indexes + + def add_inverse_layer(circuit: Circuit, single_qubit=True): """Adds an inverse gate/inverse gates at the end of a circuit (in place). From b8ccf6b80b0035d107853e44cd93d4fe2f55e623 Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Tue, 21 May 2024 18:06:42 +0400 Subject: [PATCH 02/22] fix: tests --- tests/runcards/protocols.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/runcards/protocols.yml b/tests/runcards/protocols.yml index a9c621ecc..56919c754 100644 --- a/tests/runcards/protocols.yml +++ b/tests/runcards/protocols.yml @@ -674,6 +674,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 id operation: chevron targets: [[0, 2],[1,2]] From 67573ab6aca80a8494cb4ba9b5913f726a9b9954 Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Tue, 21 May 2024 18:23:35 +0400 Subject: [PATCH 03/22] fix: tests --- .../protocols/randomized_benchmarking/standard_rb_2q_inter.py | 2 +- src/qibocal/protocols/randomized_benchmarking/utils.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py index 93f66563d..f6ffbb336 100644 --- a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py +++ b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py @@ -106,7 +106,7 @@ def _acquisition( params: StandardRB2QParameters, platform: Platform, targets: list[QubitPairId], -) -> RB2QData: +) -> RB2QInterData: """The data acquisition stage of Standard Randomized Benchmarking. 1. Set up the scan diff --git a/src/qibocal/protocols/randomized_benchmarking/utils.py b/src/qibocal/protocols/randomized_benchmarking/utils.py index 9cfecefcb..2b69af856 100644 --- a/src/qibocal/protocols/randomized_benchmarking/utils.py +++ b/src/qibocal/protocols/randomized_benchmarking/utils.py @@ -696,6 +696,7 @@ def layer_2q_interleaved_circuit( if interleaved == "CZ": new_circuit.add(gates.CZ(0, 1)) + random_indexes.append("13") # FIXME: Check the index if full_circuit is None: # instantiate in first loop full_circuit = Circuit(new_circuit.nqubits) From a87f7b540b3d73eddf561329966aaa9fe35f02ff Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Wed, 22 May 2024 16:16:22 +0400 Subject: [PATCH 04/22] fix: forgot function --- .../randomized_benchmarking/utils.py | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/qibocal/protocols/randomized_benchmarking/utils.py b/src/qibocal/protocols/randomized_benchmarking/utils.py index fe736e0b6..44d4f95fc 100644 --- a/src/qibocal/protocols/randomized_benchmarking/utils.py +++ b/src/qibocal/protocols/randomized_benchmarking/utils.py @@ -832,3 +832,36 @@ def fit(qubits, data): popts[qubit] = popt return StandardRBResult(fidelity, pulse_fidelity, popts, perrs, error_barss) + + +def layer_2q_interleaved_circuit( + rb_gen: Callable, depth: int, qubits, interleaved: str +) -> 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. + Returns: + Circuit: with `depth` many layers. + """ + + full_circuit = None + random_indexes = [] + # Build each layer, there will be depth many in the final circuit. + for _ in range(depth): + # Generate a layer. + new_layer, random_index = rb_gen.layer_gen() + new_circuit = Circuit(2) + for gate in new_layer: + new_circuit.add(gate) + random_indexes.append(random_index) + + if interleaved == "CZ": + new_circuit.add(gates.CZ(0, 1)) + random_indexes.append("13") # FIXME: Check the index + + if full_circuit is None: # instantiate in first loop + full_circuit = Circuit(new_circuit.nqubits) + full_circuit = full_circuit + new_circuit + return full_circuit, random_indexes From 67b9dc7a5ab312a16c1c9e841a574306d787e6ff Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Wed, 22 May 2024 16:38:38 +0400 Subject: [PATCH 05/22] wip: proper interleave --- .../standard_rb_2q_inter.py | 358 +----------------- .../randomized_benchmarking/utils.py | 30 +- 2 files changed, 40 insertions(+), 348 deletions(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py index f6ffbb336..334228f6f 100644 --- a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py +++ b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py @@ -1,219 +1,41 @@ -from collections import defaultdict from dataclasses import dataclass -from typing import Iterable -import numpy as np -import plotly.graph_objects as go -from qibo.backends import GlobalBackend from qibolab.platform import Platform from qibolab.qubits import QubitPairId from qibocal.auto.operation import Routine -from qibocal.auto.transpile import ( - dummy_transpiler, - execute_transpiled_circuit, - execute_transpiled_circuits, -) -from qibocal.config import raise_error -from qibocal.protocols.randomized_benchmarking import noisemodels -from qibocal.protocols.randomized_benchmarking.standard_rb import StandardRBParameters +from qibocal.protocols.randomized_benchmarking.standard_rb import _plot from qibocal.protocols.randomized_benchmarking.standard_rb_2q import ( - RB2Q_Generator, - RB2QData, - StandardRB2QResult, + StandardRB2QParameters, ) -from ..utils import table_dict, table_html -from .fitting import exp1B_func, fit_exp1B_func -from .utils import ( - RBType, - add_inverse_2q_layer, - add_measurement_layer, - data_uncertainties, - layer_2q_interleaved_circuit, - number_to_str, -) +from .utils import RB2QInterData, StandardRBResult, fit, twoq_rb_acquisition @dataclass -class RB2QInterData(RB2QData): - """The output of the acquisition function.""" - - fidelity: dict[QubitPairId, list] = None - """Number of pulses for an average clifford.""" - - -@dataclass -class StandardRB2QParameters(StandardRBParameters): +class StandardRB2QInterParameters(StandardRB2QParameters): """Parameters for the standard 2q randomized benchmarking protocol.""" interleaved: str = "CZ" """Gate to interleave""" - file: str = "2qubitCliffs.json" - """File with the cliffords to be used.""" - file_inv: str = "2qubitCliffsInv.npz" - """File with the cliffords to be used in an inverted dict.""" @dataclass -class StandardRB2QInterResult(StandardRB2QResult): +class StandardRB2QInterResult(StandardRBResult): """Standard RB outputs.""" fidelity_cz: dict[QubitPairId, list] = None """The overall fidelity for the CZ gate and its uncertainty.""" -def random_circuits( - depth: int, - targets: list[QubitPairId], - niter, - rb_gen, - file_inv, - noise_model=None, - interleaved=None, -) -> Iterable: - """Returns single-qubit random self-inverting Clifford circuits. - - Args: - params (StandardRBParameters): Parameters of the RB protocol. - targets (list[QubitId]): - list of qubits the circuit is executed on. - nqubits (int, optional): Number of qubits of the resulting circuits. - If ``None``, sets ``len(qubits)``. Defaults to ``None``. - - Returns: - Iterable: The iterator of circuits. - """ - - circuits = [] - indexes = defaultdict(list) - for _ in range(niter): - for target in targets: - circuit, random_index = layer_2q_interleaved_circuit( - rb_gen, depth, target, interleaved - ) - add_inverse_2q_layer(circuit, rb_gen.two_qubit_cliffords, file_inv) - add_measurement_layer(circuit) - if noise_model is not None: - circuit = noise_model.apply(circuit) - circuits.append(circuit) - indexes[target].append(random_index) - - return circuits, indexes - - def _acquisition( - params: StandardRB2QParameters, + params: StandardRB2QInterParameters, platform: Platform, targets: list[QubitPairId], ) -> RB2QInterData: - """The data acquisition stage of Standard Randomized Benchmarking. - - 1. Set up the scan - 2. Execute the scan - 3. Post process the data and initialize a standard rb data object with it. - - Args: - params (StandardRBParameters): All parameters in one object. - platform (Platform): Platform the experiment is executed on. - qubits (dict[int, Union[str, int]] or list[Union[str, int]]): list of qubits the experiment is executed on. - - Returns: - RBData: The depths, samples and ground state probability of each experiment in the scan. - """ - - backend = GlobalBackend() - # For simulations, a noise model can be added. - noise_model = None - if params.noise_model is not None: - if backend.name == "qibolab": - raise_error( - ValueError, - "Backend qibolab (%s) does not perform noise models simulation. ", - ) - - noise_model = getattr(noisemodels, params.noise_model)(params.noise_params) - params.noise_params = noise_model.params.tolist() - # 1. Set up the scan (here an iterator of circuits of random clifford gates with an inverse). - nqubits = len(targets) - data = RB2QInterData( - depths=params.depths, - uncertainties=params.uncertainties, - seed=params.seed, - nshots=params.nshots, - niter=params.niter, - ) + """Data acquisition for two qubit Standard Randomized Benchmarking.""" - circuits = [] - indexes = {} - samples = [] - qubits_ids = targets - rb_gen = RB2Q_Generator(params.file, params.seed) - for depth in params.depths: - # TODO: This does not generate multi qubit circuits - circuits_depth, random_indexes = random_circuits( - depth, - qubits_ids, - params.niter, - rb_gen, - params.file_inv, - noise_model, - interleaved=params.interleaved, - ) - circuits.extend(circuits_depth) - for qubit in random_indexes.keys(): - indexes[(qubit[0], qubit[1], depth)] = random_indexes[qubit] - - backend = GlobalBackend() - transpiler = dummy_transpiler(backend) - qubit_maps = [list(i) for i in targets] * (len(params.depths) * params.niter) - - # Execute the circuits - if params.unrolling: - _, executed_circuits = execute_transpiled_circuits( - circuits, - qubit_maps=qubit_maps, - backend=backend, - nshots=params.nshots, - transpiler=transpiler, - ) - else: - executed_circuits = [ - execute_transpiled_circuit( - circuit, - qubit_map=qubit_map, - backend=backend, - nshots=params.nshots, - transpiler=transpiler, - )[1] - for circuit, qubit_map in zip(circuits, qubit_maps) - ] - - zero_array = np.array([0, 0]) - for circ in executed_circuits: - # Post process [0,0] to 0 and [1,0], [0,1], [1,1] to 1 - converted_samples = [] - for sample in circ.samples(): - if np.all(sample == zero_array): - converted_samples.append(np.array(0, dtype=np.int32)) - else: - converted_samples.append(np.array(1, dtype=np.int32)) - samples.extend(converted_samples) - - samples = np.reshape(samples, (-1, nqubits, params.nshots)) - - for i, depth in enumerate(params.depths): - index = (i * params.niter, (i + 1) * params.niter) - for nqubit, qubit_id in enumerate(targets): - data.register_qubit( - RBType, - (qubit_id[0], qubit_id[1], depth), - dict( - samples=samples[index[0] : index[1]][:, nqubit], - ), - ) - data.circuits = indexes - data.npulses_per_clifford = rb_gen.calculate_average_pulses() + data = twoq_rb_acquisition(params, targets, interleave=params.interleave) fidelity = {} for target in targets: @@ -223,7 +45,7 @@ def _acquisition( return data -def _fit(data: RB2QInterData) -> StandardRB2QInterResult: +def _fit(data: RB2QInterData) -> StandardRBResult: """Takes a data frame, extracts the depths and the signal and fits it with an exponential function y = Ap^x+B. @@ -233,169 +55,25 @@ def _fit(data: RB2QInterData) -> StandardRB2QInterResult: Returns: StandardRBResult: Aggregated and processed data. """ + qubits = data.pairs + results = fit(qubits, data) - fidelity, fidelity_cz, pulse_fidelity = {}, {}, {} - popts, perrs = {}, {} - error_barss = {} + fidelity_cz = {} for qubit in qubits: - # Extract depths and probabilities - x = data.depths - probs = data.extract_probabilities(qubit) - samples_mean = np.mean(probs, axis=1) - # TODO: Should we use the median or the mean? - median = np.median(probs, axis=1) - - error_bars = data_uncertainties( - probs, - method=data.uncertainties, - data_median=median, - ) - - sigma = ( - np.max(error_bars, axis=0) if data.uncertainties is not None else error_bars - ) - - popt, perr = fit_exp1B_func(x, samples_mean, sigma=sigma, bounds=[0, 1]) - # Compute the fidelities - infidelity = (1 - popt[1]) / 2 - fidelity[qubit] = 1 - infidelity - - fid_cz = fidelity[qubit] / data.fidelity[qubit][0] - uncertainty_cz = data.fidelity[qubit][1] - + fid_cz = results.fidelity[qubits] / data.fidelity[qubits][0] + uncertainty_cz = data.fidelity[qubits][1] # FIXME: Propagate uncertainty fidelity_cz[qubit] = [fid_cz, uncertainty_cz] - pulse_fidelity[qubit] = 1 - infidelity / data.npulses_per_clifford + import pdb - # conversion from np.array to list/tuple - error_bars = error_bars.tolist() - error_barss[qubit] = error_bars - perrs[qubit] = perr - popts[qubit] = popt + pdb.set_trace() - return StandardRB2QInterResult( + results = StandardRB2QInterResult( fidelity, pulse_fidelity, popts, perrs, error_barss, fidelity_cz ) - -def _plot( - data: RB2QData, fit: StandardRB2QInterResult, target: QubitPairId -) -> tuple[list[go.Figure], str]: - """Builds the table for the qq pipe, calls the plot function of the result object - and returns the figure es list. - - Args: - data (RBData): Data object used for the table. - fit (StandardRBResult): Is called for the plot. - target (_type_): Not used yet. - - Returns: - tuple[list[go.Figure], str]: - """ - - qubits = target - fig = go.Figure() - fitting_report = "" - x = data.depths - raw_data = data.extract_probabilities(qubits) - y = np.mean(raw_data, axis=1) - raw_depths = [[depth] * data.niter for depth in data.depths] - - fig.add_trace( - go.Scatter( - x=np.hstack(raw_depths), - y=np.hstack(raw_data), - line=dict(color="#6597aa"), - mode="markers", - marker={"opacity": 0.2, "symbol": "square"}, - name="iterations", - ) - ) - - fig.add_trace( - go.Scatter( - x=x, - y=y, - line=dict(color="#aa6464"), - mode="markers", - name="average", - ) - ) - # Create a dictionary for the error bars - error_y_dict = None - if fit is not None: - popt, perr = fit.fit_parameters[qubits], fit.fit_uncertainties[qubits] - label = "Fit: y=Ap^x
A: {}
p: {}
B: {}".format( - number_to_str(popt[0], perr[0]), - number_to_str(popt[1], perr[1]), - number_to_str(popt[2], perr[2]), - ) - x_fit = np.linspace(min(x), max(x), len(x) * 20) - y_fit = exp1B_func(x_fit, *popt) - fig.add_trace( - go.Scatter( - x=x_fit, - y=y_fit, - name=label, - line=go.scatter.Line(dash="dot", color="#00cc96"), - ) - ) - if fit.error_bars is not None: - error_bars = fit.error_bars[qubits] - # Constant error bars - if isinstance(error_bars, Iterable) is False: - error_y_dict = {"type": "constant", "value": error_bars} - # Symmetric error bars - elif isinstance(error_bars[0], Iterable) is False: - error_y_dict = {"type": "data", "array": error_bars} - # Asymmetric error bars - else: - error_y_dict = { - "type": "data", - "symmetric": False, - "array": error_bars[1], - "arrayminus": error_bars[0], - } - fig.add_trace( - go.Scatter( - x=x, - y=y, - error_y=error_y_dict, - line={"color": "#aa6464"}, - mode="markers", - name="error bars", - ) - ) - if fit is not None: - fitting_report = table_html( - table_dict( - str(qubits), - ["niter", "nshots", "uncertainties", "fidelity", "pulse_fidelity"], - [ - data.niter, - data.nshots, - data.uncertainties, - number_to_str( - fit.fidelity[qubits], - np.array(fit.fit_uncertainties[qubits][1]) / 2, - ), - number_to_str( - fit.pulse_fidelity[qubits], - np.array(fit.fit_uncertainties[qubits][1]) - / (2 * data.npulses_per_clifford), - ), - ], - ) - ) - - fig.update_layout( - showlegend=True, - xaxis_title="Circuit depth", - yaxis_title="Survival Probability", - ) - - return [fig], fitting_report + return results standard_rb_2q_inter = Routine(_acquisition, _fit, _plot) diff --git a/src/qibocal/protocols/randomized_benchmarking/utils.py b/src/qibocal/protocols/randomized_benchmarking/utils.py index 44d4f95fc..4f618f6a9 100644 --- a/src/qibocal/protocols/randomized_benchmarking/utils.py +++ b/src/qibocal/protocols/randomized_benchmarking/utils.py @@ -352,6 +352,14 @@ def extract_probabilities(self, qubits): return probs +@dataclass +class RB2QInterData(RB2QData): + """The output of the acquisition function.""" + + fidelity: dict[QubitPairId, list] = None + """Number of pulses for an average clifford.""" + + @dataclass class StandardRBResult(Results): """Standard RB outputs.""" @@ -375,6 +383,7 @@ def __contains__(self, qubit: QubitId): def setup( params: Parameters, single_qubit: bool = True, + interleave: Optional[str] = None, ): backend = GlobalBackend() # For simulations, a noise model can be added. @@ -390,6 +399,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 data = cls( depths=params.depths, uncertainties=params.uncertainties, @@ -516,7 +527,7 @@ def twoq_rb_acquisition( 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.""" data, noise_model, backend = setup(params, single_qubit=False) @@ -691,7 +702,9 @@ def load_cliffords(file): return two_qubit_cliffords -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. @@ -715,18 +728,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) - - if interleaved == "CZ": - new_circuit.add(gates.CZ(0, 1)) - random_indexes.append("13") # FIXME: Check the 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) From 2d037af57f4759cba74ca872b43282d094e6d592 Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Wed, 22 May 2024 17:33:41 +0400 Subject: [PATCH 06/22] feat: Add interleave parameter --- .../standard_rb_2q_inter.py | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py index 334228f6f..b0aa4d014 100644 --- a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py +++ b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py @@ -9,6 +9,8 @@ StandardRB2QParameters, ) +import numpy as np + from .utils import RB2QInterData, StandardRBResult, fit, twoq_rb_acquisition @@ -16,7 +18,7 @@ class StandardRB2QInterParameters(StandardRB2QParameters): """Parameters for the standard 2q randomized benchmarking protocol.""" - interleaved: str = "CZ" + interleave: str = "CZ" """Gate to interleave""" @@ -58,20 +60,18 @@ def _fit(data: RB2QInterData) -> StandardRBResult: qubits = data.pairs results = fit(qubits, data) - - fidelity_cz = {} - for qubit in qubits: - fid_cz = results.fidelity[qubits] / data.fidelity[qubits][0] - uncertainty_cz = data.fidelity[qubits][1] # FIXME: Propagate uncertainty - fidelity_cz[qubit] = [fid_cz, uncertainty_cz] - - import pdb - - pdb.set_trace() - - results = StandardRB2QInterResult( - fidelity, pulse_fidelity, popts, perrs, error_barss, fidelity_cz - ) + + #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] + + results = StandardRB2QInterResult( + results.fidelity, results.pulse_fidelity, results.fit_parameters, results.fit_uncertainties, results.error_bars, fidelity_cz + ) return results From d0b8a47ea5649f53d0e68a5a8b34a4b9d325629b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 22 May 2024 13:33:53 +0000 Subject: [PATCH 07/22] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../standard_rb_2q_inter.py | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py index b0aa4d014..dcb1afad9 100644 --- a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py +++ b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py @@ -1,5 +1,6 @@ from dataclasses import dataclass +import numpy as np from qibolab.platform import Platform from qibolab.qubits import QubitPairId @@ -9,8 +10,6 @@ StandardRB2QParameters, ) -import numpy as np - from .utils import RB2QInterData, StandardRBResult, fit, twoq_rb_acquisition @@ -60,17 +59,28 @@ def _fit(data: RB2QInterData) -> StandardRBResult: qubits = data.pairs results = fit(qubits, data) - - #FIXME: I can only get the data.fidelity if there is an acquisition step + + # 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) + 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] - + results = StandardRB2QInterResult( - results.fidelity, results.pulse_fidelity, results.fit_parameters, results.fit_uncertainties, results.error_bars, fidelity_cz + results.fidelity, + results.pulse_fidelity, + results.fit_parameters, + results.fit_uncertainties, + results.error_bars, + fidelity_cz, ) return results From a636f88197306361a03913dfb162d81f2428c2aa Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Wed, 22 May 2024 17:37:44 +0400 Subject: [PATCH 08/22] fix: remove unused --- .../randomized_benchmarking/utils.py | 35 +------------------ 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/utils.py b/src/qibocal/protocols/randomized_benchmarking/utils.py index 189158012..2007c7994 100644 --- a/src/qibocal/protocols/randomized_benchmarking/utils.py +++ b/src/qibocal/protocols/randomized_benchmarking/utils.py @@ -845,37 +845,4 @@ def fit(qubits, data): perrs[qubit] = perr popts[qubit] = popt - return StandardRBResult(fidelity, pulse_fidelity, popts, perrs, error_barss) - - -def layer_2q_interleaved_circuit( - rb_gen: Callable, depth: int, qubits, interleaved: str -) -> 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. - Returns: - Circuit: with `depth` many layers. - """ - - full_circuit = None - random_indexes = [] - # Build each layer, there will be depth many in the final circuit. - for _ in range(depth): - # Generate a layer. - new_layer, random_index = rb_gen.layer_gen() - new_circuit = Circuit(2) - for gate in new_layer: - new_circuit.add(gate) - random_indexes.append(random_index) - - if interleaved == "CZ": - new_circuit.add(gates.CZ(0, 1)) - random_indexes.append("13") # FIXME: Check the index - - if full_circuit is None: # instantiate in first loop - full_circuit = Circuit(new_circuit.nqubits) - full_circuit = full_circuit + new_circuit - return full_circuit, random_indexes + return StandardRBResult(fidelity, pulse_fidelity, popts, perrs, error_barss) \ No newline at end of file From d363455cee0b681276aa8f4e6321dd2b3ea17ca0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 22 May 2024 13:38:57 +0000 Subject: [PATCH 09/22] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qibocal/protocols/randomized_benchmarking/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/utils.py b/src/qibocal/protocols/randomized_benchmarking/utils.py index 2007c7994..e8b2ac043 100644 --- a/src/qibocal/protocols/randomized_benchmarking/utils.py +++ b/src/qibocal/protocols/randomized_benchmarking/utils.py @@ -845,4 +845,4 @@ def fit(qubits, data): perrs[qubit] = perr popts[qubit] = popt - return StandardRBResult(fidelity, pulse_fidelity, popts, perrs, error_barss) \ No newline at end of file + return StandardRBResult(fidelity, pulse_fidelity, popts, perrs, error_barss) From d6ba16f80780ee3dfd868a978ed360080ff508ec Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Thu, 23 May 2024 15:29:13 +0400 Subject: [PATCH 10/22] fix: tmp_path on tests --- .../protocols/randomized_benchmarking/utils.py | 4 ++-- tests/test_randomized_benchmarking.py | 14 ++++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/utils.py b/src/qibocal/protocols/randomized_benchmarking/utils.py index 2007c7994..67251a907 100644 --- a/src/qibocal/protocols/randomized_benchmarking/utils.py +++ b/src/qibocal/protocols/randomized_benchmarking/utils.py @@ -630,7 +630,7 @@ def clifford_to_matrix(clifford): return unitary -def generate_inv_dict_cliffords_file(two_qubit_cliffords, output_file): +def generate_inv_dict_cliffords_file(two_qubit_cliffords, output_file=None): """ Generate an inverse dictionary of clifford matrices and save it to a npz file. @@ -845,4 +845,4 @@ def fit(qubits, data): perrs[qubit] = perr popts[qubit] = popt - return StandardRBResult(fidelity, pulse_fidelity, popts, perrs, error_barss) \ No newline at end of file + return StandardRBResult(fidelity, pulse_fidelity, popts, perrs, error_barss) diff --git a/tests/test_randomized_benchmarking.py b/tests/test_randomized_benchmarking.py index 221ec896a..c38b6ee96 100644 --- a/tests/test_randomized_benchmarking.py +++ b/tests/test_randomized_benchmarking.py @@ -1,5 +1,3 @@ -import os -import pathlib from functools import reduce import numpy as np @@ -172,17 +170,17 @@ def test_random_clifford(qubits, seed): assert np.allclose(matrix, result) -def test_generate_inv_dict_cliffords_file(): +def test_generate_inv_dict_cliffords_file(tmp_path): file = "2qubitCliffs.json" two_qubit_cliffords = load_cliffords(file) - path_test_inv = pathlib.Path(__file__).parent / "test.npz" - clifford_inv = generate_inv_dict_cliffords_file(two_qubit_cliffords, path_test_inv) - clifford_inv = np.load(path_test_inv) + tmp_path = tmp_path / "test.npz" + + clifford_inv = generate_inv_dict_cliffords_file(two_qubit_cliffords) + np.savez(tmp_path, **clifford_inv) + clifford_inv = np.load(tmp_path) file_inv = "2qubitCliffsInv.npz" clifford_matrices_inv = load_inverse_cliffords(file_inv) assert clifford_inv.files == clifford_matrices_inv.files - - os.remove(path_test_inv) From 3c233f442b98c5345722ac23d2ec530387d479c4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 06:01:11 +0000 Subject: [PATCH 11/22] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qibocal/protocols/randomized_benchmarking/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/utils.py b/src/qibocal/protocols/randomized_benchmarking/utils.py index fd87d50b3..ed1599c07 100644 --- a/src/qibocal/protocols/randomized_benchmarking/utils.py +++ b/src/qibocal/protocols/randomized_benchmarking/utils.py @@ -10,8 +10,8 @@ from qibo.backends import GlobalBackend from qibo.config import raise_error from qibo.models import Circuit -from qibolab.qubits import QubitId, QubitPairId from qibolab.platform import Platform +from qibolab.qubits import QubitId, QubitPairId from qibocal.auto.operation import Data, Parameters, Results from qibocal.auto.transpile import ( From 4224415b5abcdb788d7324f838abc8dc261693ca Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Tue, 23 Jul 2024 10:02:50 +0400 Subject: [PATCH 12/22] fix: platform patch --- src/qibocal/protocols/randomized_benchmarking/standard_rb_2q.py | 2 +- .../protocols/randomized_benchmarking/standard_rb_2q_inter.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q.py b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q.py index d76aa59ec..79cb260b9 100644 --- a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q.py +++ b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q.py @@ -32,7 +32,7 @@ def _acquisition( ) -> RB2QData: """Data acquisition for two qubit Standard Randomized Benchmarking.""" - return twoq_rb_acquisition(params, targets) + return twoq_rb_acquisition(params, platform, targets) def _fit(data: RB2QData) -> StandardRBResult: diff --git a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py index dcb1afad9..88436a83b 100644 --- a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py +++ b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py @@ -36,7 +36,7 @@ def _acquisition( ) -> RB2QInterData: """Data acquisition for two qubit Standard Randomized Benchmarking.""" - data = twoq_rb_acquisition(params, targets, interleave=params.interleave) + data = twoq_rb_acquisition(params, platform, targets, interleave=params.interleave) fidelity = {} for target in targets: From 29cc3dc5ee97220ac178156dc0c983505c3d799a Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Tue, 23 Jul 2024 10:23:40 +0400 Subject: [PATCH 13/22] fix: argument order --- src/qibocal/protocols/randomized_benchmarking/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/utils.py b/src/qibocal/protocols/randomized_benchmarking/utils.py index ed1599c07..8d2aff5f5 100644 --- a/src/qibocal/protocols/randomized_benchmarking/utils.py +++ b/src/qibocal/protocols/randomized_benchmarking/utils.py @@ -525,8 +525,8 @@ def rb_acquisition( def twoq_rb_acquisition( params: Parameters, targets: list[QubitPairId], - add_inverse_layer: bool = True, platform: Platform, + add_inverse_layer: bool = True, interleave: str = None, ) -> Union[RB2QData, RB2QInterData]: """ From 9c7d1b1055ce6b9e8848c54e2fbb4f9275fa3682 Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Tue, 23 Jul 2024 11:02:49 +0400 Subject: [PATCH 14/22] feat: fix tests and interleaved gate for CZ --- .../randomized_benchmarking/filtered_rb.py | 2 +- .../randomized_benchmarking/standard_rb.py | 2 +- .../standard_rb_2q_inter.py | 16 ++++++++++++---- .../protocols/randomized_benchmarking/utils.py | 12 ++++++++---- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/filtered_rb.py b/src/qibocal/protocols/randomized_benchmarking/filtered_rb.py index fb04d84df..6c949bb73 100644 --- a/src/qibocal/protocols/randomized_benchmarking/filtered_rb.py +++ b/src/qibocal/protocols/randomized_benchmarking/filtered_rb.py @@ -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, add_inverse_layer=False) + return rb_acquisition(params, platform, targets, add_inverse_layer=False) def _fit(data: RBData) -> FilteredRBResult: diff --git a/src/qibocal/protocols/randomized_benchmarking/standard_rb.py b/src/qibocal/protocols/randomized_benchmarking/standard_rb.py index 70353eb82..32329bc97 100644 --- a/src/qibocal/protocols/randomized_benchmarking/standard_rb.py +++ b/src/qibocal/protocols/randomized_benchmarking/standard_rb.py @@ -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) + return rb_acquisition(params, platform, targets) def _fit(data: RBData) -> StandardRBResult: diff --git a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py index 88436a83b..872366127 100644 --- a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py +++ b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py @@ -46,7 +46,7 @@ def _acquisition( return data -def _fit(data: RB2QInterData) -> StandardRBResult: +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. @@ -54,7 +54,7 @@ def _fit(data: RB2QInterData) -> StandardRBResult: data (RBData): Data from the data acquisition stage. Returns: - StandardRBResult: Aggregated and processed data. + StandardRB2QInterResult: Aggregated and processed data. """ qubits = data.pairs @@ -74,7 +74,7 @@ def _fit(data: RB2QInterData) -> StandardRBResult: ) fidelity_cz[qubit] = [fid_cz, uncertainty_cz] - results = StandardRB2QInterResult( + new_results = StandardRB2QInterResult( results.fidelity, results.pulse_fidelity, results.fit_parameters, @@ -82,8 +82,16 @@ def _fit(data: RB2QInterData) -> StandardRBResult: results.error_bars, fidelity_cz, ) + else: + new_results = StandardRB2QInterResult( + results.fidelity, + results.pulse_fidelity, + results.fit_parameters, + results.fit_uncertainties, + results.error_bars, + ) - return results + return new_results standard_rb_2q_inter = Routine(_acquisition, _fit, _plot) diff --git a/src/qibocal/protocols/randomized_benchmarking/utils.py b/src/qibocal/protocols/randomized_benchmarking/utils.py index 8d2aff5f5..70c9e0b59 100644 --- a/src/qibocal/protocols/randomized_benchmarking/utils.py +++ b/src/qibocal/protocols/randomized_benchmarking/utils.py @@ -122,6 +122,7 @@ def random_circuits( inverse_layer=True, single_qubit=True, file_inv=pathlib.Path(), + interleave=None, ) -> Iterable: """Returns random (self-inverting) Clifford circuits.""" @@ -129,7 +130,7 @@ def random_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) @@ -419,6 +420,7 @@ def get_circuits( add_inverse_layer, single_qubit, inv_file, + interleave, ) circuits.extend(circuits_depth) @@ -478,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, @@ -495,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 ) @@ -524,8 +527,8 @@ 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, ) -> Union[RB2QData, RB2QInterData]: @@ -542,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, 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 ) @@ -584,6 +587,7 @@ def layer_circuit( 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. From 2a79e37e978d24b87ca2412f0592762dc5222d7d Mon Sep 17 00:00:00 2001 From: Juan Cereijo Date: Fri, 26 Jul 2024 10:30:57 +0400 Subject: [PATCH 15/22] Update src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py Co-authored-by: Edoardo Pedicillo --- .../protocols/randomized_benchmarking/standard_rb_2q_inter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py index 872366127..90760903b 100644 --- a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py +++ b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py @@ -34,7 +34,7 @@ def _acquisition( platform: Platform, targets: list[QubitPairId], ) -> RB2QInterData: - """Data acquisition for two qubit Standard Randomized Benchmarking.""" + """Data acquisition for two qubit Interleaved Randomized Benchmarking.""" data = twoq_rb_acquisition(params, platform, targets, interleave=params.interleave) From 8846702752ac131d7ff6b6526d6aa7b86b2fc648 Mon Sep 17 00:00:00 2001 From: Juan Cereijo Date: Fri, 26 Jul 2024 10:31:03 +0400 Subject: [PATCH 16/22] Update src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py Co-authored-by: Edoardo Pedicillo --- .../protocols/randomized_benchmarking/standard_rb_2q_inter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py index 90760903b..208516987 100644 --- a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py +++ b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py @@ -51,7 +51,7 @@ def _fit(data: RB2QInterData) -> StandardRB2QInterResult: exponential function y = Ap^x+B. Args: - data (RBData): Data from the data acquisition stage. + data: Data from the data acquisition stage. Returns: StandardRB2QInterResult: Aggregated and processed data. From 30468ee1c87540cb43eaf1a2db6b34760bef7cae Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Fri, 26 Jul 2024 10:33:17 +0400 Subject: [PATCH 17/22] fix: docs --- src/qibocal/protocols/randomized_benchmarking/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/utils.py b/src/qibocal/protocols/randomized_benchmarking/utils.py index 70c9e0b59..7befff025 100644 --- a/src/qibocal/protocols/randomized_benchmarking/utils.py +++ b/src/qibocal/protocols/randomized_benchmarking/utils.py @@ -313,7 +313,7 @@ class RB2QInterData(RB2QData): """The output of the acquisition function.""" fidelity: dict[QubitPairId, list] = None - """Number of pulses for an average clifford.""" + """The interleaved fidelity of this qubit.""" @dataclass From b7af56e2720ee930befdd92fe9c5c93915a2260b Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Mon, 29 Jul 2024 13:09:28 +0400 Subject: [PATCH 18/22] fix: conflicts --- src/qibocal/protocols/randomized_benchmarking/utils.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/utils.py b/src/qibocal/protocols/randomized_benchmarking/utils.py index 0d5497c6c..63dd93b2e 100644 --- a/src/qibocal/protocols/randomized_benchmarking/utils.py +++ b/src/qibocal/protocols/randomized_benchmarking/utils.py @@ -482,7 +482,6 @@ def rb_acquisition( params: Parameters, platform: Platform, targets: list[QubitId], - platform: Platform, add_inverse_layer: bool = True, interleave: str = None, ) -> RBData: @@ -530,7 +529,6 @@ def twoq_rb_acquisition( params: Parameters, platform: Platform, targets: list[QubitPairId], - platform: Platform, add_inverse_layer: bool = True, interleave: str = None, ) -> Union[RB2QData, RB2QInterData]: From 79bd4dd449b9dbf466a537f583711055f560ec3c Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Wed, 7 Aug 2024 12:14:53 +0400 Subject: [PATCH 19/22] fix: comments --- src/qibocal/protocols/randomized_benchmarking/utils.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/utils.py b/src/qibocal/protocols/randomized_benchmarking/utils.py index 63dd93b2e..d44c9a195 100644 --- a/src/qibocal/protocols/randomized_benchmarking/utils.py +++ b/src/qibocal/protocols/randomized_benchmarking/utils.py @@ -364,9 +364,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 isinstance(cls, RB2QData) and interleave is not None: + if single_qubit: + cls = RBData + elif interleave is not None: cls = RB2QInterData + else: + cls = RB2QData data = cls( depths=params.depths, uncertainties=params.uncertainties, From fd141d8d37db0d31b1752510d1ee43f9f4b48fea Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Wed, 7 Aug 2024 12:15:33 +0400 Subject: [PATCH 20/22] fix: remove comment --- .../protocols/randomized_benchmarking/standard_rb_2q_inter.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py index 208516987..9078c25d1 100644 --- a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py +++ b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py @@ -60,7 +60,6 @@ def _fit(data: RB2QInterData) -> StandardRB2QInterResult: 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: From 0aa6469bfdf35b86a0dafdd6bf2e657538898c16 Mon Sep 17 00:00:00 2001 From: Andrea Date: Wed, 14 Aug 2024 16:27:10 +0400 Subject: [PATCH 21/22] fix: Minor fixes --- .../randomized_benchmarking/standard_rb.py | 1 - .../standard_rb_2q_inter.py | 42 +++++++++---------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/standard_rb.py b/src/qibocal/protocols/randomized_benchmarking/standard_rb.py index 32329bc97..0788afabf 100644 --- a/src/qibocal/protocols/randomized_benchmarking/standard_rb.py +++ b/src/qibocal/protocols/randomized_benchmarking/standard_rb.py @@ -120,7 +120,6 @@ def _plot( """ if isinstance(target, list): target = tuple(target) - qubit = target fig = go.Figure() fitting_report = "" diff --git a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py index 9078c25d1..5b4a9de5e 100644 --- a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py +++ b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py @@ -1,4 +1,4 @@ -from dataclasses import dataclass +from dataclasses import dataclass, fields import numpy as np from qibolab.platform import Platform @@ -28,6 +28,14 @@ class StandardRB2QInterResult(StandardRBResult): fidelity_cz: dict[QubitPairId, list] = None """The overall fidelity for the CZ gate and its uncertainty.""" + def __contains__(self, value: QubitPairId): + 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, @@ -60,9 +68,9 @@ def _fit(data: RB2QInterData) -> StandardRB2QInterResult: qubits = data.pairs results = fit(qubits, data) - if data.fidelity is not None: - fidelity_cz = {} - for qubit in qubits: + fidelity_cz = {} + for qubit in qubits: + if data.fidelity[qubit] is not None: fid_cz = results.fidelity[qubit] / data.fidelity[qubit][0] uncertainty_cz = np.sqrt( 1 @@ -73,24 +81,14 @@ def _fit(data: RB2QInterData) -> StandardRB2QInterResult: ) 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 + 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) From 667e954a6d5d5a1cabd16f54d4917e88f902c3bb Mon Sep 17 00:00:00 2001 From: Andrea Date: Wed, 14 Aug 2024 17:24:28 +0400 Subject: [PATCH 22/22] fix: Fix dummy tests --- .../randomized_benchmarking/standard_rb_2q_inter.py | 4 +++- src/qibocal/protocols/randomized_benchmarking/utils.py | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py index 5b4a9de5e..c6a657e05 100644 --- a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py +++ b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q_inter.py @@ -29,6 +29,8 @@ class StandardRB2QInterResult(StandardRBResult): """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) @@ -70,7 +72,7 @@ def _fit(data: RB2QInterData) -> StandardRB2QInterResult: fidelity_cz = {} for qubit in qubits: - if data.fidelity[qubit] is not None: + 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 diff --git a/src/qibocal/protocols/randomized_benchmarking/utils.py b/src/qibocal/protocols/randomized_benchmarking/utils.py index d44c9a195..381056e8f 100644 --- a/src/qibocal/protocols/randomized_benchmarking/utils.py +++ b/src/qibocal/protocols/randomized_benchmarking/utils.py @@ -312,7 +312,7 @@ def extract_probabilities(self, qubits): class RB2QInterData(RB2QData): """The output of the acquisition function.""" - fidelity: dict[QubitPairId, list] = None + fidelity: dict[QubitPairId, list] = field(default_factory=dict) """The interleaved fidelity of this qubit.""" @@ -328,7 +328,9 @@ 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."""