diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a51872df5..10a1b88d4 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.12.1 + rev: 24.1.1 hooks: - id: black - repo: https://github.com/pycqa/isort diff --git a/serverscripts/qibocal-index-reports.py b/serverscripts/qibocal-index-reports.py index 3f6eb8989..4bc31c155 100644 --- a/serverscripts/qibocal-index-reports.py +++ b/serverscripts/qibocal-index-reports.py @@ -1,6 +1,7 @@ """qibocal-index-reports.py Generates a JSON index with reports information. """ + import json import pathlib import sys diff --git a/src/qibocal/auto/draw.py b/src/qibocal/auto/draw.py index f4b22bdc8..79603c772 100644 --- a/src/qibocal/auto/draw.py +++ b/src/qibocal/auto/draw.py @@ -1,4 +1,5 @@ """Drawing utilities for execution graphs.""" + from typing import Set import networkx as nx diff --git a/src/qibocal/auto/execute.py b/src/qibocal/auto/execute.py index a3c4f82fc..769a703f9 100644 --- a/src/qibocal/auto/execute.py +++ b/src/qibocal/auto/execute.py @@ -1,4 +1,5 @@ """Tasks execution.""" + from dataclasses import dataclass, field from pathlib import Path from typing import Optional, Set diff --git a/src/qibocal/auto/graph.py b/src/qibocal/auto/graph.py index 70e97490b..937af0d60 100644 --- a/src/qibocal/auto/graph.py +++ b/src/qibocal/auto/graph.py @@ -1,4 +1,5 @@ """Execution graph and navigation utilities.""" + import networkx as nx from .runcard import Action, Id diff --git a/src/qibocal/auto/runcard.py b/src/qibocal/auto/runcard.py index afa897708..f2e360a8e 100644 --- a/src/qibocal/auto/runcard.py +++ b/src/qibocal/auto/runcard.py @@ -1,4 +1,5 @@ """Specify runcard layout, handles (de)serialization.""" + import os from functools import cached_property from typing import Any, NewType, Optional, Union @@ -33,9 +34,9 @@ class Action: """Alternative subsequent actions, branching from the current one.""" priority: Optional[int] = None """Priority level, determining the execution order.""" - qubits: Union[ - list[QubitId], list[tuple[QubitId, QubitId]], list[list[QubitId]] - ] = Field(default_factory=list) + qubits: Union[list[QubitId], list[tuple[QubitId, QubitId]], list[list[QubitId]]] = ( + Field(default_factory=list) + ) """Local qubits (optional).""" update: bool = True """Runcard update mechanism.""" diff --git a/src/qibocal/auto/task.py b/src/qibocal/auto/task.py index 4dcb529af..d48b11176 100644 --- a/src/qibocal/auto/task.py +++ b/src/qibocal/auto/task.py @@ -1,4 +1,5 @@ """Action execution tracker.""" + import copy from dataclasses import dataclass, field from pathlib import Path @@ -142,9 +143,9 @@ def run( if self.parameters.nshots is None: self.action.parameters["nshots"] = platform.settings.nshots if self.parameters.relaxation_time is None: - self.action.parameters[ - "relaxation_time" - ] = platform.settings.relaxation_time + self.action.parameters["relaxation_time"] = ( + platform.settings.relaxation_time + ) operation: Routine = self.operation parameters = self.parameters diff --git a/src/qibocal/auto/validate.py b/src/qibocal/auto/validate.py index 1af4ede80..5ba543ce7 100644 --- a/src/qibocal/auto/validate.py +++ b/src/qibocal/auto/validate.py @@ -1,4 +1,5 @@ """Extra tools to validate an execution graph.""" + from .graph import Graph diff --git a/src/qibocal/auto/validation.py b/src/qibocal/auto/validation.py index a4c196864..9ceaf98f2 100644 --- a/src/qibocal/auto/validation.py +++ b/src/qibocal/auto/validation.py @@ -1,4 +1,5 @@ """Validation module.""" + from dataclasses import dataclass, field from typing import Callable, NewType, Optional, Union diff --git a/src/qibocal/auto/validators/chi2.py b/src/qibocal/auto/validators/chi2.py index ca7ba4b4d..9d06d84af 100644 --- a/src/qibocal/auto/validators/chi2.py +++ b/src/qibocal/auto/validators/chi2.py @@ -1,4 +1,5 @@ """Chi2 validation""" + from typing import List, Optional, Union import numpy as np diff --git a/src/qibocal/cli/__init__.py b/src/qibocal/cli/__init__.py index 39ace6eff..91f980149 100644 --- a/src/qibocal/cli/__init__.py +++ b/src/qibocal/cli/__init__.py @@ -1,2 +1,3 @@ """CLI entry point.""" + from ._base import command diff --git a/src/qibocal/cli/_base.py b/src/qibocal/cli/_base.py index 6599f3242..027946144 100644 --- a/src/qibocal/cli/_base.py +++ b/src/qibocal/cli/_base.py @@ -1,4 +1,5 @@ """Adds global CLI options.""" + import getpass import pathlib diff --git a/src/qibocal/cli/fit.py b/src/qibocal/cli/fit.py index 63813d9ba..df4891afe 100644 --- a/src/qibocal/cli/fit.py +++ b/src/qibocal/cli/fit.py @@ -47,7 +47,9 @@ def fit(path, update): # dump updated runcard if platform is not None and update: # pragma: no cover # cannot test update since dummy may produce wrong values and trigger errors + (path / UPDATED_PLATFORM).mkdir(parents=True, exist_ok=True) dump_runcard(platform, path / UPDATED_PLATFORM) # dump json + (path / META).write_text(json.dumps(meta, indent=4)) diff --git a/src/qibocal/cli/upload.py b/src/qibocal/cli/upload.py index a78b36f11..feee352a9 100644 --- a/src/qibocal/cli/upload.py +++ b/src/qibocal/cli/upload.py @@ -1,4 +1,5 @@ """Upload report to server.""" + import base64 import json import pathlib diff --git a/src/qibocal/cli/utils.py b/src/qibocal/cli/utils.py index 89a4e2d67..21e064a2d 100644 --- a/src/qibocal/cli/utils.py +++ b/src/qibocal/cli/utils.py @@ -1,4 +1,5 @@ """Helper functions for the cli module""" + import datetime import getpass import json diff --git a/src/qibocal/config.py b/src/qibocal/config.py index 07e65d265..53ccaebba 100644 --- a/src/qibocal/config.py +++ b/src/qibocal/config.py @@ -1,4 +1,5 @@ """Custom logger implemenation.""" + import logging import os diff --git a/src/qibocal/fitting/classifier/run.py b/src/qibocal/fitting/classifier/run.py index 5431d1c49..045854d93 100644 --- a/src/qibocal/fitting/classifier/run.py +++ b/src/qibocal/fitting/classifier/run.py @@ -163,6 +163,7 @@ def create_model(self, hyperpars): @dataclass class BenchmarkResults: r"""Class that stores the models information.""" + accuracy: float testing_time: float training_time: float diff --git a/src/qibocal/protocols/characterization/couplers/coupler_resonator_spectroscopy.py b/src/qibocal/protocols/characterization/couplers/coupler_resonator_spectroscopy.py index e4e0c685a..fca7f0ba4 100644 --- a/src/qibocal/protocols/characterization/couplers/coupler_resonator_spectroscopy.py +++ b/src/qibocal/protocols/characterization/couplers/coupler_resonator_spectroscopy.py @@ -165,7 +165,16 @@ def _plot( for qubit in qubit_pair: if qubit in data.data.keys(): - return flux_dependence_plot(data, fit, qubit) + fig = flux_dependence_plot(data, fit, qubit)[0] + fig.update_yaxes(title_text="Pulse Amplitude [a.u.]", row=1, col=1) + fig.layout.annotations[0].update( + text="Signal [a.u.] Qubit" + str(qubit), + ) + fig.layout.annotations[1].update( + text="Phase [rad] Qubit" + str(qubit), + ) + + return [fig], "" def _update(results: CouplerSpectroscopyResults, platform: Platform, qubit: QubitId): diff --git a/src/qibocal/protocols/characterization/dispersive_shift.py b/src/qibocal/protocols/characterization/dispersive_shift.py index 1bb7aac4c..6938331af 100644 --- a/src/qibocal/protocols/characterization/dispersive_shift.py +++ b/src/qibocal/protocols/characterization/dispersive_shift.py @@ -274,9 +274,11 @@ def _plot(data: DispersiveShiftData, qubit, fit: DispersiveShiftResults): 2 * len(q_data), ) params = data_fit[ - "fitted_parameters_state_zero" - if i == 0 - else "fitted_parameters_state_one" + ( + "fitted_parameters_state_zero" + if i == 0 + else "fitted_parameters_state_one" + ) ][qubit] fig.add_trace( go.Scatter( diff --git a/src/qibocal/protocols/characterization/dispersive_shift_qutrit.py b/src/qibocal/protocols/characterization/dispersive_shift_qutrit.py index da7e5cb77..712ae6c49 100644 --- a/src/qibocal/protocols/characterization/dispersive_shift_qutrit.py +++ b/src/qibocal/protocols/characterization/dispersive_shift_qutrit.py @@ -247,12 +247,14 @@ def _plot(data: DispersiveShiftQutritData, qubit, fit: DispersiveShiftQutritResu 2 * len(q_data), ) params = data_fit[ - "fitted_parameters_state_zero" - if i == 0 - else ( - "fitted_parameters_state_one" - if i == 1 - else "fitted_parameters_state_two" + ( + "fitted_parameters_state_zero" + if i == 0 + else ( + "fitted_parameters_state_one" + if i == 1 + else "fitted_parameters_state_two" + ) ) ][qubit] fig.add_trace( diff --git a/src/qibocal/protocols/characterization/flux_dependence/qubit_crosstalk.py b/src/qibocal/protocols/characterization/flux_dependence/qubit_crosstalk.py index 556d908bc..0e5905b3b 100644 --- a/src/qibocal/protocols/characterization/flux_dependence/qubit_crosstalk.py +++ b/src/qibocal/protocols/characterization/flux_dependence/qubit_crosstalk.py @@ -71,12 +71,7 @@ def _acquisition( sequence = PulseSequence() ro_pulses = {} qd_pulses = {} - Ec = {} - Ej = {} for qubit in qubits: - Ec[qubit] = qubits[qubit].Ec - Ej[qubit] = qubits[qubit].Ej - qd_pulses[qubit] = platform.create_qubit_drive_pulse( qubit, start=0, duration=params.drive_duration ) @@ -124,7 +119,7 @@ def _acquisition( ) for flux_qubit in flux_qubits ] - data = QubitCrosstalkData(resonator_type=platform.resonator_type, Ec=Ec, Ej=Ej) + data = QubitCrosstalkData(resonator_type=platform.resonator_type) options = ExecutionParameters( nshots=params.nshots, diff --git a/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py b/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py index 562f3aad3..6a2ec0d0b 100644 --- a/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py +++ b/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py @@ -1,5 +1,4 @@ from dataclasses import dataclass, field -from functools import partial from typing import Optional import numpy as np @@ -13,12 +12,11 @@ from qibocal import update from qibocal.auto.operation import Data, Parameters, Qubits, Results, Routine -from qibocal.config import log from qibocal.protocols.characterization.qubit_spectroscopy_ef import ( DEFAULT_ANHARMONICITY, ) -from ..utils import GHZ_TO_HZ, HZ_TO_GHZ +from ..utils import GHZ_TO_HZ, HZ_TO_GHZ, table_dict, table_html from . import utils @@ -40,9 +38,7 @@ class QubitFluxParameters(Parameters): transition: Optional[str] = "01" """Flux spectroscopy transition type ("01" or "02"). Default value is 01""" drive_duration: int = 2000 - """ - Duration of the drive pulse. - """ + """Duration of the drive pulse.""" @dataclass @@ -53,8 +49,12 @@ class QubitFluxResults(Results): """Sweetspot for each qubit.""" frequency: dict[QubitId, float] """Drive frequency for each qubit.""" + d: dict[QubitId, float] + """Asymmetry.""" fitted_parameters: dict[QubitId, dict[str, float]] """Raw fitting output.""" + matrix_element: dict[QubitId, float] + """V_ii coefficient.""" QubitFluxType = np.dtype( @@ -72,15 +72,11 @@ class QubitFluxResults(Results): class QubitFluxData(Data): """QubitFlux acquisition outputs.""" - """Resonator type.""" resonator_type: str + """Resonator type.""" - """ResonatorFlux acquisition outputs.""" - Ec: dict[QubitId, float] = field(default_factory=dict) - """Qubit Ec provided by the user.""" - - Ej: dict[QubitId, float] = field(default_factory=dict) - """Qubit Ej provided by the user.""" + qubit_frequency: dict[QubitId, float] = field(default_factory=dict) + """Qubit frequencies.""" data: dict[QubitId, npt.NDArray[QubitFluxType]] = field(default_factory=dict) """Raw data acquired.""" @@ -103,15 +99,12 @@ def _acquisition( sequence = PulseSequence() ro_pulses = {} qd_pulses = {} - Ec = {} - Ej = {} + qubit_frequency = {} for qubit in qubits: - Ec[qubit] = qubits[qubit].Ec - Ej[qubit] = qubits[qubit].Ej - qd_pulses[qubit] = platform.create_qubit_drive_pulse( qubit, start=0, duration=params.drive_duration ) + qubit_frequency[qubit] = platform.qubits[qubit].drive_frequency if params.transition == "02": if qubits[qubit].anharmonicity: @@ -150,7 +143,9 @@ def _acquisition( type=SweeperType.OFFSET, ) ] - data = QubitFluxData(resonator_type=platform.resonator_type, Ec=Ec, Ej=Ej) + data = QubitFluxData( + resonator_type=platform.resonator_type, qubit_frequency=qubit_frequency + ) options = ExecutionParameters( nshots=params.nshots, @@ -171,7 +166,6 @@ def _acquisition( freq=delta_frequency_range + qd_pulses[qubit].frequency, bias=delta_bias_range + sweetspot, ) - return data @@ -185,131 +179,85 @@ def _fit(data: QubitFluxData) -> QubitFluxResults: qubits = data.qubits frequency = {} sweetspot = {} + d = {} + matrix_element = {} fitted_parameters = {} for qubit in qubits: qubit_data = data[qubit] - Ec = data.Ec[qubit] - Ej = data.Ej[qubit] - - frequency[qubit] = 0 - sweetspot[qubit] = 0 - fitted_parameters[qubit] = { - "Xi": 0, - "d": 0, - "Ec": 0, - "Ej": 0, - "f_q_offset": 0, - "C_ii": 0, - } biases = qubit_data.bias frequencies = qubit_data.freq signal = qubit_data.signal - if data.resonator_type == "2D": - signal = -signal - - frequencies, biases = utils.image_to_curve( - frequencies, biases, signal, signal_mask=0.3 - ) - max_c = biases[np.argmax(frequencies)] - min_c = biases[np.argmin(frequencies)] - xi = 1 / (2 * abs(max_c - min_c)) # Convert bias to flux. - - # First order approximation: Ec and Ej NOT provided - if Ec == 0 and Ej == 0: - try: - f_q_0 = np.max( - frequencies - ) # Initial estimation for qubit frequency at sweet spot. - popt = curve_fit( - utils.freq_q_transmon, - biases, - frequencies / GHZ_TO_HZ, - p0=[max_c, xi, 0, f_q_0 / GHZ_TO_HZ], - bounds=((-np.inf, 0, 0, 0), (np.inf, np.inf, np.inf, np.inf)), - maxfev=2000000, - )[0] - popt[3] *= GHZ_TO_HZ - f_qs = popt[3] # Qubit frequency at sweet spot. - f_q_offset = utils.freq_q_transmon( - 0, *popt - ) # Qubit frequenct at zero current. - C_ii = (f_qs - f_q_offset) / popt[ - 0 - ] # Corresponding flux matrix element. - - frequency[qubit] = f_qs * HZ_TO_GHZ - sweetspot[qubit] = popt[0] - fitted_parameters[qubit] = { - "Xi": popt[1], - "d": abs(popt[2]), - "f_q_offset": f_q_offset, - "C_ii": C_ii, - } - except: - log.warning( - "qubit_flux_fit: The first order approximation fitting was not succesful" - ) - - # Second order approximation: Ec and Ej provided + if data.resonator_type == "3D": + frequencies, biases = utils.extract_min_feature( + frequencies, + biases, + signal, + ) else: - try: - freq_q_mathieu1 = partial(utils.freq_q_mathieu, p5=0.4999) - popt = curve_fit( - freq_q_mathieu1, - biases, - frequencies / GHZ_TO_HZ, - p0=[max_c, xi, 0, Ec / GHZ_TO_HZ, Ej / GHZ_TO_HZ], - bounds=( - (-np.inf, 0, 0, 0, 0), - (np.inf, np.inf, np.inf, np.inf, np.inf), - ), - maxfev=2000000, - )[0] - popt[3] *= GHZ_TO_HZ - popt[4] *= GHZ_TO_HZ - f_qs = utils.freq_q_mathieu( - popt[0], *popt - ) # Qubit frequency at sweet spot. - f_q_offset = utils.freq_q_mathieu( - 0, *popt - ) # Qubit frequenct at zero current. - C_ii = (f_qs - f_q_offset) / popt[ - 0 - ] # Corresponding flux matrix element. - - frequency[qubit] = f_qs - sweetspot[qubit] = popt[0] - fitted_parameters[qubit] = { - "Xi": popt[1], - "d": abs(popt[2]), - "Ec": popt[3], - "Ej": popt[4], - "f_q_offset": f_q_offset, - "C_ii": C_ii, - } - except: - log.warning( - "qubit_flux_fit: The second order approximation fitting was not succesful" - ) + frequencies, biases = utils.extract_max_feature( + frequencies, + biases, + signal, + ) + + popt = curve_fit( + utils.transmon_frequency, + biases, + frequencies * HZ_TO_GHZ, + bounds=utils.qubit_flux_dependence_fit_bounds( + data.qubit_frequency[qubit], qubit_data.bias + ), + maxfev=100000, + )[0] + fitted_parameters[qubit] = popt.tolist() + frequency[qubit] = popt[0] * GHZ_TO_HZ + d[qubit] = popt[1] + sweetspot[qubit] = popt[3] + matrix_element[qubit] = popt[2] return QubitFluxResults( frequency=frequency, sweetspot=sweetspot, + d=d, + matrix_element=matrix_element, fitted_parameters=fitted_parameters, ) def _plot(data: QubitFluxData, fit: QubitFluxResults, qubit): """Plotting function for QubitFlux Experiment.""" - return utils.flux_dependence_plot(data, fit, qubit) + figures = utils.flux_dependence_plot( + data, fit, qubit, fit_function=utils.transmon_frequency + ) + if fit is not None: + fitting_report = table_html( + table_dict( + qubit, + [ + "Sweetspot [V]", + "Qubit Frequency at Sweetspot [Hz]", + "Asymmetry d", + "V_ii [V]", + ], + [ + np.round(fit.sweetspot[qubit], 4), + np.round(fit.frequency[qubit], 4), + np.round(fit.d[qubit], 4), + np.round(fit.matrix_element[qubit], 4), + ], + ) + ) + return figures, fitting_report + return figures, "" def _update(results: QubitFluxResults, platform: Platform, qubit: QubitId): update.drive_frequency(results.frequency[qubit], platform, qubit) update.sweetspot(results.sweetspot[qubit], platform, qubit) + update.asymmetry(results.d[qubit], platform, qubit) qubit_flux = Routine(_acquisition, _fit, _plot, _update) diff --git a/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_tracking.py b/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_tracking.py index 6a14a1cf5..3952362bb 100644 --- a/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_tracking.py +++ b/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_tracking.py @@ -1,97 +1,37 @@ -from dataclasses import dataclass, field -from functools import partial -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 -from qibolab.qubits import QubitId from qibolab.sweeper import Parameter, Sweeper, SweeperType -from scipy.optimize import curve_fit -from qibocal import update -from qibocal.auto.operation import Data, Parameters, Qubits, Results, Routine -from qibocal.config import log, raise_error +from qibocal.auto.operation import Qubits, Routine +from qibocal.config import raise_error from ..qubit_spectroscopy_ef import DEFAULT_ANHARMONICITY -from ..utils import GHZ_TO_HZ, HZ_TO_GHZ -from . import utils - -"""Initial guess for anharmonicity.""" +from . import qubit_flux_dependence, utils @dataclass -class QubitFluxParameters(Parameters): - """QubitFlux runcard inputs.""" - - freq_width: int - """Width for frequency sweep relative to the qubit frequency [Hz].""" - freq_step: int - """Frequency step for sweep [Hz].""" - bias_width: float - """Width for bias sweep [V].""" - bias_step: float - """Bias step for sweep [a.u.].""" - drive_duration: int - """Drive pulse duration [ns]. Same for all qubits.""" - drive_amplitude: Optional[float] = None - """Drive amplitude (optional). If defined, same amplitude will be used in all qubits. - Otherwise the default amplitude defined on the platform runcard will be used""" - nshots: Optional[int] = None - """Number of shots.""" - relaxation_time: Optional[int] = None - """Relaxation time [ns].""" - transition: Optional[str] = "01" - """Flux spectroscopy transition type ("01" or "02"). Default value is 01""" +class QubitFluxTrackParameters(qubit_flux_dependence.QubitFluxParameters): + """QubitFluxTrack runcard inputs.""" @dataclass -class QubitFluxResults(Results): - """QubitFlux outputs.""" - - sweetspot: dict[QubitId, float] - """Sweetspot for each qubit.""" - frequency: dict[QubitId, float] - """Drive frequency for each qubit.""" - fitted_parameters: dict[QubitId, dict[str, float]] - """Raw fitting output.""" - - -QubitFluxType = np.dtype( - [ - ("freq", np.float64), - ("bias", np.float64), - ("signal", np.float64), - ("phase", np.float64), - ] -) -"""Custom dtype for resonator flux dependence.""" +class QubitFluxTrackResults(qubit_flux_dependence.QubitFluxParameters): + """QubitFluxTrack outputs.""" @dataclass -class QubitFluxData(Data): - """QubitFlux acquisition outputs.""" - - """Resonator type.""" - resonator_type: str - - """ResonatorFlux acquisition outputs.""" - Ec: dict[QubitId, int] = field(default_factory=dict) - """Qubit Ec provided by the user.""" - - Ej: dict[QubitId, int] = field(default_factory=dict) - """Qubit Ej provided by the user.""" - - data: dict[QubitId, npt.NDArray[QubitFluxType]] = field(default_factory=dict) - """Raw data acquired.""" +class QubitFluxTrackData(qubit_flux_dependence.QubitFluxData): + """QubitFluxTrack acquisition outputs.""" def register_qubit_track(self, qubit, freq, bias, signal, phase): """Store output for single qubit.""" # to be able to handle the 1D sweeper case size = len(freq) - ar = np.empty(size, dtype=QubitFluxType) + ar = np.empty(size, dtype=qubit_flux_dependence.QubitFluxType) ar["freq"] = freq ar["bias"] = [bias] * size ar["signal"] = signal @@ -103,10 +43,10 @@ def register_qubit_track(self, qubit, freq, bias, signal, phase): def _acquisition( - params: QubitFluxParameters, + params: QubitFluxTrackResults, platform: Platform, qubits: Qubits, -) -> QubitFluxData: +) -> QubitFluxTrackData: """Data acquisition for QubitFlux Experiment.""" # create a sequence of pulses for the experiment: # MZ @@ -115,16 +55,12 @@ def _acquisition( sequence = PulseSequence() ro_pulses = {} qd_pulses = {} - Ec = {} - Ej = {} + qubit_frequency = {} for qubit in qubits: - Ec[qubit] = qubits[qubit].Ec - Ej[qubit] = qubits[qubit].Ej - qd_pulses[qubit] = platform.create_qubit_drive_pulse( qubit, start=0, duration=params.drive_duration ) - + qubit_frequency[qubit] = platform.qubits[qubit].drive_frequency if params.transition == "02": if qubits[qubit].anharmonicity != 0: qd_pulses[qubit].frequency -= qubits[qubit].anharmonicity / 2 @@ -156,21 +92,21 @@ def _acquisition( type=SweeperType.OFFSET, ) - data = QubitFluxData(resonator_type=platform.resonator_type, Ec=Ec, Ej=Ej) + data = QubitFluxTrackData( + resonator_type=platform.resonator_type, qubit_frequency=qubit_frequency + ) for bias in delta_bias_range: for qubit in qubits: try: - freq_resonator = utils.get_resonator_freq_flux( + freq_resonator = utils.transmon_readout_frequency( bias, - qubits[qubit].sweetspot, - qubits[qubit].flux_to_bias, + qubits[qubit].drive_frequency, qubits[qubit].asymmetry, + qubits[qubit].Cii, + qubits[qubit].sweetspot, + qubits[qubit].bare_resonator_frequency, qubits[qubit].g, - qubits[qubit].brf, - qubits[qubit].ssf_brf, - qubits[qubit].Ec, - qubits[qubit].Ej, ) # modify qubit resonator frequency qubits[qubit].readout_frequency = freq_resonator @@ -210,142 +146,10 @@ def _acquisition( return data -def _fit(data: QubitFluxData) -> QubitFluxResults: - """ - Post-processing for QubitFlux Experiment. See arxiv:0703002 - Fit frequency as a function of current for the flux qubit spectroscopy - data (QubitFluxData): data object with information on the feature response at each current point. - """ - - qubits = data.qubits - frequency = {} - sweetspot = {} - fitted_parameters = {} - - for qubit in qubits: - qubit_data = data[qubit] - Ec = data.Ec[qubit] - Ej = data.Ej[qubit] - - frequency[qubit] = 0 - sweetspot[qubit] = 0 - fitted_parameters[qubit] = { - "Xi": 0, - "d": 0, - "Ec": 0, - "Ej": 0, - "f_q_offset": 0, - "C_ii": 0, - } - - biases = qubit_data.bias - frequencies = qubit_data.freq - signal = qubit_data.signal - - if data.resonator_type == "2D": - signal = -signal - - frequencies, biases = utils.image_to_curve( - frequencies, biases, signal, signal_mask=0.3 - ) - max_c = biases[np.argmax(frequencies)] - min_c = biases[np.argmin(frequencies)] - xi = 1 / (2 * abs(max_c - min_c)) # Convert bias to flux. - - # First order approximation: Ec and Ej NOT provided - if Ec == 0 and Ej == 0: - try: - f_q_0 = np.max( - frequencies - ) # Initial estimation for qubit frequency at sweet spot. - popt = curve_fit( - utils.freq_q_transmon, - biases, - frequencies / GHZ_TO_HZ, - p0=[max_c, xi, 0, f_q_0 / GHZ_TO_HZ], - bounds=((-np.inf, 0, 0, 0), (np.inf, np.inf, np.inf, np.inf)), - maxfev=2000000, - )[0] - popt[3] *= GHZ_TO_HZ - f_qs = popt[3] # Qubit frequency at sweet spot. - f_q_offset = utils.freq_q_transmon( - 0, *popt - ) # Qubit frequenct at zero current. - C_ii = (f_qs - f_q_offset) / popt[ - 0 - ] # Corresponding flux matrix element. - - frequency[qubit] = f_qs * HZ_TO_GHZ - sweetspot[qubit] = popt[0] - fitted_parameters[qubit] = { - "Xi": popt[1], - "d": abs(popt[2]), - "f_q_offset": f_q_offset, - "C_ii": C_ii, - } - except: - log.warning( - "qubit_flux_fit: The first order approximation fitting was not succesful" - ) - - # Second order approximation: Ec and Ej provided - else: - try: - freq_q_mathieu1 = partial(utils.freq_q_mathieu, p5=0.4999) - popt = curve_fit( - freq_q_mathieu1, - biases, - frequencies / GHZ_TO_HZ, - p0=[max_c, xi, 0, Ec / GHZ_TO_HZ, Ej / GHZ_TO_HZ], - bounds=( - (-np.inf, 0, 0, 0, 0), - (np.inf, np.inf, np.inf, np.inf, np.inf), - ), - maxfev=2000000, - )[0] - popt[3] *= GHZ_TO_HZ - popt[4] *= GHZ_TO_HZ - f_qs = utils.freq_q_mathieu( - popt[0], *popt - ) # Qubit frequency at sweet spot. - f_q_offset = utils.freq_q_mathieu( - 0, *popt - ) # Qubit frequenct at zero current. - C_ii = (f_qs - f_q_offset) / popt[ - 0 - ] # Corresponding flux matrix element. - - frequency[qubit] = f_qs * HZ_TO_GHZ - sweetspot[qubit] = popt[0] - fitted_parameters[qubit] = { - "Xi": popt[1], - "d": abs(popt[2]), - "Ec": popt[3], - "Ej": popt[4], - "f_q_offset": f_q_offset, - "C_ii": C_ii, - } - except: - log.warning( - "qubit_flux_fit: The second order approximation fitting was not succesful" - ) - - return QubitFluxResults( - frequency=frequency, - sweetspot=sweetspot, - fitted_parameters=fitted_parameters, - ) - - -def _plot(data: QubitFluxData, fit: QubitFluxResults, qubit): - """Plotting function for QubitFlux Experiment.""" - return utils.flux_dependence_plot(data, fit, qubit) - - -def _update(results: QubitFluxResults, platform: Platform, qubit: QubitId): - update.sweetspot(results.sweetspot[qubit], platform, qubit) - update.drive_frequency(results.frequency[qubit], platform, qubit) - - -qubit_flux_tracking = Routine(_acquisition, _fit, _plot, _update) -"""QubitFlux Routine object.""" +qubit_flux_tracking = Routine( + _acquisition, + qubit_flux_dependence._fit, + qubit_flux_dependence._plot, + qubit_flux_dependence._update, +) +"""QubitFluxTrack Routine object.""" diff --git a/src/qibocal/protocols/characterization/flux_dependence/resonator_crosstalk.py b/src/qibocal/protocols/characterization/flux_dependence/resonator_crosstalk.py index c14c188e1..94f74788d 100644 --- a/src/qibocal/protocols/characterization/flux_dependence/resonator_crosstalk.py +++ b/src/qibocal/protocols/characterization/flux_dependence/resonator_crosstalk.py @@ -66,15 +66,13 @@ def _acquisition( # taking advantage of multiplexing, apply the same set of gates to all qubits in parallel sequence = PulseSequence() ro_pulses = {} - Ec = {} - Ej = {} - g = {} bare_resonator_frequency = {} + qubit_frequency = {} for qubit in qubits: - Ec[qubit] = qubits[qubit].Ec - Ej[qubit] = qubits[qubit].Ej - g[qubit] = qubits[qubit].g - bare_resonator_frequency[qubit] = qubits[qubit].bare_resonator_frequency + bare_resonator_frequency[qubit] = platform.qubits[ + qubit + ].bare_resonator_frequency + qubit_frequency[qubit] = platform.qubits[qubit].drive_frequency ro_pulses[qubit] = platform.create_qubit_readout_pulse(qubit, start=0) sequence.add(ro_pulses[qubit]) @@ -111,9 +109,7 @@ def _acquisition( data = ResCrosstalkData( resonator_type=platform.resonator_type, - Ec=Ec, - Ej=Ej, - g=g, + qubit_frequency=qubit_frequency, bare_resonator_frequency=bare_resonator_frequency, ) options = ExecutionParameters( diff --git a/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py b/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py index 31e37adcf..542f9e524 100644 --- a/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py +++ b/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py @@ -1,5 +1,4 @@ from dataclasses import dataclass, field -from functools import partial import numpy as np import numpy.typing as npt @@ -12,9 +11,8 @@ from qibocal import update from qibocal.auto.operation import Data, Parameters, Qubits, Results, Routine -from qibocal.config import log -from ..utils import GHZ_TO_HZ, HZ_TO_GHZ +from ..utils import GHZ_TO_HZ, HZ_TO_GHZ, table_dict, table_html from . import utils @@ -40,22 +38,18 @@ class ResonatorFluxResults(Results): """Readout frequency for each qubit.""" sweetspot: dict[QubitId, float] """Sweetspot for each qubit.""" - flux_to_bias: dict[QubitId, float] - """flux_to_bias for each qubit.""" - asymmetry: dict[QubitId, float] - """asymmetry for each qubit.""" - Gs: dict[QubitId, float] - """readout_coupling for each qubit.""" - brf: dict[QubitId, float] - """bare_resonator_frequency for each qubit.""" - ssf_brf: dict[QubitId, float] - """sweetspot_qubit_frequency/bare_resonator_frequency for each qubit.""" - ECs: dict[QubitId, float] - """Ec for each qubit.""" - EJs: dict[QubitId, float] - """Ej for each qubit.""" + d: dict[QubitId, float] + """Asymmetry.""" + bare_frequency: dict[QubitId, float] + """Resonator bare frequency.""" + drive_frequency: dict[QubitId, float] + """Qubit frequency at sweetspot.""" fitted_parameters: dict[QubitId, dict[str, float]] """Raw fitting output.""" + g: dict[QubitId, float] + """Coupling.""" + matrix_element: dict[QubitId, float] + """C_ii coefficient.""" ResFluxType = np.dtype( @@ -71,19 +65,13 @@ class ResonatorFluxResults(Results): @dataclass class ResonatorFluxData(Data): - """Resonator type.""" - - resonator_type: str - """ResonatorFlux acquisition outputs.""" - Ec: dict[QubitId, float] = field(default_factory=dict) - """Qubit Ec provided by the user.""" - Ej: dict[QubitId, float] = field(default_factory=dict) - """Qubit Ej provided by the user.""" + """Resonator type.""" + resonator_type: str - g: dict[QubitId, float] = field(default_factory=dict) - """Qubit g provided by the user.""" + qubit_frequency: dict[QubitId, float] = field(default_factory=dict) + """Qubit frequencies.""" bare_resonator_frequency: dict[QubitId, int] = field(default_factory=dict) """Qubit bare resonator frequency power provided by the user.""" @@ -108,15 +96,13 @@ def _acquisition( # taking advantage of multiplexing, apply the same set of gates to all qubits in parallel sequence = PulseSequence() ro_pulses = {} - Ec = {} - Ej = {} - g = {} + qubit_frequency = {} bare_resonator_frequency = {} for qubit in qubits: - Ec[qubit] = qubits[qubit].Ec - Ej[qubit] = qubits[qubit].Ej - g[qubit] = qubits[qubit].g - bare_resonator_frequency[qubit] = qubits[qubit].bare_resonator_frequency + qubit_frequency[qubit] = platform.qubits[qubit].drive_frequency + bare_resonator_frequency[qubit] = platform.qubits[ + qubit + ].bare_resonator_frequency ro_pulses[qubit] = platform.create_qubit_readout_pulse(qubit, start=0) sequence.add(ro_pulses[qubit]) @@ -146,11 +132,10 @@ def _acquisition( data = ResonatorFluxData( resonator_type=platform.resonator_type, - Ec=Ec, - Ej=Ej, - g=g, + qubit_frequency=qubit_frequency, bare_resonator_frequency=bare_resonator_frequency, ) + options = ExecutionParameters( nshots=params.nshots, relaxation_time=params.relaxation_time, @@ -184,226 +169,107 @@ def _fit(data: ResonatorFluxData) -> ResonatorFluxResults: qubits = data.qubits frequency = {} sweetspot = {} - flux_to_bias = {} - asymmetry = {} - Gs = {} - brf = {} - ssf_brf = {} - ECs = {} - EJs = {} - + d = {} + bare_frequency = {} + drive_frequency = {} fitted_parameters = {} + matrix_element = {} + g = {} for qubit in qubits: qubit_data = data[qubit] - Ec = data.Ec[qubit] - Ej = data.Ej[qubit] - - frequency[qubit] = 0 - sweetspot[qubit] = 0 - flux_to_bias[qubit] = 0 - asymmetry[qubit] = 0 - Gs[qubit] = 0 - brf[qubit] = 0 - ssf_brf[qubit] = 0 - ECs[qubit] = 0 - EJs[qubit] = 0 - - fitted_parameters[qubit] = { - "Xi": 0, - "d": 0, - "g": 0, - "Ec": 0, - "Ej": 0, - "bare_resonator_frequency": 0, - "f_qs": 0, - "f_r_offset": 0, - "C_ii": 0, - } biases = qubit_data.bias frequencies = qubit_data.freq signal = qubit_data.signal if data.resonator_type == "3D": - signal = -signal - - frequencies, biases = utils.image_to_curve( - frequencies, biases, signal, signal_mask=0.5 - ) - - bare_resonator_frequency = data.bare_resonator_frequency[ - qubit - ] # Resonator frequency at high power. - g = data.g[qubit] # Readout coupling. - max_c = biases[np.argmax(frequencies)] - min_c = biases[np.argmin(frequencies)] - xi = 1 / (2 * abs(max_c - min_c)) # Convert bias to flux. - - # First order approximation: bare_resonator_frequency, g provided - if (Ec == 0 and Ej == 0) and (bare_resonator_frequency != 0 and g != 0): - try: - # Initial estimation for resonator frequency at sweet spot. - f_r_0 = np.max(frequencies) - # Initial estimation for qubit frequency at sweet spot. - f_q_0 = bare_resonator_frequency - g**2 / ( - f_r_0 - bare_resonator_frequency - ) - popt = curve_fit( - utils.freq_r_transmon, - biases, - frequencies / GHZ_TO_HZ, - p0=[ - max_c, - xi, - 0, - f_q_0 / bare_resonator_frequency, - g / GHZ_TO_HZ, - bare_resonator_frequency / GHZ_TO_HZ, - ], - bounds=( - (-np.inf, 0, 0, 0, 0, 0), - (np.inf, np.inf, np.inf, np.inf, np.inf, np.inf), - ), - maxfev=2000000, - )[0] - - sweetspot[qubit] = popt[0] - flux_to_bias[qubit] = popt[1] - asymmetry[qubit] = popt[2] - Gs[qubit] = popt[4] - brf[qubit] = popt[5] - ssf_brf[qubit] = popt[3] - - popt[4] *= GHZ_TO_HZ - popt[5] *= GHZ_TO_HZ - f_qs = popt[3] * popt[5] # Qubit frequency at sweet spot. - f_rs = utils.freq_r_transmon( - popt[0], *popt - ) # Resonator frequency at sweet spot. - f_r_offset = utils.freq_r_transmon( - 0, *popt - ) # Resonator frequency at zero current. - C_ii = (f_rs - f_r_offset) / popt[ - 0 - ] # Corresponding flux matrix element. - - frequency[qubit] = f_rs * HZ_TO_GHZ - fitted_parameters[qubit] = { - "Xi": popt[1], - "d": abs(popt[2]), - "g": popt[4], - "bare_resonator_frequency": popt[5], - "f_qs": f_qs, - "f_r_offset": f_r_offset, - "C_ii": C_ii, - } - - except: - log.warning( - "resonator_flux_fit: First order approximation fitting was not succesful" - ) - - # Second order approximation: bare_resonator_frequency, g, Ec, Ej provided - elif Ec != 0 and Ej != 0 and bare_resonator_frequency != 0 and g != 0: - try: - freq_r_mathieu1 = partial(utils.freq_r_mathieu, p7=0.4999) - popt = curve_fit( - freq_r_mathieu1, - biases, - frequencies / GHZ_TO_HZ, - p0=[ - bare_resonator_frequency / GHZ_TO_HZ, - g / GHZ_TO_HZ, - max_c, - xi, - 0, - Ec / GHZ_TO_HZ, - Ej / GHZ_TO_HZ, - ], - bounds=( - (0, 0, -np.inf, 0, 0, 0, 0), - (np.inf, np.inf, np.inf, np.inf, np.inf, np.inf, np.inf), - ), - maxfev=2000000, - )[0] - - sweetspot[qubit] = popt[2] - flux_to_bias[qubit] = popt[3] - asymmetry[qubit] = popt[4] - Gs[qubit] = popt[1] - brf[qubit] = popt[0] - ECs[qubit] = popt[5] - EJs[qubit] = popt[6] - - popt[0] *= GHZ_TO_HZ - popt[1] *= GHZ_TO_HZ - popt[5] *= GHZ_TO_HZ - popt[6] *= GHZ_TO_HZ - f_qs = utils.freq_q_mathieu( - popt[2], *popt[2::] - ) # Qubit frequency at sweet spot. - f_rs = utils.freq_r_mathieu( - popt[2], *popt - ) # Resonator frequency at sweet spot. - f_r_offset = utils.freq_r_mathieu( - 0, *popt - ) # Resonator frequenct at zero current. - C_ii = (f_rs - f_r_offset) / popt[ - 2 - ] # Corresponding flux matrix element. - - frequency[qubit] = f_rs * HZ_TO_GHZ - sweetspot[qubit] = popt[2] - fitted_parameters[qubit] = { - "Xi": popt[3], - "d": abs(popt[4]), - "g": popt[1], - "Ec": popt[5], - "Ej": popt[6], - "bare_resonator_frequency": popt[0], - "f_qs": f_qs, - "f_r_offset": f_r_offset, - "C_ii": C_ii, - } - except: - log.warning( - "resonator_flux_fit: Second order approximation fitting was not succesful" - ) - + frequencies, biases = utils.extract_max_feature( + frequencies, + biases, + signal, + ) else: - log.warning("resonator_flux_fit: Not enought guess parameters provided") + frequencies, biases = utils.extract_min_feature( + frequencies, + biases, + signal, + ) + + popt = curve_fit( + utils.transmon_readout_frequency, + biases, + frequencies * HZ_TO_GHZ, + bounds=utils.resonator_flux_dependence_fit_bounds( + data.qubit_frequency[qubit], + qubit_data.bias, + data.bare_resonator_frequency[qubit], + ), + maxfev=100000, + )[0] + fitted_parameters[qubit] = popt.tolist() + + # frequency corresponds to transmon readout frequency + # at the sweetspot popt[3] + frequency[qubit] = utils.transmon_readout_frequency(popt[3], *popt) * GHZ_TO_HZ + sweetspot[qubit] = popt[3] + d[qubit] = popt[1] + bare_frequency[qubit] = popt[4] * GHZ_TO_HZ + drive_frequency[qubit] = popt[0] * GHZ_TO_HZ + g[qubit] = popt[5] + matrix_element[qubit] = popt[2] return ResonatorFluxResults( frequency=frequency, sweetspot=sweetspot, - flux_to_bias=flux_to_bias, - asymmetry=asymmetry, - Gs=Gs, - brf=brf, - ssf_brf=ssf_brf, - ECs=ECs, - EJs=EJs, + d=d, + bare_frequency=bare_frequency, + drive_frequency=drive_frequency, + g=g, + matrix_element=matrix_element, fitted_parameters=fitted_parameters, ) def _plot(data: ResonatorFluxData, fit: ResonatorFluxResults, qubit): """Plotting function for ResonatorFlux Experiment.""" - return utils.flux_dependence_plot(data, fit, qubit) + figures = utils.flux_dependence_plot( + data, fit, qubit, utils.transmon_readout_frequency + ) + if fit is not None: + fitting_report = table_html( + table_dict( + qubit, + [ + "Sweetspot [V]", + "Bare Resonator Frequency [Hz]", + "Readout Frequency [Hz]", + "Qubit Frequency at Sweetspot [Hz]", + "Asymmetry d", + "Coupling g", + "V_ii [V]", + ], + [ + np.round(fit.sweetspot[qubit], 4), + np.round(fit.bare_frequency[qubit], 4), + np.round(fit.frequency[qubit], 4), + np.round(fit.drive_frequency[qubit], 4), + np.round(fit.d[qubit], 4), + np.round(fit.g[qubit], 4), + np.round(fit.matrix_element[qubit], 4), + ], + ) + ) + return figures, fitting_report + return figures, "" def _update(results: ResonatorFluxResults, platform: Platform, qubit: QubitId): - update.bare_resonator_frequency_sweetspot(results.brf[qubit], platform, qubit) + update.bare_resonator_frequency(results.bare_frequency[qubit], platform, qubit) update.readout_frequency(results.frequency[qubit], platform, qubit) - update.flux_to_bias(results.flux_to_bias[qubit], platform, qubit) - update.asymmetry(results.asymmetry[qubit], platform, qubit) - update.ratio_sweetspot_qubit_freq_bare_resonator_freq( - results.ssf_brf[qubit], platform, qubit - ) - update.charging_energy(results.ECs[qubit], platform, qubit) - update.josephson_energy(results.EJs[qubit], platform, qubit) - update.coupling(results.Gs[qubit], platform, qubit) + update.drive_frequency(results.drive_frequency[qubit], platform, qubit) + update.asymmetry(results.d[qubit], platform, qubit) + update.coupling(results.g[qubit], platform, qubit) resonator_flux = Routine(_acquisition, _fit, _plot, _update) diff --git a/src/qibocal/protocols/characterization/flux_dependence/utils.py b/src/qibocal/protocols/characterization/flux_dependence/utils.py index 8eb50413d..785667549 100644 --- a/src/qibocal/protocols/characterization/flux_dependence/utils.py +++ b/src/qibocal/protocols/characterization/flux_dependence/utils.py @@ -1,23 +1,8 @@ import numpy as np import plotly.graph_objects as go from plotly.subplots import make_subplots -from scipy.special import mathieu_a, mathieu_b -from sklearn.linear_model import Ridge -from ..utils import HZ_TO_GHZ, table_dict, table_html - -FLUX_PARAMETERS = { - "Xi": "Constant to map flux to bias [V]", - "d": "Junction asymmetry", - "Ec": "Charge energy Ec [Hz]", - "Ej": "Josephson energy Ej [Hz]", - "f_q_offset": "Qubit frequency offset [Hz]", - "C_ii": "Flux matrix element C_ii [Hz/V]", - "g": "Readout coupling", - "bare_resonator_frequency": "Bare resonator frequency [Hz]", - "f_qs": "Qubit frequency [Hz]", - "f_r_offset": "Resonator frequency offset [Hz]", -} +from ..utils import HZ_TO_GHZ def is_crosstalk(data): @@ -37,22 +22,15 @@ def create_data_array(freq, bias, signal, phase, dtype): return np.rec.array(ar) -def flux_dependence_plot(data, fit, qubit): +def flux_dependence_plot(data, fit, qubit, fit_function=None): figures = [] - fitting_report = "" - qubit_data = data[qubit] + frequencies = qubit_data.freq * HZ_TO_GHZ - if not data.__class__.__name__ == "CouplerSpectroscopyData": - subplot_titles = ( - "Signal [a.u.]", - "Phase [rad]", - ) - else: - subplot_titles = ( - "Signal [a.u.] Qubit" + str(qubit), - "Phase [rad] Qubit" + str(qubit), - ) + subplot_titles = ( + "Signal [a.u.]", + "Phase [rad]", + ) fig = make_subplots( rows=1, @@ -61,27 +39,10 @@ def flux_dependence_plot(data, fit, qubit): vertical_spacing=0.1, subplot_titles=subplot_titles, ) - frequencies = qubit_data.freq * HZ_TO_GHZ - signal = qubit_data.signal - if data.__class__.__name__ == "ResonatorFluxData": - signal_mask = 0.5 - if data.resonator_type == "3D": - signal = -signal - elif ( - data.__class__.__name__ == "QubitFluxData" - or data.__class__.__name__ == "CouplerSpectroscopyData" - ): - signal_mask = 0.3 - if data.resonator_type == "2D": - signal = -signal - - frequencies1, biases1 = image_to_curve( - frequencies, qubit_data.bias, signal, signal_mask - ) fig.add_trace( go.Heatmap( - x=frequencies, + x=qubit_data.freq * HZ_TO_GHZ, y=qubit_data.bias, z=qubit_data.signal, colorbar_x=0.46, @@ -90,107 +51,33 @@ def flux_dependence_plot(data, fit, qubit): col=1, ) - if not data.__class__.__name__ == "CouplerSpectroscopyData": + # TODO: This fit is for frequency, can it be reused here, do we even want the fit ? + if fit is not None and not data.__class__.__name__ == "CouplerSpectroscopyData": + params = fit.fitted_parameters[qubit] + bias = np.unique(qubit_data.bias) fig.add_trace( go.Scatter( - x=frequencies1, - y=biases1, - mode="markers", - marker_color="green", + x=fit_function(bias, *params), + y=bias, showlegend=True, - name="Curve estimation", + name="Fit", + marker=dict(color="black"), ), row=1, col=1, ) - # TODO: This fit is for frequency, can it be reused here, do we even want the fit ? - if fit is not None and not data.__class__.__name__ == "CouplerSpectroscopyData": - fitting_report = "" - params = fit.fitted_parameters[qubit] - fitting_report_label = "Frequency" - if fit.frequency[qubit] != 0: - if data.__class__.__name__ == "ResonatorFluxData": - fitting_report_label = "Resonator Frequency [Hz]" - if all(param in params for param in ["Ec", "Ej"]): - popt = [ - params["bare_resonator_frequency"], - params["g"], - fit.sweetspot[qubit], - params["Xi"], - params["d"], - params["Ec"], - params["Ej"], - ] - freq_fit = freq_r_mathieu(biases1, *popt) - else: - popt = [ - fit.sweetspot[qubit], - params["Xi"], - params["d"], - params["f_q/bare_resonator_frequency"], - params["g"], - params["bare_resonator_frequency"], - ] - freq_fit = freq_r_transmon(biases1, *popt) - elif data.__class__.__name__ == "QubitFluxData": - fitting_report_label = "Qubit Frequency [Hz]" - if all(param in params for param in ["Ec", "Ej"]): - popt = [ - fit.sweetspot[qubit], - params["Xi"], - params["d"], - params["Ec"], - params["Ej"], - ] - freq_fit = freq_q_mathieu(biases1, *popt) - else: - popt = [ - fit.sweetspot[qubit], - params["Xi"], - params["d"], - fit.frequency[qubit], - ] - freq_fit = freq_q_transmon(biases1, *popt) - - fig.add_trace( - go.Scatter( - x=freq_fit, - y=biases1, - showlegend=True, - name="Fit", - ), - row=1, - col=1, - ) - - parameters = [] - values = [] - - for key, value in fit.fitted_parameters[qubit].items(): - values.append(np.round(value, 5)) - parameters.append(FLUX_PARAMETERS[key]) - - parameters.extend([fitting_report_label, "Sweetspot"]) - values.extend( - [np.round(fit.frequency[qubit], 5), np.round(fit.sweetspot[qubit], 3)] - ) - - fitting_report = table_html(table_dict(qubit, parameters, values)) - fig.update_xaxes( title_text=f"Frequency [GHz]", row=1, col=1, ) - if not data.__class__.__name__ == "CouplerSpectroscopyData": - fig.update_yaxes(title_text="Bias [V]", row=1, col=1) - else: - fig.update_yaxes(title_text="Pulse Amplitude [a.u.]", row=1, col=1) + + fig.update_yaxes(title_text="Bias [V]", row=1, col=1) fig.add_trace( go.Heatmap( - x=frequencies, + x=qubit_data.freq * HZ_TO_GHZ, y=qubit_data.bias, z=qubit_data.phase, colorbar_x=1.01, @@ -204,11 +91,6 @@ def flux_dependence_plot(data, fit, qubit): col=2, ) - if not data.__class__.__name__ == "CouplerSpectroscopyData": - fig.update_yaxes(title_text="Bias [V]", row=1, col=2) - else: - fig.update_yaxes(title_text="Pulse Amplitude [a.u.]", row=1, col=2) - fig.update_layout(xaxis1=dict(range=[np.min(frequencies), np.max(frequencies)])) fig.update_layout( @@ -218,7 +100,7 @@ def flux_dependence_plot(data, fit, qubit): figures.append(fig) - return figures, fitting_report + return figures def flux_crosstalk_plot(data, qubit): @@ -275,280 +157,111 @@ def flux_crosstalk_plot(data, qubit): return figures, fitting_report -def G_f_d(x, p0, p1, p2): - """ - Auxiliary function to calculate the qubit frequency as a function of bias for the qubit flux spectroscopy. It also determines the flux dependence of :math:`E_J`, :math:`E_J(\\phi)=E_J(0)G_f_d^2`. - - Args: - p[0] (float): bias offset. - p[1] (float): constant to convert flux (:math:`\\phi_0`) to bias (:math:`v_0`). Typically denoted as :math:`\\Xi`. :math:`v_0 = \\Xi \\phi_0`. - p[2] (float): asymmetry between the two junctions of the transmon. Typically denoted as :math:`d`. :math:`d = (E_J^1 - E_J^2) / (E_J^1 + E_J^2)`. - - Returns: - (float) - """ - G = np.sqrt( - np.cos(np.pi * (x - p0) * p1) ** 2 - + p2**2 * np.sin(np.pi * (x - p0) * p1) ** 2 - ) - return np.sqrt(G) - - -def freq_q_transmon(x, p0, p1, p2, p3): - """ - Qubit frequency in the boson description. Close to the half-flux quantum (:math:'\\phi=0.5`), :math:`E_J/E_C = E_J(\\phi=0)*d/E_C` can be too small for a quasi-symmetric split-transmon to apply this expression. We assume that the qubit frequencty :math:`\\gg E_C`. - - Args: - p[0] (float): bias offset. - p[1] (float): constant to convert flux (:math:`\\phi_0`) to bias (:math:`v_0`). Typically denoted as :math:`\\Xi`. :math:`v_0 = \\Xi \\phi_0`. - p[2] (float): asymmetry between the two junctions of the transmon. Typically denoted as :math:`d`. :math:`d = (E_J^1 - E_J^2) / (E_J^1 + E_J^2)`. - p[3] (float): qubit frequency at the sweetspot. - - Returns: - (float) - """ - return p3 * G_f_d(x, p0, p1, p2) - - -def freq_r_transmon(x, p0, p1, p2, p3, p4, p5): - """ - Flux dependent resonator frequency in the transmon limit. - - Args: - p[0] (float): bias offset. - p[1] (float): constant to convert flux (:math:`\\phi_0`) to bias (:math:`v_0`). Typically denoted as :math:`\\Xi`. :math:`v_0 = \\Xi \\phi_0`. - p[2] (float): asymmetry between the two junctions of the transmon. Typically denoted as :math:`d`. :math:`d = (E_J^1 - E_J^2) / (E_J^1 + E_J^2)`. - p[3] (float): qubit frequency at the sweetspot / high power resonator frequency, - p[4] (float): readout coupling at the sweetspot. Typically denoted as :math:`g`. - p[5] (float): high power resonator frequency. - - Returns: - (float) - """ - return p5 + p4**2 * G_f_d(x, p0, p1, p2) / (p5 - p3 * p5 * G_f_d(x, p0, p1, p2)) - - -def kordering(m, ng=0.4999): - """ - Auxilliary function to compute the qubit frequency in the CPB model (useful when the boson description fails). It sorts the eigenvalues :math:`|m,ng\\rangle` for the Schrodinger equation for the - Cooper pair box circuit in the phase basis. - - Args: - m (integer): index denoting the m eigenvector. - ng (float): effective offset charge. The sorting does not work for ng integer or half-integer. To study the sweet spot at :math:`ng = 0.5` for instance, one should insert an approximation like :math:`ng = 0.4999`. +def G_f_d(x, offset, d, element): + """Auxiliary function to calculate qubit frequency as a function of bias. - Returns: - (float) - """ - - a1 = (round(2 * ng + 1 / 2) % 2) * (round(ng) + 1 * (-1) ** m * divmod(m + 1, 2)[0]) - a2 = (round(2 * ng - 1 / 2) % 2) * (round(ng) - 1 * (-1) ** m * divmod(m + 1, 2)[0]) - return a1 + a2 - - -def mathieu(index, x): - """ - Mathieu's characteristic value. Auxilliary function to compute the qubit frequency in the CPB model. + It also determines the flux dependence of :math:`E_J`,:math:`E_J(\\phi)=E_J(0)G_f_d^2`. + For more details see: https://arxiv.org/pdf/cond-mat/0703002.pdf Args: - index (integer): index to specify the Mathieu's characteristic value. + offset (float): bias offset. + matrix_element(float): constant to convert flux (:math:`\\phi_0`) to bias (:math:`v_0`). Typically denoted as :math:`\\Xi`. :math:`v_0 = \\Xi \\phi_0`. + d (float): asymmetry between the two junctions of the transmon. + Typically denoted as :math:`d`. :math:`d = (E_J^1 - E_J^2) / (E_J^1 + E_J^2)`. Returns: (float) """ - if index < 0: - return mathieu_b(-index, x) - else: - return mathieu_a(index, x) + return (d**2 + (1 - d**2) * np.cos(np.pi * (x - offset) * element) ** 2) ** 0.25 -def freq_q_mathieu(x, p0, p1, p2, p3, p4, p5=0.499): - """ - Qubit frequency in the CPB model. It is useful when the boson description fails and to determine :math:`E_C` and :math:`E_J`. +def transmon_frequency(x, w_max, d, element, offset): + r"""Approximation to transmon frequency. - Args: - p[0] (float): bias offset. - p[1] (float): constant to convert flux (:math:`\\phi_0`) to bias (:math:`v_0`). Typically denoted as :math:`\\Xi`. :math:`v_0 = \\Xi \\phi_0`. - p[2] (float): asymmetry between the two junctions of the transmon. Typically denoted as :math:`d`. :math:`d = (E_J^1 - E_J^2) / (E_J^1 + E_J^2)`. - p[3] (float): charge energy at the sweetspot, :math:`E_C`. - p[4] (float): Josephson energy, :math:`E_J`. - p[5] (float): effective offset charge, :math:`ng`. + The formula holds in the transmon regime Ej / Ec >> 1. - Returns: - (float) - """ - index1 = int(2 * (p5 + kordering(1, p5))) - index0 = int(2 * (p5 + kordering(0, p5))) - p4 = p4 * G_f_d(x, p0, p1, p2) - return p3 * (mathieu(index1, -p4 / (2 * p3)) - mathieu(index0, -p4 / (2 * p3))) - - -def freq_r_mathieu(x, p0, p1, p2, p3, p4, p5, p6, p7=0.499): - """ - Resonator frequency in the CPB model. + See https://arxiv.org/pdf/cond-mat/0703002.pdf for the complete formula. Args: - p[0] (float): high power resonator frequency. - p[1] (float): readout coupling at the sweetspot. - p[2] (float): bias offset. - p[3] (float): constant to convert flux (:math:`\\phi_0`) to bias (:math:`v_0`). Typically denoted as :math:`\\Xi`. :math:`v_0 = \\Xi \\phi_0`. - p[4] (float): asymmetry between the two junctions of the transmon. Typically denoted as :math:`d`. :math:`d = (E_J^1 - E_J^2) / (E_J^1 + E_J^2)`. - p[5] (float): charge energy at the sweetspot, :math:`E_C`. - p[6] (float): Josephson energy, :math:`E_J`. - p[7] (float): effective offset charge, :math:`ng`. - - Returns: - (float) - """ - G = G_f_d(x, p2, p3, p4) - f_q = freq_q_mathieu(x, p2, p3, p4, p5, p6, p7) - f_r = p0 + p1**2 * G / (p0 - f_q) - return f_r - - -def line(x, p0, p1): + x (float): bias value + w_max (float): maximum frequency :math:`w_{max} = \sqrt{8 E_j E_c} + d (float): d (float): asymmetry between the two junctions of the transmon. + element (float): matrix element + offset (float): bias corresponding to zero flux (sweetspot). + + Returns: + (float): qubit frequency as a function of bias. """ - Linear fit. + return w_max * G_f_d(x, offset=offset, d=d, element=element) - Args: - p[0] (float): slope. - p[1] (float): intercept. - Returns: - (float) - """ - return p0 * x + p1 +def transmon_readout_frequency(x, w_max, d, element, offset, resonator_freq, g): + r"""Approximation to flux dependent resonator frequency. + The formula holds in the transmon regime Ej / Ec >> 1. -def feature(x, order=3): - """ - Auxilliary function for the function image_to_curve(). It generates a polynomial feature of the form [1, x, x^2, ..., x^order]. + See https://arxiv.org/pdf/cond-mat/0703002.pdf for the complete formula. Args: - x (ndarray) column vector. - - Returns: - (ndarray) + x (float): bias value + w_max (float): maximum frequency :math:`w_{max} = \sqrt{8 E_j E_c} + d (float): d (float): asymmetry between the two junctions of the transmon. + element (float): matrix element + offset (float): bias corresponding to zero flux (sweetspot). + resonator_freq (float): bare resonator frequency [GHz] + g (float): readout coupling. + + Returns: + (float): resonator frequency as a function of bias. """ - x = x.reshape(-1, 1) - return np.power(x, np.arange(order + 1).reshape(1, -1)) + return resonator_freq + g**2 * G_f_d(x, offset, d, element) / ( + resonator_freq - transmon_frequency(x, w_max, d, element, offset) + ) -def image_to_curve(x, y, z, signal_mask=0.5, alpha=1e-5, order=50): - """ - Extracts a feature characterized by min(z(x, y)). It considers all the data and applies Ridge regression on a polynomial ansatz in x. This allows obtaining a set of points describing the feature as y vs x. +def extract_min_feature(freq, bias, signal, threshold=1.5): + """Extract min feature using SNR.""" + mean_signal = np.mean(signal) + std_signal = np.std(signal) + snr_map = (signal - mean_signal) / std_signal + binary_mask = snr_map < -threshold + return freq[binary_mask], bias[binary_mask] + + +def extract_max_feature(freq, bias, signal, threshold=1.5): + """Extract max feature using SNR.""" + mean_signal = np.mean(signal) + std_signal = np.std(signal) + snr_map = (signal - mean_signal) / std_signal + binary_mask = snr_map > threshold + return freq[binary_mask], bias[binary_mask] + + +def qubit_flux_dependence_fit_bounds(qubit_frequency: float, bias: np.array): + """Returns bounds for qubit flux fit.""" + return ( + [ + qubit_frequency * HZ_TO_GHZ - 1, + 0, + 0, + np.mean(bias) - 0.5, + ], + [ + qubit_frequency * HZ_TO_GHZ + 1, + 1, + np.inf, + np.mean(bias) + 0.5, + ], + ) - Args: - x (ndarray) frequencies - y (ndarray) bias - z (ndarray) signal - Returns: - y_pred (ndarray) frequencies - x_pred (ndarray) bias - """ - max_x = np.max(x) - min_x = np.min(x) - lenx = int((max_x - min_x) / (x[1] - x[0])) + 1 - max_y = np.max(y) - min_y = np.min(y) - leny = int(len(y) / (lenx)) - z = np.array(z, float) - if len(z) != leny * lenx: - lenx += 1 - leny = int(len(y) / (lenx)) - x = np.linspace(min_x, max_x, lenx) - y = np.linspace(min_y, max_y, leny) - z = np.reshape(z, (leny, lenx)) - zmax, zmin = z.max(), z.min() - znorm = (z - zmin) / (zmax - zmin) - - # Mask out region - mask = znorm < signal_mask - z = np.argwhere(mask) - weights = znorm[mask] / float(znorm.max()) - # Column indices - x_fit = y[z[:, 0].reshape(-1, 1)] - # Row indices to predict. - y_fit = x[z[:, 1]] - - # Ridge regression, i.e., least squares with l2 regularization - A = feature(x_fit, order) - model = Ridge(alpha=alpha) - model.fit(A, y_fit, sample_weight=weights) - x_pred = y - X_pred = feature(x_pred, order) - y_pred = model.predict(X_pred) - return y_pred, x_pred - - -def get_resonator_freq_flux( - bias, sweetspot, flux_to_bias, asymmetry, g, brf, ssf_brf, Ec, Ej +def resonator_flux_dependence_fit_bounds( + qubit_frequency: float, bias: np.array, bare_resonator_frequency: float ): - """ - Estimate the resonator frequency for a give "bias". - - Args: - bias (float): Bias value where the resonator frequency should be estimated - sweetspot (float): qubit sweetspot - flux_to_bias (float): Resonator value to convert from flux to bias - asymmetry (float): Resonator asymmetry - g (float): Readout coupling - brf (float): Bare resonator frequency at sweetspot - ssf_brf (float): Estimated sweetspot qubit frequency divided by the bare_resonator_frequency - Ec (float): Readout Charge Energy - Ej (float): Readout Josephson Energy - - Returns: - freq_resonator (float): Estimated Resonator frequency at the provided bias point - """ - if ( - flux_to_bias == 0.0 - or asymmetry == 0.0 - or g == 0.0 - or brf == 0.0 - or Ec == 0.0 - or Ej == 0.0 - ): - raise ValueError( - "Not enough parameters to estimate the resonator frequency for the given bias" - ) - - if ssf_brf == 0: - # First order approximation used during resonator flux fitting - # 'sweetspot_0':p0, - # 'flux_to_bias':p1, - # 'asymmetry':p2, - # 'readout_coupling':p4, - # 'bare_resonator_frequency_0':p5 - # 'sweetspot_qubit_frequency/bare_resonator_frequency':p3, - freq_resonator = freq_r_transmon( - bias, - sweetspot, - flux_to_bias, - asymmetry, - ssf_brf, - g, - brf, - ) - else: - # Second order approximation used during resonator flux fitting - # 'sweetspot_0':p2, - # 'flux_to_bias':p3, - # 'asymmetry':p4, - # 'readout_coupling':p1, - # 'bare_resonator_frequency_0':p0, - # 'Ec':p5, - # 'Ej:p6' - freq_resonator = freq_r_mathieu( - bias, - brf, - g, - sweetspot, - flux_to_bias, - asymmetry, - Ec, - Ej, - ) - - return freq_resonator + """Returns bounds for resonator flux fit.""" + left_bound, right_bound = qubit_flux_dependence_fit_bounds( + qubit_frequency=qubit_frequency, bias=bias + ) + left_bound += [bare_resonator_frequency * HZ_TO_GHZ - 0.5, 0] + right_bound += [bare_resonator_frequency * HZ_TO_GHZ + 0.5, 1] + return (left_bound, right_bound) diff --git a/src/qibocal/protocols/characterization/randomized_benchmarking/circuit_tools.py b/src/qibocal/protocols/characterization/randomized_benchmarking/circuit_tools.py index d5677c7de..ddbc637f8 100644 --- a/src/qibocal/protocols/characterization/randomized_benchmarking/circuit_tools.py +++ b/src/qibocal/protocols/characterization/randomized_benchmarking/circuit_tools.py @@ -1,4 +1,5 @@ """Collection of function to generate qibo circuits.""" + from copy import deepcopy from typing import Callable diff --git a/src/qibocal/protocols/characterization/randomized_benchmarking/fitting.py b/src/qibocal/protocols/characterization/randomized_benchmarking/fitting.py index 3c61368c8..f4f25f28e 100644 --- a/src/qibocal/protocols/characterization/randomized_benchmarking/fitting.py +++ b/src/qibocal/protocols/characterization/randomized_benchmarking/fitting.py @@ -1,6 +1,7 @@ """In this python script the fitting methods for the gate set protocols are defined. They consist mostly of exponential decay fitting. """ + from typing import Optional, Union import numpy as np diff --git a/src/qibocal/protocols/characterization/readout_mitigation_matrix.py b/src/qibocal/protocols/characterization/readout_mitigation_matrix.py index e2be1abb7..cf4fcda57 100644 --- a/src/qibocal/protocols/characterization/readout_mitigation_matrix.py +++ b/src/qibocal/protocols/characterization/readout_mitigation_matrix.py @@ -31,9 +31,9 @@ class ReadoutMitigationMatrixParameters(Parameters): @dataclass class ReadoutMitigationMatrixResults(Results): - readout_mitigation_matrix: dict[ - tuple[QubitId, ...], npt.NDArray[np.float64] - ] = field(default_factory=dict) + readout_mitigation_matrix: dict[tuple[QubitId, ...], npt.NDArray[np.float64]] = ( + field(default_factory=dict) + ) """Readout mitigation matrices (inverse of measurement matrix).""" measurement_matrix: dict[tuple[QubitId, ...], npt.NDArray[np.float64]] = field( default_factory=dict diff --git a/src/qibocal/protocols/characterization/two_qubit_interaction/chevron.py b/src/qibocal/protocols/characterization/two_qubit_interaction/chevron.py index 31cbb5150..b4bb77991 100644 --- a/src/qibocal/protocols/characterization/two_qubit_interaction/chevron.py +++ b/src/qibocal/protocols/characterization/two_qubit_interaction/chevron.py @@ -1,4 +1,5 @@ """SWAP experiment for two qubit gates, chevron plot.""" + from dataclasses import dataclass, field from typing import Optional diff --git a/src/qibocal/protocols/characterization/two_qubit_interaction/chsh/utils.py b/src/qibocal/protocols/characterization/two_qubit_interaction/chsh/utils.py index 9665029f0..fea6225d1 100644 --- a/src/qibocal/protocols/characterization/two_qubit_interaction/chsh/utils.py +++ b/src/qibocal/protocols/characterization/two_qubit_interaction/chsh/utils.py @@ -1,6 +1,5 @@ """Auxiliary functions to run CHSH protocol.""" - READOUT_BASIS = ["ZZ", "ZX", "XZ", "XX"] diff --git a/src/qibocal/protocols/characterization/two_qubit_interaction/cz_virtualz.py b/src/qibocal/protocols/characterization/two_qubit_interaction/cz_virtualz.py index fa48a5268..ef2356122 100644 --- a/src/qibocal/protocols/characterization/two_qubit_interaction/cz_virtualz.py +++ b/src/qibocal/protocols/characterization/two_qubit_interaction/cz_virtualz.py @@ -1,4 +1,5 @@ """CZ virtual correction experiment for two qubit gates, tune landscape.""" + from dataclasses import dataclass, field from typing import Optional diff --git a/src/qibocal/protocols/characterization/utils.py b/src/qibocal/protocols/characterization/utils.py index ac95f3090..5a1ecefa0 100644 --- a/src/qibocal/protocols/characterization/utils.py +++ b/src/qibocal/protocols/characterization/utils.py @@ -597,7 +597,7 @@ def plot_results(data: Data, qubit: QubitId, qubit_states: list, fit: Results): "accuracy", "testing time [s]", "training time [s]", - ) + ), # pylint: disable=E1101 ) for i, model in enumerate(models_name): diff --git a/src/qibocal/update.py b/src/qibocal/update.py index b7c685cf0..7ed1bcb3c 100644 --- a/src/qibocal/update.py +++ b/src/qibocal/update.py @@ -1,4 +1,5 @@ """Helper functions to update parameters in platform.""" + from typing import Union from qibolab import pulses @@ -182,35 +183,9 @@ def anharmonicity(anharmonicity: float, platform: Platform, qubit: QubitId): platform.qubits[qubit].anharmonicity = int(anharmonicity) -def bare_resonator_frequency_sweetspot( - bare_resonator_frequency_sweetspot: float, platform: Platform, qubit: QubitId -): - platform.qubits[qubit].bare_resonator_frequency_sweetspot = int( - bare_resonator_frequency_sweetspot - ) - - -def flux_to_bias(flux_to_bias: float, platform: Platform, qubit: QubitId): - platform.qubits[qubit].flux_to_bias = float(flux_to_bias) - - def asymmetry(asymmetry: float, platform: Platform, qubit: QubitId): platform.qubits[qubit].asymmetry = float(asymmetry) -def ratio_sweetspot_qubit_freq_bare_resonator_freq( - ssf_brf: float, platform: Platform, qubit: QubitId -): - platform.qubits[qubit].ssf_brf = float(ssf_brf) - - -def charging_energy(Ec: float, platform: Platform, qubit: QubitId): - platform.qubits[qubit].Ec = float(Ec) - - -def josephson_energy(Ej: float, platform: Platform, qubit: QubitId): - platform.qubits[qubit].Ej = float(Ej) - - def coupling(g: float, platform: Platform, qubit: QubitId): platform.qubits[qubit].g = float(g) diff --git a/tests/test_auto.py b/tests/test_auto.py index 6ace2b6da..763046852 100644 --- a/tests/test_auto.py +++ b/tests/test_auto.py @@ -1,4 +1,5 @@ """Test graph execution.""" + import pathlib import pytest diff --git a/tests/test_cli_utils.py b/tests/test_cli_utils.py index 38bc14203..8123cfac6 100644 --- a/tests/test_cli_utils.py +++ b/tests/test_cli_utils.py @@ -1,4 +1,5 @@ """Testing cli utils.""" + from qibolab import create_platform from qibocal.cli.utils import create_qubits_dict diff --git a/tests/test_protocols.py b/tests/test_protocols.py index d94fd7f3f..6428867be 100644 --- a/tests/test_protocols.py +++ b/tests/test_protocols.py @@ -1,4 +1,5 @@ """Test routines' acquisition method using dummy platform""" + import pathlib import pytest @@ -95,8 +96,9 @@ def test_acquire_command(runcard, backend, platform, tmp_path): assert results_report.exit_code == 0 +@pytest.mark.parametrize("update", ["--update", "--no-update"]) @pytest.mark.parametrize("runcard", generate_runcard_single_protocol(), ids=idfn) -def test_fit_command(runcard, tmp_path): +def test_fit_command(runcard, update, tmp_path): """Test fit builder and report generated.""" (tmp_path / SINGLE_ACTION_RUNCARD).write_text(yaml.safe_dump(runcard)) runner = CliRunner() @@ -116,7 +118,7 @@ def test_fit_command(runcard, tmp_path): assert results.exit_code == 0 # perform fit - results_fit = runner.invoke(command, ["fit", str(tmp_path), "--no-update"]) + results_fit = runner.invoke(command, ["fit", str(tmp_path), update]) assert not results_fit.exception assert results_fit.exit_code == 0 diff --git a/tests/test_task_options.py b/tests/test_task_options.py index 37298cfc0..de5e0f46f 100644 --- a/tests/test_task_options.py +++ b/tests/test_task_options.py @@ -1,4 +1,5 @@ """Test routines' acquisition method using dummy platform""" + from copy import deepcopy import pytest diff --git a/tests/test_update.py b/tests/test_update.py index 0038f6ae4..7eab83aa8 100644 --- a/tests/test_update.py +++ b/tests/test_update.py @@ -1,4 +1,5 @@ """Testing update_* helper functions. """ + import random import re