From 19534600101d54c5c294d6559cef82bb6c965af8 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Tue, 3 Oct 2023 17:52:50 +0400 Subject: [PATCH 01/13] 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/13] 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/13] 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/13] 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/13] 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 6448c95cd1b2135877cc2ad4da98f7157476b1ea Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Mon, 23 Oct 2023 19:56:44 +0400 Subject: [PATCH 06/13] 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 106dea8c39a4b3cc3a251dc116bf949f2bfebed2 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Mon, 23 Oct 2023 20:46:34 +0400 Subject: [PATCH 07/13] 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 4a272a00014858e7839953cdec553fe415531430 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Tue, 24 Oct 2023 09:45:09 +0400 Subject: [PATCH 08/13] 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 f6bc9bdf5f9f110b76ae519a3ad26f2bc04e2b92 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Tue, 24 Oct 2023 10:46:01 +0400 Subject: [PATCH 09/13] 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 10/13] 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 b3040cf9bd730ae89319e4b3c24f3468a6838ec5 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Wed, 25 Oct 2023 11:33:37 +0400 Subject: [PATCH 11/13] 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 12/13] 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 13/13] 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