From 19534600101d54c5c294d6559cef82bb6c965af8 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Tue, 3 Oct 2023 17:52:50 +0400 Subject: [PATCH 01/44] qutrit classification routine --- src/qibocal/cli/report.py | 1 - .../protocols/characterization/__init__.py | 2 + .../characterization/classification.py | 114 +++++------------- .../protocols/characterization/utils.py | 44 +++++++ 4 files changed, 79 insertions(+), 82 deletions(-) diff --git a/src/qibocal/cli/report.py b/src/qibocal/cli/report.py index f254b08ba..295187ab0 100644 --- a/src/qibocal/cli/report.py +++ b/src/qibocal/cli/report.py @@ -30,7 +30,6 @@ def report(path): # load executor executor = Executor.load(runcard, path) - # produce html builder = ReportBuilder(path, runcard.qubits, executor, meta) builder.run(path) diff --git a/src/qibocal/protocols/characterization/__init__.py b/src/qibocal/protocols/characterization/__init__.py index 28cf8fe7f..02eed9496 100644 --- a/src/qibocal/protocols/characterization/__init__.py +++ b/src/qibocal/protocols/characterization/__init__.py @@ -20,6 +20,7 @@ ) from .qubit_spectroscopy import qubit_spectroscopy from .qubit_spectroscopy_ef import qubit_spectroscopy_ef +from .qutrit_classification import qutrit_classification from .rabi.amplitude import rabi_amplitude from .rabi.ef import rabi_amplitude_ef from .rabi.length import rabi_length @@ -78,3 +79,4 @@ class Operation(Enum): twpa_power = twpa_power rabi_amplitude_ef = rabi_amplitude_ef qubit_spectroscopy_ef = qubit_spectroscopy_ef + qutrit_classification = qutrit_classification diff --git a/src/qibocal/protocols/characterization/classification.py b/src/qibocal/protocols/characterization/classification.py index 8c1a1ca37..c1318de53 100644 --- a/src/qibocal/protocols/characterization/classification.py +++ b/src/qibocal/protocols/characterization/classification.py @@ -25,10 +25,13 @@ ) from qibocal.auto.serialize import serialize from qibocal.fitting.classifier import run -from qibocal.protocols.characterization.utils import get_color_state0, get_color_state1 +from qibocal.protocols.characterization.utils import ( + MESH_SIZE, + evaluate_grid, + get_color_state0, + get_color_state1, +) -MESH_SIZE = 50 -MARGIN = 0 COLUMNWIDTH = 600 ROC_LENGHT = 800 ROC_WIDTH = 800 @@ -70,55 +73,51 @@ class SingleShotClassificationData(Data): def register_qubit(self, qubit, state, i, q): """Store output for single qubit.""" - ar = np.empty(i.shape, dtype=ClassificationType) + shape = (1,) if np.isscalar(i) else i.shape + ar = np.empty(shape, dtype=ClassificationType) ar["i"] = i ar["q"] = q ar["state"] = state - self.data[qubit] = np.rec.array(ar) - - def add_data(self, qubit, state, i, q): - """Store output for single qubit.""" - ar = np.empty(i.shape, dtype=ClassificationType) - ar["i"] = i - ar["q"] = q - ar["state"] = state - self.data[qubit] = np.append(self.data[qubit], np.rec.array(ar)) + if qubit in self.data: + self.data[qubit] = np.rec.array(np.concatenate((self.data[qubit], ar))) + else: + self.data[qubit] = np.rec.array(ar) @dataclass class SingleShotClassificationResults(Results): """SingleShotClassification outputs.""" - y_tests: dict[QubitId, list] - """States of the testing set.""" - x_tests: dict[QubitId, list] - """I,Q couples to evaluate accuracy and test time.""" names: list """List of models name.""" - threshold: dict[QubitId, float] - """Threshold for classification.""" - rotation_angle: dict[QubitId, float] - """Threshold for classification.""" - mean_gnd_states: dict[QubitId, list[float]] - """Centroid of the ground state blob.""" - mean_exc_states: dict[QubitId, list[float]] - """Centroid of the excited state blob.""" - fidelity: dict[QubitId, float] - """Fidelity evaluated only with the `qubit_fit` model.""" - assignment_fidelity: dict[QubitId, float] - """Assignment fidelity evaluated only with the `qubit_fit` model.""" savedir: str """Dumping folder of the classification results.""" y_preds: dict[QubitId, list] """Models' predictions of the test set.""" grid_preds: dict[QubitId, list] """Models' prediction of the contour grid.""" + threshold: dict[QubitId, float] = field(default_factory=dict) + """Threshold for classification.""" + rotation_angle: dict[QubitId, float] = field(default_factory=dict) + """Threshold for classification.""" + mean_gnd_states: dict[QubitId, list[float]] = field(default_factory=dict) + """Centroid of the ground state blob.""" + mean_exc_states: dict[QubitId, list[float]] = field(default_factory=dict) + """Centroid of the excited state blob.""" + fidelity: dict[QubitId, float] = field(default_factory=dict) + """Fidelity evaluated only with the `qubit_fit` model.""" + assignment_fidelity: dict[QubitId, float] = field(default_factory=dict) + """Assignment fidelity evaluated only with the `qubit_fit` model.""" models: dict[QubitId, list] = field(default_factory=list) """List of trained classification models.""" benchmark_table: Optional[dict[QubitId, pd.DataFrame]] = field(default_factory=dict) """Benchmark tables.""" classifiers_hpars: Optional[dict[QubitId, dict]] = field(default_factory=dict) """Classifiers hyperparameters.""" + x_tests: dict[QubitId, list] = field(default_factory=dict) + """Test set.""" + y_tests: dict[QubitId, list] = field(default_factory=dict) + """Test set.""" def save(self, path): classifiers = run.import_classifiers(self.names) @@ -234,7 +233,9 @@ def _acquisition( # retrieve and store the results for every qubit for qubit in qubits: result = state1_results[ro_pulses[qubit].serial] - data.add_data(qubit=qubit, state=1, i=result.voltage_i, q=result.voltage_q) + data.register_qubit( + qubit=qubit, state=1, i=result.voltage_i, q=result.voltage_q + ) return data @@ -267,10 +268,8 @@ def _fit(data: SingleShotClassificationData) -> SingleShotClassificationResults: hpars[qubit] = {} y_preds = [] grid_preds = [] - state0_data = qubit_data[qubit_data["state"] == 0] - state1_data = qubit_data[qubit_data["state"] == 1] - grid = evaluate_grid(state0_data, state1_data) + grid = evaluate_grid(qubit_data) for i, model_name in enumerate(names): hpars[qubit][model_name] = hpars_list[i] try: @@ -320,7 +319,7 @@ def _plot( qubit_data = data.data[qubit] state0_data = qubit_data[qubit_data["state"] == 0] state1_data = qubit_data[qubit_data["state"] == 1] - grid = evaluate_grid(state0_data, state1_data) + grid = evaluate_grid(qubit_data) fig = make_subplots( rows=1, @@ -569,50 +568,3 @@ def _update( single_shot_classification = Routine(_acquisition, _fit, _plot, _update) - - -def evaluate_grid( - state0_data: npt.NDArray, - state1_data: npt.NDArray, -): - """ - This function returns a matrix grid evaluated from - the datapoints `state0_data` and `state1_data`. - """ - max_x = ( - max( - 0, - state0_data["i"].max(), - state1_data["i"].max(), - ) - + MARGIN - ) - max_y = ( - max( - 0, - state0_data["q"].max(), - state1_data["q"].max(), - ) - + MARGIN - ) - min_x = ( - min( - 0, - state0_data["i"].min(), - state1_data["i"].min(), - ) - - MARGIN - ) - min_y = ( - min( - 0, - state0_data["q"].min(), - state1_data["q"].min(), - ) - - MARGIN - ) - i_values, q_values = np.meshgrid( - np.linspace(min_x, max_x, num=MESH_SIZE), - np.linspace(min_y, max_y, num=MESH_SIZE), - ) - return np.vstack([i_values.ravel(), q_values.ravel()]).T diff --git a/src/qibocal/protocols/characterization/utils.py b/src/qibocal/protocols/characterization/utils.py index 0387e70c3..e2eefd142 100644 --- a/src/qibocal/protocols/characterization/utils.py +++ b/src/qibocal/protocols/characterization/utils.py @@ -18,6 +18,8 @@ HZ_TO_GHZ = 1e-9 V_TO_UV = 1e6 S_TO_NS = 1e9 +MESH_SIZE = 50 +MARGIN = 0 class PowerLevel(str, Enum): @@ -349,3 +351,45 @@ def significant_digit(number: float): position = max(position, np.ceil(-np.log10(abs(np.imag(number))))) return int(position) + + +def evaluate_grid( + data: npt.NDArray, +): + """ + This function returns a matrix grid evaluated from + the datapoints `data`. + """ + max_x = ( + max( + 0, + data["i"].max(), + ) + + MARGIN + ) + max_y = ( + max( + 0, + data["q"].max(), + ) + + MARGIN + ) + min_x = ( + min( + 0, + data["i"].min(), + ) + - MARGIN + ) + min_y = ( + min( + 0, + data["q"].min(), + ) + - MARGIN + ) + i_values, q_values = np.meshgrid( + np.linspace(min_x, max_x, num=MESH_SIZE), + np.linspace(min_y, max_y, num=MESH_SIZE), + ) + return np.vstack([i_values.ravel(), q_values.ravel()]).T From dde3a5d61a5a511df261e1026cc44639780627e7 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Tue, 3 Oct 2023 18:35:54 +0400 Subject: [PATCH 02/44] abstract plot function --- .../characterization/classification.py | 257 +++--------------- .../protocols/characterization/utils.py | 154 +++++++++++ 2 files changed, 190 insertions(+), 221 deletions(-) diff --git a/src/qibocal/protocols/characterization/classification.py b/src/qibocal/protocols/characterization/classification.py index c1318de53..baba17016 100644 --- a/src/qibocal/protocols/characterization/classification.py +++ b/src/qibocal/protocols/characterization/classification.py @@ -7,7 +7,6 @@ import numpy.typing as npt import pandas as pd import plotly.graph_objects as go -from plotly.subplots import make_subplots from qibolab import AcquisitionType, ExecutionParameters from qibolab.platform import Platform from qibolab.pulses import PulseSequence @@ -26,18 +25,17 @@ from qibocal.auto.serialize import serialize from qibocal.fitting.classifier import run from qibocal.protocols.characterization.utils import ( + LEGEND_FONT_SIZE, MESH_SIZE, + TITLE_SIZE, evaluate_grid, get_color_state0, - get_color_state1, + plot_results, ) -COLUMNWIDTH = 600 ROC_LENGHT = 800 ROC_WIDTH = 800 -LEGEND_FONT_SIZE = 20 -TITLE_SIZE = 25 -SPACING = 0.1 +DEFAULT_CLASSIFIER = "qubit_fit" @dataclass @@ -48,7 +46,9 @@ class SingleShotClassificationParameters(Parameters): """Number of shots.""" relaxation_time: Optional[int] = None """Relaxation time (ns).""" - classifiers_list: Optional[list[str]] = field(default_factory=lambda: ["qubit_fit"]) + classifiers_list: Optional[list[str]] = field( + default_factory=lambda: [DEFAULT_CLASSIFIER] + ) """List of models to classify the qubit states""" savedir: Optional[str] = " " """Dumping folder of the classification results""" @@ -68,7 +68,9 @@ class SingleShotClassificationData(Data): """Dumping folder of the classification results""" data: dict[QubitId, npt.NDArray] = field(default_factory=dict) """Raw data acquired.""" - classifiers_list: Optional[list[str]] = field(default_factory=lambda: ["qubit_fit"]) + classifiers_list: Optional[list[str]] = field( + default_factory=lambda: [DEFAULT_CLASSIFIER] + ) """List of models to classify the qubit states""" def register_qubit(self, qubit, state, i, q): @@ -313,144 +315,21 @@ def _fit(data: SingleShotClassificationData) -> SingleShotClassificationResults: def _plot( data: SingleShotClassificationData, qubit, fit: SingleShotClassificationResults ): - figures = [] fitting_report = None models_name = data.classifiers_list - qubit_data = data.data[qubit] - state0_data = qubit_data[qubit_data["state"] == 0] - state1_data = qubit_data[qubit_data["state"] == 1] - grid = evaluate_grid(qubit_data) - - fig = make_subplots( - rows=1, - cols=len(models_name), - horizontal_spacing=SPACING * 3 / len(models_name) * 3, - vertical_spacing=SPACING, - subplot_titles=[run.pretty_name(model) for model in models_name], - column_width=[COLUMNWIDTH] * len(models_name), - ) - - if len(models_name) != 1 and fit is not None: - fig_roc = go.Figure() - fig_roc.add_shape( - type="line", line=dict(dash="dash"), x0=0.0, x1=1.0, y0=0.0, y1=1.0 - ) - fig_benchmarks = make_subplots( - rows=1, - cols=3, - horizontal_spacing=SPACING, - vertical_spacing=SPACING, - subplot_titles=("accuracy", "training time (s)", "testing time (s)"), - # pylint: disable=E1101 - ) - + figures = plot_results(data, qubit, 2, fit) if fit is not None: y_test = fit.y_tests[qubit] - x_test = fit.x_tests[qubit] - - for i, model in enumerate(models_name): - if fit is not None: - y_pred = fit.y_preds[qubit][i] - predictions = fit.grid_preds[qubit][i] - fig.add_trace( - go.Contour( - x=grid[:, 0], - y=grid[:, 1], - z=np.array(predictions).flatten(), - showscale=False, - colorscale=[get_color_state0(i), get_color_state1(i)], - opacity=0.2, - name="Score", - hoverinfo="skip", - showlegend=True, - ), - row=1, - col=i + 1, - ) - - model = run.pretty_name(model) - max_x = max(grid[:, 0]) - max_y = max(grid[:, 1]) - min_x = min(grid[:, 0]) - min_y = min(grid[:, 1]) - - fig.add_trace( - go.Scatter( - x=state0_data["i"], - y=state0_data["q"], - name=f"{model}: state 0", - legendgroup=f"{model}: state 0", - mode="markers", - showlegend=True, - opacity=0.7, - marker=dict(size=3, color=get_color_state0(i)), - ), - row=1, - col=i + 1, - ) + y_pred = fit.y_preds[qubit] - fig.add_trace( - go.Scatter( - x=state1_data["i"], - y=state1_data["q"], - name=f"{model}: state 1", - legendgroup=f"{model}: state 1", - mode="markers", - showlegend=True, - opacity=0.7, - marker=dict(size=3, color=get_color_state1(i)), - ), - row=1, - col=i + 1, - ) - - fig.add_trace( - go.Scatter( - x=[np.average(state0_data["i"])], - y=[np.average(state0_data["q"])], - name=f"{model}: state 0", - legendgroup=f"{model}: state 0", - showlegend=False, - mode="markers", - marker=dict(size=10, color=get_color_state0(i)), - ), - row=1, - col=i + 1, - ) - - fig.add_trace( - go.Scatter( - x=[np.average(state1_data["i"])], - y=[np.average(state1_data["q"])], - name=f"{model}: state 1", - legendgroup=f"{model}: state 1", - showlegend=False, - mode="markers", - marker=dict(size=10, color=get_color_state1(i)), - ), - row=1, - col=i + 1, - ) - fig.update_xaxes( - title_text=f"i (V)", - range=[min_x, max_x], - row=1, - col=i + 1, - autorange=False, - rangeslider=dict(visible=False), - ) - fig.update_yaxes( - title_text="q (V)", - range=[min_y, max_y], - scaleanchor="x", - scaleratio=1, - row=1, - col=i + 1, - ) - - if fit is not None: - if len(models_name) != 1: - # Evaluate the ROC curve + if len(models_name) != 1: + # Evaluate the ROC curve + fig_roc = go.Figure() + fig_roc.add_shape( + type="line", line=dict(dash="dash"), x0=0.0, x1=1.0, y0=0.0, y1=1.0 + ) + for i, model in enumerate(models_name): + y_pred = fit.y_preds[qubit][i] fpr, tpr, _ = roc_curve(y_test, y_pred) auc_score = roc_auc_score(y_test, y_pred) name = f"{model} (AUC={auc_score:.2f})" @@ -463,66 +342,23 @@ def _plot( marker=dict(size=3, color=get_color_state0(i)), ) ) - fig_benchmarks.add_trace( - go.Scatter( - x=[model], - y=[fit.benchmark_table[qubit][i][0]], - mode="markers", - showlegend=False, - marker=dict(size=10, color=get_color_state1(i)), - ), - row=1, - col=1, - ) - - fig_benchmarks.add_trace( - go.Scatter( - x=[model], - y=[fit.benchmark_table[qubit][i][2]], - mode="markers", - showlegend=False, - marker=dict(size=10, color=get_color_state1(i)), - ), - row=1, - col=2, - ) - - fig_benchmarks.add_trace( - go.Scatter( - x=[model], - y=[fit.benchmark_table[qubit][i][1]], - mode="markers", - showlegend=False, - marker=dict(size=10, color=get_color_state1(i)), - ), - row=1, - col=3, - ) - - fig_benchmarks.update_yaxes(type="log", row=1, col=2) - fig_benchmarks.update_yaxes(type="log", row=1, col=3) - fig_benchmarks.update_layout( - autosize=False, - height=COLUMNWIDTH, - width=COLUMNWIDTH * 3, - title=dict(text="Benchmarks", font=dict(size=TITLE_SIZE)), - ) - fig_roc.update_layout( - width=ROC_WIDTH, - height=ROC_LENGHT, - title=dict(text="ROC curves", font=dict(size=TITLE_SIZE)), - legend=dict(font=dict(size=LEGEND_FONT_SIZE)), - ) - fig_roc.update_xaxes( - title_text=f"False Positive Rate", - range=[0, 1], - ) - fig_roc.update_yaxes( - title_text="True Positive Rate", - range=[0, 1], - ) + fig_roc.update_layout( + width=ROC_WIDTH, + height=ROC_LENGHT, + title=dict(text="ROC curves", font=dict(size=TITLE_SIZE)), + legend=dict(font=dict(size=LEGEND_FONT_SIZE)), + ) + fig_roc.update_xaxes( + title_text=f"False Positive Rate", + range=[0, 1], + ) + fig_roc.update_yaxes( + title_text="True Positive Rate", + range=[0, 1], + ) + figures.append(fig_roc) - if models_name[i] == "qubit_fit": + if model == "qubit_fit": fitting_report = "" fitting_report += f"{qubit} | average state 0: {np.round(fit.mean_gnd_states[qubit], 3)}
" fitting_report += f"{qubit} | average state 1: {np.round(fit.mean_exc_states[qubit], 3)}
" @@ -533,27 +369,6 @@ def _plot( fitting_report += f"{qubit} | fidelity: {fit.fidelity[qubit]:.3f}
" fitting_report += f"{qubit} | assignment fidelity: {fit.assignment_fidelity[qubit]:.3f}
" - fig.update_layout( - uirevision="0", # ``uirevision`` allows zooming while live plotting - autosize=False, - height=COLUMNWIDTH, - width=COLUMNWIDTH * len(models_name), - title=dict(text="Results", font=dict(size=TITLE_SIZE)), - legend=dict( - orientation="h", - yanchor="bottom", - xanchor="left", - y=-0.3, - x=0, - itemsizing="constant", - font=dict(size=LEGEND_FONT_SIZE), - ), - ) - figures.append(fig) - - if len(models_name) != 1 and fit is not None: - figures.append(fig_roc) - figures.append(fig_benchmarks) return figures, fitting_report diff --git a/src/qibocal/protocols/characterization/utils.py b/src/qibocal/protocols/characterization/utils.py index e2eefd142..af84bc572 100644 --- a/src/qibocal/protocols/characterization/utils.py +++ b/src/qibocal/protocols/characterization/utils.py @@ -13,6 +13,7 @@ from qibocal.auto.operation import Data, Results from qibocal.config import log +from qibocal.fitting.classifier import run GHZ_TO_HZ = 1e9 HZ_TO_GHZ = 1e-9 @@ -20,6 +21,10 @@ S_TO_NS = 1e9 MESH_SIZE = 50 MARGIN = 0 +SPACING = 0.1 +COLUMNWIDTH = 600 +LEGEND_FONT_SIZE = 20 +TITLE_SIZE = 25 class PowerLevel(str, Enum): @@ -393,3 +398,152 @@ def evaluate_grid( np.linspace(min_y, max_y, num=MESH_SIZE), ) return np.vstack([i_values.ravel(), q_values.ravel()]).T + + +def plot_results(data: Data, qubit, qubit_states, fit: Results): + figures = [] + models_name = data.classifiers_list + qubit_data = data.data[qubit] + grid = evaluate_grid(qubit_data) + + fig = make_subplots( + rows=1, + cols=len(models_name), + horizontal_spacing=SPACING * 3 / len(models_name) * 3, + vertical_spacing=SPACING, + subplot_titles=[run.pretty_name(model) for model in models_name], + column_width=[COLUMNWIDTH] * len(models_name), + ) + if len(models_name) != 1 and fit is not None: + fig_benchmarks = make_subplots( + rows=1, + cols=3, + horizontal_spacing=SPACING, + vertical_spacing=SPACING, + subplot_titles=( + "accuracy", + "testing time (s)", + "training time (s)", + ) + # pylint: disable=E1101 + ) + + for i, model in enumerate(models_name): + if fit is not None: + predictions = fit.grid_preds[qubit][i] + fig.add_trace( + go.Contour( + x=grid[:, 0], + y=grid[:, 1], + z=np.array(predictions).flatten(), + showscale=False, + colorscale=[get_color_state0(i), get_color_state1(i)], + opacity=0.2, + name="Score", + hoverinfo="skip", + showlegend=True, + ), + row=1, + col=i + 1, + ) + + model = run.pretty_name(model) + max_x = max(grid[:, 0]) + max_y = max(grid[:, 1]) + min_x = min(grid[:, 0]) + min_y = min(grid[:, 1]) + + for state in range(qubit_states): + state_data = qubit_data[qubit_data["state"] == state] + + fig.add_trace( + go.Scatter( + x=state_data["i"], + y=state_data["q"], + name=f"{model}: state {state}", + legendgroup=f"{model}: state {state}", + mode="markers", + showlegend=True, + opacity=0.7, + marker=dict(size=3), + ), + row=1, + col=i + 1, + ) + + fig.add_trace( + go.Scatter( + x=[np.average(state_data["i"])], + y=[np.average(state_data["q"])], + name=f"{model}: state {state}", + legendgroup=f"{model}: state {state}", + showlegend=False, + mode="markers", + marker=dict(size=10), + ), + row=1, + col=i + 1, + ) + + fig.update_xaxes( + title_text=f"i (V)", + range=[min_x, max_x], + row=1, + col=i + 1, + autorange=False, + rangeslider=dict(visible=False), + ) + fig.update_yaxes( + title_text="q (V)", + range=[min_y, max_y], + scaleanchor="x", + scaleratio=1, + row=1, + col=i + 1, + ) + + if fit is not None: + if len(models_name) != 1: + for plot in range(3): + fig_benchmarks.add_trace( + go.Scatter( + x=[model], + y=[fit.benchmark_table[qubit][i][plot]], + mode="markers", + showlegend=False, + marker=dict(size=10, color=get_color_state1(i)), + ), + row=1, + col=plot + 1, + ) + + fig_benchmarks.update_yaxes(type="log", row=1, col=2) + fig_benchmarks.update_yaxes(type="log", row=1, col=3) + fig_benchmarks.update_layout( + autosize=False, + height=COLUMNWIDTH, + width=COLUMNWIDTH * 3, + title=dict(text="Benchmarks", font=dict(size=TITLE_SIZE)), + ) + + fig.update_layout( + uirevision="0", # ``uirevision`` allows zooming while live plotting + autosize=False, + height=COLUMNWIDTH, + width=COLUMNWIDTH * len(models_name), + title=dict(text="Results", font=dict(size=TITLE_SIZE)), + legend=dict( + orientation="h", + yanchor="bottom", + xanchor="left", + y=-0.3, + x=0, + itemsizing="constant", + font=dict(size=LEGEND_FONT_SIZE), + ), + ) + figures.append(fig) + + if len(models_name) != 1 and fit is not None: + figures.append(fig_benchmarks) + return figures From 605fe478a840c599022eb9e628fba34e5b6f0938 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Wed, 4 Oct 2023 11:07:28 +0400 Subject: [PATCH 03/44] load qutrit_classification --- .../characterization/qutrit_classification.py | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 src/qibocal/protocols/characterization/qutrit_classification.py diff --git a/src/qibocal/protocols/characterization/qutrit_classification.py b/src/qibocal/protocols/characterization/qutrit_classification.py new file mode 100644 index 000000000..fc2a41d92 --- /dev/null +++ b/src/qibocal/protocols/characterization/qutrit_classification.py @@ -0,0 +1,196 @@ +from dataclasses import dataclass, field +from typing import Optional + +import numpy as np +from qibolab import AcquisitionType, ExecutionParameters +from qibolab.platform import Platform +from qibolab.pulses import PulseSequence +from qibolab.qubits import QubitId + +from qibocal import update +from qibocal.auto.operation import Qubits, Routine +from qibocal.fitting.classifier import run +from qibocal.protocols.characterization.classification import ( + SingleShotClassificationData, + SingleShotClassificationParameters, + SingleShotClassificationResults, +) +from qibocal.protocols.characterization.utils import ( + MESH_SIZE, + evaluate_grid, + plot_results, +) + +COLUMNWIDTH = 600 +LEGEND_FONT_SIZE = 20 +TITLE_SIZE = 25 +SPACING = 0.1 +DEFAULT_CLASSIFIER = "naive_bayes" + + +@dataclass +class QutritClassificationParameters(SingleShotClassificationParameters): + """SingleShotClassification runcard inputs.""" + + classifiers_list: Optional[list[str]] = field( + default_factory=lambda: [DEFAULT_CLASSIFIER] + ) + """List of models to classify the qubit states""" + + +@dataclass +class QutritClassificationData(SingleShotClassificationData): + classifiers_list: Optional[list[str]] = field( + default_factory=lambda: [DEFAULT_CLASSIFIER] + ) + """List of models to classify the qubit states""" + + +def _acquisition( + params: QutritClassificationParameters, + platform: Platform, + qubits: Qubits, +) -> QutritClassificationData: + """ + Args: + nshots (int): number of times the pulse sequence will be repeated. + classifiers (list): list of classifiers, the available ones are: + - linear_svm + - ada_boost + - gaussian_process + - naive_bayes + - nn + - qubit_fit + - random_forest + - rbf_svm + - qblox_fit. + The default value is `["qubit_fit"]`. + savedir (str): Dumping folder of the classification results. + If not given the dumping folder will be the report one. + relaxation_time (float): Relaxation time. + + Example: + .. code-block:: yaml + + - id: single_shot_classification_1 + priority: 0 + operation: single_shot_classification + parameters: + nshots: 5000 + savedir: "single_shot" + classifiers_list: ["qubit_fit","naive_bayes", "linear_svm"] + + """ + + # create two sequences of pulses: + # state0_sequence: I - MZ + # state1_sequence: RX - MZ + + # taking advantage of multiplexing, apply the same set of gates to all qubits in parallel + states_sequences = [PulseSequence() for _ in range(3)] + ro_pulses = {} + hpars = {} + for qubit in qubits: + rx_pulse = platform.create_RX_pulse(qubit, start=0) + rx12_pulse = platform.create_RX12_pulse(qubit, start=rx_pulse.finish) + drive_pulses = [rx_pulse, rx12_pulse] + ro_pulses[qubit] = platform.create_qubit_readout_pulse( + qubit, start=rx12_pulse.finish + ) + hpars[qubit] = qubits[qubit].classifiers_hpars + for i, sequence in enumerate(states_sequences): + sequence.add(*drive_pulses[:i]) + sequence.add(ro_pulses[qubit]) + # create a DataUnits object to store the results + data = QutritClassificationData( + nshots=params.nshots, + classifiers_list=params.classifiers_list, + classifiers_hpars=hpars, + savedir=params.savedir, + ) + states_results = [] + for sequence in states_sequences: + states_results.append( + platform.execute_pulse_sequence( + sequence, + ExecutionParameters( + nshots=params.nshots, + relaxation_time=params.relaxation_time, + acquisition_type=AcquisitionType.INTEGRATION, + ), + ) + ) + + for qubit in qubits: + for state, state_result in enumerate(states_results): + result = state_result[ro_pulses[qubit].serial] + data.register_qubit( + qubit=qubit, state=state, i=result.voltage_i, q=result.voltage_q + ) + + return data + + +def _fit(data: QutritClassificationData) -> SingleShotClassificationResults: + qubits = data.qubits + + benchmark_tables = {} + models_dict = {} + y_tests = {} + x_tests = {} + hpars = {} + y_test_predict = {} + grid_preds_dict = {} + for qubit in qubits: + qubit_data = data.data[qubit] + benchmark_table, y_test, x_test, models, names, hpars_list = run.train_qubit( + data, qubit + ) + benchmark_tables[qubit] = benchmark_table.values.tolist() + models_dict[qubit] = models + y_tests[qubit] = y_test.tolist() + x_tests[qubit] = x_test.tolist() + hpars[qubit] = {} + y_preds = [] + grid_preds = [] + + grid = evaluate_grid(qubit_data) + for i, model_name in enumerate(names): + hpars[qubit][model_name] = hpars_list[i] + try: + y_preds.append(models[i].predict_proba(x_test)[:, 1].tolist()) + except AttributeError: + y_preds.append(models[i].predict(x_test).tolist()) + grid_preds.append( + np.round(np.reshape(models[i].predict(grid), (MESH_SIZE, MESH_SIZE))) + .astype(np.int64) + .tolist() + ) + y_test_predict[qubit] = y_preds + grid_preds_dict[qubit] = grid_preds + return SingleShotClassificationResults( + benchmark_table=benchmark_tables, + names=names, + classifiers_hpars=hpars, + models=models_dict, + savedir=data.savedir, + y_preds=y_test_predict, + grid_preds=grid_preds_dict, + ) + + +def _plot(data: QutritClassificationData, qubit, fit: SingleShotClassificationResults): + figures = plot_results(data, qubit, 3, fit) + fitting_report = None + return figures, fitting_report + + +def _update( + results: SingleShotClassificationResults, platform: Platform, qubit: QubitId +): + update.classifiers_hpars( + results.classifiers_hpars[qubit], platform, qubit + ) # TODO: implement a qutrit classifiers hpars (?) + + +qutrit_classification = Routine(_acquisition, _fit, _plot, _update) From 1370e7e0c2ba109b1bce1bdc4b8c76d133e1f9c0 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Wed, 4 Oct 2023 16:41:58 +0400 Subject: [PATCH 04/44] reduce branching in plot_results --- .../protocols/characterization/utils.py | 74 +++++++++---------- 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/src/qibocal/protocols/characterization/utils.py b/src/qibocal/protocols/characterization/utils.py index af84bc572..4695b5299 100644 --- a/src/qibocal/protocols/characterization/utils.py +++ b/src/qibocal/protocols/characterization/utils.py @@ -414,19 +414,6 @@ def plot_results(data: Data, qubit, qubit_states, fit: Results): subplot_titles=[run.pretty_name(model) for model in models_name], column_width=[COLUMNWIDTH] * len(models_name), ) - if len(models_name) != 1 and fit is not None: - fig_benchmarks = make_subplots( - rows=1, - cols=3, - horizontal_spacing=SPACING, - vertical_spacing=SPACING, - subplot_titles=( - "accuracy", - "testing time (s)", - "training time (s)", - ) - # pylint: disable=E1101 - ) for i, model in enumerate(models_name): if fit is not None: @@ -502,30 +489,6 @@ def plot_results(data: Data, qubit, qubit_states, fit: Results): col=i + 1, ) - if fit is not None: - if len(models_name) != 1: - for plot in range(3): - fig_benchmarks.add_trace( - go.Scatter( - x=[model], - y=[fit.benchmark_table[qubit][i][plot]], - mode="markers", - showlegend=False, - marker=dict(size=10, color=get_color_state1(i)), - ), - row=1, - col=plot + 1, - ) - - fig_benchmarks.update_yaxes(type="log", row=1, col=2) - fig_benchmarks.update_yaxes(type="log", row=1, col=3) - fig_benchmarks.update_layout( - autosize=False, - height=COLUMNWIDTH, - width=COLUMNWIDTH * 3, - title=dict(text="Benchmarks", font=dict(size=TITLE_SIZE)), - ) - fig.update_layout( uirevision="0", # ``uirevision`` allows zooming while live plotting autosize=False, @@ -544,6 +507,41 @@ def plot_results(data: Data, qubit, qubit_states, fit: Results): ) figures.append(fig) - if len(models_name) != 1 and fit is not None: + if fit is not None and len(models_name) != 1: + fig_benchmarks = make_subplots( + rows=1, + cols=3, + horizontal_spacing=SPACING, + vertical_spacing=SPACING, + subplot_titles=( + "accuracy", + "testing time (s)", + "training time (s)", + ) + # pylint: disable=E1101 + ) + for i, model in enumerate(models_name): + for plot in range(3): + fig_benchmarks.add_trace( + go.Scatter( + x=[model], + y=[fit.benchmark_table[qubit][i][plot]], + mode="markers", + showlegend=False, + marker=dict(size=10, color=get_color_state1(i)), + ), + row=1, + col=plot + 1, + ) + + fig_benchmarks.update_yaxes(type="log", row=1, col=2) + fig_benchmarks.update_yaxes(type="log", row=1, col=3) + fig_benchmarks.update_layout( + autosize=False, + height=COLUMNWIDTH, + width=COLUMNWIDTH * 3, + title=dict(text="Benchmarks", font=dict(size=TITLE_SIZE)), + ) + figures.append(fig_benchmarks) return figures From 88267094db7e22de08b22b3a69e78887918f996f Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Mon, 9 Oct 2023 15:22:23 +0400 Subject: [PATCH 05/44] deploy decision tree classifier --- .../fitting/classifier/decision_tree.py | 40 +++++++++++++++++++ src/qibocal/fitting/classifier/run.py | 2 + .../fitting/classifier/scikit_utils.py | 2 +- .../characterization/qutrit_classification.py | 2 +- src/qibocal/update.py | 5 +++ 5 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 src/qibocal/fitting/classifier/decision_tree.py diff --git a/src/qibocal/fitting/classifier/decision_tree.py b/src/qibocal/fitting/classifier/decision_tree.py new file mode 100644 index 000000000..41b80e46b --- /dev/null +++ b/src/qibocal/fitting/classifier/decision_tree.py @@ -0,0 +1,40 @@ +from sklearn.model_selection import GridSearchCV, RepeatedStratifiedKFold +from sklearn.tree import DecisionTreeClassifier + +from . import scikit_utils + + +def constructor(hyperpars): + r"""Return the model class. + + Args: + hyperparams: Model hyperparameters. + """ + return DecisionTreeClassifier().set_params(**hyperpars) + + +def hyperopt(x_train, y_train, _path): + r"""Perform an hyperparameter optimization and return the hyperparameters. + + Args: + x_train: Training inputs. + y_train: Training outputs. + _path (path): Model save path. + + Returns: + Dictionary with model's hyperparameters. + """ + clf = DecisionTreeClassifier() + cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1) + space = {} + space["criterion"] = ["gini", "entropy", "log_loss"] + space["splitter"] = ["best", "random"] + search = GridSearchCV(clf, space, scoring="accuracy", n_jobs=-1, cv=cv) + _ = search.fit(x_train, y_train) + + return search.best_params_ + + +normalize = scikit_utils.scikit_normalize +dump = scikit_utils.scikit_dump +predict_from_file = scikit_utils.scikit_predict diff --git a/src/qibocal/fitting/classifier/run.py b/src/qibocal/fitting/classifier/run.py index 59a93e10b..eccfc6ce1 100644 --- a/src/qibocal/fitting/classifier/run.py +++ b/src/qibocal/fitting/classifier/run.py @@ -23,6 +23,7 @@ "random_forest", "rbf_svm", "qblox_fit", + "decision_tree", ] PRETTY_NAME = [ @@ -35,6 +36,7 @@ "Random Forest", "RBF SVM", "Qblox Fit", + "Decision Tree", ] diff --git a/src/qibocal/fitting/classifier/scikit_utils.py b/src/qibocal/fitting/classifier/scikit_utils.py index 5a7de87d7..60d207f1c 100644 --- a/src/qibocal/fitting/classifier/scikit_utils.py +++ b/src/qibocal/fitting/classifier/scikit_utils.py @@ -30,7 +30,7 @@ def scikit_normalize(constructor): def scikit_dump(model, path: Path): r"""Dumps scikit `model` in `path`""" - initial_type = [("float_input", FloatTensorType([1, 2]))] + initial_type = [("float_input", FloatTensorType([None, 2]))] onx = to_onnx(model, initial_types=initial_type) with open(path.with_suffix(".onnx"), "wb") as f: f.write(onx.SerializeToString()) diff --git a/src/qibocal/protocols/characterization/qutrit_classification.py b/src/qibocal/protocols/characterization/qutrit_classification.py index fc2a41d92..a128668e3 100644 --- a/src/qibocal/protocols/characterization/qutrit_classification.py +++ b/src/qibocal/protocols/characterization/qutrit_classification.py @@ -188,7 +188,7 @@ def _plot(data: QutritClassificationData, qubit, fit: SingleShotClassificationRe def _update( results: SingleShotClassificationResults, platform: Platform, qubit: QubitId ): - update.classifiers_hpars( + update.qutrit_classifiers_hpars( results.classifiers_hpars[qubit], platform, qubit ) # TODO: implement a qutrit classifiers hpars (?) diff --git a/src/qibocal/update.py b/src/qibocal/update.py index ca51341ea..40962622e 100644 --- a/src/qibocal/update.py +++ b/src/qibocal/update.py @@ -88,6 +88,11 @@ def classifiers_hpars(hpars: list, platform: Platform, qubit: QubitId): platform.qubits[qubit].classifiers_hpars = hpars +def qutrit_classifiers_hpars(hpars: list, platform: Platform, qubit: QubitId): + """Update qutrit classifier hyperparameters in platform for specific qubit.""" + platform.qubits[qubit].qutrit_classifiers_hpars = hpars + + def virtual_phases(phases: dict[QubitId, float], platform: Platform, pair: QubitPairId): """Update virtual phases for given qubits in pair in results.""" virtual_z_pulses = { From 6d73fd1e87dcbe5d7753a1913e8b3c19be18dd18 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Wed, 18 Oct 2023 17:32:11 +0400 Subject: [PATCH 06/44] deploy rabi with probabilities --- .../protocols/characterization/__init__.py | 4 + .../characterization/rabi/amplitude.py | 27 +-- .../characterization/rabi/amplitude_msr.py | 208 +++++++++++++++++ .../protocols/characterization/rabi/length.py | 29 +-- .../characterization/rabi/length_msr.py | 218 ++++++++++++++++++ .../protocols/characterization/rabi/utils.py | 74 ++++++ 6 files changed, 527 insertions(+), 33 deletions(-) create mode 100644 src/qibocal/protocols/characterization/rabi/amplitude_msr.py create mode 100644 src/qibocal/protocols/characterization/rabi/length_msr.py diff --git a/src/qibocal/protocols/characterization/__init__.py b/src/qibocal/protocols/characterization/__init__.py index f0f8fda33..0469b5d44 100644 --- a/src/qibocal/protocols/characterization/__init__.py +++ b/src/qibocal/protocols/characterization/__init__.py @@ -20,7 +20,9 @@ ) from .qubit_spectroscopy import qubit_spectroscopy from .rabi.amplitude import rabi_amplitude +from .rabi.amplitude_msr import rabi_amplitude_msr from .rabi.length import rabi_length +from .rabi.length_msr import rabi_length_msr from .rabi.length_sequences import rabi_length_sequences from .ramsey import ramsey from .ramsey_sequences import ramsey_sequences @@ -52,6 +54,8 @@ class Operation(Enum): rabi_amplitude = rabi_amplitude rabi_length = rabi_length rabi_length_sequences = rabi_length_sequences + rabi_amplitude_msr = rabi_amplitude_msr + rabi_length_msr = rabi_length_msr ramsey = ramsey ramsey_sequences = ramsey_sequences t1 = t1 diff --git a/src/qibocal/protocols/characterization/rabi/amplitude.py b/src/qibocal/protocols/characterization/rabi/amplitude.py index db9258c55..9857a432a 100644 --- a/src/qibocal/protocols/characterization/rabi/amplitude.py +++ b/src/qibocal/protocols/characterization/rabi/amplitude.py @@ -44,9 +44,7 @@ class RabiAmplitudeResults(Results): """Raw fitted parameters.""" -RabiAmpType = np.dtype( - [("amp", np.float64), ("msr", np.float64), ("phase", np.float64)] -) +RabiAmpType = np.dtype([("amp", np.float64), ("prob", np.float64)]) """Custom dtype for rabi amplitude.""" @@ -59,12 +57,11 @@ class RabiAmplitudeData(Data): data: dict[QubitId, npt.NDArray[RabiAmpType]] = field(default_factory=dict) """Raw data acquired.""" - def register_qubit(self, qubit, amp, msr, phase): + def register_qubit(self, qubit, amp, prob): """Store output for single qubit.""" ar = np.empty(amp.shape, dtype=RabiAmpType) ar["amp"] = amp - ar["msr"] = msr - ar["phase"] = phase + ar["prob"] = prob self.data[qubit] = np.rec.array(ar) @@ -119,19 +116,17 @@ def _acquisition( ExecutionParameters( nshots=params.nshots, relaxation_time=params.relaxation_time, - acquisition_type=AcquisitionType.INTEGRATION, - averaging_mode=AveragingMode.CYCLIC, + acquisition_type=AcquisitionType.DISCRIMINATION, + averaging_mode=AveragingMode.SINGLESHOT, ), sweeper, ) for qubit in qubits: # average msr, phase, i and q over the number of shots defined in the runcard - result = results[ro_pulses[qubit].serial] data.register_qubit( qubit, amp=qd_pulses[qubit].amplitude * qd_pulse_amplitude_range, - msr=result.magnitude, - phase=result.phase, + prob=results[qubit].probability(state=1), ) return data @@ -147,14 +142,14 @@ def _fit(data: RabiAmplitudeData) -> RabiAmplitudeResults: qubit_data = data[qubit] rabi_parameter = qubit_data.amp - voltages = qubit_data.msr + prob = qubit_data.prob - y_min = np.min(voltages) - y_max = np.max(voltages) + y_min = np.min(prob) + y_max = np.max(prob) x_min = np.min(rabi_parameter) x_max = np.max(rabi_parameter) x = (rabi_parameter - x_min) / (x_max - x_min) - y = (voltages - y_min) / (y_max - y_min) + y = (prob - y_min) / (y_max - y_min) # Guessing period using fourier transform ft = np.fft.rfft(y) @@ -197,7 +192,7 @@ def _fit(data: RabiAmplitudeData) -> RabiAmplitudeResults: def _plot(data: RabiAmplitudeData, qubit, fit: RabiAmplitudeResults = None): """Plotting function for RabiAmplitude.""" - return utils.plot(data, qubit, fit) + return utils.plot_proba(data, qubit, fit) def _update(results: RabiAmplitudeResults, platform: Platform, qubit: QubitId): diff --git a/src/qibocal/protocols/characterization/rabi/amplitude_msr.py b/src/qibocal/protocols/characterization/rabi/amplitude_msr.py new file mode 100644 index 000000000..15733b1dc --- /dev/null +++ b/src/qibocal/protocols/characterization/rabi/amplitude_msr.py @@ -0,0 +1,208 @@ +from dataclasses import dataclass, field +from typing import Optional + +import numpy as np +import numpy.typing as npt +from qibolab import AcquisitionType, AveragingMode, ExecutionParameters +from qibolab.platform import Platform +from qibolab.pulses import PulseSequence +from qibolab.qubits import QubitId +from qibolab.sweeper import Parameter, Sweeper, SweeperType +from scipy.optimize import curve_fit +from scipy.signal import find_peaks + +from qibocal import update +from qibocal.auto.operation import Data, Parameters, Qubits, Results, Routine +from qibocal.config import log + +from . import utils + + +@dataclass +class RabiAmplitudeParameters(Parameters): + """RabiAmplitude runcard inputs.""" + + min_amp_factor: float + """Minimum amplitude multiplicative factor.""" + max_amp_factor: float + """Maximum amplitude multiplicative factor.""" + step_amp_factor: float + """Step amplitude multiplicative factor.""" + pulse_length: Optional[float] + """RX pulse duration (ns).""" + + +@dataclass +class RabiAmplitudeResults(Results): + """RabiAmplitude outputs.""" + + amplitude: dict[QubitId, float] = field(metadata=dict(update="drive_amplitude")) + """Drive amplitude for each qubit.""" + length: dict[QubitId, float] = field(metadata=dict(update="drive_length")) + """Drive pulse duration. Same for all qubits.""" + fitted_parameters: dict[QubitId, dict[str, float]] + """Raw fitted parameters.""" + + +RabiAmpType = np.dtype( + [("amp", np.float64), ("msr", np.float64), ("phase", np.float64)] +) +"""Custom dtype for rabi amplitude.""" + + +@dataclass +class RabiAmplitudeData(Data): + """RabiAmplitude data acquisition.""" + + durations: dict[QubitId, float] = field(default_factory=dict) + """Pulse durations provided by the user.""" + data: dict[QubitId, npt.NDArray[RabiAmpType]] = field(default_factory=dict) + """Raw data acquired.""" + + def register_qubit(self, qubit, amp, msr, phase): + """Store output for single qubit.""" + ar = np.empty(amp.shape, dtype=RabiAmpType) + ar["amp"] = amp + ar["msr"] = msr + ar["phase"] = phase + self.data[qubit] = np.rec.array(ar) + + +def _acquisition( + params: RabiAmplitudeParameters, platform: Platform, qubits: Qubits +) -> RabiAmplitudeData: + r""" + Data acquisition for Rabi experiment sweeping amplitude. + In the Rabi experiment we apply a pulse at the frequency of the qubit and scan the drive pulse amplitude + to find the drive pulse amplitude that creates a rotation of a desired angle. + """ + + # create a sequence of pulses for the experiment + sequence = PulseSequence() + qd_pulses = {} + ro_pulses = {} + durations = {} + for qubit in qubits: + qd_pulses[qubit] = platform.create_RX_pulse(qubit, start=0) + if params.pulse_length is not None: + qd_pulses[qubit].duration = params.pulse_length + + durations[qubit] = qd_pulses[qubit].duration + ro_pulses[qubit] = platform.create_qubit_readout_pulse( + qubit, start=qd_pulses[qubit].finish + ) + sequence.add(qd_pulses[qubit]) + sequence.add(ro_pulses[qubit]) + + # define the parameter to sweep and its range: + # qubit drive pulse amplitude + qd_pulse_amplitude_range = np.arange( + params.min_amp_factor, + params.max_amp_factor, + params.step_amp_factor, + ) + sweeper = Sweeper( + Parameter.amplitude, + qd_pulse_amplitude_range, + [qd_pulses[qubit] for qubit in qubits], + type=SweeperType.FACTOR, + ) + + # create a DataUnits object to store the results, + # DataUnits stores by default MSR, phase, i, q + # additionally include qubit drive pulse amplitude + data = RabiAmplitudeData(durations=durations) + + # sweep the parameter + results = platform.sweep( + sequence, + ExecutionParameters( + nshots=params.nshots, + relaxation_time=params.relaxation_time, + acquisition_type=AcquisitionType.INTEGRATION, + averaging_mode=AveragingMode.CYCLIC, + ), + sweeper, + ) + for qubit in qubits: + # average msr, phase, i and q over the number of shots defined in the runcard + result = results[ro_pulses[qubit].serial] + data.register_qubit( + qubit, + amp=qd_pulses[qubit].amplitude * qd_pulse_amplitude_range, + msr=result.magnitude, + phase=result.phase, + ) + return data + + +def _fit(data: RabiAmplitudeData) -> RabiAmplitudeResults: + """Post-processing for RabiAmplitude.""" + qubits = data.qubits + + pi_pulse_amplitudes = {} + fitted_parameters = {} + + for qubit in qubits: + qubit_data = data[qubit] + + rabi_parameter = qubit_data.amp + voltages = qubit_data.msr + + y_min = np.min(voltages) + y_max = np.max(voltages) + x_min = np.min(rabi_parameter) + x_max = np.max(rabi_parameter) + x = (rabi_parameter - x_min) / (x_max - x_min) + y = (voltages - y_min) / (y_max - y_min) + + # Guessing period using fourier transform + ft = np.fft.rfft(y) + mags = abs(ft) + local_maxima = find_peaks(mags, threshold=10)[0] + index = local_maxima[0] if len(local_maxima) > 0 else None + # 0.5 hardcoded guess for less than one oscillation + f = x[index] / (x[1] - x[0]) if index is not None else 0.5 + pguess = [0.5, 1, f, np.pi / 2] + try: + popt, _ = curve_fit( + utils.rabi_amplitude_fit, + x, + y, + p0=pguess, + maxfev=100000, + bounds=( + [0, 0, 0, -np.pi], + [1, 1, np.inf, np.pi], + ), + ) + translated_popt = [ + y_min + (y_max - y_min) * popt[0], + (y_max - y_min) * popt[1], + popt[2] / (x_max - x_min), + popt[3] - 2 * np.pi * x_min / (x_max - x_min) * popt[2], + ] + pi_pulse_parameter = np.abs((1.0 / translated_popt[2]) / 2) + + except: + log.warning("rabi_fit: the fitting was not succesful") + pi_pulse_parameter = 0 + fitted_parameters = [0] * 4 + + pi_pulse_amplitudes[qubit] = pi_pulse_parameter + fitted_parameters[qubit] = translated_popt + + return RabiAmplitudeResults(pi_pulse_amplitudes, data.durations, fitted_parameters) + + +def _plot(data: RabiAmplitudeData, qubit, fit: RabiAmplitudeResults = None): + """Plotting function for RabiAmplitude.""" + return utils.plot(data, qubit, fit) + + +def _update(results: RabiAmplitudeResults, platform: Platform, qubit: QubitId): + update.drive_amplitude(results.amplitude[qubit], platform, qubit) + + +rabi_amplitude_msr = Routine(_acquisition, _fit, _plot, _update) +"""RabiAmplitude Routine object.""" diff --git a/src/qibocal/protocols/characterization/rabi/length.py b/src/qibocal/protocols/characterization/rabi/length.py index 78de6157d..a7023db30 100644 --- a/src/qibocal/protocols/characterization/rabi/length.py +++ b/src/qibocal/protocols/characterization/rabi/length.py @@ -44,9 +44,7 @@ class RabiLengthResults(Results): """Raw fitting output.""" -RabiLenType = np.dtype( - [("length", np.float64), ("msr", np.float64), ("phase", np.float64)] -) +RabiLenType = np.dtype([("length", np.float64), ("prob", np.float64)]) """Custom dtype for rabi amplitude.""" @@ -59,14 +57,13 @@ class RabiLengthData(Data): data: dict[QubitId, npt.NDArray[RabiLenType]] = field(default_factory=dict) """Raw data acquired.""" - def register_qubit(self, qubit, length, msr, phase): + def register_qubit(self, qubit, length, prob): """Store output for single qubit.""" # to be able to handle the non-sweeper case shape = (1,) if np.isscalar(length) else length.shape ar = np.empty(shape, dtype=RabiLenType) ar["length"] = length - ar["msr"] = msr - ar["phase"] = phase + ar["prob"] = prob if qubit in self.data: self.data[qubit] = np.rec.array(np.concatenate((self.data[qubit], ar))) else: @@ -128,20 +125,18 @@ def _acquisition( ExecutionParameters( nshots=params.nshots, relaxation_time=params.relaxation_time, - acquisition_type=AcquisitionType.INTEGRATION, - averaging_mode=AveragingMode.CYCLIC, + acquisition_type=AcquisitionType.DISCRIMINATION, + averaging_mode=AveragingMode.SINGLESHOT, ), sweeper, ) for qubit in qubits: - # average msr, phase, i and q over the number of shots defined in the runcard - result = results[ro_pulses[qubit].serial] + # average prob, phase, i and q over the number of shots defined in the runcard data.register_qubit( qubit, length=qd_pulse_duration_range, - msr=result.magnitude, - phase=result.phase, + prob=results[qubit].probability(state=1), ) return data @@ -156,14 +151,14 @@ def _fit(data: RabiLengthData) -> RabiLengthResults: for qubit in qubits: qubit_data = data[qubit] rabi_parameter = qubit_data.length - voltages = qubit_data.msr + prob = qubit_data.prob - y_min = np.min(voltages) - y_max = np.max(voltages) + y_min = np.min(prob) + y_max = np.max(prob) x_min = np.min(rabi_parameter) x_max = np.max(rabi_parameter) x = (rabi_parameter - x_min) / (x_max - x_min) - y = (voltages - y_min) / (y_max - y_min) + y = (prob - y_min) / (y_max - y_min) # Guessing period using fourier transform ft = np.fft.rfft(y) @@ -211,7 +206,7 @@ def _update(results: RabiLengthResults, platform: Platform, qubit: QubitId): def _plot(data: RabiLengthData, fit: RabiLengthResults, qubit): """Plotting function for RabiLength experiment.""" - return utils.plot(data, qubit, fit) + return utils.plot_proba(data, qubit, fit) rabi_length = Routine(_acquisition, _fit, _plot, _update) diff --git a/src/qibocal/protocols/characterization/rabi/length_msr.py b/src/qibocal/protocols/characterization/rabi/length_msr.py new file mode 100644 index 000000000..ed2709f9b --- /dev/null +++ b/src/qibocal/protocols/characterization/rabi/length_msr.py @@ -0,0 +1,218 @@ +from dataclasses import dataclass, field +from typing import Optional + +import numpy as np +import numpy.typing as npt +from qibolab import AcquisitionType, AveragingMode, ExecutionParameters +from qibolab.platform import Platform +from qibolab.pulses import PulseSequence +from qibolab.qubits import QubitId +from qibolab.sweeper import Parameter, Sweeper, SweeperType +from scipy.optimize import curve_fit +from scipy.signal import find_peaks + +from qibocal import update +from qibocal.auto.operation import Data, Parameters, Qubits, Results, Routine +from qibocal.config import log + +from . import utils + + +@dataclass +class RabiLengthParameters(Parameters): + """RabiLength runcard inputs.""" + + pulse_duration_start: float + """Initial pi pulse duration (ns).""" + pulse_duration_end: float + """Final pi pulse duration (ns).""" + pulse_duration_step: float + """Step pi pulse duration (ns).""" + pulse_amplitude: Optional[float] = None + """Pi pulse amplitude. Same for all qubits.""" + + +@dataclass +class RabiLengthResults(Results): + """RabiLength outputs.""" + + length: dict[QubitId, int] = field(metadata=dict(update="drive_length")) + """Pi pulse duration for each qubit.""" + amplitude: dict[QubitId, float] = field(metadata=dict(update="drive_amplitude")) + """Pi pulse amplitude. Same for all qubits.""" + fitted_parameters: dict[QubitId, dict[str, float]] + """Raw fitting output.""" + + +RabiLenType = np.dtype( + [("length", np.float64), ("msr", np.float64), ("phase", np.float64)] +) +"""Custom dtype for rabi amplitude.""" + + +@dataclass +class RabiLengthData(Data): + """RabiLength acquisition outputs.""" + + amplitudes: dict[QubitId, float] = field(default_factory=dict) + """Pulse durations provided by the user.""" + data: dict[QubitId, npt.NDArray[RabiLenType]] = field(default_factory=dict) + """Raw data acquired.""" + + def register_qubit(self, qubit, length, msr, phase): + """Store output for single qubit.""" + # to be able to handle the non-sweeper case + shape = (1,) if np.isscalar(length) else length.shape + ar = np.empty(shape, dtype=RabiLenType) + ar["length"] = length + ar["msr"] = msr + ar["phase"] = phase + if qubit in self.data: + self.data[qubit] = np.rec.array(np.concatenate((self.data[qubit], ar))) + else: + self.data[qubit] = np.rec.array(ar) + + +def _acquisition( + params: RabiLengthParameters, platform: Platform, qubits: Qubits +) -> RabiLengthData: + r""" + Data acquisition for RabiLength Experiment. + In the Rabi experiment we apply a pulse at the frequency of the qubit and scan the drive pulse length + to find the drive pulse length that creates a rotation of a desired angle. + """ + + # create a sequence of pulses for the experiment + sequence = PulseSequence() + qd_pulses = {} + ro_pulses = {} + amplitudes = {} + for qubit in qubits: + # TODO: made duration optional for qd pulse? + qd_pulses[qubit] = platform.create_qubit_drive_pulse( + qubit, start=0, duration=params.pulse_duration_start + ) + if params.pulse_amplitude is not None: + qd_pulses[qubit].amplitude = params.pulse_amplitude + amplitudes[qubit] = qd_pulses[qubit].amplitude + + ro_pulses[qubit] = platform.create_qubit_readout_pulse( + qubit, start=qd_pulses[qubit].finish + ) + sequence.add(qd_pulses[qubit]) + sequence.add(ro_pulses[qubit]) + + # define the parameter to sweep and its range: + # qubit drive pulse duration time + qd_pulse_duration_range = np.arange( + params.pulse_duration_start, + params.pulse_duration_end, + params.pulse_duration_step, + ) + + sweeper = Sweeper( + Parameter.duration, + qd_pulse_duration_range, + [qd_pulses[qubit] for qubit in qubits], + type=SweeperType.ABSOLUTE, + ) + + # create a DataUnits object to store the results, + # DataUnits stores by default MSR, phase, i, q + # additionally include qubit drive pulse length + data = RabiLengthData(amplitudes=amplitudes) + + # execute the sweep + results = platform.sweep( + sequence, + ExecutionParameters( + nshots=params.nshots, + relaxation_time=params.relaxation_time, + acquisition_type=AcquisitionType.INTEGRATION, + averaging_mode=AveragingMode.CYCLIC, + ), + sweeper, + ) + + for qubit in qubits: + # average msr, phase, i and q over the number of shots defined in the runcard + result = results[ro_pulses[qubit].serial] + data.register_qubit( + qubit, + length=qd_pulse_duration_range, + msr=result.magnitude, + phase=result.phase, + ) + return data + + +def _fit(data: RabiLengthData) -> RabiLengthResults: + """Post-processing for RabiLength experiment.""" + + qubits = data.qubits + fitted_parameters = {} + durations = {} + + for qubit in qubits: + qubit_data = data[qubit] + rabi_parameter = qubit_data.length + voltages = qubit_data.msr + + y_min = np.min(voltages) + y_max = np.max(voltages) + x_min = np.min(rabi_parameter) + x_max = np.max(rabi_parameter) + x = (rabi_parameter - x_min) / (x_max - x_min) + y = (voltages - y_min) / (y_max - y_min) + + # Guessing period using fourier transform + ft = np.fft.rfft(y) + mags = abs(ft) + local_maxima = find_peaks(mags, threshold=1)[0] + index = local_maxima[0] if len(local_maxima) > 0 else None + # 0.5 hardcoded guess for less than one oscillation + f = x[index] / (x[1] - x[0]) if index is not None else 0.5 + + pguess = [1, 1, f, np.pi / 2, 0] + try: + popt, pcov = curve_fit( + utils.rabi_length_fit, + x, + y, + p0=pguess, + maxfev=100000, + bounds=( + [0, 0, 0, -np.pi, 0], + [1, 1, np.inf, np.pi, np.inf], + ), + ) + translated_popt = [ + (y_max - y_min) * popt[0] + y_min, + (y_max - y_min) * popt[1] * np.exp(x_min * popt[4] / (x_max - x_min)), + popt[2] / (x_max - x_min), + popt[3] - 2 * np.pi * x_min * popt[2] / (x_max - x_min), + popt[4] / (x_max - x_min), + ] + pi_pulse_parameter = np.abs((1.0 / translated_popt[2]) / 2) + except: + log.warning("rabi_fit: the fitting was not succesful") + pi_pulse_parameter = 0 + translated_popt = [0] * 5 + + durations[qubit] = pi_pulse_parameter + fitted_parameters[qubit] = translated_popt + + return RabiLengthResults(durations, data.amplitudes, fitted_parameters) + + +def _update(results: RabiLengthResults, platform: Platform, qubit: QubitId): + update.drive_duration(results.length[qubit], platform, qubit) + + +def _plot(data: RabiLengthData, fit: RabiLengthResults, qubit): + """Plotting function for RabiLength experiment.""" + return utils.plot(data, qubit, fit) + + +rabi_length_msr = Routine(_acquisition, _fit, _plot, _update) +"""RabiLength Routine object.""" diff --git a/src/qibocal/protocols/characterization/rabi/utils.py b/src/qibocal/protocols/characterization/rabi/utils.py index 177b16162..bd1b4df86 100644 --- a/src/qibocal/protocols/characterization/rabi/utils.py +++ b/src/qibocal/protocols/characterization/rabi/utils.py @@ -116,3 +116,77 @@ def plot(data, qubit, fit): figures.append(fig) return figures, fitting_report + + +def plot_proba(data, qubit, fit): + if data.__class__.__name__ == "RabiAmplitudeData": + quantity = "amp" + title = "Amplitude (dimensionless)" + fitting = rabi_amplitude_fit + elif data.__class__.__name__ == "RabiLengthData": + quantity = "length" + title = "Time (ns)" + fitting = rabi_length_fit + + figures = [] + fitting_report = "" + + fig = make_subplots( + rows=1, + cols=1, + horizontal_spacing=0.1, + vertical_spacing=0.1, + subplot_titles=("Probability",), + ) + + qubit_data = data[qubit] + + rabi_parameters = getattr(qubit_data, quantity) + fig.add_trace( + go.Scatter( + x=rabi_parameters, + y=qubit_data.prob, + opacity=1, + name="Voltage", + showlegend=True, + legendgroup="Voltage", + ), + ) + + if fit is not None: + rabi_parameter_range = np.linspace( + min(rabi_parameters), + max(rabi_parameters), + 2 * len(rabi_parameters), + ) + params = fit.fitted_parameters[qubit] + fig.add_trace( + go.Scatter( + x=rabi_parameter_range, + y=fitting(rabi_parameter_range, *params), + name="Fit", + line=go.scatter.Line(dash="dot"), + marker_color="rgb(255, 130, 67)", + ), + row=1, + col=1, + ) + + fitting_report = table_html( + table_dict( + qubit, + ["Pi pulse amplitude", "Pi pulse length"], + [np.round(fit.amplitude[qubit], 3), np.round(fit.length[qubit], 3)], + ) + ) + + fig.update_layout( + showlegend=True, + uirevision="0", # ``uirevision`` allows zooming while live plotting + xaxis_title=title, + yaxis_title="Excited state probability", + ) + + figures.append(fig) + + return figures, fitting_report From e2ed165417cee96294ed1b91bcd5992ca2d1d117 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Thu, 19 Oct 2023 14:35:34 +0400 Subject: [PATCH 07/44] add error bands --- .../characterization/rabi/amplitude.py | 53 +++++++-------- .../characterization/rabi/amplitude_msr.py | 64 +++++++---------- .../protocols/characterization/rabi/length.py | 55 +++++++-------- .../characterization/rabi/length_msr.py | 68 +++++++------------ .../protocols/characterization/rabi/utils.py | 55 ++++++++------- .../protocols/characterization/utils.py | 2 + src/qibocal/update.py | 15 ++-- 7 files changed, 141 insertions(+), 171 deletions(-) diff --git a/src/qibocal/protocols/characterization/rabi/amplitude.py b/src/qibocal/protocols/characterization/rabi/amplitude.py index 9857a432a..ccc7f7f8a 100644 --- a/src/qibocal/protocols/characterization/rabi/amplitude.py +++ b/src/qibocal/protocols/characterization/rabi/amplitude.py @@ -36,15 +36,21 @@ class RabiAmplitudeParameters(Parameters): class RabiAmplitudeResults(Results): """RabiAmplitude outputs.""" - amplitude: dict[QubitId, float] = field(metadata=dict(update="drive_amplitude")) + amplitude: dict[QubitId, tuple[float, Optional[float]]] = field( + metadata=dict(update="drive_amplitude") + ) """Drive amplitude for each qubit.""" - length: dict[QubitId, float] = field(metadata=dict(update="drive_length")) + length: dict[QubitId, tuple[float, Optional[float]]] = field( + metadata=dict(update="drive_length") + ) """Drive pulse duration. Same for all qubits.""" fitted_parameters: dict[QubitId, dict[str, float]] """Raw fitted parameters.""" -RabiAmpType = np.dtype([("amp", np.float64), ("prob", np.float64)]) +RabiAmpType = np.dtype( + [("amp", np.float64), ("prob", np.float64), ("error", np.float64)] +) """Custom dtype for rabi amplitude.""" @@ -57,11 +63,12 @@ class RabiAmplitudeData(Data): data: dict[QubitId, npt.NDArray[RabiAmpType]] = field(default_factory=dict) """Raw data acquired.""" - def register_qubit(self, qubit, amp, prob): + def register_qubit(self, qubit, amp, prob, error): """Store output for single qubit.""" ar = np.empty(amp.shape, dtype=RabiAmpType) ar["amp"] = amp ar["prob"] = prob + ar["error"] = error self.data[qubit] = np.rec.array(ar) @@ -123,10 +130,12 @@ def _acquisition( ) for qubit in qubits: # average msr, phase, i and q over the number of shots defined in the runcard + prob = results[qubit].probability(state=1) data.register_qubit( qubit, amp=qd_pulses[qubit].amplitude * qd_pulse_amplitude_range, - prob=results[qubit].probability(state=1), + prob=prob.tolist(), + error=np.sqrt(prob * (1 - prob) / params.nshots).tolist(), ) return data @@ -141,15 +150,8 @@ def _fit(data: RabiAmplitudeData) -> RabiAmplitudeResults: for qubit in qubits: qubit_data = data[qubit] - rabi_parameter = qubit_data.amp - prob = qubit_data.prob - - y_min = np.min(prob) - y_max = np.max(prob) - x_min = np.min(rabi_parameter) - x_max = np.max(rabi_parameter) - x = (rabi_parameter - x_min) / (x_max - x_min) - y = (prob - y_min) / (y_max - y_min) + x = qubit_data.amp + y = qubit_data.prob # Guessing period using fourier transform ft = np.fft.rfft(y) @@ -160,7 +162,7 @@ def _fit(data: RabiAmplitudeData) -> RabiAmplitudeResults: f = x[index] / (x[1] - x[0]) if index is not None else 0.5 pguess = [0.5, 1, f, np.pi / 2] try: - popt, _ = curve_fit( + popt, perr = curve_fit( utils.rabi_amplitude_fit, x, y, @@ -170,24 +172,21 @@ def _fit(data: RabiAmplitudeData) -> RabiAmplitudeResults: [0, 0, 0, -np.pi], [1, 1, np.inf, np.pi], ), + sigma=qubit_data.error, ) - translated_popt = [ - y_min + (y_max - y_min) * popt[0], - (y_max - y_min) * popt[1], - popt[2] / (x_max - x_min), - popt[3] - 2 * np.pi * x_min / (x_max - x_min) * popt[2], - ] - pi_pulse_parameter = np.abs((1.0 / translated_popt[2]) / 2) + perr = np.sqrt(np.diag(perr)) + pi_pulse_parameter = np.abs(popt[2] / 2) except: log.warning("rabi_fit: the fitting was not succesful") pi_pulse_parameter = 0 - fitted_parameters = [0] * 4 - - pi_pulse_amplitudes[qubit] = pi_pulse_parameter - fitted_parameters[qubit] = translated_popt + popt = [0] * 4 + perr = [1] * 4 - return RabiAmplitudeResults(pi_pulse_amplitudes, data.durations, fitted_parameters) + pi_pulse_amplitudes[qubit] = (pi_pulse_parameter, perr[2] / 2) + fitted_parameters[qubit] = popt.tolist() + durations = {key: (value, 0) for key, value in data.durations.items()} + return RabiAmplitudeResults(pi_pulse_amplitudes, durations, fitted_parameters) def _plot(data: RabiAmplitudeData, qubit, fit: RabiAmplitudeResults = None): diff --git a/src/qibocal/protocols/characterization/rabi/amplitude_msr.py b/src/qibocal/protocols/characterization/rabi/amplitude_msr.py index 15733b1dc..60d106d18 100644 --- a/src/qibocal/protocols/characterization/rabi/amplitude_msr.py +++ b/src/qibocal/protocols/characterization/rabi/amplitude_msr.py @@ -1,8 +1,6 @@ -from dataclasses import dataclass, field -from typing import Optional +from dataclasses import dataclass import numpy as np -import numpy.typing as npt from qibolab import AcquisitionType, AveragingMode, ExecutionParameters from qibolab.platform import Platform from qibolab.pulses import PulseSequence @@ -12,56 +10,40 @@ from scipy.signal import find_peaks from qibocal import update -from qibocal.auto.operation import Data, Parameters, Qubits, Results, Routine +from qibocal.auto.operation import Qubits, Routine from qibocal.config import log +from qibocal.protocols.characterization.rabi.amplitude import ( + RabiAmplitudeData, + RabiAmplitudeParameters, + RabiAmplitudeResults, +) from . import utils @dataclass -class RabiAmplitudeParameters(Parameters): +class RabiAmplitudeVoltParameters(RabiAmplitudeParameters): """RabiAmplitude runcard inputs.""" - min_amp_factor: float - """Minimum amplitude multiplicative factor.""" - max_amp_factor: float - """Maximum amplitude multiplicative factor.""" - step_amp_factor: float - """Step amplitude multiplicative factor.""" - pulse_length: Optional[float] - """RX pulse duration (ns).""" - @dataclass -class RabiAmplitudeResults(Results): +class RabiAmplitudeVoltResults(RabiAmplitudeResults): """RabiAmplitude outputs.""" - amplitude: dict[QubitId, float] = field(metadata=dict(update="drive_amplitude")) - """Drive amplitude for each qubit.""" - length: dict[QubitId, float] = field(metadata=dict(update="drive_length")) - """Drive pulse duration. Same for all qubits.""" - fitted_parameters: dict[QubitId, dict[str, float]] - """Raw fitted parameters.""" - -RabiAmpType = np.dtype( +RabiAmpVoltType = np.dtype( [("amp", np.float64), ("msr", np.float64), ("phase", np.float64)] ) """Custom dtype for rabi amplitude.""" @dataclass -class RabiAmplitudeData(Data): +class RabiAmplitudeVoltData(RabiAmplitudeData): """RabiAmplitude data acquisition.""" - durations: dict[QubitId, float] = field(default_factory=dict) - """Pulse durations provided by the user.""" - data: dict[QubitId, npt.NDArray[RabiAmpType]] = field(default_factory=dict) - """Raw data acquired.""" - def register_qubit(self, qubit, amp, msr, phase): """Store output for single qubit.""" - ar = np.empty(amp.shape, dtype=RabiAmpType) + ar = np.empty(amp.shape, dtype=RabiAmpVoltType) ar["amp"] = amp ar["msr"] = msr ar["phase"] = phase @@ -69,8 +51,8 @@ def register_qubit(self, qubit, amp, msr, phase): def _acquisition( - params: RabiAmplitudeParameters, platform: Platform, qubits: Qubits -) -> RabiAmplitudeData: + params: RabiAmplitudeVoltParameters, platform: Platform, qubits: Qubits +) -> RabiAmplitudeVoltData: r""" Data acquisition for Rabi experiment sweeping amplitude. In the Rabi experiment we apply a pulse at the frequency of the qubit and scan the drive pulse amplitude @@ -111,7 +93,7 @@ def _acquisition( # create a DataUnits object to store the results, # DataUnits stores by default MSR, phase, i, q # additionally include qubit drive pulse amplitude - data = RabiAmplitudeData(durations=durations) + data = RabiAmplitudeVoltData(durations=durations) # sweep the parameter results = platform.sweep( @@ -136,7 +118,7 @@ def _acquisition( return data -def _fit(data: RabiAmplitudeData) -> RabiAmplitudeResults: +def _fit(data: RabiAmplitudeVoltData) -> RabiAmplitudeVoltResults: """Post-processing for RabiAmplitude.""" qubits = data.qubits @@ -163,7 +145,7 @@ def _fit(data: RabiAmplitudeData) -> RabiAmplitudeResults: index = local_maxima[0] if len(local_maxima) > 0 else None # 0.5 hardcoded guess for less than one oscillation f = x[index] / (x[1] - x[0]) if index is not None else 0.5 - pguess = [0.5, 1, f, np.pi / 2] + pguess = [0.5, 1, 1 / f, np.pi / 2] try: popt, _ = curve_fit( utils.rabi_amplitude_fit, @@ -176,10 +158,10 @@ def _fit(data: RabiAmplitudeData) -> RabiAmplitudeResults: [1, 1, np.inf, np.pi], ), ) - translated_popt = [ + translated_popt = [ # Change it according to fit function changes y_min + (y_max - y_min) * popt[0], (y_max - y_min) * popt[1], - popt[2] / (x_max - x_min), + popt[2] * (x_max - x_min), popt[3] - 2 * np.pi * x_min / (x_max - x_min) * popt[2], ] pi_pulse_parameter = np.abs((1.0 / translated_popt[2]) / 2) @@ -192,15 +174,17 @@ def _fit(data: RabiAmplitudeData) -> RabiAmplitudeResults: pi_pulse_amplitudes[qubit] = pi_pulse_parameter fitted_parameters[qubit] = translated_popt - return RabiAmplitudeResults(pi_pulse_amplitudes, data.durations, fitted_parameters) + return RabiAmplitudeVoltResults( + pi_pulse_amplitudes, data.durations, fitted_parameters + ) -def _plot(data: RabiAmplitudeData, qubit, fit: RabiAmplitudeResults = None): +def _plot(data: RabiAmplitudeVoltData, qubit, fit: RabiAmplitudeVoltResults = None): """Plotting function for RabiAmplitude.""" return utils.plot(data, qubit, fit) -def _update(results: RabiAmplitudeResults, platform: Platform, qubit: QubitId): +def _update(results: RabiAmplitudeVoltResults, platform: Platform, qubit: QubitId): update.drive_amplitude(results.amplitude[qubit], platform, qubit) diff --git a/src/qibocal/protocols/characterization/rabi/length.py b/src/qibocal/protocols/characterization/rabi/length.py index a7023db30..3ec2fa153 100644 --- a/src/qibocal/protocols/characterization/rabi/length.py +++ b/src/qibocal/protocols/characterization/rabi/length.py @@ -36,15 +36,21 @@ class RabiLengthParameters(Parameters): class RabiLengthResults(Results): """RabiLength outputs.""" - length: dict[QubitId, int] = field(metadata=dict(update="drive_length")) + length: dict[QubitId, tuple[int, Optional[float]]] = field( + metadata=dict(update="drive_length") + ) """Pi pulse duration for each qubit.""" - amplitude: dict[QubitId, float] = field(metadata=dict(update="drive_amplitude")) + amplitude: dict[QubitId, tuple[float, Optional[float]]] = field( + metadata=dict(update="drive_amplitude") + ) """Pi pulse amplitude. Same for all qubits.""" fitted_parameters: dict[QubitId, dict[str, float]] """Raw fitting output.""" -RabiLenType = np.dtype([("length", np.float64), ("prob", np.float64)]) +RabiLenType = np.dtype( + [("length", np.float64), ("prob", np.float64), ("error", np.float64)] +) """Custom dtype for rabi amplitude.""" @@ -57,13 +63,14 @@ class RabiLengthData(Data): data: dict[QubitId, npt.NDArray[RabiLenType]] = field(default_factory=dict) """Raw data acquired.""" - def register_qubit(self, qubit, length, prob): + def register_qubit(self, qubit, length, prob, error): """Store output for single qubit.""" # to be able to handle the non-sweeper case shape = (1,) if np.isscalar(length) else length.shape ar = np.empty(shape, dtype=RabiLenType) ar["length"] = length ar["prob"] = prob + ar["error"] = error if qubit in self.data: self.data[qubit] = np.rec.array(np.concatenate((self.data[qubit], ar))) else: @@ -133,10 +140,12 @@ def _acquisition( for qubit in qubits: # average prob, phase, i and q over the number of shots defined in the runcard + prob = results[qubit].probability(state=1) data.register_qubit( qubit, length=qd_pulse_duration_range, - prob=results[qubit].probability(state=1), + prob=prob, + error=np.sqrt(prob * (1 - prob) / params.nshots).tolist(), ) return data @@ -150,15 +159,8 @@ def _fit(data: RabiLengthData) -> RabiLengthResults: for qubit in qubits: qubit_data = data[qubit] - rabi_parameter = qubit_data.length - prob = qubit_data.prob - - y_min = np.min(prob) - y_max = np.max(prob) - x_min = np.min(rabi_parameter) - x_max = np.max(rabi_parameter) - x = (rabi_parameter - x_min) / (x_max - x_min) - y = (prob - y_min) / (y_max - y_min) + x = qubit_data.length + y = qubit_data.prob # Guessing period using fourier transform ft = np.fft.rfft(y) @@ -168,9 +170,9 @@ def _fit(data: RabiLengthData) -> RabiLengthResults: # 0.5 hardcoded guess for less than one oscillation f = x[index] / (x[1] - x[0]) if index is not None else 0.5 - pguess = [1, 1, f, np.pi / 2, 0] + pguess = [1, 1, f, np.pi / 2, np.max(x) / 2] try: - popt, pcov = curve_fit( + popt, perr = curve_fit( utils.rabi_length_fit, x, y, @@ -180,24 +182,19 @@ def _fit(data: RabiLengthData) -> RabiLengthResults: [0, 0, 0, -np.pi, 0], [1, 1, np.inf, np.pi, np.inf], ), + sigma=qubit_data.error, ) - translated_popt = [ - (y_max - y_min) * popt[0] + y_min, - (y_max - y_min) * popt[1] * np.exp(x_min * popt[4] / (x_max - x_min)), - popt[2] / (x_max - x_min), - popt[3] - 2 * np.pi * x_min * popt[2] / (x_max - x_min), - popt[4] / (x_max - x_min), - ] - pi_pulse_parameter = np.abs((1.0 / translated_popt[2]) / 2) + perr = np.sqrt(np.diag(perr)) + pi_pulse_parameter = np.abs(popt[2] / 2) except: log.warning("rabi_fit: the fitting was not succesful") pi_pulse_parameter = 0 - translated_popt = [0] * 5 - - durations[qubit] = pi_pulse_parameter - fitted_parameters[qubit] = translated_popt + popt = [0] * 4 + [1] - return RabiLengthResults(durations, data.amplitudes, fitted_parameters) + durations[qubit] = (pi_pulse_parameter, 0) + fitted_parameters[qubit] = popt.tolist() + amplitudes = {key: (value, 0) for key, value in data.amplitudes.items()} + return RabiLengthResults(durations, amplitudes, fitted_parameters) def _update(results: RabiLengthResults, platform: Platform, qubit: QubitId): diff --git a/src/qibocal/protocols/characterization/rabi/length_msr.py b/src/qibocal/protocols/characterization/rabi/length_msr.py index ed2709f9b..f84044c80 100644 --- a/src/qibocal/protocols/characterization/rabi/length_msr.py +++ b/src/qibocal/protocols/characterization/rabi/length_msr.py @@ -1,8 +1,6 @@ -from dataclasses import dataclass, field -from typing import Optional +from dataclasses import dataclass import numpy as np -import numpy.typing as npt from qibolab import AcquisitionType, AveragingMode, ExecutionParameters from qibolab.platform import Platform from qibolab.pulses import PulseSequence @@ -12,58 +10,42 @@ from scipy.signal import find_peaks from qibocal import update -from qibocal.auto.operation import Data, Parameters, Qubits, Results, Routine +from qibocal.auto.operation import Qubits, Routine from qibocal.config import log +from qibocal.protocols.characterization.rabi.length import ( + RabiLengthData, + RabiLengthParameters, + RabiLengthResults, +) from . import utils @dataclass -class RabiLengthParameters(Parameters): +class RabiLengthVoltParameters(RabiLengthParameters): """RabiLength runcard inputs.""" - pulse_duration_start: float - """Initial pi pulse duration (ns).""" - pulse_duration_end: float - """Final pi pulse duration (ns).""" - pulse_duration_step: float - """Step pi pulse duration (ns).""" - pulse_amplitude: Optional[float] = None - """Pi pulse amplitude. Same for all qubits.""" - @dataclass -class RabiLengthResults(Results): +class RabiLengthVoltResults(RabiLengthResults): """RabiLength outputs.""" - length: dict[QubitId, int] = field(metadata=dict(update="drive_length")) - """Pi pulse duration for each qubit.""" - amplitude: dict[QubitId, float] = field(metadata=dict(update="drive_amplitude")) - """Pi pulse amplitude. Same for all qubits.""" - fitted_parameters: dict[QubitId, dict[str, float]] - """Raw fitting output.""" - -RabiLenType = np.dtype( +RabiLenVoltType = np.dtype( [("length", np.float64), ("msr", np.float64), ("phase", np.float64)] ) """Custom dtype for rabi amplitude.""" @dataclass -class RabiLengthData(Data): +class RabiLengthVoltData(RabiLengthData): """RabiLength acquisition outputs.""" - amplitudes: dict[QubitId, float] = field(default_factory=dict) - """Pulse durations provided by the user.""" - data: dict[QubitId, npt.NDArray[RabiLenType]] = field(default_factory=dict) - """Raw data acquired.""" - def register_qubit(self, qubit, length, msr, phase): """Store output for single qubit.""" # to be able to handle the non-sweeper case shape = (1,) if np.isscalar(length) else length.shape - ar = np.empty(shape, dtype=RabiLenType) + ar = np.empty(shape, dtype=RabiLenVoltType) ar["length"] = length ar["msr"] = msr ar["phase"] = phase @@ -74,8 +56,8 @@ def register_qubit(self, qubit, length, msr, phase): def _acquisition( - params: RabiLengthParameters, platform: Platform, qubits: Qubits -) -> RabiLengthData: + params: RabiLengthVoltParameters, platform: Platform, qubits: Qubits +) -> RabiLengthVoltData: r""" Data acquisition for RabiLength Experiment. In the Rabi experiment we apply a pulse at the frequency of the qubit and scan the drive pulse length @@ -120,7 +102,7 @@ def _acquisition( # create a DataUnits object to store the results, # DataUnits stores by default MSR, phase, i, q # additionally include qubit drive pulse length - data = RabiLengthData(amplitudes=amplitudes) + data = RabiLengthVoltData(amplitudes=amplitudes) # execute the sweep results = platform.sweep( @@ -146,7 +128,7 @@ def _acquisition( return data -def _fit(data: RabiLengthData) -> RabiLengthResults: +def _fit(data: RabiLengthVoltData) -> RabiLengthVoltResults: """Post-processing for RabiLength experiment.""" qubits = data.qubits @@ -173,9 +155,9 @@ def _fit(data: RabiLengthData) -> RabiLengthResults: # 0.5 hardcoded guess for less than one oscillation f = x[index] / (x[1] - x[0]) if index is not None else 0.5 - pguess = [1, 1, f, np.pi / 2, 0] + pguess = [1, 1, 1 / f, np.pi / 2, x_max] try: - popt, pcov = curve_fit( + popt, _ = curve_fit( utils.rabi_length_fit, x, y, @@ -186,30 +168,30 @@ def _fit(data: RabiLengthData) -> RabiLengthResults: [1, 1, np.inf, np.pi, np.inf], ), ) - translated_popt = [ + translated_popt = [ # change it according to the fit function (y_max - y_min) * popt[0] + y_min, (y_max - y_min) * popt[1] * np.exp(x_min * popt[4] / (x_max - x_min)), - popt[2] / (x_max - x_min), + popt[2] * (x_max - x_min), popt[3] - 2 * np.pi * x_min * popt[2] / (x_max - x_min), - popt[4] / (x_max - x_min), + popt[4] * (x_max - x_min), ] pi_pulse_parameter = np.abs((1.0 / translated_popt[2]) / 2) except: log.warning("rabi_fit: the fitting was not succesful") pi_pulse_parameter = 0 - translated_popt = [0] * 5 + translated_popt = [0, 0, 1, 0, 1] durations[qubit] = pi_pulse_parameter fitted_parameters[qubit] = translated_popt - return RabiLengthResults(durations, data.amplitudes, fitted_parameters) + return RabiLengthVoltResults(durations, data.amplitudes, fitted_parameters) -def _update(results: RabiLengthResults, platform: Platform, qubit: QubitId): +def _update(results: RabiLengthVoltResults, platform: Platform, qubit: QubitId): update.drive_duration(results.length[qubit], platform, qubit) -def _plot(data: RabiLengthData, fit: RabiLengthResults, qubit): +def _plot(data: RabiLengthVoltData, fit: RabiLengthVoltResults, qubit): """Plotting function for RabiLength experiment.""" return utils.plot(data, qubit, fit) diff --git a/src/qibocal/protocols/characterization/rabi/utils.py b/src/qibocal/protocols/characterization/rabi/utils.py index bd1b4df86..29b7d623c 100644 --- a/src/qibocal/protocols/characterization/rabi/utils.py +++ b/src/qibocal/protocols/characterization/rabi/utils.py @@ -2,7 +2,7 @@ import plotly.graph_objects as go from plotly.subplots import make_subplots -from ..utils import V_TO_UV, table_dict, table_html +from ..utils import COLORBAND, COLORBAND_LINE, V_TO_UV, table_dict, table_html def rabi_amplitude_fit(x, p0, p1, p2, p3): @@ -12,7 +12,7 @@ def rabi_amplitude_fit(x, p0, p1, p2, p3): # Period T : 1/p[2] # Phase : p[3] # Arbitrary parameter T_2 : 1/p[4] - return p0 + p1 * np.sin(2 * np.pi * x * p2 + p3) + return p0 + p1 * np.sin(2 * np.pi * x / p2 + p3) def rabi_length_fit(x, p0, p1, p2, p3, p4): @@ -22,15 +22,15 @@ def rabi_length_fit(x, p0, p1, p2, p3, p4): # Period T : 1/p[2] # Phase : p[3] # Arbitrary parameter T_2 : 1/p[4] - return p0 + p1 * np.sin(2 * np.pi * x * p2 + p3) * np.exp(-x * p4) + return p0 + p1 * np.sin(2 * np.pi * x / p2 + p3) * np.exp(-x / p4) def plot(data, qubit, fit): - if data.__class__.__name__ == "RabiAmplitudeData": + if data.__class__.__name__ == "RabiAmplitudeVoltData": quantity = "amp" title = "Amplitude (dimensionless)" fitting = rabi_amplitude_fit - elif data.__class__.__name__ == "RabiLengthData": + elif data.__class__.__name__ == "RabiLengthVoltData": quantity = "length" title = "Time (ns)" fitting = rabi_length_fit @@ -131,26 +131,32 @@ def plot_proba(data, qubit, fit): figures = [] fitting_report = "" - fig = make_subplots( - rows=1, - cols=1, - horizontal_spacing=0.1, - vertical_spacing=0.1, - subplot_titles=("Probability",), - ) - qubit_data = data[qubit] + probs = qubit_data.prob + error_bars = qubit_data.error rabi_parameters = getattr(qubit_data, quantity) - fig.add_trace( - go.Scatter( - x=rabi_parameters, - y=qubit_data.prob, - opacity=1, - name="Voltage", - showlegend=True, - legendgroup="Voltage", - ), + fig = go.Figure( + [ + go.Scatter( + x=rabi_parameters, + y=qubit_data.prob, + opacity=1, + name="Probability", + showlegend=True, + legendgroup="Probability", + mode="lines", + ), + go.Scatter( + x=np.concatenate((rabi_parameters, rabi_parameters[::-1])), + y=np.concatenate((probs + error_bars, (probs - error_bars)[::-1])), + fill="toself", + fillcolor=COLORBAND, + line=dict(color=COLORBAND_LINE), + showlegend=True, + name="Errors", + ), + ] ) if fit is not None: @@ -168,15 +174,14 @@ def plot_proba(data, qubit, fit): line=go.scatter.Line(dash="dot"), marker_color="rgb(255, 130, 67)", ), - row=1, - col=1, ) fitting_report = table_html( table_dict( qubit, ["Pi pulse amplitude", "Pi pulse length"], - [np.round(fit.amplitude[qubit], 3), np.round(fit.length[qubit], 3)], + [fit.amplitude[qubit], fit.length[qubit]], + display_error=True, ) ) diff --git a/src/qibocal/protocols/characterization/utils.py b/src/qibocal/protocols/characterization/utils.py index a5507dcbf..141ee9578 100644 --- a/src/qibocal/protocols/characterization/utils.py +++ b/src/qibocal/protocols/characterization/utils.py @@ -21,6 +21,8 @@ S_TO_NS = 1e9 EXTREME_CHI = 1e4 """Chi2 output when errors list contains zero elements""" +COLORBAND = "rgba(0,100,80,0.2)" +COLORBAND_LINE = "rgba(255,255,255,0)" def calculate_frequencies(results, qubit_list): diff --git a/src/qibocal/update.py b/src/qibocal/update.py index 53c0fc845..3fc2b8e88 100644 --- a/src/qibocal/update.py +++ b/src/qibocal/update.py @@ -44,23 +44,24 @@ def readout_attenuation(att: int, platform: Platform, qubit: QubitId): def drive_frequency(freq: Union[float, tuple], platform: Platform, qubit: QubitId): """Update drive frequency value in platform for specific qubit.""" - if isinstance( - freq, tuple - ): # TODO: remove this branching after error bars propagation - freq = int(freq[0] * GHZ_TO_HZ) - else: - freq = int(freq * GHZ_TO_HZ) + if isinstance(freq, tuple): + freq = freq[0] + freq = int(freq * GHZ_TO_HZ) platform.qubits[qubit].native_gates.RX.frequency = int(freq) platform.qubits[qubit].drive_frequency = int(freq) -def drive_amplitude(amp: float, platform: Platform, qubit: QubitId): +def drive_amplitude(amp: Union[float, tuple], platform: Platform, qubit: QubitId): """Update drive frequency value in platform for specific qubit.""" + if isinstance(amp, tuple): + amp = amp[0] platform.qubits[qubit].native_gates.RX.amplitude = float(amp) def drive_duration(duration: int, platform: Platform, qubit: QubitId): """Update drive duration value in platform for specific qubit.""" + if isinstance(duration, tuple): + duration = duration[0] platform.qubits[qubit].native_gates.RX.duration = int(duration) From 0a3c7653b309a6e5358a9a1c8ee3436b7c3288da Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Thu, 19 Oct 2023 16:02:14 +0400 Subject: [PATCH 08/44] fix popt eval --- src/qibocal/protocols/characterization/rabi/amplitude.py | 2 +- src/qibocal/protocols/characterization/rabi/length_msr.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibocal/protocols/characterization/rabi/amplitude.py b/src/qibocal/protocols/characterization/rabi/amplitude.py index ccc7f7f8a..6c84ed0e7 100644 --- a/src/qibocal/protocols/characterization/rabi/amplitude.py +++ b/src/qibocal/protocols/characterization/rabi/amplitude.py @@ -175,7 +175,7 @@ def _fit(data: RabiAmplitudeData) -> RabiAmplitudeResults: sigma=qubit_data.error, ) perr = np.sqrt(np.diag(perr)) - pi_pulse_parameter = np.abs(popt[2] / 2) + pi_pulse_parameter = np.abs(1 / popt[2] / 2) except: log.warning("rabi_fit: the fitting was not succesful") diff --git a/src/qibocal/protocols/characterization/rabi/length_msr.py b/src/qibocal/protocols/characterization/rabi/length_msr.py index f84044c80..8d2e7e8c4 100644 --- a/src/qibocal/protocols/characterization/rabi/length_msr.py +++ b/src/qibocal/protocols/characterization/rabi/length_msr.py @@ -175,7 +175,7 @@ def _fit(data: RabiLengthVoltData) -> RabiLengthVoltResults: popt[3] - 2 * np.pi * x_min * popt[2] / (x_max - x_min), popt[4] * (x_max - x_min), ] - pi_pulse_parameter = np.abs((1.0 / translated_popt[2]) / 2) + pi_pulse_parameter = np.abs(translated_popt[2] / 2) except: log.warning("rabi_fit: the fitting was not succesful") pi_pulse_parameter = 0 From cbf9dccb0615ee81bb22a581a9de2ee2a9699c5d Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Thu, 19 Oct 2023 19:34:00 +0400 Subject: [PATCH 09/44] fix frequency guess and evaluate chi2 --- .../characterization/rabi/amplitude.py | 15 +++++++++++++-- .../protocols/characterization/rabi/length.py | 18 ++++++++++++++---- .../characterization/rabi/length_msr.py | 9 +++++---- .../protocols/characterization/rabi/utils.py | 6 +++--- 4 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/qibocal/protocols/characterization/rabi/amplitude.py b/src/qibocal/protocols/characterization/rabi/amplitude.py index ccc7f7f8a..5be50cce8 100644 --- a/src/qibocal/protocols/characterization/rabi/amplitude.py +++ b/src/qibocal/protocols/characterization/rabi/amplitude.py @@ -15,6 +15,7 @@ from qibocal.auto.operation import Data, Parameters, Qubits, Results, Routine from qibocal.config import log +from ..utils import chi2_reduced from . import utils @@ -46,6 +47,7 @@ class RabiAmplitudeResults(Results): """Drive pulse duration. Same for all qubits.""" fitted_parameters: dict[QubitId, dict[str, float]] """Raw fitted parameters.""" + chi2: dict[QubitId, tuple[float, Optional[float]]] = field(default_factory=dict) RabiAmpType = np.dtype( @@ -146,6 +148,7 @@ def _fit(data: RabiAmplitudeData) -> RabiAmplitudeResults: pi_pulse_amplitudes = {} fitted_parameters = {} + chi2 = {} for qubit in qubits: qubit_data = data[qubit] @@ -160,7 +163,7 @@ def _fit(data: RabiAmplitudeData) -> RabiAmplitudeResults: index = local_maxima[0] if len(local_maxima) > 0 else None # 0.5 hardcoded guess for less than one oscillation f = x[index] / (x[1] - x[0]) if index is not None else 0.5 - pguess = [0.5, 1, f, np.pi / 2] + pguess = [0.5, 0.5, np.max(x) / f, np.pi / 2] try: popt, perr = curve_fit( utils.rabi_amplitude_fit, @@ -186,7 +189,15 @@ def _fit(data: RabiAmplitudeData) -> RabiAmplitudeResults: pi_pulse_amplitudes[qubit] = (pi_pulse_parameter, perr[2] / 2) fitted_parameters[qubit] = popt.tolist() durations = {key: (value, 0) for key, value in data.durations.items()} - return RabiAmplitudeResults(pi_pulse_amplitudes, durations, fitted_parameters) + chi2[qubit] = ( + chi2_reduced( + y, + utils.rabi_amplitude_fit(x, *popt), + qubit_data.error, + ), + np.sqrt(2 / len(y)), + ) + return RabiAmplitudeResults(pi_pulse_amplitudes, durations, fitted_parameters, chi2) def _plot(data: RabiAmplitudeData, qubit, fit: RabiAmplitudeResults = None): diff --git a/src/qibocal/protocols/characterization/rabi/length.py b/src/qibocal/protocols/characterization/rabi/length.py index 3ec2fa153..f53e154e5 100644 --- a/src/qibocal/protocols/characterization/rabi/length.py +++ b/src/qibocal/protocols/characterization/rabi/length.py @@ -15,6 +15,7 @@ from qibocal.auto.operation import Data, Parameters, Qubits, Results, Routine from qibocal.config import log +from ..utils import chi2_reduced from . import utils @@ -46,6 +47,7 @@ class RabiLengthResults(Results): """Pi pulse amplitude. Same for all qubits.""" fitted_parameters: dict[QubitId, dict[str, float]] """Raw fitting output.""" + chi2: dict[QubitId, tuple[float, Optional[float]]] = field(default_factory=dict) RabiLenType = np.dtype( @@ -156,6 +158,7 @@ def _fit(data: RabiLengthData) -> RabiLengthResults: qubits = data.qubits fitted_parameters = {} durations = {} + chi2 = {} for qubit in qubits: qubit_data = data[qubit] @@ -169,8 +172,7 @@ def _fit(data: RabiLengthData) -> RabiLengthResults: index = local_maxima[0] if len(local_maxima) > 0 else None # 0.5 hardcoded guess for less than one oscillation f = x[index] / (x[1] - x[0]) if index is not None else 0.5 - - pguess = [1, 1, f, np.pi / 2, np.max(x) / 2] + pguess = [1 / 2.0, 1 / 2.0, np.max(x) / f, np.pi / 2, 0] try: popt, perr = curve_fit( utils.rabi_length_fit, @@ -191,10 +193,18 @@ def _fit(data: RabiLengthData) -> RabiLengthResults: pi_pulse_parameter = 0 popt = [0] * 4 + [1] - durations[qubit] = (pi_pulse_parameter, 0) + durations[qubit] = (pi_pulse_parameter, perr[2] / 2) fitted_parameters[qubit] = popt.tolist() amplitudes = {key: (value, 0) for key, value in data.amplitudes.items()} - return RabiLengthResults(durations, amplitudes, fitted_parameters) + chi2[qubit] = ( + chi2_reduced( + y, + utils.rabi_length_fit(x, *popt), + qubit_data.error, + ), + np.sqrt(2 / len(y)), + ) + return RabiLengthResults(durations, amplitudes, fitted_parameters, chi2) def _update(results: RabiLengthResults, platform: Platform, qubit: QubitId): diff --git a/src/qibocal/protocols/characterization/rabi/length_msr.py b/src/qibocal/protocols/characterization/rabi/length_msr.py index f84044c80..a509f7ab2 100644 --- a/src/qibocal/protocols/characterization/rabi/length_msr.py +++ b/src/qibocal/protocols/characterization/rabi/length_msr.py @@ -155,7 +155,7 @@ def _fit(data: RabiLengthVoltData) -> RabiLengthVoltResults: # 0.5 hardcoded guess for less than one oscillation f = x[index] / (x[1] - x[0]) if index is not None else 0.5 - pguess = [1, 1, 1 / f, np.pi / 2, x_max] + pguess = [1, 1, 1 / f, np.pi / 2, 0] try: popt, _ = curve_fit( utils.rabi_length_fit, @@ -173,13 +173,13 @@ def _fit(data: RabiLengthVoltData) -> RabiLengthVoltResults: (y_max - y_min) * popt[1] * np.exp(x_min * popt[4] / (x_max - x_min)), popt[2] * (x_max - x_min), popt[3] - 2 * np.pi * x_min * popt[2] / (x_max - x_min), - popt[4] * (x_max - x_min), + popt[4] / (x_max - x_min), ] - pi_pulse_parameter = np.abs((1.0 / translated_popt[2]) / 2) + pi_pulse_parameter = np.abs((translated_popt[2]) / 2) except: log.warning("rabi_fit: the fitting was not succesful") pi_pulse_parameter = 0 - translated_popt = [0, 0, 1, 0, 1] + translated_popt = [0, 0, 1, 0, 0] durations[qubit] = pi_pulse_parameter fitted_parameters[qubit] = translated_popt @@ -188,6 +188,7 @@ def _fit(data: RabiLengthVoltData) -> RabiLengthVoltResults: def _update(results: RabiLengthVoltResults, platform: Platform, qubit: QubitId): + print(results.length[qubit]) update.drive_duration(results.length[qubit], platform, qubit) diff --git a/src/qibocal/protocols/characterization/rabi/utils.py b/src/qibocal/protocols/characterization/rabi/utils.py index 29b7d623c..0239c5b4b 100644 --- a/src/qibocal/protocols/characterization/rabi/utils.py +++ b/src/qibocal/protocols/characterization/rabi/utils.py @@ -22,7 +22,7 @@ def rabi_length_fit(x, p0, p1, p2, p3, p4): # Period T : 1/p[2] # Phase : p[3] # Arbitrary parameter T_2 : 1/p[4] - return p0 + p1 * np.sin(2 * np.pi * x / p2 + p3) * np.exp(-x / p4) + return p0 + p1 * np.sin(2 * np.pi * x / p2 + p3) * np.exp(-x * p4) def plot(data, qubit, fit): @@ -179,8 +179,8 @@ def plot_proba(data, qubit, fit): fitting_report = table_html( table_dict( qubit, - ["Pi pulse amplitude", "Pi pulse length"], - [fit.amplitude[qubit], fit.length[qubit]], + ["Pi pulse amplitude", "Pi pulse length", "chi2 reduced"], + [fit.amplitude[qubit], fit.length[qubit], fit.chi2[qubit]], display_error=True, ) ) From a9ce9ea93f0751109cba262dbf51302209688ad7 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Mon, 23 Oct 2023 10:56:36 +0400 Subject: [PATCH 10/44] fix amplitude fit --- src/qibocal/protocols/characterization/rabi/amplitude.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibocal/protocols/characterization/rabi/amplitude.py b/src/qibocal/protocols/characterization/rabi/amplitude.py index 688f730fa..4523a85f4 100644 --- a/src/qibocal/protocols/characterization/rabi/amplitude.py +++ b/src/qibocal/protocols/characterization/rabi/amplitude.py @@ -173,7 +173,7 @@ def _fit(data: RabiAmplitudeData) -> RabiAmplitudeResults: sigma=qubit_data.error, ) perr = np.sqrt(np.diag(perr)) - pi_pulse_parameter = np.abs(1 / popt[2] / 2) + pi_pulse_parameter = np.abs(popt[2] / 2) except: log.warning("rabi_fit: the fitting was not succesful") From e75bf5f1aa7c5fa77e6b3eff933252ecb42dd52a Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Mon, 23 Oct 2023 11:31:15 +0400 Subject: [PATCH 11/44] fix frequency bug --- src/qibocal/protocols/characterization/rabi/amplitude.py | 2 +- src/qibocal/protocols/characterization/rabi/amplitude_msr.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibocal/protocols/characterization/rabi/amplitude.py b/src/qibocal/protocols/characterization/rabi/amplitude.py index 4523a85f4..644905514 100644 --- a/src/qibocal/protocols/characterization/rabi/amplitude.py +++ b/src/qibocal/protocols/characterization/rabi/amplitude.py @@ -158,7 +158,7 @@ def _fit(data: RabiAmplitudeData) -> RabiAmplitudeResults: index = local_maxima[0] if len(local_maxima) > 0 else None # 0.5 hardcoded guess for less than one oscillation f = x[index] / (x[1] - x[0]) if index is not None else 0.5 - pguess = [0.5, 0.5, np.max(x) / f, np.pi / 2] + pguess = [0.5, 0.5, 1 / f, np.pi / 2] try: popt, perr = curve_fit( utils.rabi_amplitude_fit, diff --git a/src/qibocal/protocols/characterization/rabi/amplitude_msr.py b/src/qibocal/protocols/characterization/rabi/amplitude_msr.py index 60d106d18..438ab6681 100644 --- a/src/qibocal/protocols/characterization/rabi/amplitude_msr.py +++ b/src/qibocal/protocols/characterization/rabi/amplitude_msr.py @@ -164,7 +164,7 @@ def _fit(data: RabiAmplitudeVoltData) -> RabiAmplitudeVoltResults: popt[2] * (x_max - x_min), popt[3] - 2 * np.pi * x_min / (x_max - x_min) * popt[2], ] - pi_pulse_parameter = np.abs((1.0 / translated_popt[2]) / 2) + pi_pulse_parameter = np.abs((translated_popt[2]) / 2) except: log.warning("rabi_fit: the fitting was not succesful") From 72057f3ff259c6a1d548613c97410eae6068d53a Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Mon, 23 Oct 2023 12:20:43 +0400 Subject: [PATCH 12/44] fix amplitude --- .../characterization/rabi/amplitude_msr.py | 19 +++++++------------ .../protocols/characterization/rabi/length.py | 5 +++-- .../characterization/rabi/length_msr.py | 2 +- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/qibocal/protocols/characterization/rabi/amplitude_msr.py b/src/qibocal/protocols/characterization/rabi/amplitude_msr.py index 438ab6681..ccd56d4fc 100644 --- a/src/qibocal/protocols/characterization/rabi/amplitude_msr.py +++ b/src/qibocal/protocols/characterization/rabi/amplitude_msr.py @@ -41,14 +41,6 @@ class RabiAmplitudeVoltResults(RabiAmplitudeResults): class RabiAmplitudeVoltData(RabiAmplitudeData): """RabiAmplitude data acquisition.""" - def register_qubit(self, qubit, amp, msr, phase): - """Store output for single qubit.""" - ar = np.empty(amp.shape, dtype=RabiAmpVoltType) - ar["amp"] = amp - ar["msr"] = msr - ar["phase"] = phase - self.data[qubit] = np.rec.array(ar) - def _acquisition( params: RabiAmplitudeVoltParameters, platform: Platform, qubits: Qubits @@ -110,10 +102,13 @@ def _acquisition( # average msr, phase, i and q over the number of shots defined in the runcard result = results[ro_pulses[qubit].serial] data.register_qubit( - qubit, - amp=qd_pulses[qubit].amplitude * qd_pulse_amplitude_range, - msr=result.magnitude, - phase=result.phase, + RabiAmpVoltType, + (qubit), + dict( + amp=qd_pulses[qubit].amplitude * qd_pulse_amplitude_range, + msr=result.magnitude, + phase=result.phase, + ), ) return data diff --git a/src/qibocal/protocols/characterization/rabi/length.py b/src/qibocal/protocols/characterization/rabi/length.py index 7ce73e502..caf8a9da1 100644 --- a/src/qibocal/protocols/characterization/rabi/length.py +++ b/src/qibocal/protocols/characterization/rabi/length.py @@ -162,7 +162,8 @@ def _fit(data: RabiLengthData) -> RabiLengthResults: index = local_maxima[0] if len(local_maxima) > 0 else None # 0.5 hardcoded guess for less than one oscillation f = x[index] / (x[1] - x[0]) if index is not None else 0.5 - pguess = [1 / 2.0, 1 / 2.0, np.max(x) / f, np.pi / 2, 0] + pguess = [0.5, 0.5, np.max(x) / f, np.pi / 2, 0] + print(pguess) try: popt, perr = curve_fit( utils.rabi_length_fit, @@ -182,7 +183,7 @@ def _fit(data: RabiLengthData) -> RabiLengthResults: log.warning("rabi_fit: the fitting was not succesful") pi_pulse_parameter = 0 popt = [0] * 4 + [1] - + print(popt) durations[qubit] = (pi_pulse_parameter, perr[2] / 2) fitted_parameters[qubit] = popt.tolist() amplitudes = {key: (value, 0) for key, value in data.amplitudes.items()} diff --git a/src/qibocal/protocols/characterization/rabi/length_msr.py b/src/qibocal/protocols/characterization/rabi/length_msr.py index 07f5b960b..fa6f7b47c 100644 --- a/src/qibocal/protocols/characterization/rabi/length_msr.py +++ b/src/qibocal/protocols/characterization/rabi/length_msr.py @@ -145,7 +145,7 @@ def _fit(data: RabiLengthVoltData) -> RabiLengthVoltResults: # 0.5 hardcoded guess for less than one oscillation f = x[index] / (x[1] - x[0]) if index is not None else 0.5 - pguess = [1, 1, 1 / f, np.pi / 2, 0] + pguess = [0.5, 0.5, 1 / f, np.pi / 2, 0] try: popt, _ = curve_fit( utils.rabi_length_fit, From e35db0a41e9e16558fe3297571cb614ad8066db9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 11:28:07 +0000 Subject: [PATCH 13/44] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qibocal/protocols/characterization/rabi/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qibocal/protocols/characterization/rabi/utils.py b/src/qibocal/protocols/characterization/rabi/utils.py index 8ba2cdc51..4864baa3b 100644 --- a/src/qibocal/protocols/characterization/rabi/utils.py +++ b/src/qibocal/protocols/characterization/rabi/utils.py @@ -26,7 +26,6 @@ def rabi_length_fit(x, p0, p1, p2, p3, p4): def plot(data, qubit, fit): - if "RabiAmplitude" in data.__class__.__name__: quantity = "amp" title = "Amplitude (dimensionless)" From 7ea177e2cbc0caf2622ab38d927545d68ba18746 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Mon, 23 Oct 2023 16:28:03 +0400 Subject: [PATCH 14/44] fix tests --- .../protocols/characterization/rabi/ef.py | 14 +++++++------- .../characterization/rabi/length_sequences.py | 16 ++++++++-------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/qibocal/protocols/characterization/rabi/ef.py b/src/qibocal/protocols/characterization/rabi/ef.py index bff13b562..ade1f1170 100644 --- a/src/qibocal/protocols/characterization/rabi/ef.py +++ b/src/qibocal/protocols/characterization/rabi/ef.py @@ -10,21 +10,21 @@ from qibocal import update from qibocal.auto.operation import Qubits, Routine -from . import amplitude, utils +from . import amplitude_msr, utils @dataclass -class RabiAmplitudeEFParameters(amplitude.RabiAmplitudeParameters): +class RabiAmplitudeEFParameters(amplitude_msr.RabiAmplitudeVoltParameters): """RabiAmplitudeEF runcard inputs.""" @dataclass -class RabiAmplitudeEFResults(amplitude.RabiAmplitudeResults): +class RabiAmplitudeEFResults(amplitude_msr.RabiAmplitudeVoltResults): """RabiAmplitudeEF outputs.""" @dataclass -class RabiAmplitudeEFData(amplitude.RabiAmplitudeData): +class RabiAmplitudeEFData(amplitude_msr.RabiAmplitudeVoltData): """RabiAmplitude data acquisition.""" @@ -96,7 +96,7 @@ def _acquisition( # average msr, phase, i and q over the number of shots defined in the runcard result = results[ro_pulses[qubit].serial] data.register_qubit( - amplitude.RabiAmpType, + amplitude_msr.RabiAmpVoltType, (qubit), dict( amp=qd_pulses[qubit].amplitude * qd_pulse_amplitude_range, @@ -116,9 +116,9 @@ def _plot(data: RabiAmplitudeEFData, qubit, fit: RabiAmplitudeEFResults = None): def _update(results: RabiAmplitudeEFResults, platform: Platform, qubit: QubitId): - """Update RX2 amplitude""" + """Update RX2 amplitude_msr""" update.drive_12_amplitude(results.amplitude[qubit], platform, qubit) -rabi_amplitude_ef = Routine(_acquisition, amplitude._fit, _plot, _update) +rabi_amplitude_ef = Routine(_acquisition, amplitude_msr._fit, _plot, _update) """RabiAmplitudeEF Routine object.""" diff --git a/src/qibocal/protocols/characterization/rabi/length_sequences.py b/src/qibocal/protocols/characterization/rabi/length_sequences.py index da57e989b..da58d287b 100644 --- a/src/qibocal/protocols/characterization/rabi/length_sequences.py +++ b/src/qibocal/protocols/characterization/rabi/length_sequences.py @@ -5,10 +5,10 @@ from qibocal.auto.operation import Qubits, Routine -from .length import ( - RabiLengthData, - RabiLengthParameters, - RabiLenType, +from .length_msr import ( + RabiLengthVoltData, + RabiLengthVoltParameters, + RabiLenVoltType, _fit, _plot, _update, @@ -16,8 +16,8 @@ def _acquisition( - params: RabiLengthParameters, platform: Platform, qubits: Qubits -) -> RabiLengthData: + params: RabiLengthVoltParameters, platform: Platform, qubits: Qubits +) -> RabiLengthVoltData: r""" Data acquisition for RabiLength Experiment. In the Rabi experiment we apply a pulse at the frequency of the qubit and scan the drive pulse length @@ -53,7 +53,7 @@ def _acquisition( # create a DataUnits object to store the results, # DataUnits stores by default MSR, phase, i, q # additionally include qubit drive pulse length - data = RabiLengthData(amplitudes=amplitudes) + data = RabiLengthVoltData(amplitudes=amplitudes) # sweep the parameter for duration in qd_pulse_duration_range: @@ -76,7 +76,7 @@ def _acquisition( # average msr, phase, i and q over the number of shots defined in the runcard result = results[ro_pulses[qubit].serial] data.register_qubit( - RabiLenType, + RabiLenVoltType, (qubit), dict( length=np.array([duration]), From 6448c95cd1b2135877cc2ad4da98f7157476b1ea Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Mon, 23 Oct 2023 19:56:44 +0400 Subject: [PATCH 15/44] fix lint --- src/qibocal/protocols/characterization/classification.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/qibocal/protocols/characterization/classification.py b/src/qibocal/protocols/characterization/classification.py index 9777c1552..c5e9fab2e 100644 --- a/src/qibocal/protocols/characterization/classification.py +++ b/src/qibocal/protocols/characterization/classification.py @@ -31,6 +31,8 @@ evaluate_grid, get_color_state0, plot_results, + table_dict, + table_html, ) ROC_LENGHT = 800 From f5afaa845cb07927cae73364e5c2e01ac20f081e Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Mon, 23 Oct 2023 20:17:26 +0400 Subject: [PATCH 16/44] fix tests --- tests/runcards/protocols.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/runcards/protocols.yml b/tests/runcards/protocols.yml index c390063b5..5cb5ea15b 100644 --- a/tests/runcards/protocols.yml +++ b/tests/runcards/protocols.yml @@ -186,6 +186,17 @@ actions: pulse_length: 30 nshots: 1024 + - id: rabi1 + priority: 0 + operation: rabi_amplitude + parameters: + min_amp_factor: 0.0 + max_amp_factor: 4.0 + step_amp_factor: 1.0 + pulse_length: 30 + nshots: 10 + + - id: rabi_ef priority: 0 operation: rabi_amplitude_ef From 106dea8c39a4b3cc3a251dc116bf949f2bfebed2 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Mon, 23 Oct 2023 20:46:34 +0400 Subject: [PATCH 17/44] fix finish pulse bug --- .../characterization/qutrit_classification.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/qibocal/protocols/characterization/qutrit_classification.py b/src/qibocal/protocols/characterization/qutrit_classification.py index dec28314d..ea7e9919e 100644 --- a/src/qibocal/protocols/characterization/qutrit_classification.py +++ b/src/qibocal/protocols/characterization/qutrit_classification.py @@ -95,13 +95,16 @@ def _acquisition( rx_pulse = platform.create_RX_pulse(qubit, start=0) rx12_pulse = platform.create_RX12_pulse(qubit, start=rx_pulse.finish) drive_pulses = [rx_pulse, rx12_pulse] - ro_pulses[qubit] = platform.create_qubit_readout_pulse( - qubit, start=rx12_pulse.finish - ) hpars[qubit] = qubits[qubit].classifiers_hpars + ro_pulses[qubit] = [] for i, sequence in enumerate(states_sequences): sequence.add(*drive_pulses[:i]) - sequence.add(ro_pulses[qubit]) + start = drive_pulses[i - 1].finish if i != 0 else 0 + ro_pulses[qubit].append( + platform.create_qubit_readout_pulse(qubit, start=start) + ) + sequence.add(ro_pulses[qubit][-1]) + # create a DataUnits object to store the results data = QutritClassificationData( nshots=params.nshots, @@ -124,8 +127,7 @@ def _acquisition( for qubit in qubits: for state, state_result in enumerate(states_results): - result = state_result[ro_pulses[qubit].serial] - print(params.nshots, len(result.voltage_i)) + result = state_result[ro_pulses[qubit][state].serial] data.register_qubit( ClassificationType, (qubit), From 722cbc712c4912703cd9053065b538deb42229ba Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Mon, 23 Oct 2023 20:50:23 +0400 Subject: [PATCH 18/44] remove rabi test --- tests/runcards/protocols.yml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tests/runcards/protocols.yml b/tests/runcards/protocols.yml index 5cb5ea15b..c390063b5 100644 --- a/tests/runcards/protocols.yml +++ b/tests/runcards/protocols.yml @@ -186,17 +186,6 @@ actions: pulse_length: 30 nshots: 1024 - - id: rabi1 - priority: 0 - operation: rabi_amplitude - parameters: - min_amp_factor: 0.0 - max_amp_factor: 4.0 - step_amp_factor: 1.0 - pulse_length: 30 - nshots: 10 - - - id: rabi_ef priority: 0 operation: rabi_amplitude_ef From 7eda0f95f2955a05a9fb74552cee91b56976f7b8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 21:07:35 +0000 Subject: [PATCH 19/44] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 23.9.1 → 23.10.1](https://github.com/psf/black/compare/23.9.1...23.10.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b5c681571..1ad0e7a1b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: - id: check-merge-conflict - id: debug-statements - repo: https://github.com/psf/black - rev: 23.9.1 + rev: 23.10.1 hooks: - id: black - repo: https://github.com/pycqa/isort From 3d3492dd84d35deb806978889c06ea86ca6fca6c Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Tue, 24 Oct 2023 09:35:09 +0400 Subject: [PATCH 20/44] update test runcard --- tests/runcards/protocols.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/runcards/protocols.yml b/tests/runcards/protocols.yml index c390063b5..65f3112d5 100644 --- a/tests/runcards/protocols.yml +++ b/tests/runcards/protocols.yml @@ -186,6 +186,17 @@ actions: pulse_length: 30 nshots: 1024 + - id: rabi msr + priority: 0 + operation: rabi_amplitude_msr + parameters: + min_amp_factor: 0.0 + max_amp_factor: 4.0 + step_amp_factor: 0.1 + pulse_length: 30 + nshots: 1024 + + - id: rabi_ef priority: 0 operation: rabi_amplitude_ef @@ -208,6 +219,16 @@ actions: pulse_amplitude: 0.5 nshots: 1024 + - id: rabi length msr + priority: 0 + operation: rabi_length_msr + parameters: + pulse_duration_start: 4 + pulse_duration_end: 84 + pulse_duration_step: 8 + pulse_amplitude: 0.5 + nshots: 1024 + - id: rabi length sequences priority: 0 operation: rabi_length_sequences From 4a272a00014858e7839953cdec553fe415531430 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Tue, 24 Oct 2023 09:45:09 +0400 Subject: [PATCH 21/44] update test runcard --- tests/runcards/protocols.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/runcards/protocols.yml b/tests/runcards/protocols.yml index c390063b5..3169cb789 100644 --- a/tests/runcards/protocols.yml +++ b/tests/runcards/protocols.yml @@ -564,3 +564,11 @@ actions: parameters: amplitude_step: 0.1 amplitude_stop: 0.5 + + - id: qutrit + priority: 0 + qubits: [0,1] + operation: qutrit_classification + parameters: + nshots: 100 + classifiers_list: ["naive_bayes", "decision_tree"] From 2f967e2c95bb2ebbbbd37e4949d8b722b74faa29 Mon Sep 17 00:00:00 2001 From: Edoardo Pedicillo Date: Tue, 24 Oct 2023 09:48:47 +0400 Subject: [PATCH 22/44] Update src/qibocal/update.py Co-authored-by: Gabriele Palazzo <73099233+GabrielePalazzo@users.noreply.github.com> --- src/qibocal/update.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibocal/update.py b/src/qibocal/update.py index 08fadcdaf..b83a60f99 100644 --- a/src/qibocal/update.py +++ b/src/qibocal/update.py @@ -58,7 +58,7 @@ def drive_amplitude(amp: Union[float, tuple], platform: Platform, qubit: QubitId platform.qubits[qubit].native_gates.RX.amplitude = float(amp) -def drive_duration(duration: int, platform: Platform, qubit: QubitId): +def drive_duration(duration: Union[int, tuple], platform: Platform, qubit: QubitId): """Update drive duration value in platform for specific qubit.""" if isinstance(duration, tuple): duration = duration[0] From f6bc9bdf5f9f110b76ae519a3ad26f2bc04e2b92 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Tue, 24 Oct 2023 10:46:01 +0400 Subject: [PATCH 23/44] update docs --- .../characterization/qutrit_classification.py | 21 ++----------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/src/qibocal/protocols/characterization/qutrit_classification.py b/src/qibocal/protocols/characterization/qutrit_classification.py index ea7e9919e..1c912fe2f 100644 --- a/src/qibocal/protocols/characterization/qutrit_classification.py +++ b/src/qibocal/protocols/characterization/qutrit_classification.py @@ -56,31 +56,14 @@ def _acquisition( Args: nshots (int): number of times the pulse sequence will be repeated. classifiers (list): list of classifiers, the available ones are: - - linear_svm - - ada_boost - - gaussian_process - naive_bayes - nn - - qubit_fit - random_forest - - rbf_svm - - qblox_fit. - The default value is `["qubit_fit"]`. + - decision_tree + The default value is `["naive_bayes"]`. savedir (str): Dumping folder of the classification results. If not given the dumping folder will be the report one. relaxation_time (float): Relaxation time. - - Example: - .. code-block:: yaml - - - id: single_shot_classification_1 - priority: 0 - operation: single_shot_classification - parameters: - nshots: 5000 - savedir: "single_shot" - classifiers_list: ["qubit_fit","naive_bayes", "linear_svm"] - """ # create two sequences of pulses: From 2ee95b93cd43f62185b37a32063f8a9211fecb9b Mon Sep 17 00:00:00 2001 From: Edoardo Pedicillo Date: Tue, 24 Oct 2023 10:49:44 +0400 Subject: [PATCH 24/44] Update src/qibocal/protocols/characterization/qutrit_classification.py Co-authored-by: Gabriele Palazzo <73099233+GabrielePalazzo@users.noreply.github.com> --- src/qibocal/protocols/characterization/qutrit_classification.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibocal/protocols/characterization/qutrit_classification.py b/src/qibocal/protocols/characterization/qutrit_classification.py index 1c912fe2f..073fe16e4 100644 --- a/src/qibocal/protocols/characterization/qutrit_classification.py +++ b/src/qibocal/protocols/characterization/qutrit_classification.py @@ -62,7 +62,7 @@ def _acquisition( - decision_tree The default value is `["naive_bayes"]`. savedir (str): Dumping folder of the classification results. - If not given the dumping folder will be the report one. + If not given, the dumping folder will be the report one. relaxation_time (float): Relaxation time. """ From 889916e0d7644af9f2517baeaaf062e0380a9c78 Mon Sep 17 00:00:00 2001 From: Andrea Date: Tue, 24 Oct 2023 16:40:33 +0400 Subject: [PATCH 25/44] Add page about Qibocal API in doc --- doc/source/getting-started/interface.rst | 2 + doc/source/tutorials/api.rst | 107 ++++++++++++++++++ doc/source/tutorials/classification_plot.png | Bin 0 -> 174076 bytes doc/source/tutorials/classification_table.png | Bin 0 -> 31602 bytes doc/source/tutorials/index.rst | 1 + 5 files changed, 110 insertions(+) create mode 100644 doc/source/tutorials/api.rst create mode 100644 doc/source/tutorials/classification_plot.png create mode 100644 doc/source/tutorials/classification_table.png diff --git a/doc/source/getting-started/interface.rst b/doc/source/getting-started/interface.rst index fadf174eb..c252d2c6d 100644 --- a/doc/source/getting-started/interface.rst +++ b/doc/source/getting-started/interface.rst @@ -1,3 +1,5 @@ +.. _interface: + How to use Qibocal? =================== diff --git a/doc/source/tutorials/api.rst b/doc/source/tutorials/api.rst new file mode 100644 index 000000000..05c442ffc --- /dev/null +++ b/doc/source/tutorials/api.rst @@ -0,0 +1,107 @@ +How to use Qibocal as a library +=============================== + +Qibocal also allows executing protocols without the standard :ref:`interface `. + +In the following tutorial we show how to run a single protocol using Qibocal as a library. +For this particular example we will focus on the `single shot classification protocol +`_. + +.. code-block:: python + + from qibocal.protocols.characterization import Operation + from qibolab import create_platform + + # allocate platform + platform = create_platform("....") + # get qubits from platform + qubits = platform.qubits + + # we select the protocol + protocol = Operation.single_shot_classification.value + +``protocol`` is a `Routine `_ object which contains all the necessary +methods to execute the experiment. + +In order to run a protocol the user needs to specify the parameters. +The user can check which parameters need to be provided either by checking the +documentation of the specific protocol or by simply inspecting ``protocol.parameters_type``. +For ``single_shot_classification`` we can pass just the number of shots +in the following way: + +.. code-block:: python + + parameters = experiment.parameters_type.load(dict(nshots=1024)) + + +After defining the user can perform the acquisition using +``experiment.acquisition`` which accepts the following parameters: + +* params (experiment.parameters_type): inputs parameters for the experiment +* platform (qibolab.platform.Platform): Qibolab platform class +* qubits (dict[QubitId, QubitPairId]) dictionary with qubits where the acquisition will run + +and returns the following: +* data (experiment.data_type): data acquired +* acquisition_time (float): acquisition time on hardware + +.. code-block:: python + + data, acquisition_time = experiment.acquisition(params=parameters, + platform=platform, + qubits=qubits) + + +The user can now use the raw data acquired by the quantum processor to perform +an arbitrary post-processing analysis. This one of the main advatanges of this API +compared to the cli execution. + +The fitting associate with the experiment (``experiment.fit``) can be launched in the +following way: + +.. code-block:: python + + fit, fit_time = experiment.fit(data) + +To be more specific the user should pass as input ``data`` which is of type +``experiment.data_type`` and the outputs are the following: + +* fit: (experiment.results_type) inputs parameters for the experiment +* fit_time (float): post-processing time + + +It is also possible to access the plots and the tables generated in the +report using ``experiment.report`` which accept the following parameters: + +* data: (``experiment.data_type``) data structure used by ``experiment`` +* qubit (Union[QubitId, QubitPairId]): post-processing time +* fit: (``experiment.results_type``): data structure for post-processing used by ``experiment`` + +.. code-block:: python + + # Plot for qubit 0 + qubit = 0 + figs, html_content = experiment.report(data=data, qubit=0, fit=fit) + +``experiment.report`` returns the following: + +* figs: list of plotly figures +* html_content: raw html with additional information usually in the form of a table + +In our case we get the following figure for qubit 0: + +.. code-block:: python + + figs[0] + + +.. image:: classification_plot.png + +and we can render the html content in the following way: + +.. code-block:: python + + import IPython + IPython.display.HTML(html_content) + +.. image:: classification_table.png diff --git a/doc/source/tutorials/classification_plot.png b/doc/source/tutorials/classification_plot.png new file mode 100644 index 0000000000000000000000000000000000000000..8a1cc60cd0e734c6cd977fea2082df84286442c3 GIT binary patch literal 174076 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
rs0Z0MHu+@V&mL63 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 z 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 Yz_<`%OMQNSANB_+ary66q6Pu~1p#!0zyJUM literal 0 HcmV?d00001 diff --git a/doc/source/tutorials/index.rst b/doc/source/tutorials/index.rst index 35332a383..167a4514d 100644 --- a/doc/source/tutorials/index.rst +++ b/doc/source/tutorials/index.rst @@ -8,4 +8,5 @@ In this section we present code examples from basic to advanced features impleme .. toctree:: :maxdepth: 2 + api protocol From 01c9b73b05972fa7a7f26419f187d961ba9d3fbe Mon Sep 17 00:00:00 2001 From: Andrea Pasquale Date: Tue, 24 Oct 2023 17:52:02 +0400 Subject: [PATCH 26/44] Improve grammar Co-authored-by: Gabriele Palazzo <73099233+GabrielePalazzo@users.noreply.github.com> --- doc/source/tutorials/api.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/source/tutorials/api.rst b/doc/source/tutorials/api.rst index 05c442ffc..cb6edec83 100644 --- a/doc/source/tutorials/api.rst +++ b/doc/source/tutorials/api.rst @@ -34,10 +34,10 @@ in the following way: parameters = experiment.parameters_type.load(dict(nshots=1024)) -After defining the user can perform the acquisition using +After defining the parameters, the user can perform the acquisition using ``experiment.acquisition`` which accepts the following parameters: -* params (experiment.parameters_type): inputs parameters for the experiment +* params (experiment.parameters_type): input parameters for the experiment * platform (qibolab.platform.Platform): Qibolab platform class * qubits (dict[QubitId, QubitPairId]) dictionary with qubits where the acquisition will run @@ -53,10 +53,10 @@ and returns the following: The user can now use the raw data acquired by the quantum processor to perform -an arbitrary post-processing analysis. This one of the main advatanges of this API +an arbitrary post-processing analysis. This is one of the main advantages of this API compared to the cli execution. -The fitting associate with the experiment (``experiment.fit``) can be launched in the +The fitting corresponding to the experiment (``experiment.fit``) can be launched in the following way: .. code-block:: python @@ -66,12 +66,12 @@ following way: To be more specific the user should pass as input ``data`` which is of type ``experiment.data_type`` and the outputs are the following: -* fit: (experiment.results_type) inputs parameters for the experiment +* fit: (experiment.results_type) input parameters for the experiment * fit_time (float): post-processing time It is also possible to access the plots and the tables generated in the -report using ``experiment.report`` which accept the following parameters: +report using ``experiment.report`` which accepts the following parameters: * data: (``experiment.data_type``) data structure used by ``experiment`` * qubit (Union[QubitId, QubitPairId]): post-processing time From 36946a94b2732d0ebb5a23d5d3d57e07b077c8a9 Mon Sep 17 00:00:00 2001 From: Andrea Date: Tue, 24 Oct 2023 18:25:11 +0400 Subject: [PATCH 27/44] Reduce overhead by moving skops to relative import --- src/qibocal/fitting/classifier/qubit_fit.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/qibocal/fitting/classifier/qubit_fit.py b/src/qibocal/fitting/classifier/qubit_fit.py index 1525cc6c8..78b436d76 100644 --- a/src/qibocal/fitting/classifier/qubit_fit.py +++ b/src/qibocal/fitting/classifier/qubit_fit.py @@ -4,7 +4,6 @@ import numpy as np import numpy.typing as npt -import skops.io as sio from qibocal.protocols.characterization.utils import cumulative @@ -37,6 +36,9 @@ def hyperopt(_x_train, _y_train, _path): def dump(model, save_path: Path): r"""Dumps the `model` in `save_path`""" + # relative import to reduce overhead when importing qibocal + import skops.io as sio + sio.dump(model, save_path.with_suffix(".skops")) @@ -44,6 +46,9 @@ def predict_from_file(loading_path: Path, input: np.typing.NDArray): r"""This function loads the model saved in `loading_path` and returns the predictions of `input`. """ + # relative import to reduce overhead when importing qibocal + import skops.io as sio + model = sio.load(loading_path, trusted=True) return model.predict(input) From a7dc507ded5806a9bfd6dc215110b8892a520b9a Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Wed, 25 Oct 2023 10:44:34 +0400 Subject: [PATCH 28/44] remove outdated comments --- .../protocols/characterization/rabi/amplitude.py | 12 ++---------- .../characterization/rabi/amplitude_msr.py | 4 ---- src/qibocal/protocols/characterization/rabi/ef.py | 4 ---- .../protocols/characterization/rabi/length.py | 14 ++------------ .../protocols/characterization/rabi/length_msr.py | 5 ----- .../characterization/rabi/length_sequences.py | 4 ---- 6 files changed, 4 insertions(+), 39 deletions(-) diff --git a/src/qibocal/protocols/characterization/rabi/amplitude.py b/src/qibocal/protocols/characterization/rabi/amplitude.py index 644905514..96ceabf9d 100644 --- a/src/qibocal/protocols/characterization/rabi/amplitude.py +++ b/src/qibocal/protocols/characterization/rabi/amplitude.py @@ -37,13 +37,9 @@ class RabiAmplitudeParameters(Parameters): class RabiAmplitudeResults(Results): """RabiAmplitude outputs.""" - amplitude: dict[QubitId, tuple[float, Optional[float]]] = field( - metadata=dict(update="drive_amplitude") - ) + amplitude: dict[QubitId, tuple[float, Optional[float]]] """Drive amplitude for each qubit.""" - length: dict[QubitId, tuple[float, Optional[float]]] = field( - metadata=dict(update="drive_length") - ) + length: dict[QubitId, tuple[float, Optional[float]]] """Drive pulse duration. Same for all qubits.""" fitted_parameters: dict[QubitId, dict[str, float]] """Raw fitted parameters.""" @@ -106,9 +102,6 @@ def _acquisition( type=SweeperType.FACTOR, ) - # create a DataUnits object to store the results, - # DataUnits stores by default MSR, phase, i, q - # additionally include qubit drive pulse amplitude data = RabiAmplitudeData(durations=durations) # sweep the parameter @@ -123,7 +116,6 @@ def _acquisition( sweeper, ) for qubit in qubits: - # average msr, phase, i and q over the number of shots defined in the runcard prob = results[qubit].probability(state=1) data.register_qubit( RabiAmpType, diff --git a/src/qibocal/protocols/characterization/rabi/amplitude_msr.py b/src/qibocal/protocols/characterization/rabi/amplitude_msr.py index ccd56d4fc..cf4a5a1d3 100644 --- a/src/qibocal/protocols/characterization/rabi/amplitude_msr.py +++ b/src/qibocal/protocols/characterization/rabi/amplitude_msr.py @@ -82,9 +82,6 @@ def _acquisition( type=SweeperType.FACTOR, ) - # create a DataUnits object to store the results, - # DataUnits stores by default MSR, phase, i, q - # additionally include qubit drive pulse amplitude data = RabiAmplitudeVoltData(durations=durations) # sweep the parameter @@ -99,7 +96,6 @@ def _acquisition( sweeper, ) for qubit in qubits: - # average msr, phase, i and q over the number of shots defined in the runcard result = results[ro_pulses[qubit].serial] data.register_qubit( RabiAmpVoltType, diff --git a/src/qibocal/protocols/characterization/rabi/ef.py b/src/qibocal/protocols/characterization/rabi/ef.py index ade1f1170..ac632be4a 100644 --- a/src/qibocal/protocols/characterization/rabi/ef.py +++ b/src/qibocal/protocols/characterization/rabi/ef.py @@ -76,9 +76,6 @@ def _acquisition( type=SweeperType.FACTOR, ) - # create a DataUnits object to store the results, - # DataUnits stores by default MSR, phase, i, q - # additionally include qubit drive pulse amplitude data = RabiAmplitudeEFData(durations=durations) # sweep the parameter @@ -93,7 +90,6 @@ def _acquisition( sweeper, ) for qubit in qubits: - # average msr, phase, i and q over the number of shots defined in the runcard result = results[ro_pulses[qubit].serial] data.register_qubit( amplitude_msr.RabiAmpVoltType, diff --git a/src/qibocal/protocols/characterization/rabi/length.py b/src/qibocal/protocols/characterization/rabi/length.py index caf8a9da1..aea1138f8 100644 --- a/src/qibocal/protocols/characterization/rabi/length.py +++ b/src/qibocal/protocols/characterization/rabi/length.py @@ -37,13 +37,9 @@ class RabiLengthParameters(Parameters): class RabiLengthResults(Results): """RabiLength outputs.""" - length: dict[QubitId, tuple[int, Optional[float]]] = field( - metadata=dict(update="drive_length") - ) + length: dict[QubitId, tuple[int, Optional[float]]] """Pi pulse duration for each qubit.""" - amplitude: dict[QubitId, tuple[float, Optional[float]]] = field( - metadata=dict(update="drive_amplitude") - ) + amplitude: dict[QubitId, tuple[float, Optional[float]]] """Pi pulse amplitude. Same for all qubits.""" fitted_parameters: dict[QubitId, dict[str, float]] """Raw fitting output.""" @@ -110,9 +106,6 @@ def _acquisition( type=SweeperType.ABSOLUTE, ) - # create a DataUnits object to store the results, - # DataUnits stores by default MSR, phase, i, q - # additionally include qubit drive pulse length data = RabiLengthData(amplitudes=amplitudes) # execute the sweep @@ -128,7 +121,6 @@ def _acquisition( ) for qubit in qubits: - # average prob, phase, i and q over the number of shots defined in the runcard prob = results[qubit].probability(state=1) data.register_qubit( RabiLenType, @@ -163,7 +155,6 @@ def _fit(data: RabiLengthData) -> RabiLengthResults: # 0.5 hardcoded guess for less than one oscillation f = x[index] / (x[1] - x[0]) if index is not None else 0.5 pguess = [0.5, 0.5, np.max(x) / f, np.pi / 2, 0] - print(pguess) try: popt, perr = curve_fit( utils.rabi_length_fit, @@ -183,7 +174,6 @@ def _fit(data: RabiLengthData) -> RabiLengthResults: log.warning("rabi_fit: the fitting was not succesful") pi_pulse_parameter = 0 popt = [0] * 4 + [1] - print(popt) durations[qubit] = (pi_pulse_parameter, perr[2] / 2) fitted_parameters[qubit] = popt.tolist() amplitudes = {key: (value, 0) for key, value in data.amplitudes.items()} diff --git a/src/qibocal/protocols/characterization/rabi/length_msr.py b/src/qibocal/protocols/characterization/rabi/length_msr.py index fa6f7b47c..12f7799f9 100644 --- a/src/qibocal/protocols/characterization/rabi/length_msr.py +++ b/src/qibocal/protocols/characterization/rabi/length_msr.py @@ -86,9 +86,6 @@ def _acquisition( type=SweeperType.ABSOLUTE, ) - # create a DataUnits object to store the results, - # DataUnits stores by default MSR, phase, i, q - # additionally include qubit drive pulse length data = RabiLengthVoltData(amplitudes=amplitudes) # execute the sweep @@ -104,7 +101,6 @@ def _acquisition( ) for qubit in qubits: - # average msr, phase, i and q over the number of shots defined in the runcard result = results[ro_pulses[qubit].serial] data.register_qubit( RabiLenVoltType, @@ -178,7 +174,6 @@ def _fit(data: RabiLengthVoltData) -> RabiLengthVoltResults: def _update(results: RabiLengthVoltResults, platform: Platform, qubit: QubitId): - print(results.length[qubit]) update.drive_duration(results.length[qubit], platform, qubit) diff --git a/src/qibocal/protocols/characterization/rabi/length_sequences.py b/src/qibocal/protocols/characterization/rabi/length_sequences.py index da58d287b..b10399834 100644 --- a/src/qibocal/protocols/characterization/rabi/length_sequences.py +++ b/src/qibocal/protocols/characterization/rabi/length_sequences.py @@ -50,9 +50,6 @@ def _acquisition( params.pulse_duration_step, ) - # create a DataUnits object to store the results, - # DataUnits stores by default MSR, phase, i, q - # additionally include qubit drive pulse length data = RabiLengthVoltData(amplitudes=amplitudes) # sweep the parameter @@ -73,7 +70,6 @@ def _acquisition( ) for qubit in qubits: - # average msr, phase, i and q over the number of shots defined in the runcard result = results[ro_pulses[qubit].serial] data.register_qubit( RabiLenVoltType, From de29aace79f8b97ddbaf3bc5b75d6bf4a4259139 Mon Sep 17 00:00:00 2001 From: Edoardo Pedicillo Date: Wed, 25 Oct 2023 10:45:41 +0400 Subject: [PATCH 29/44] Update src/qibocal/protocols/characterization/rabi/utils.py Co-authored-by: Andrea Pasquale --- src/qibocal/protocols/characterization/rabi/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibocal/protocols/characterization/rabi/utils.py b/src/qibocal/protocols/characterization/rabi/utils.py index 4864baa3b..0efda1405 100644 --- a/src/qibocal/protocols/characterization/rabi/utils.py +++ b/src/qibocal/protocols/characterization/rabi/utils.py @@ -118,7 +118,7 @@ def plot(data, qubit, fit): return figures, fitting_report -def plot_proba(data, qubit, fit): +def plot_probabilities(data, qubit, fit): if data.__class__.__name__ == "RabiAmplitudeData": quantity = "amp" title = "Amplitude (dimensionless)" From 2731d4836317e891f4f396e820fd45f843acee22 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Wed, 25 Oct 2023 10:48:33 +0400 Subject: [PATCH 30/44] propagate plot_probabilities --- src/qibocal/protocols/characterization/rabi/amplitude.py | 2 +- src/qibocal/protocols/characterization/rabi/length.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibocal/protocols/characterization/rabi/amplitude.py b/src/qibocal/protocols/characterization/rabi/amplitude.py index 96ceabf9d..15a7ebac6 100644 --- a/src/qibocal/protocols/characterization/rabi/amplitude.py +++ b/src/qibocal/protocols/characterization/rabi/amplitude.py @@ -189,7 +189,7 @@ def _fit(data: RabiAmplitudeData) -> RabiAmplitudeResults: def _plot(data: RabiAmplitudeData, qubit, fit: RabiAmplitudeResults = None): """Plotting function for RabiAmplitude.""" - return utils.plot_proba(data, qubit, fit) + return utils.plot_probabilities(data, qubit, fit) def _update(results: RabiAmplitudeResults, platform: Platform, qubit: QubitId): diff --git a/src/qibocal/protocols/characterization/rabi/length.py b/src/qibocal/protocols/characterization/rabi/length.py index aea1138f8..83bb332be 100644 --- a/src/qibocal/protocols/characterization/rabi/length.py +++ b/src/qibocal/protocols/characterization/rabi/length.py @@ -194,7 +194,7 @@ def _update(results: RabiLengthResults, platform: Platform, qubit: QubitId): def _plot(data: RabiLengthData, fit: RabiLengthResults, qubit): """Plotting function for RabiLength experiment.""" - return utils.plot_proba(data, qubit, fit) + return utils.plot_probabilities(data, qubit, fit) rabi_length = Routine(_acquisition, _fit, _plot, _update) From b3040cf9bd730ae89319e4b3c24f3468a6838ec5 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Wed, 25 Oct 2023 11:33:37 +0400 Subject: [PATCH 31/44] remove outdated comments, delete hpars, update docs --- .../characterization/classification.py | 2 +- .../characterization/qutrit_classification.py | 21 +++++-------------- src/qibocal/update.py | 10 --------- 3 files changed, 6 insertions(+), 27 deletions(-) diff --git a/src/qibocal/protocols/characterization/classification.py b/src/qibocal/protocols/characterization/classification.py index c5e9fab2e..417f8710f 100644 --- a/src/qibocal/protocols/characterization/classification.py +++ b/src/qibocal/protocols/characterization/classification.py @@ -379,9 +379,9 @@ def _update( update.threshold(results.threshold[qubit], platform, qubit) update.mean_gnd_states(results.mean_gnd_states[qubit], platform, qubit) update.mean_exc_states(results.mean_exc_states[qubit], platform, qubit) - update.classifiers_hpars(results.classifiers_hpars[qubit], platform, qubit) update.readout_fidelity(results.fidelity[qubit], platform, qubit) update.assignment_fidelity(results.assignment_fidelity[qubit], platform, qubit) single_shot_classification = Routine(_acquisition, _fit, _plot, _update) +"""Qubit classification routine object.""" diff --git a/src/qibocal/protocols/characterization/qutrit_classification.py b/src/qibocal/protocols/characterization/qutrit_classification.py index 073fe16e4..486690133 100644 --- a/src/qibocal/protocols/characterization/qutrit_classification.py +++ b/src/qibocal/protocols/characterization/qutrit_classification.py @@ -5,9 +5,7 @@ from qibolab import AcquisitionType, ExecutionParameters from qibolab.platform import Platform from qibolab.pulses import PulseSequence -from qibolab.qubits import QubitId -from qibocal import update from qibocal.auto.operation import Qubits, Routine from qibocal.fitting.classifier import run from qibocal.protocols.characterization.classification import ( @@ -53,6 +51,9 @@ def _acquisition( qubits: Qubits, ) -> QutritClassificationData: """ + This Routine prepares the qubits in 0,1 and 2 states and measures their + respective I, Q values. + Args: nshots (int): number of times the pulse sequence will be repeated. classifiers (list): list of classifiers, the available ones are: @@ -66,10 +67,6 @@ def _acquisition( relaxation_time (float): Relaxation time. """ - # create two sequences of pulses: - # state0_sequence: I - MZ - # state1_sequence: RX - MZ - # taking advantage of multiplexing, apply the same set of gates to all qubits in parallel states_sequences = [PulseSequence() for _ in range(3)] ro_pulses = {} @@ -88,7 +85,6 @@ def _acquisition( ) sequence.add(ro_pulses[qubit][-1]) - # create a DataUnits object to store the results data = QutritClassificationData( nshots=params.nshots, classifiers_list=params.classifiers_list, @@ -178,12 +174,5 @@ def _plot(data: QutritClassificationData, qubit, fit: SingleShotClassificationRe return figures, fitting_report -def _update( - results: SingleShotClassificationResults, platform: Platform, qubit: QubitId -): - update.qutrit_classifiers_hpars( - results.classifiers_hpars[qubit], platform, qubit - ) # TODO: implement a qutrit classifiers hpars (?) - - -qutrit_classification = Routine(_acquisition, _fit, _plot, _update) +qutrit_classification = Routine(_acquisition, _fit, _plot) +"""Qutrit classification Routine object.""" diff --git a/src/qibocal/update.py b/src/qibocal/update.py index f5a286911..aa8d1c595 100644 --- a/src/qibocal/update.py +++ b/src/qibocal/update.py @@ -93,16 +93,6 @@ def assignment_fidelity(fidelity: float, platform: Platform, qubit: QubitId): platform.qubits[qubit].assignment_fidelity = float(fidelity) -def classifiers_hpars(hpars: list, platform: Platform, qubit: QubitId): - """Update classifier hyperparameters in platform for specific qubit.""" - platform.qubits[qubit].classifiers_hpars = hpars - - -def qutrit_classifiers_hpars(hpars: list, platform: Platform, qubit: QubitId): - """Update qutrit classifier hyperparameters in platform for specific qubit.""" - platform.qubits[qubit].qutrit_classifiers_hpars = hpars - - def virtual_phases(phases: dict[QubitId, float], platform: Platform, pair: QubitPairId): """Update virtual phases for given qubits in pair in results.""" virtual_z_pulses = { From 6347116c4225d6a4bf9bc5bf3fb87c2b032ba210 Mon Sep 17 00:00:00 2001 From: Edoardo Pedicillo Date: Wed, 25 Oct 2023 11:34:03 +0400 Subject: [PATCH 32/44] Update src/qibocal/protocols/characterization/qutrit_classification.py Co-authored-by: Andrea Pasquale --- src/qibocal/protocols/characterization/qutrit_classification.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibocal/protocols/characterization/qutrit_classification.py b/src/qibocal/protocols/characterization/qutrit_classification.py index 486690133..67f426387 100644 --- a/src/qibocal/protocols/characterization/qutrit_classification.py +++ b/src/qibocal/protocols/characterization/qutrit_classification.py @@ -170,7 +170,7 @@ def _fit(data: QutritClassificationData) -> SingleShotClassificationResults: def _plot(data: QutritClassificationData, qubit, fit: SingleShotClassificationResults): figures = plot_results(data, qubit, 3, fit) - fitting_report = None + fitting_report = "" return figures, fitting_report From 4ece52516f2ee6024bf68d7d2e7732f571530a47 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Wed, 25 Oct 2023 11:49:13 +0400 Subject: [PATCH 33/44] fix tests --- tests/test_update.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/test_update.py b/tests/test_update.py index d3701045d..591175e38 100644 --- a/tests/test_update.py +++ b/tests/test_update.py @@ -71,13 +71,11 @@ def test_classification_update(qubit): # generate random lists mean_gnd_state = generate_update_list(2) mean_exc_state = generate_update_list(2) - classifiers_hpars = generate_update_list(4) # perform update update.iq_angle(RANDOM_FLOAT, PLATFORM, qubit.name) update.threshold(RANDOM_FLOAT, PLATFORM, qubit.name) update.mean_gnd_states(mean_gnd_state, PLATFORM, qubit.name) update.mean_exc_states(mean_exc_state, PLATFORM, qubit.name) - update.classifiers_hpars(classifiers_hpars, PLATFORM, qubit.name) update.readout_fidelity(RANDOM_FLOAT, PLATFORM, qubit.name) update.assignment_fidelity(RANDOM_FLOAT, PLATFORM, qubit.name) @@ -86,7 +84,6 @@ def test_classification_update(qubit): assert qubit.threshold == RANDOM_FLOAT assert qubit.mean_gnd_states == mean_gnd_state assert qubit.mean_exc_states == mean_exc_state - assert qubit.classifiers_hpars == classifiers_hpars assert qubit.readout_fidelity == RANDOM_FLOAT assert qubit.assignment_fidelity == RANDOM_FLOAT From b76923d45205c51bdc429b57e736e053357bc525 Mon Sep 17 00:00:00 2001 From: Andrea Date: Wed, 25 Oct 2023 13:02:41 +0400 Subject: [PATCH 34/44] Improve coverage --- src/qibocal/fitting/classifier/run.py | 4 ++-- tests/test_auto.py | 5 ++--- tests/test_classifiers.py | 16 ++++++++++++++++ tests/test_protocols.py | 24 ++++++++++-------------- tests/test_task_options.py | 6 ++---- 5 files changed, 32 insertions(+), 23 deletions(-) diff --git a/src/qibocal/fitting/classifier/run.py b/src/qibocal/fitting/classifier/run.py index c309b83b7..32796157d 100644 --- a/src/qibocal/fitting/classifier/run.py +++ b/src/qibocal/fitting/classifier/run.py @@ -63,10 +63,10 @@ def pretty_name(classifier_name: str): class Classifier: - r"""Classs to define the different classifiers used in the benchmarking. + r"""Class to define the different classifiers used in the benchmarking. Args: - mod: Classsification model. + mod: Classification model. base_dir (Path): Where to store the classification results. """ diff --git a/tests/test_auto.py b/tests/test_auto.py index a04cb121e..6ace2b6da 100644 --- a/tests/test_auto.py +++ b/tests/test_auto.py @@ -1,6 +1,5 @@ """Test graph execution.""" import pathlib -import tempfile import pytest import yaml @@ -29,7 +28,7 @@ class TestCard: @pytest.mark.parametrize("card", cards.glob("*.yaml")) -def test_execution(card: pathlib.Path): +def test_execution(card: pathlib.Path, tmp_path): """Execute a set of example runcards. The declared result is asserted to be the expected one. @@ -38,7 +37,7 @@ def test_execution(card: pathlib.Path): testcard = TestCard(**yaml.safe_load(card.read_text(encoding="utf-8"))) executor = Executor.load( testcard.runcard, - output=pathlib.Path(tempfile.mkdtemp()), + output=tmp_path, qubits=testcard.runcard.qubits, ) list(executor.run(mode=ExecutionMode.acquire)) diff --git a/tests/test_classifiers.py b/tests/test_classifiers.py index 3f4c4a5d2..403654606 100644 --- a/tests/test_classifiers.py +++ b/tests/test_classifiers.py @@ -1,5 +1,10 @@ +import numpy as np + from qibocal.fitting.classifier import run +MODEL_FILE = "model.skops" +"""Filename for storing the model.""" + def test_load_model(tmp_path): classifier = run.Classifier(run.import_classifiers(["qubit_fit"])[0], tmp_path) @@ -7,3 +12,14 @@ def test_load_model(tmp_path): classifier.dump_hyper(tmp_path) new_classifier = run.Classifier.model_from_dir(tmp_path / "qubit_fit") assert new_classifier == classifier.trainable_model + + +def test_predict_from_file(tmp_path): + """Testing predict_from_file method.""" + classifier = run.Classifier(run.import_classifiers(["qubit_fit"])[0], tmp_path) + model = classifier.create_model({"par1": 1}) + iqs = np.random.rand(10, 2) + classifier.mod.dump(model, classifier.base_dir / MODEL_FILE) + target_predictions = model.predict(iqs) + predictions = classifier.mod.predict_from_file(tmp_path / MODEL_FILE, iqs) + assert np.array_equal(target_predictions, predictions) diff --git a/tests/test_protocols.py b/tests/test_protocols.py index b1bee8a77..beb73fe76 100644 --- a/tests/test_protocols.py +++ b/tests/test_protocols.py @@ -1,6 +1,5 @@ """Test routines' acquisition method using dummy platform""" import pathlib -import tempfile import pytest import yaml @@ -29,28 +28,25 @@ def idfn(val): @pytest.mark.parametrize("update", [True, False]) @pytest.mark.parametrize("runcard", generate_runcard_single_protocol(), ids=idfn) -def test_action_builder(runcard, update): +def test_action_builder(runcard, update, tmp_path): """Test ActionBuilder for all protocols.""" - path = pathlib.Path(tempfile.mkdtemp()) - autocalibrate(runcard, path, force=True, update=update) - report(path) + autocalibrate(runcard, tmp_path, force=True, update=update) + report(tmp_path) @pytest.mark.parametrize("runcard", generate_runcard_single_protocol(), ids=idfn) -def test_acquisition_builder(runcard): +def test_acquisition_builder(runcard, tmp_path): """Test AcquisitionBuilder for all protocols.""" - path = pathlib.Path(tempfile.mkdtemp()) - acquire(runcard, path, force=True) - report(path) + acquire(runcard, tmp_path, force=True) + report(tmp_path) @pytest.mark.parametrize("runcard", generate_runcard_single_protocol(), ids=idfn) -def test_fit_builder(runcard): +def test_fit_builder(runcard, tmp_path): """Test FitBuilder.""" - output_folder = pathlib.Path(tempfile.mkdtemp()) - acquire(runcard, output_folder, force=True) - fit(output_folder, update=False) - report(output_folder) + acquire(runcard, tmp_path, force=True) + fit(tmp_path, update=False) + report(tmp_path) # TODO: compare report by calling qq report diff --git a/tests/test_task_options.py b/tests/test_task_options.py index 37dd26f71..b8e88d647 100644 --- a/tests/test_task_options.py +++ b/tests/test_task_options.py @@ -1,6 +1,4 @@ """Test routines' acquisition method using dummy platform""" -import pathlib -import tempfile from copy import deepcopy import pytest @@ -93,7 +91,7 @@ def test_qubits_argument(platform, local_qubits): @pytest.mark.parametrize("global_update", [True, False]) @pytest.mark.parametrize("local_update", [True, False]) -def test_update_argument(global_update, local_update): +def test_update_argument(global_update, local_update, tmp_path): """Test possible update combinations between global and local.""" platform = deepcopy(create_platform("dummy")) old_readout_frequency = platform.qubits[0].readout_frequency @@ -101,7 +99,7 @@ def test_update_argument(global_update, local_update): NEW_CARD = modify_card(deepcopy(UPDATE_CARD), update=local_update) executor = Executor.load( Runcard.load(NEW_CARD), - pathlib.Path(tempfile.mkdtemp()), + tmp_path, platform, platform.qubits, global_update, From 1d8318a271885a536c530e20d705adbdc79e526d Mon Sep 17 00:00:00 2001 From: Andrea Date: Wed, 25 Oct 2023 13:19:16 +0400 Subject: [PATCH 35/44] Add links in tutorial --- doc/source/tutorials/api.rst | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/doc/source/tutorials/api.rst b/doc/source/tutorials/api.rst index cb6edec83..ac750dfa9 100644 --- a/doc/source/tutorials/api.rst +++ b/doc/source/tutorials/api.rst @@ -37,12 +37,13 @@ in the following way: After defining the parameters, the user can perform the acquisition using ``experiment.acquisition`` which accepts the following parameters: -* params (experiment.parameters_type): input parameters for the experiment -* platform (qibolab.platform.Platform): Qibolab platform class -* qubits (dict[QubitId, QubitPairId]) dictionary with qubits where the acquisition will run +* params (`experiment.parameters_type `_): input parameters for the experiment +* platform (`qibolab.platform.Platform `_): Qibolab platform class +* qubits (dict[`QubitId `_, `QubitPairId `_]) dictionary with qubits where the acquisition will run and returns the following: -* data (experiment.data_type): data acquired + +* data (`experiment.data_type `_): data acquired * acquisition_time (float): acquisition time on hardware .. code-block:: python @@ -66,16 +67,16 @@ following way: To be more specific the user should pass as input ``data`` which is of type ``experiment.data_type`` and the outputs are the following: -* fit: (experiment.results_type) input parameters for the experiment +* fit: (`experiment.results_type `_) input parameters for the experiment * fit_time (float): post-processing time It is also possible to access the plots and the tables generated in the report using ``experiment.report`` which accepts the following parameters: -* data: (``experiment.data_type``) data structure used by ``experiment`` -* qubit (Union[QubitId, QubitPairId]): post-processing time -* fit: (``experiment.results_type``): data structure for post-processing used by ``experiment`` +* data: (`experiment.data_type `_) data structure used by ``experiment`` +* qubit (dict[`QubitId `_, `QubitPairId `_]): qubit / qubit pair to be plotted +* fit: (`experiment.results_type `_): data structure for post-processing used by ``experiment`` .. code-block:: python From 40e0d8d0920e665d994324626bbff5fdf057b56b Mon Sep 17 00:00:00 2001 From: Stefano Carrazza Date: Wed, 25 Oct 2023 14:38:34 +0200 Subject: [PATCH 36/44] fixing ordering table qq upload --- serverscripts/web/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serverscripts/web/index.html b/serverscripts/web/index.html index 316835e15..5193423b4 100644 --- a/serverscripts/web/index.html +++ b/serverscripts/web/index.html @@ -21,7 +21,7 @@ var data = json['data']; var global_table = $('#global').DataTable({ "data": data, - "order": [[2, "desc"]], + "order": [[1, "desc"]], "dom": "frtipl" }); }); From 8e03a7bab336d025fbca3dfcaa9f8e5f4dd69456 Mon Sep 17 00:00:00 2001 From: Stefano Carrazza Date: Wed, 25 Oct 2023 15:16:49 +0200 Subject: [PATCH 37/44] adding author flag to upload --- serverscripts/qibocal-index-reports.py | 16 +++++++++++++--- src/qibocal/cli/_base.py | 12 ++++++++++-- src/qibocal/cli/upload.py | 7 ++++--- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/serverscripts/qibocal-index-reports.py b/serverscripts/qibocal-index-reports.py index 8f5064080..3f6eb8989 100644 --- a/serverscripts/qibocal-index-reports.py +++ b/serverscripts/qibocal-index-reports.py @@ -16,8 +16,17 @@ "start-time": "-", "end-time": "-", "tag": "-", + "author": "-", +} +REQUIRED_FILE_METADATA = { + "title", + "date", + "platform", + "start-time", + "end-time", + "tag", + "author", } -REQUIRED_FILE_METADATA = {"title", "date", "platform", "start-time" "end-time", "tag"} def meta_from_path(p): @@ -36,17 +45,18 @@ def meta_from_path(p): def register(p): path_meta = meta_from_path(p) - title, date, platform, start_time, end_time, tag = ( + title, date, platform, start_time, end_time, tag, author = ( path_meta["title"], path_meta["date"], path_meta["platform"], path_meta["start-time"], path_meta["end-time"], path_meta["tag"], + path_meta["author"], ) url = ROOT_URL + p.name titlelink = f'{title}' - return (titlelink, date, platform, start_time, end_time, tag) + return (titlelink, date, platform, start_time, end_time, tag, author) def make_index(): diff --git a/src/qibocal/cli/_base.py b/src/qibocal/cli/_base.py index 49fb67693..637c543b7 100644 --- a/src/qibocal/cli/_base.py +++ b/src/qibocal/cli/_base.py @@ -1,5 +1,7 @@ """Adds global CLI options.""" +import os import pathlib +import pwd import click import yaml @@ -126,11 +128,17 @@ def fit(folder: pathlib.Path, update): type=str, help="Optional tag.", ) -def upload(path, tag): +@click.option( + "--author", + default=pwd.getpwuid(os.getuid())[0], + type=str, + help="Default is UID username.", +) +def upload(path, tag, author): """Uploads output folder to server Arguments: - FOLDER: input folder. """ - upload_report(path, tag) + upload_report(path, tag, author) diff --git a/src/qibocal/cli/upload.py b/src/qibocal/cli/upload.py index 0d53ed6db..d0bb3f4de 100644 --- a/src/qibocal/cli/upload.py +++ b/src/qibocal/cli/upload.py @@ -23,12 +23,13 @@ ROOT_URL = "http://login.qrccluster.com:9000/" -def upload_report(path: pathlib.Path, tag: str): +def upload_report(path: pathlib.Path, tag: str, author: str): # load meta and update tag + meta = yaml.safe_load((path / META).read_text()) + meta["author"] = author if tag is not None: - meta = yaml.safe_load((path / META).read_text()) meta["tag"] = tag - (path / META).write_text(json.dumps(meta, indent=4)) + (path / META).write_text(json.dumps(meta, indent=4)) # check the rsync command exists. if not shutil.which("rsync"): From 203d8a08b806726e9894dbda7886b3d0c30f3511 Mon Sep 17 00:00:00 2001 From: Stefano Carrazza Date: Wed, 25 Oct 2023 15:40:12 +0200 Subject: [PATCH 38/44] replacing pwd with getpass --- src/qibocal/cli/_base.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/qibocal/cli/_base.py b/src/qibocal/cli/_base.py index 637c543b7..bad9c2e32 100644 --- a/src/qibocal/cli/_base.py +++ b/src/qibocal/cli/_base.py @@ -1,7 +1,6 @@ """Adds global CLI options.""" -import os +import getpass import pathlib -import pwd import click import yaml @@ -130,7 +129,7 @@ def fit(folder: pathlib.Path, update): ) @click.option( "--author", - default=pwd.getpwuid(os.getuid())[0], + default=getpass.getuser(), type=str, help="Default is UID username.", ) From 9f28412afa1a73629c18e3a9ca91f61986d1c0c5 Mon Sep 17 00:00:00 2001 From: andrea-pasquale Date: Thu, 26 Oct 2023 11:22:26 +0400 Subject: [PATCH 39/44] Push fix for phase parameter --- src/qibocal/protocols/characterization/rabi/amplitude_msr.py | 2 +- src/qibocal/protocols/characterization/rabi/length_msr.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibocal/protocols/characterization/rabi/amplitude_msr.py b/src/qibocal/protocols/characterization/rabi/amplitude_msr.py index cf4a5a1d3..ce0b1ca9c 100644 --- a/src/qibocal/protocols/characterization/rabi/amplitude_msr.py +++ b/src/qibocal/protocols/characterization/rabi/amplitude_msr.py @@ -153,7 +153,7 @@ def _fit(data: RabiAmplitudeVoltData) -> RabiAmplitudeVoltResults: y_min + (y_max - y_min) * popt[0], (y_max - y_min) * popt[1], popt[2] * (x_max - x_min), - popt[3] - 2 * np.pi * x_min / (x_max - x_min) * popt[2], + popt[3] - 2 * np.pi * x_min / (x_max - x_min) / popt[2], ] pi_pulse_parameter = np.abs((translated_popt[2]) / 2) diff --git a/src/qibocal/protocols/characterization/rabi/length_msr.py b/src/qibocal/protocols/characterization/rabi/length_msr.py index 12f7799f9..72b30c6ee 100644 --- a/src/qibocal/protocols/characterization/rabi/length_msr.py +++ b/src/qibocal/protocols/characterization/rabi/length_msr.py @@ -158,7 +158,7 @@ def _fit(data: RabiLengthVoltData) -> RabiLengthVoltResults: (y_max - y_min) * popt[0] + y_min, (y_max - y_min) * popt[1] * np.exp(x_min * popt[4] / (x_max - x_min)), popt[2] * (x_max - x_min), - popt[3] - 2 * np.pi * x_min * popt[2] / (x_max - x_min), + popt[3] - 2 * np.pi * x_min / popt[2] / (x_max - x_min), popt[4] / (x_max - x_min), ] pi_pulse_parameter = np.abs(translated_popt[2] / 2) From 75161ef8f8ec0d79e0d5c676deb6d7b80b66bde0 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Thu, 26 Oct 2023 11:49:11 +0400 Subject: [PATCH 40/44] fix hpars bug --- src/qibocal/fitting/classifier/run.py | 12 ++-- .../characterization/classification.py | 56 +++++++++---------- .../characterization/qutrit_classification.py | 8 +-- .../protocols/characterization/utils.py | 11 +++- 4 files changed, 48 insertions(+), 39 deletions(-) diff --git a/src/qibocal/fitting/classifier/run.py b/src/qibocal/fitting/classifier/run.py index 154884212..3d2e49c67 100644 --- a/src/qibocal/fitting/classifier/run.py +++ b/src/qibocal/fitting/classifier/run.py @@ -251,12 +251,12 @@ def train_qubit( classifier = Classifier(mod, qubit_dir) classifier.savedir.mkdir(exist_ok=True) logging.info(f"Training quibt with classifier: {pretty_name(classifier.name)}") - if classifier.name not in cls_data.classifiers_hpars[qubit]: - hyperpars = classifier.hyperopt( - x_train, y_train.astype(np.int64), classifier.savedir - ) - else: - hyperpars = cls_data.classifiers_hpars[qubit][classifier.name] + # if classifier.name not in cls_data.classifiers_hpars[qubit]: + hyperpars = classifier.hyperopt( + x_train, y_train.astype(np.int64), classifier.savedir + ) + # else: + # hyperpars = cls_data.classifiers_hpars[qubit][classifier.name] hpars_list.append(hyperpars) classifier.dump_hyper(hyperpars) model = classifier.create_model(hyperpars) diff --git a/src/qibocal/protocols/characterization/classification.py b/src/qibocal/protocols/characterization/classification.py index c5e9fab2e..28559fead 100644 --- a/src/qibocal/protocols/characterization/classification.py +++ b/src/qibocal/protocols/characterization/classification.py @@ -60,8 +60,8 @@ class SingleShotClassificationParameters(Parameters): class SingleShotClassificationData(Data): nshots: int """Number of shots.""" - classifiers_hpars: dict[QubitId, dict] - """Models' hyperparameters""" + # classifiers_hpars: dict[QubitId, dict] + # """Models' hyperparameters""" savedir: str """Dumping folder of the classification results""" data: dict[QubitId, npt.NDArray] = field(default_factory=dict) @@ -173,7 +173,7 @@ def _acquisition( RX_pulses = {} ro_pulses = {} - hpars = {} + # hpars = {} for qubit in qubits: RX_pulses[qubit] = platform.create_RX_pulse(qubit, start=0) ro_pulses[qubit] = platform.create_qubit_readout_pulse( @@ -183,12 +183,12 @@ def _acquisition( state0_sequence.add(ro_pulses[qubit]) state1_sequence.add(RX_pulses[qubit]) state1_sequence.add(ro_pulses[qubit]) - hpars[qubit] = qubits[qubit].classifiers_hpars + # hpars[qubit] = qubits[qubit].classifiers_hpars # create a DataUnits object to store the results data = SingleShotClassificationData( nshots=params.nshots, classifiers_list=params.classifiers_list, - classifiers_hpars=hpars, + # classifiers_hpars=hpars, savedir=params.savedir, ) @@ -303,7 +303,7 @@ def _fit(data: SingleShotClassificationData) -> SingleShotClassificationResults: def _plot( data: SingleShotClassificationData, qubit, fit: SingleShotClassificationResults ): - fitting_report = None + fitting_report = "" models_name = data.classifiers_list figures = plot_results(data, qubit, 2, fit) if fit is not None: @@ -346,28 +346,28 @@ def _plot( ) figures.append(fig_roc) - if models_name[i] == "qubit_fit": - fitting_report = table_html( - table_dict( - qubit, - [ - "Average State 0", - "Average State 1", - "Rotational Angle", - "Threshold", - "Readout Fidelity", - "Assignment Fidelity", - ], - [ - np.round(fit.mean_gnd_states[qubit], 3), - np.round(fit.mean_exc_states[qubit], 3), - np.round(fit.rotation_angle[qubit], 3), - np.round(fit.threshold[qubit], 6), - np.round(fit.fidelity[qubit], 3), - np.round(fit.assignment_fidelity[qubit], 3), - ], - ) + if "qubit_fit" in models_name: + fitting_report = table_html( + table_dict( + qubit, + [ + "Average State 0", + "Average State 1", + "Rotational Angle", + "Threshold", + "Readout Fidelity", + "Assignment Fidelity", + ], + [ + np.round(fit.mean_gnd_states[qubit], 3), + np.round(fit.mean_exc_states[qubit], 3), + np.round(fit.rotation_angle[qubit], 3), + np.round(fit.threshold[qubit], 6), + np.round(fit.fidelity[qubit], 3), + np.round(fit.assignment_fidelity[qubit], 3), + ], ) + ) return figures, fitting_report @@ -379,7 +379,7 @@ def _update( update.threshold(results.threshold[qubit], platform, qubit) update.mean_gnd_states(results.mean_gnd_states[qubit], platform, qubit) update.mean_exc_states(results.mean_exc_states[qubit], platform, qubit) - update.classifiers_hpars(results.classifiers_hpars[qubit], platform, qubit) + # update.classifiers_hpars(results.classifiers_hpars[qubit], platform, qubit) update.readout_fidelity(results.fidelity[qubit], platform, qubit) update.assignment_fidelity(results.assignment_fidelity[qubit], platform, qubit) diff --git a/src/qibocal/protocols/characterization/qutrit_classification.py b/src/qibocal/protocols/characterization/qutrit_classification.py index ea7e9919e..c3f3e7ea8 100644 --- a/src/qibocal/protocols/characterization/qutrit_classification.py +++ b/src/qibocal/protocols/characterization/qutrit_classification.py @@ -90,12 +90,12 @@ def _acquisition( # taking advantage of multiplexing, apply the same set of gates to all qubits in parallel states_sequences = [PulseSequence() for _ in range(3)] ro_pulses = {} - hpars = {} + # hpars = {} for qubit in qubits: rx_pulse = platform.create_RX_pulse(qubit, start=0) rx12_pulse = platform.create_RX12_pulse(qubit, start=rx_pulse.finish) drive_pulses = [rx_pulse, rx12_pulse] - hpars[qubit] = qubits[qubit].classifiers_hpars + # hpars[qubit] = qubits[qubit].classifiers_hpars ro_pulses[qubit] = [] for i, sequence in enumerate(states_sequences): sequence.add(*drive_pulses[:i]) @@ -109,7 +109,7 @@ def _acquisition( data = QutritClassificationData( nshots=params.nshots, classifiers_list=params.classifiers_list, - classifiers_hpars=hpars, + # classifiers_hpars=hpars, savedir=params.savedir, ) states_results = [] @@ -191,7 +191,7 @@ def _fit(data: QutritClassificationData) -> SingleShotClassificationResults: def _plot(data: QutritClassificationData, qubit, fit: SingleShotClassificationResults): figures = plot_results(data, qubit, 3, fit) - fitting_report = None + fitting_report = "" return figures, fitting_report diff --git a/src/qibocal/protocols/characterization/utils.py b/src/qibocal/protocols/characterization/utils.py index c6fadefb1..9889c88e7 100644 --- a/src/qibocal/protocols/characterization/utils.py +++ b/src/qibocal/protocols/characterization/utils.py @@ -424,7 +424,16 @@ def evaluate_grid( return np.vstack([i_values.ravel(), q_values.ravel()]).T -def plot_results(data: Data, qubit, qubit_states, fit: Results): +def plot_results(data: Data, qubit: QubitId, qubit_states: list, fit: Results): + """ + Plots for the qubit and qutrit classification. + + Args: + data (Data): acquisition data + qubit (QubitID): qubit + qubit_states (list): list of qubit states available. + fit (Results): fit results + """ figures = [] models_name = data.classifiers_list qubit_data = data.data[qubit] From a1885dbd662770de214ff84e58586bd1f21cbc91 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Thu, 26 Oct 2023 11:52:45 +0400 Subject: [PATCH 41/44] remove comments --- src/qibocal/protocols/characterization/classification.py | 8 +------- .../protocols/characterization/qutrit_classification.py | 7 +------ 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/qibocal/protocols/characterization/classification.py b/src/qibocal/protocols/characterization/classification.py index 28559fead..e47313d77 100644 --- a/src/qibocal/protocols/characterization/classification.py +++ b/src/qibocal/protocols/characterization/classification.py @@ -60,8 +60,6 @@ class SingleShotClassificationParameters(Parameters): class SingleShotClassificationData(Data): nshots: int """Number of shots.""" - # classifiers_hpars: dict[QubitId, dict] - # """Models' hyperparameters""" savedir: str """Dumping folder of the classification results""" data: dict[QubitId, npt.NDArray] = field(default_factory=dict) @@ -173,7 +171,6 @@ def _acquisition( RX_pulses = {} ro_pulses = {} - # hpars = {} for qubit in qubits: RX_pulses[qubit] = platform.create_RX_pulse(qubit, start=0) ro_pulses[qubit] = platform.create_qubit_readout_pulse( @@ -183,12 +180,10 @@ def _acquisition( state0_sequence.add(ro_pulses[qubit]) state1_sequence.add(RX_pulses[qubit]) state1_sequence.add(ro_pulses[qubit]) - # hpars[qubit] = qubits[qubit].classifiers_hpars - # create a DataUnits object to store the results + data = SingleShotClassificationData( nshots=params.nshots, classifiers_list=params.classifiers_list, - # classifiers_hpars=hpars, savedir=params.savedir, ) @@ -379,7 +374,6 @@ def _update( update.threshold(results.threshold[qubit], platform, qubit) update.mean_gnd_states(results.mean_gnd_states[qubit], platform, qubit) update.mean_exc_states(results.mean_exc_states[qubit], platform, qubit) - # update.classifiers_hpars(results.classifiers_hpars[qubit], platform, qubit) update.readout_fidelity(results.fidelity[qubit], platform, qubit) update.assignment_fidelity(results.assignment_fidelity[qubit], platform, qubit) diff --git a/src/qibocal/protocols/characterization/qutrit_classification.py b/src/qibocal/protocols/characterization/qutrit_classification.py index c3f3e7ea8..331156535 100644 --- a/src/qibocal/protocols/characterization/qutrit_classification.py +++ b/src/qibocal/protocols/characterization/qutrit_classification.py @@ -90,12 +90,10 @@ def _acquisition( # taking advantage of multiplexing, apply the same set of gates to all qubits in parallel states_sequences = [PulseSequence() for _ in range(3)] ro_pulses = {} - # hpars = {} for qubit in qubits: rx_pulse = platform.create_RX_pulse(qubit, start=0) rx12_pulse = platform.create_RX12_pulse(qubit, start=rx_pulse.finish) drive_pulses = [rx_pulse, rx12_pulse] - # hpars[qubit] = qubits[qubit].classifiers_hpars ro_pulses[qubit] = [] for i, sequence in enumerate(states_sequences): sequence.add(*drive_pulses[:i]) @@ -109,7 +107,6 @@ def _acquisition( data = QutritClassificationData( nshots=params.nshots, classifiers_list=params.classifiers_list, - # classifiers_hpars=hpars, savedir=params.savedir, ) states_results = [] @@ -198,9 +195,7 @@ def _plot(data: QutritClassificationData, qubit, fit: SingleShotClassificationRe def _update( results: SingleShotClassificationResults, platform: Platform, qubit: QubitId ): - update.qutrit_classifiers_hpars( - results.classifiers_hpars[qubit], platform, qubit - ) # TODO: implement a qutrit classifiers hpars (?) + update.qutrit_classifiers_hpars(results.classifiers_hpars[qubit], platform, qubit) qutrit_classification = Routine(_acquisition, _fit, _plot, _update) From aaf4360b2ae21accd274f9af640f389a1e6181b2 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Thu, 26 Oct 2023 12:00:13 +0400 Subject: [PATCH 42/44] remove comment in run.py --- src/qibocal/fitting/classifier/run.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/qibocal/fitting/classifier/run.py b/src/qibocal/fitting/classifier/run.py index 174941d05..5431d1c49 100644 --- a/src/qibocal/fitting/classifier/run.py +++ b/src/qibocal/fitting/classifier/run.py @@ -251,12 +251,9 @@ def train_qubit( classifier = Classifier(mod, qubit_dir) classifier.savedir.mkdir(exist_ok=True) logging.info(f"Training quibt with classifier: {pretty_name(classifier.name)}") - # if classifier.name not in cls_data.classifiers_hpars[qubit]: hyperpars = classifier.hyperopt( x_train, y_train.astype(np.int64), classifier.savedir ) - # else: - # hyperpars = cls_data.classifiers_hpars[qubit][classifier.name] hpars_list.append(hyperpars) classifier.dump_hyper(hyperpars) model = classifier.create_model(hyperpars) From 90e8670f45749d3edf5d032ee5aa50ab1b20f90d Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Thu, 26 Oct 2023 15:27:27 +0400 Subject: [PATCH 43/44] fix --- .../characterization/two_qubit_interaction/chevron.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/qibocal/protocols/characterization/two_qubit_interaction/chevron.py b/src/qibocal/protocols/characterization/two_qubit_interaction/chevron.py index daba102aa..1a2c9e8b5 100644 --- a/src/qibocal/protocols/characterization/two_qubit_interaction/chevron.py +++ b/src/qibocal/protocols/characterization/two_qubit_interaction/chevron.py @@ -124,9 +124,12 @@ def _aquisition( sequence.add(cz.get_qubit_pulses(ordered_pair[1])) # Patch to get the coupler until the routines use QubitPair - sequence.add( - cz.coupler_pulses(platform.pairs[tuple(sorted(ordered_pair))].coupler.name) - ) + if platform.couplers: + sequence.add( + cz.coupler_pulses( + platform.pairs[tuple(sorted(ordered_pair))].coupler.name + ) + ) if params.parking: for pulse in cz: From 099476bb0f6541c3f5bfd591886a6f2640239d18 Mon Sep 17 00:00:00 2001 From: Stefano Carrazza Date: Thu, 26 Oct 2023 13:47:30 +0200 Subject: [PATCH 44/44] adding missing column --- serverscripts/web/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/serverscripts/web/index.html b/serverscripts/web/index.html index 5193423b4..56b41fa02 100644 --- a/serverscripts/web/index.html +++ b/serverscripts/web/index.html @@ -55,6 +55,7 @@

Uploaded Reports

Start-time (UTC) End-time (UTC) Tag + Author