From 8f78c9ca87a693fbe451f8b00617f90ee21aacbe Mon Sep 17 00:00:00 2001 From: olivers3uiuc Date: Wed, 8 Apr 2026 18:28:27 -0500 Subject: [PATCH 1/3] edited: each measure_dummy to use the datagen modeling instead of models from individual file classes. some classes at naming conflicts with fitfuncs.generic, so imports do not currently have consistent naming across files protocol --- .../operations/single_qubit/pi_spec.py | 10 ++- .../operations/single_qubit/power_rabi.py | 26 ++----- .../operations/single_qubit/res_spec.py | 32 ++------- .../single_qubit/res_spec_after_pi.py | 22 ++---- .../single_qubit/res_spec_vs_gain.py | 35 +++++---- .../operations/single_qubit/sat_spec.py | 72 +++---------------- .../protocols/operations/single_qubit/t1.py | 8 +-- .../protocols/operations/single_qubit/t2e.py | 8 +-- .../protocols/operations/single_qubit/t2r.py | 8 +-- 9 files changed, 62 insertions(+), 159 deletions(-) diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/pi_spec.py b/src/cqedtoolbox/protocols/operations/single_qubit/pi_spec.py index 0d28af5..a928bbf 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/pi_spec.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/pi_spec.py @@ -10,8 +10,9 @@ from labcore.analysis import DatasetAnalysis from labcore.analysis.fitfuncs.generic import Gaussian from labcore.measurement.storage import run_and_save_sweep -from labcore.measurement.sweep import sweep_parameter +from labcore.measurement.sweep import sweep_parameter, Sweep from labcore.measurement.record import record_as +from labcore.data.datagen import Gaussian as GaussianDataGen from labcore.data.datadict_storage import datadict_from_hdf5 from labcore.protocols.base import (ProtocolOperation, OperationStatus, serialize_fit_params, @@ -162,11 +163,8 @@ def _measure_dummy(self) -> Path: frequencies = np.linspace(self.start_freq(), self.end_freq(), int(self.steps())) center = (self.start_freq() + self.end_freq()) / 2 + self._SIM_CENTER - def generate(frequencies): - return (self._SIM_AMP * np.exp(-0.5 * ((frequencies - center) / self._SIM_SIGMA) ** 2) - + self._SIM_NOISE_AMP * (np.random.randn() + 1j * np.random.randn())) - - sweep = sweep_parameter("frequencies", frequencies, record_as(generate, "signal")) + generator = GaussianDataGen(x0=center, sigma=self._SIM_SIGMA, A=self._SIM_AMP, of=0, noise_std=self._SIM_NOISE_AMP) + sweep = sweep_parameter("frequencies", frequencies) * Sweep(record_as(generator.generate(frequencies), "signal")) loc, _ = run_and_save_sweep(sweep, "data", self.name) logger.info("Dummy measurement complete") return loc diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/power_rabi.py b/src/cqedtoolbox/protocols/operations/single_qubit/power_rabi.py index 4bd5211..7cc7ca2 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/power_rabi.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/power_rabi.py @@ -10,7 +10,8 @@ from labcore.analysis import DatasetAnalysis from labcore.analysis.fitfuncs.generic import Cosine from labcore.measurement.storage import run_and_save_sweep -from labcore.measurement import sweep_parameter, record_as +from labcore.measurement import sweep_parameter, record_as, Sweep +from labcore.data.datagen import PowerRabi as PowerRabiDataGen from labcore.data.datadict_storage import datadict_from_hdf5 from labcore.protocols.base import ( @@ -258,20 +259,6 @@ def report_output(self) -> str: return f"gain range: [{self._last_new_start:.3f}, {self._last_new_end:.3f}]" -# --------------------------------------------------------------------------- -# Synthetic data helper -# --------------------------------------------------------------------------- - -@dataclass -class SyntheticPowerRabiData: - pi_amp: float - noise_amp: float - - def generate(self, gains: float) -> np.complex128: - signal = (np.cos(2 * np.pi * gains / (2 * self.pi_amp)) + 2) - 1j * (np.cos(2 * np.pi * gains / (2 * self.pi_amp)) + 2) - noise = self.noise_amp * (np.random.randn() + 1j * np.random.randn()) - return signal + noise - # --------------------------------------------------------------------------- # Operation @@ -371,14 +358,9 @@ def _measure_qick(self) -> Path: def _measure_dummy(self): logger.info("Starting dummy power rabi measurement") gains = np.linspace(self.start_gain(), self.end_gain(), int(self.steps_gain())) - generator = SyntheticPowerRabiData( - pi_amp = self._SIM_PI_AMP, - noise_amp = self._SIM_NOISE_AMP - ) - - sweep = sweep_parameter("gains", gains, record_as(generator.generate, "signal")) + generator = PowerRabiDataGen(pi_amp=self._SIM_PI_AMP, noise_std=self._SIM_NOISE_AMP) + sweep = sweep_parameter("gains", gains) * Sweep(record_as(generator.generate(gains), "signal")) loc, _ = run_and_save_sweep(sweep, "data", self.name) - logger.info("Dummy measurement complete") return loc diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/res_spec.py b/src/cqedtoolbox/protocols/operations/single_qubit/res_spec.py index 383706e..0191dde 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/res_spec.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/res_spec.py @@ -9,7 +9,8 @@ from labcore.analysis import DatasetAnalysis, FitResult from labcore.measurement.storage import run_and_save_sweep from labcore.data.datadict_storage import datadict_from_hdf5 -from labcore.measurement import sweep_parameter, record_as +from labcore.measurement import sweep_parameter, record_as, Sweep +from labcore.data.datagen import HangerResonator from labcore.protocols.base import (ProtocolOperation, OperationStatus, serialize_fit_params, ParamImprovement, CorrectionParameter, CheckResult, Correction) @@ -36,20 +37,6 @@ class UnwindAndFitRet: fig: plt.Figure ax: plt.Axes -@dataclass -class SyntheticHangerResonatorData: - f0: float - Qc: float - Qi: float - A: float - phi: float - noise_amp: float - - def generate(self, frequencies: ArrayLike) -> ArrayLike: - Q_l = 1./(1./self.Qc + 1./self.Qi) - Q_e_complex = self.Qc * np.exp(-1j * self.phi) - response = self.A * (1 - (Q_l / Q_e_complex) / (1 + 2j * Q_l * (frequencies - self.f0) / self.f0)) - return response + self.noise_amp * (np.random.randn() + 1j * np.random.randn()) @dataclass @@ -351,19 +338,10 @@ def _measure_qick(self) -> Path: def _measure_dummy(self): logger.info("Starting dummy resonator spectroscopy measurement") - frequencies = np.linspace(self.start_frequency(), self.end_frequency(), int(self.steps())) - generator = SyntheticHangerResonatorData( - f0 = self._SIM_F0, - Qi = self._SIM_QI, - Qc = self._SIM_QC, - A = self._SIM_A, - phi = self._SIM_PHI, - noise_amp = self._SIM_NOISE_AMP - ) - - sweep = sweep_parameter("frequencies", frequencies + self.readout_lo(), record_as(generator.generate, "signal")) + frequencies = np.linspace(self.start_frequency(), self.end_frequency(), int(self.steps())) + self.readout_freq() + generator = HangerResonator(f0=self._SIM_F0, Qc=self._SIM_QC, Qi=self._SIM_QI, A=self._SIM_A, phi=self._SIM_PHI, noise_std=self._SIM_NOISE_AMP) + sweep = sweep_parameter("frequencies", frequencies) * Sweep(record_as(generator.generate(frequencies), "signal")) loc, _ = run_and_save_sweep(sweep, "data", self.name) - logger.info("Dummy measurement complete") return loc diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_after_pi.py b/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_after_pi.py index 88eea2d..6d2a486 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_after_pi.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_after_pi.py @@ -9,8 +9,9 @@ from labcore.analysis import DatasetAnalysis from labcore.measurement.storage import run_and_save_sweep -from labcore.measurement.sweep import sweep_parameter +from labcore.measurement.sweep import sweep_parameter, Sweep from labcore.measurement.record import record_as +from labcore.data.datagen import HangerResonator from labcore.data.datadict_storage import datadict_from_hdf5 from labcore.protocols.base import (ProtocolOperation, OperationStatus, serialize_fit_params, @@ -27,7 +28,7 @@ from cqedtoolbox.measurement_lib.qick.single_transmon_v2 import FreqSweepProgram, ResProbeProgram from cqedtoolbox.fitfuncs.resonators import HangerResponseBruno -from cqedtoolbox.protocols.operations.single_qubit.res_spec import SyntheticHangerResonatorData +from cqedtoolbox.protocols.operations.single_qubit.res_spec import ResonatorSpectroscopy as _RS logger = logging.getLogger(__name__) @@ -126,23 +127,14 @@ def __init__(self, params): def _measure_dummy(self) -> Path: logger.info("Starting dummy resonator spectroscopy before/after pi measurement") - from cqedtoolbox.protocols.operations.single_qubit.res_spec import ResonatorSpectroscopy as _RS frequencies = np.linspace(self.start_freq(), self.end_freq(), int(self.steps())) - gen_before = SyntheticHangerResonatorData( - f0=_RS._SIM_F0, Qi=_RS._SIM_QI, Qc=_RS._SIM_QC, - A=_RS._SIM_A, phi=_RS._SIM_PHI, noise_amp=_RS._SIM_NOISE_AMP - ) - # signal_before = gen_before.generate(frequencies) - sweep_before = sweep_parameter("frequencies", frequencies, record_as(gen_before.generate, "signal")) + gen_before = HangerResonator(f0=_RS._SIM_F0, Qc=_RS._SIM_QC, Qi=_RS._SIM_QI, A=_RS._SIM_A, phi=_RS._SIM_PHI, noise_std=_RS._SIM_NOISE_AMP) + sweep_before = sweep_parameter("frequencies", frequencies) * Sweep(record_as(gen_before.generate(frequencies), "signal")) loc_before, _ = run_and_save_sweep(sweep_before, "data", f"{self.name}_before") - gen_after = SyntheticHangerResonatorData( - f0=_RS._SIM_F0 + self._SIM_CHI, Qi=_RS._SIM_QI, Qc=_RS._SIM_QC, - A=_RS._SIM_A, phi=_RS._SIM_PHI, noise_amp=_RS._SIM_NOISE_AMP - ) - # signal_after = gen_after.generate(frequencies) - sweep_after = sweep_parameter("frequencies", frequencies, record_as(gen_after.generate, "signal")) + gen_after = HangerResonator(f0=_RS._SIM_F0 + self._SIM_CHI, Qc=_RS._SIM_QC, Qi=_RS._SIM_QI, A=_RS._SIM_A, phi=_RS._SIM_PHI, noise_std=_RS._SIM_NOISE_AMP) + sweep_after = sweep_parameter("frequencies", frequencies) * Sweep(record_as(gen_after.generate(frequencies), "signal")) loc_after, _ = run_and_save_sweep(sweep_after, "data", f"{self.name}_after") self.data_loc_before = loc_before diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_vs_gain.py b/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_vs_gain.py index 67335a2..15d43e8 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_vs_gain.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_vs_gain.py @@ -11,6 +11,7 @@ from labcore.analysis import DatasetAnalysis from labcore.measurement.storage import run_and_save_sweep from labcore.data.datadict_storage import datadict_from_hdf5 +from labcore.data.datagen import HangerResonator from labcore.measurement import sweep_parameter from labcore.measurement.record import recording, dep, indep @@ -23,7 +24,7 @@ ReadoutGain, ReadoutLength, StartReadoutGain, EndReadoutGain, ResonatorSpecSteps, ResonatorSpecVsGainSteps, ) -from cqedtoolbox.protocols.operations.single_qubit.res_spec import ResonatorSpectroscopy, SyntheticHangerResonatorData +from cqedtoolbox.protocols.operations.single_qubit.res_spec import ResonatorSpectroscopy as _RS from cqedtoolbox.measurement_lib.qick.single_transmon_v2 import FreqGainSweepProgram @@ -35,6 +36,9 @@ class ResSpecVsGainSNRThreshold(CorrectionParameter): name: str = field(default="res_spec_vs_gain_snr_threshold", init=False) description: str = field(default="SNR threshold for low-gain quality check", init=False) + # def _dummy_getter(self): return 2.0 # hardcoded default for DUMMY mode so analyze() runs without a real params object + # def _dummy_setter(self, _): pass + def _qick_getter(self): return self.params.corrections.res_spec_vs_gain.snr() @@ -47,6 +51,9 @@ class ResSpecVsGainMaxFitParamError(CorrectionParameter): name: str = field(default="res_spec_vs_gain_max_fit_param_error", init=False) description: str = field(default="Max fractional fit parameter error (e.g. 1.0 = 100%)", init=False) + # def _dummy_getter(self): return 1.0 # hardcoded default for DUMMY mode so analyze() runs without a real params object + # def _dummy_setter(self, _): pass + def _qick_getter(self): return self.params.corrections.res_spec_vs_gain.max_fit_param_error() @@ -59,6 +66,9 @@ class ResSpecVsGainHighSNRThreshold(CorrectionParameter): name: str = field(default="res_spec_vs_gain_high_snr_threshold", init=False) description: str = field(default="High SNR threshold — at least one trace must exceed this", init=False) + # def _dummy_getter(self): return 2.0 # hardcoded default for DUMMY mode so analyze() runs without a real params object + # def _dummy_setter(self, _): pass + def _qick_getter(self): return self.params.corrections.res_spec_vs_gain.high_snr() @@ -71,6 +81,9 @@ class ResSpecVsGainRepetitionFactor(CorrectionParameter): name: str = field(default="res_spec_vs_gain_repetition_factor", init=False) description: str = field(default="Factor by which repetitions are increased on retry", init=False) + # def _dummy_getter(self): return 2.0 # hardcoded default for DUMMY mode so analyze() runs without a real params object + # def _dummy_setter(self, _): pass + def _qick_getter(self): return self.params.corrections.res_spec_vs_gain.rep_factor() @@ -83,6 +96,9 @@ class ResSpecVsGainMaxRepetitionIncreases(CorrectionParameter): name: str = field(default="res_spec_vs_gain_max_rep_increases", init=False) description: str = field(default="Maximum number of repetition increases to attempt", init=False) + # def _dummy_getter(self): return 3 # hardcoded default for DUMMY mode so analyze() runs without a real params object + # def _dummy_setter(self, _): pass + def _qick_getter(self): return int(self.params.corrections.res_spec_vs_gain.max_rep_increases()) @@ -203,16 +219,9 @@ def generate_signals(frequencies): gains = np.linspace(self.start_gain(), self.end_gain(), self._SIM_N_GAIN_STEPS) ret_signal = [] for i in range(self._SIM_N_GAIN_STEPS): - shifted_center = ResonatorSpectroscopy._SIM_F0 + freq_shift_per_gain_unit * i - generator = SyntheticHangerResonatorData( - f0=shifted_center, - Qi=ResonatorSpectroscopy._SIM_QI, - Qc=ResonatorSpectroscopy._SIM_QC, - A=ResonatorSpectroscopy._SIM_A, - phi=ResonatorSpectroscopy._SIM_PHI, - noise_amp=ResonatorSpectroscopy._SIM_NOISE_AMP - ) - ret_signal.append(generator.generate(frequencies)) + shifted_center = _RS._SIM_F0 + freq_shift_per_gain_unit * i + generator = HangerResonator(f0=shifted_center, Qc=_RS._SIM_QC, Qi=_RS._SIM_QI, A=_RS._SIM_A, phi=_RS._SIM_PHI, noise_std=_RS._SIM_NOISE_AMP) + ret_signal.append(generator.generate(np.atleast_1d(frequencies))) return gains, ret_signal @@ -304,7 +313,7 @@ def analyze(self): folder_name = f"resonator_spec_vs_gain_i={i}_g={g}" # Use the static method from ResonatorSpectroscopy - ret = ResonatorSpectroscopy.add_mag_and_unwind_and_fit( + ret = _RS.add_mag_and_unwind_and_fit( freqs, trace_signal, f"Gain = {g}" ) @@ -318,7 +327,7 @@ def analyze(self): f"Trace {i} (gain={g}): stderr is None for params " f"{_null_stderr_params} — re-fitting" ) - ret = ResonatorSpectroscopy.add_mag_and_unwind_and_fit( + ret = _RS.add_mag_and_unwind_and_fit( freqs, trace_signal, f"Gain = {g}" ) diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/sat_spec.py b/src/cqedtoolbox/protocols/operations/single_qubit/sat_spec.py index 6abab81..b11abc1 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/sat_spec.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/sat_spec.py @@ -3,9 +3,7 @@ from dataclasses import dataclass, field import numpy as np -from numpy.typing import ArrayLike import matplotlib.pyplot as plt -from scipy.constants import h plt.switch_backend("agg") @@ -13,8 +11,9 @@ from labcore.analysis.fitfuncs.generic import Lorentzian from labcore.measurement.storage import run_and_save_sweep from labcore.data.datadict_storage import datadict_from_hdf5 -from labcore.measurement.sweep import sweep_parameter +from labcore.measurement.sweep import sweep_parameter, Sweep from labcore.measurement.record import record_as +from labcore.data.datagen import Lorentzian as LorentzianDataGen from labcore.protocols.base import (ProtocolOperation, OperationStatus, serialize_fit_params, ParamImprovement, CorrectionParameter, CheckResult, Correction, @@ -359,29 +358,6 @@ def report_output(self) -> str: return self._last_change -# --------------------------------------------------------------------------- -# Synthetic data helper -# --------------------------------------------------------------------------- - -@dataclass -class SyntheticSatSpecData: - fq: float - delta_fr: float - f_rabi: float - gamma1: float - gamma2: float - angle: float - noise_amp: float - - def generate(self, frequencies: ArrayLike) -> ArrayLike: - """Saturation spec model based on Blais circuit qed eqn 127""" - signal = 0.5 * self.f_rabi**2 / ( - self.gamma1 * self.gamma2 + (frequencies - self.fq) ** 2 * self.gamma1 / self.gamma2 + self.f_rabi ** 2 - ) - signal_re = signal * np.cos(self.angle) + self.noise_amp * np.random.randn(*frequencies.shape) - signal_imag = signal * np.sin(self.angle) + self.noise_amp * np.random.randn(*frequencies.shape) - return signal_re + 1j * signal_imag - # --------------------------------------------------------------------------- # Operation @@ -390,23 +366,9 @@ def generate(self, frequencies: ArrayLike) -> ArrayLike: class SaturationSpectroscopy(ProtocolOperation): _DUMMY_F_Q = 5e9 - _DUMMY_F_R = 7e9 - _DUMMY_DELTA = _DUMMY_F_R - _DUMMY_F_Q - - _DUMMY_P_IN = 1e-16 - _DUMMY_G = 50e6 - _DUMMY_KAPPA_R = 0.2e6 - - _DUMMY_T1 = 50e-6 - _DUMMY_T2 = 50e-6 - _DUMMY_GAMMA_1 = 1 / _DUMMY_T1 - _DUMMY_GAMMA_2 = 1 / (np.pi * _DUMMY_T2) - - _DUMMY_OMEGA = 2 * (_DUMMY_G / _DUMMY_DELTA) * np.sqrt(_DUMMY_KAPPA_R * _DUMMY_P_IN / (h * _DUMMY_F_Q)) - _DUMMY_GAMMA_Q = np.sqrt( (1 / _DUMMY_T2) ** 2 + ((2 * np.pi * _DUMMY_OMEGA) ** 2 * _DUMMY_T1 / _DUMMY_T2) ) / np.pi # blais eq 127 - + _DUMMY_GAMMA = 1e6 + _DUMMY_A = 0.5 _DUMMY_NOISE_AMP = 0.05 - _DUMMY_ANGLE = np.pi / 4 def __init__(self, params): @@ -510,30 +472,12 @@ def _load_data_qick(self): self.dependents["signal"] = data["signal"]["values"] def _measure_dummy(self) -> Path: - """Create synthetic saturation spectroscopy data using a sweep""" logger.info("Starting dummy saturation spectroscopy measurement") - - start_f = self.start_freq() - end_f = self.end_freq() - num_steps = int(self.steps()) - - frequencies = np.linspace(start_f, end_f, num_steps) - - generator = SyntheticSatSpecData( - fq=self._DUMMY_F_Q, - delta_fr=self._DUMMY_DELTA, - f_rabi=self._DUMMY_OMEGA, - gamma1=self._DUMMY_GAMMA_1, - gamma2=self._DUMMY_GAMMA_2, - angle=self._DUMMY_ANGLE, - noise_amp=self._DUMMY_NOISE_AMP - ) - - sweep = sweep_parameter('frequencies', frequencies, record_as(generator.generate, 'signal')) - + frequencies = np.linspace(self.start_freq(), self.end_freq(), int(self.steps())) + generator = LorentzianDataGen(x0=self._DUMMY_F_Q, gamma=self._DUMMY_GAMMA, A=self._DUMMY_A, of=0, noise_std=self._DUMMY_NOISE_AMP) + sweep = sweep_parameter('frequencies', frequencies) * Sweep(record_as(generator.generate(frequencies), 'signal')) loc, _ = run_and_save_sweep(sweep, "data", self.name) - logger.info("Dummy saturation spectroscopy measurement complete.") - + logger.info("Dummy measurement complete") return loc def _load_data_dummy(self): diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/t1.py b/src/cqedtoolbox/protocols/operations/single_qubit/t1.py index 1c08201..7771c1b 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/t1.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/t1.py @@ -10,8 +10,9 @@ from labcore.analysis import DatasetAnalysis from labcore.analysis.fitfuncs.generic import ExponentialDecay from labcore.measurement.storage import run_and_save_sweep -from labcore.measurement.sweep import sweep_parameter +from labcore.measurement.sweep import sweep_parameter, Sweep from labcore.measurement.record import record_as +from labcore.data.datagen import ExponentialDecay as ExponentialDecayDataGen from labcore.data.datadict_storage import datadict_from_hdf5 from labcore.protocols.base import ( @@ -235,9 +236,8 @@ def __init__(self, params): def _measure_dummy(self) -> Path: logger.info("Starting dummy T1 measurement") delays = np.linspace(0, 5 * self._SIM_T1, int(self.steps())) - signal_gen = lambda delays: (self._SIM_AMP * np.exp(-delays / self._SIM_T1) - + self._SIM_NOISE_AMP * (np.random.randn() + 1j * np.random.randn())) - sweep = sweep_parameter("delays", delays, record_as(signal_gen, "signal")) + generator = ExponentialDecayDataGen(A=self._SIM_AMP, tau=self._SIM_T1, of=0, noise_std=self._SIM_NOISE_AMP) + sweep = sweep_parameter("delays", delays) * Sweep(record_as(generator.generate(delays), "signal")) loc, _ = run_and_save_sweep(sweep, "data", self.name) logger.info("Dummy measurement complete") return loc diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/t2e.py b/src/cqedtoolbox/protocols/operations/single_qubit/t2e.py index 78772a1..3e062b3 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/t2e.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/t2e.py @@ -10,8 +10,9 @@ from labcore.analysis import DatasetAnalysis from labcore.analysis.fitfuncs.generic import ExponentiallyDecayingSine from labcore.measurement.storage import run_and_save_sweep -from labcore.measurement.sweep import sweep_parameter +from labcore.measurement.sweep import sweep_parameter, Sweep from labcore.measurement.record import record_as +from labcore.data.datagen import ExponentialDecayingSine from labcore.data.datadict_storage import datadict_from_hdf5 from labcore.protocols.base import ( @@ -221,9 +222,8 @@ def __init__(self, params): def _measure_dummy(self) -> Path: logger.info("Starting dummy T2 Echo measurement") delays = np.linspace(0, 5 * self._SIM_T2E, int(self.steps())) - signal_gen = lambda delays: (self._SIM_AMP * np.exp(-delays / self._SIM_T2E) * np.exp(2j * np.pi * self._SIM_DETUNING * delays) - + self._SIM_NOISE_AMP * (np.random.randn() + 1j * np.random.randn())) - sweep = sweep_parameter("delays", delays, record_as(signal_gen, "signal")) + generator = ExponentialDecayingSine(A=self._SIM_AMP, f=self._SIM_DETUNING, phi=0, tau=self._SIM_T2E, of=0, noise_std=self._SIM_NOISE_AMP) + sweep = sweep_parameter("delays", delays) * Sweep(record_as(generator.generate(delays), "signal")) loc, _ = run_and_save_sweep(sweep, "data", self.name) logger.info("Dummy measurement complete") return loc diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/t2r.py b/src/cqedtoolbox/protocols/operations/single_qubit/t2r.py index 53204b8..413bf38 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/t2r.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/t2r.py @@ -10,8 +10,9 @@ from labcore.analysis import DatasetAnalysis from labcore.analysis.fitfuncs.generic import ExponentiallyDecayingSine from labcore.measurement.storage import run_and_save_sweep -from labcore.measurement.sweep import sweep_parameter +from labcore.measurement.sweep import sweep_parameter, Sweep from labcore.measurement.record import record_as +from labcore.data.datagen import ExponentialDecayingSine from labcore.data.datadict_storage import datadict_from_hdf5 from labcore.protocols.base import ( @@ -170,9 +171,8 @@ def __init__(self, params): def _measure_dummy(self) -> Path: logger.info("Starting dummy T2 Ramsey measurement") delays = np.linspace(0, 5 * self._SIM_T2R, int(self.steps())) - signal_gen = lambda delays: (self._SIM_AMP * np.exp(-delays / self._SIM_T2R) * np.exp(2j * np.pi * self._SIM_DETUNING * delays) - + self._SIM_NOISE_AMP * (np.random.randn() + 1j * np.random.randn())) - sweep = sweep_parameter("delays", delays, record_as(signal_gen, "signal")) + generator = ExponentialDecayingSine(A=self._SIM_AMP, f=self._SIM_DETUNING, phi=0, tau=self._SIM_T2R, of=0, noise_std=self._SIM_NOISE_AMP) + sweep = sweep_parameter("delays", delays) * Sweep(record_as(generator.generate(delays), "signal")) loc, _ = run_and_save_sweep(sweep, "data", self.name) logger.info("Dummy measurement complete") return loc From b5d8ac9c80434ae48dfb67f89a94767905ed6a3f Mon Sep 17 00:00:00 2001 From: olivers3uiuc Date: Tue, 14 Apr 2026 19:27:00 -0500 Subject: [PATCH 2/3] added dummy getters for each protocol. sweeps aren't zipped (achieved using lambda functions). hanger resonator and sat spec moved from datagen in labcore repo to cqedtoolbox repo because not generic. --- .../operations/single_qubit/pi_spec.py | 12 ++- .../operations/single_qubit/power_rabi.py | 24 +++++- .../operations/single_qubit/res_spec.py | 51 ++++++++++-- .../single_qubit/res_spec_after_pi.py | 14 +++- .../single_qubit/res_spec_vs_gain.py | 17 ++-- .../operations/single_qubit/sat_spec.py | 79 +++++++++++++++++-- .../protocols/operations/single_qubit/t1.py | 16 +++- .../protocols/operations/single_qubit/t2e.py | 14 +++- .../protocols/operations/single_qubit/t2r.py | 12 ++- 9 files changed, 202 insertions(+), 37 deletions(-) diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/pi_spec.py b/src/cqedtoolbox/protocols/operations/single_qubit/pi_spec.py index a928bbf..290a4e5 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/pi_spec.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/pi_spec.py @@ -10,7 +10,7 @@ from labcore.analysis import DatasetAnalysis from labcore.analysis.fitfuncs.generic import Gaussian from labcore.measurement.storage import run_and_save_sweep -from labcore.measurement.sweep import sweep_parameter, Sweep +from labcore.measurement.sweep import sweep_parameter from labcore.measurement.record import record_as from labcore.data.datagen import Gaussian as GaussianDataGen from labcore.data.datadict_storage import datadict_from_hdf5 @@ -38,6 +38,8 @@ class PiSpecSNRThreshold(CorrectionParameter): name: str = field(default="pi_spec_snr_threshold", init=False) description: str = field(default="Minimum SNR for a successful pi spectroscopy fit", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.pi_spec.snr() @@ -50,6 +52,8 @@ class PiSpecMaxFitParamError(CorrectionParameter): name: str = field(default="pi_spec_max_fit_param_error", init=False) description: str = field(default="Max allowed fractional fit parameter error (e.g. 1.0 = 100%)", init=False) + def _dummy_getter(self): return 1.0 + def _qick_getter(self): return self.params.corrections.pi_spec.max_fit_param_error() @@ -62,6 +66,8 @@ class PiSpecAveragingFactor(CorrectionParameter): name: str = field(default="pi_spec_averaging_factor", init=False) description: str = field(default="Factor by which to multiply repetitions on retry", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.pi_spec.averaging_factor() @@ -74,6 +80,8 @@ class PiSpecMaxAveragingIncreases(CorrectionParameter): name: str = field(default="pi_spec_max_averaging_increases", init=False) description: str = field(default="Maximum number of repetition increases to attempt", init=False) + def _dummy_getter(self): return 3 + def _qick_getter(self): return int(self.params.corrections.pi_spec.max_averaging_increases()) @@ -164,7 +172,7 @@ def _measure_dummy(self) -> Path: center = (self.start_freq() + self.end_freq()) / 2 + self._SIM_CENTER generator = GaussianDataGen(x0=center, sigma=self._SIM_SIGMA, A=self._SIM_AMP, of=0, noise_std=self._SIM_NOISE_AMP) - sweep = sweep_parameter("frequencies", frequencies) * Sweep(record_as(generator.generate(frequencies), "signal")) + sweep = sweep_parameter("frequencies", frequencies, record_as(lambda frequencies: generator.generate(np.atleast_1d(frequencies)), "signal")) loc, _ = run_and_save_sweep(sweep, "data", self.name) logger.info("Dummy measurement complete") return loc diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/power_rabi.py b/src/cqedtoolbox/protocols/operations/single_qubit/power_rabi.py index 7cc7ca2..6f965ad 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/power_rabi.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/power_rabi.py @@ -10,7 +10,7 @@ from labcore.analysis import DatasetAnalysis from labcore.analysis.fitfuncs.generic import Cosine from labcore.measurement.storage import run_and_save_sweep -from labcore.measurement import sweep_parameter, record_as, Sweep +from labcore.measurement import sweep_parameter, record_as from labcore.data.datagen import PowerRabi as PowerRabiDataGen from labcore.data.datadict_storage import datadict_from_hdf5 @@ -41,6 +41,8 @@ class SNRThreshold(CorrectionParameter): name: str = field(default="power_rabi_snr_threshold", init=False) description: str = field(default="SNR threshold for power rabi quality check", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.power_rabi.snr() def _qick_setter(self, v): self.params.corrections.power_rabi.snr(v) @@ -50,6 +52,8 @@ class MaxFitParamError(CorrectionParameter): name: str = field(default="power_rabi_max_fit_param_error", init=False) description: str = field(default="Maximum allowed fractional fit parameter error (e.g. 1.0 = 100%)", init=False) + def _dummy_getter(self): return 1.0 + def _qick_getter(self): return self.params.corrections.power_rabi.max_fit_param_error() def _qick_setter(self, v): self.params.corrections.power_rabi.max_fit_param_error(v) @@ -59,6 +63,8 @@ class AveragingIncreaseFactor(CorrectionParameter): name: str = field(default="power_rabi_averaging_factor", init=False) description: str = field(default="Factor by which to increase repetitions", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.power_rabi.averaging_factor() def _qick_setter(self, v): self.params.corrections.power_rabi.averaging_factor(v) @@ -68,6 +74,8 @@ class MaxAveragingIncreases(CorrectionParameter): name: str = field(default="power_rabi_max_averaging_increases", init=False) description: str = field(default="Maximum number of averaging increases to try", init=False) + def _dummy_getter(self): return 3 + def _qick_getter(self): return int(self.params.corrections.power_rabi.max_averaging_increases()) def _qick_setter(self, v): self.params.corrections.power_rabi.max_averaging_increases(v) @@ -77,6 +85,8 @@ class SamplingIncreaseFactor(CorrectionParameter): name: str = field(default="power_rabi_sampling_factor", init=False) description: str = field(default="Factor by which to increase gain steps", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.power_rabi.sampling_factor() def _qick_setter(self, v): self.params.corrections.power_rabi.sampling_factor(v) @@ -86,6 +96,8 @@ class MaxSamplingIncreases(CorrectionParameter): name: str = field(default="power_rabi_max_sampling_increases", init=False) description: str = field(default="Maximum number of step count increases to try", init=False) + def _dummy_getter(self): return 3 + def _qick_getter(self): return int(self.params.corrections.power_rabi.max_sampling_increases()) def _qick_setter(self, v): self.params.corrections.power_rabi.max_sampling_increases(v) @@ -95,6 +107,8 @@ class DelayIncreaseFactor(CorrectionParameter): name: str = field(default="power_rabi_delay_factor", init=False) description: str = field(default="Factor by which to increase delay between shots", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.power_rabi.delay_factor() def _qick_setter(self, v): self.params.corrections.power_rabi.delay_factor(v) @@ -104,6 +118,8 @@ class MaxDelayIncreases(CorrectionParameter): name: str = field(default="power_rabi_max_delay_increases", init=False) description: str = field(default="Maximum number of delay increases to try", init=False) + def _dummy_getter(self): return 3 + def _qick_getter(self): return int(self.params.corrections.power_rabi.max_delay_increases()) def _qick_setter(self, v): self.params.corrections.power_rabi.max_delay_increases(v) @@ -113,6 +129,8 @@ class GainRangeShrinkFactor(CorrectionParameter): name: str = field(default="power_rabi_gain_shrink_factor", init=False) description: str = field(default="Factor by which to divide the gain half-span on each shrink", init=False) + def _dummy_getter(self): return 0.5 + def _qick_getter(self): return self.params.corrections.power_rabi.gain_shrink_factor() def _qick_setter(self, v): self.params.corrections.power_rabi.gain_shrink_factor(v) @@ -122,6 +140,8 @@ class MaxGainRangeShrinks(CorrectionParameter): name: str = field(default="power_rabi_max_gain_shrinks", init=False) description: str = field(default="Maximum number of gain range shrink steps to try", init=False) + def _dummy_getter(self): return 3 + def _qick_getter(self): return int(self.params.corrections.power_rabi.max_gain_shrinks()) def _qick_setter(self, v): self.params.corrections.power_rabi.max_gain_shrinks(v) @@ -359,7 +379,7 @@ def _measure_dummy(self): logger.info("Starting dummy power rabi measurement") gains = np.linspace(self.start_gain(), self.end_gain(), int(self.steps_gain())) generator = PowerRabiDataGen(pi_amp=self._SIM_PI_AMP, noise_std=self._SIM_NOISE_AMP) - sweep = sweep_parameter("gains", gains) * Sweep(record_as(generator.generate(gains), "signal")) + sweep = sweep_parameter("gains", gains, record_as(lambda gains: generator.generate(np.atleast_1d(gains)), "signal")) loc, _ = run_and_save_sweep(sweep, "data", self.name) logger.info("Dummy measurement complete") return loc diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/res_spec.py b/src/cqedtoolbox/protocols/operations/single_qubit/res_spec.py index 0191dde..a8ad4cd 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/res_spec.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/res_spec.py @@ -1,16 +1,17 @@ import logging from pathlib import Path from dataclasses import dataclass, field +from typing import Any import numpy as np -from numpy.typing import ArrayLike +from numpy.typing import ArrayLike, NDArray import matplotlib.pyplot as plt from labcore.analysis import DatasetAnalysis, FitResult from labcore.measurement.storage import run_and_save_sweep from labcore.data.datadict_storage import datadict_from_hdf5 -from labcore.measurement import sweep_parameter, record_as, Sweep -from labcore.data.datagen import HangerResonator +from labcore.measurement import sweep_parameter, record_as +from labcore.data.datagen import DataGen from labcore.protocols.base import (ProtocolOperation, OperationStatus, serialize_fit_params, ParamImprovement, CorrectionParameter, CheckResult, Correction) @@ -44,6 +45,8 @@ class SNRThreshold(CorrectionParameter): name: str = field(default="resonator_spec_SNR_threshold", init=False) description: str = field(default="SNR threshold", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.res_spec.snr() @@ -56,6 +59,8 @@ class MaxWindowShifts(CorrectionParameter): name: str = field(default="res_spec_max_window_shifts", init=False) description: str = field(default="Number of ±n window shifts to try", init=False) + def _dummy_getter(self): return 3 + def _qick_getter(self): return int(self.params.corrections.res_spec.max_window_shifts()) @@ -68,6 +73,8 @@ class SamplingIncreaseFactor(CorrectionParameter): name: str = field(default="res_spec_sampling_increase_factor", init=False) description: str = field(default="Factor by which to increase frequency steps", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.res_spec.sampling_factor() @@ -80,6 +87,8 @@ class MaxSamplingIncreases(CorrectionParameter): name: str = field(default="res_spec_max_sampling_increases", init=False) description: str = field(default="Maximum number of sampling rate increases to try", init=False) + def _dummy_getter(self): return 3 + def _qick_getter(self): return int(self.params.corrections.res_spec.max_sampling_increases()) @@ -92,6 +101,8 @@ class AveragingIncreaseFactor(CorrectionParameter): name: str = field(default="res_spec_averaging_increase_factor", init=False) description: str = field(default="Factor by which to increase repetitions", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.res_spec.averaging_factor() @@ -104,6 +115,8 @@ class MaxAveragingIncreases(CorrectionParameter): name: str = field(default="res_spec_max_averaging_increases", init=False) description: str = field(default="Maximum number of averaging increases to try", init=False) + def _dummy_getter(self): return 3 + def _qick_getter(self): return int(self.params.corrections.res_spec.max_averaging_increases()) @@ -116,6 +129,8 @@ class MaxFitParamError(CorrectionParameter): name: str = field(default="res_spec_max_fit_param_error", init=False) description: str = field(default="Maximum allowed fractional fit parameter error (e.g. 1.0 = 100%)", init=False) + def _dummy_getter(self): return 1.0 + def _qick_getter(self): return self.params.corrections.res_spec.max_fit_param_error() @@ -239,6 +254,32 @@ def report_output(self) -> str: return self._last_change +# --------------------------------------------------------------------------- +# HangerResonator DataGen +# --------------------------------------------------------------------------- + +@dataclass +class HangerResonator(DataGen): + A: float = 1 + Qc: float = 1000 + Qi: float = 1000 + f0: float = 1e9 + phi: float = 0 + imaginary: bool = True + + @staticmethod + def model( + coordinates: NDArray[Any], A: float, Qc: float, Qi: float, f0: float, phi: float + ) -> NDArray[Any]: + Q_l = 1.0 / (1.0 / Qc + 1.0 / Qi) + Q_e_complex = Qc * np.exp(-1j * phi) + return A * (1 - (Q_l / Q_e_complex) / (1 + 2j * Q_l * (coordinates - f0) / f0)) + + +# --------------------------------------------------------------------------- +# Operation +# --------------------------------------------------------------------------- + class ResonatorSpectroscopy(ProtocolOperation): _SIM_F0 = 7e9 @@ -338,9 +379,9 @@ def _measure_qick(self) -> Path: def _measure_dummy(self): logger.info("Starting dummy resonator spectroscopy measurement") - frequencies = np.linspace(self.start_frequency(), self.end_frequency(), int(self.steps())) + self.readout_freq() + frequencies = np.linspace(self.start_frequency(), self.end_frequency(), int(self.steps())) generator = HangerResonator(f0=self._SIM_F0, Qc=self._SIM_QC, Qi=self._SIM_QI, A=self._SIM_A, phi=self._SIM_PHI, noise_std=self._SIM_NOISE_AMP) - sweep = sweep_parameter("frequencies", frequencies) * Sweep(record_as(generator.generate(frequencies), "signal")) + sweep = sweep_parameter("frequencies", frequencies, record_as(lambda frequencies: generator.generate(np.atleast_1d(frequencies)), "signal")) loc, _ = run_and_save_sweep(sweep, "data", self.name) logger.info("Dummy measurement complete") return loc diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_after_pi.py b/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_after_pi.py index 6d2a486..37e49df 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_after_pi.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_after_pi.py @@ -9,9 +9,9 @@ from labcore.analysis import DatasetAnalysis from labcore.measurement.storage import run_and_save_sweep -from labcore.measurement.sweep import sweep_parameter, Sweep +from labcore.measurement.sweep import sweep_parameter from labcore.measurement.record import record_as -from labcore.data.datagen import HangerResonator +from cqedtoolbox.protocols.operations.single_qubit.res_spec import HangerResonator from labcore.data.datadict_storage import datadict_from_hdf5 from labcore.protocols.base import (ProtocolOperation, OperationStatus, serialize_fit_params, @@ -41,6 +41,8 @@ class ResSpecAfterPiSNRThreshold(CorrectionParameter): name: str = field(default="res_spec_after_pi_snr_threshold", init=False) description: str = field(default="Minimum SNR for a successful resonator fit", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.res_spec_after_pi.snr() @@ -53,6 +55,8 @@ class ResSpecAfterPiMaxFitParamError(CorrectionParameter): name: str = field(default="res_spec_after_pi_max_fit_param_error", init=False) description: str = field(default="Max allowed fractional fit parameter error (e.g. 1.0 = 100%)", init=False) + def _dummy_getter(self): return 1.0 + def _qick_getter(self): return self.params.corrections.res_spec_after_pi.max_fit_param_error() @@ -65,6 +69,8 @@ class DetuningThreshold(CorrectionParameter): name: str = field(default="res_spec_after_pi_detuning_threshold", init=False) description: str = field(default="Minimum Chi to consider the dispersive shift valid (MHz)", init=False) + def _dummy_getter(self): return 1.0e6 + def _qick_getter(self): return self.params.corrections.res_spec_after_pi.detuning_threshold() @@ -130,11 +136,11 @@ def _measure_dummy(self) -> Path: frequencies = np.linspace(self.start_freq(), self.end_freq(), int(self.steps())) gen_before = HangerResonator(f0=_RS._SIM_F0, Qc=_RS._SIM_QC, Qi=_RS._SIM_QI, A=_RS._SIM_A, phi=_RS._SIM_PHI, noise_std=_RS._SIM_NOISE_AMP) - sweep_before = sweep_parameter("frequencies", frequencies) * Sweep(record_as(gen_before.generate(frequencies), "signal")) + sweep_before = sweep_parameter("frequencies", frequencies, record_as(lambda frequencies: gen_before.generate(np.atleast_1d(frequencies)), "signal")) loc_before, _ = run_and_save_sweep(sweep_before, "data", f"{self.name}_before") gen_after = HangerResonator(f0=_RS._SIM_F0 + self._SIM_CHI, Qc=_RS._SIM_QC, Qi=_RS._SIM_QI, A=_RS._SIM_A, phi=_RS._SIM_PHI, noise_std=_RS._SIM_NOISE_AMP) - sweep_after = sweep_parameter("frequencies", frequencies) * Sweep(record_as(gen_after.generate(frequencies), "signal")) + sweep_after = sweep_parameter("frequencies", frequencies, record_as(lambda frequencies: gen_after.generate(np.atleast_1d(frequencies)), "signal")) loc_after, _ = run_and_save_sweep(sweep_after, "data", f"{self.name}_after") self.data_loc_before = loc_before diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_vs_gain.py b/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_vs_gain.py index 15d43e8..f91f535 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_vs_gain.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_vs_gain.py @@ -11,7 +11,7 @@ from labcore.analysis import DatasetAnalysis from labcore.measurement.storage import run_and_save_sweep from labcore.data.datadict_storage import datadict_from_hdf5 -from labcore.data.datagen import HangerResonator +from cqedtoolbox.protocols.operations.single_qubit.res_spec import HangerResonator from labcore.measurement import sweep_parameter from labcore.measurement.record import recording, dep, indep @@ -36,8 +36,7 @@ class ResSpecVsGainSNRThreshold(CorrectionParameter): name: str = field(default="res_spec_vs_gain_snr_threshold", init=False) description: str = field(default="SNR threshold for low-gain quality check", init=False) - # def _dummy_getter(self): return 2.0 # hardcoded default for DUMMY mode so analyze() runs without a real params object - # def _dummy_setter(self, _): pass + def _dummy_getter(self): return 2.0 def _qick_getter(self): return self.params.corrections.res_spec_vs_gain.snr() @@ -51,8 +50,7 @@ class ResSpecVsGainMaxFitParamError(CorrectionParameter): name: str = field(default="res_spec_vs_gain_max_fit_param_error", init=False) description: str = field(default="Max fractional fit parameter error (e.g. 1.0 = 100%)", init=False) - # def _dummy_getter(self): return 1.0 # hardcoded default for DUMMY mode so analyze() runs without a real params object - # def _dummy_setter(self, _): pass + def _dummy_getter(self): return 1.0 def _qick_getter(self): return self.params.corrections.res_spec_vs_gain.max_fit_param_error() @@ -66,8 +64,7 @@ class ResSpecVsGainHighSNRThreshold(CorrectionParameter): name: str = field(default="res_spec_vs_gain_high_snr_threshold", init=False) description: str = field(default="High SNR threshold — at least one trace must exceed this", init=False) - # def _dummy_getter(self): return 2.0 # hardcoded default for DUMMY mode so analyze() runs without a real params object - # def _dummy_setter(self, _): pass + def _dummy_getter(self): return 2.0 def _qick_getter(self): return self.params.corrections.res_spec_vs_gain.high_snr() @@ -81,8 +78,7 @@ class ResSpecVsGainRepetitionFactor(CorrectionParameter): name: str = field(default="res_spec_vs_gain_repetition_factor", init=False) description: str = field(default="Factor by which repetitions are increased on retry", init=False) - # def _dummy_getter(self): return 2.0 # hardcoded default for DUMMY mode so analyze() runs without a real params object - # def _dummy_setter(self, _): pass + def _dummy_getter(self): return 2.0 def _qick_getter(self): return self.params.corrections.res_spec_vs_gain.rep_factor() @@ -96,8 +92,7 @@ class ResSpecVsGainMaxRepetitionIncreases(CorrectionParameter): name: str = field(default="res_spec_vs_gain_max_rep_increases", init=False) description: str = field(default="Maximum number of repetition increases to attempt", init=False) - # def _dummy_getter(self): return 3 # hardcoded default for DUMMY mode so analyze() runs without a real params object - # def _dummy_setter(self, _): pass + def _dummy_getter(self): return 3 def _qick_getter(self): return int(self.params.corrections.res_spec_vs_gain.max_rep_increases()) diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/sat_spec.py b/src/cqedtoolbox/protocols/operations/single_qubit/sat_spec.py index b11abc1..9950ea6 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/sat_spec.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/sat_spec.py @@ -1,9 +1,13 @@ import logging from pathlib import Path from dataclasses import dataclass, field +from typing import Any import numpy as np import matplotlib.pyplot as plt +from numpy.typing import NDArray +from scipy.constants import h + plt.switch_backend("agg") @@ -11,9 +15,9 @@ from labcore.analysis.fitfuncs.generic import Lorentzian from labcore.measurement.storage import run_and_save_sweep from labcore.data.datadict_storage import datadict_from_hdf5 -from labcore.measurement.sweep import sweep_parameter, Sweep +from labcore.measurement.sweep import sweep_parameter from labcore.measurement.record import record_as -from labcore.data.datagen import Lorentzian as LorentzianDataGen +from labcore.data.datagen import DataGen from labcore.protocols.base import (ProtocolOperation, OperationStatus, serialize_fit_params, ParamImprovement, CorrectionParameter, CheckResult, Correction, @@ -39,6 +43,8 @@ class SNRThreshold(CorrectionParameter): name: str = field(default="sat_spec_snr_threshold", init=False) description: str = field(default="SNR threshold for saturation spectroscopy fit quality", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.sat_spec.snr() @@ -51,6 +57,8 @@ class MaxFitParamError(CorrectionParameter): name: str = field(default="sat_spec_max_fit_param_error", init=False) description: str = field(default="Maximum allowed fractional fit parameter error (e.g. 1.0 = 100%)", init=False) + def _dummy_getter(self): return 1.0 + def _qick_getter(self): return self.params.corrections.sat_spec.max_fit_param_error() @@ -63,6 +71,8 @@ class MaxWindowShifts(CorrectionParameter): name: str = field(default="sat_spec_max_window_shifts", init=False) description: str = field(default="Number of ±n window shifts to try", init=False) + def _dummy_getter(self): return 3 + def _qick_getter(self): return int(self.params.corrections.sat_spec.max_window_shifts()) @@ -75,6 +85,8 @@ class AveragingIncreaseFactor(CorrectionParameter): name: str = field(default="sat_spec_averaging_increase_factor", init=False) description: str = field(default="Factor by which to increase repetitions", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.sat_spec.averaging_factor() @@ -87,6 +99,8 @@ class MaxAveragingIncreases(CorrectionParameter): name: str = field(default="sat_spec_max_averaging_increases", init=False) description: str = field(default="Maximum number of averaging increases to try", init=False) + def _dummy_getter(self): return 3 + def _qick_getter(self): return int(self.params.corrections.sat_spec.max_averaging_increases()) @@ -99,6 +113,8 @@ class SamplingIncreaseFactor(CorrectionParameter): name: str = field(default="sat_spec_sampling_increase_factor", init=False) description: str = field(default="Factor by which to increase frequency steps", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.sat_spec.sampling_factor() @@ -111,6 +127,8 @@ class MaxSamplingIncreases(CorrectionParameter): name: str = field(default="sat_spec_max_sampling_increases", init=False) description: str = field(default="Maximum number of sampling rate increases to try", init=False) + def _dummy_getter(self): return 3 + def _qick_getter(self): return int(self.params.corrections.sat_spec.max_sampling_increases()) @@ -123,6 +141,8 @@ class MaxPowerIncreases(CorrectionParameter): name: str = field(default="sat_spec_max_power_increases", init=False) description: str = field(default="Maximum number of drive power increases to try", init=False) + def _dummy_getter(self): return 3 + def _qick_getter(self): return int(self.params.corrections.sat_spec.max_power_increases()) @@ -135,6 +155,8 @@ class PowerIncreaseFactor(CorrectionParameter): name: str = field(default="sat_spec_power_increase_factor", init=False) description: str = field(default="Multiplicative factor for increasing drive gain (e.g. 1.1 = +10%)", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.sat_spec.power_increase_factor() @@ -147,6 +169,8 @@ class SinglePeakSNRThreshold(CorrectionParameter): name: str = field(default="sat_spec_single_peak_snr", init=False) description: str = field(default="SNR threshold for detecting a second peak in the fit residuals", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.sat_spec.single_peak_snr() @@ -159,6 +183,8 @@ class SinglePeakMaxPowerReductions(CorrectionParameter): name: str = field(default="sat_spec_single_peak_max_reductions", init=False) description: str = field(default="Maximum number of drive power reductions to try", init=False) + def _dummy_getter(self): return 3 + def _qick_getter(self): return int(self.params.corrections.sat_spec.single_peak_max_reductions()) @@ -171,6 +197,8 @@ class PowerReductionFactor(CorrectionParameter): name: str = field(default="sat_spec_power_reduction_factor", init=False) description: str = field(default="Multiplicative factor for reducing drive gain (e.g. 0.9 = -10%)", init=False) + def _dummy_getter(self): return 0.5 + def _qick_getter(self): return self.params.corrections.sat_spec.power_reduction_factor() @@ -357,6 +385,28 @@ def apply(self) -> None: def report_output(self) -> str: return self._last_change +# --------------------------------------------------------------------------- +# SatSpec DataGen +# --------------------------------------------------------------------------- + +@dataclass +class SatSpec(DataGen): + fq: float = 5e9 + f_rabi: float = 1e6 + gamma1: float = 1e6 + gamma2: float = 2e6 + angle: float = 0.0 + imaginary: bool = True + + @staticmethod + def model(frequencies: NDArray[Any], fq: float, f_rabi: float, gamma1: float, gamma2: float, angle: float) -> NDArray[Any]: + """Saturation spec model based on Blais circuit qed eqn 127""" + signal = 0.5 * f_rabi**2 / ( + gamma1 * gamma2 + (frequencies - fq) ** 2 * gamma1 / gamma2 + f_rabi ** 2 + ) + signal_re = signal * np.cos(angle) + signal_imag = signal * np.sin(angle) + return signal_re + 1j * signal_imag # --------------------------------------------------------------------------- @@ -366,9 +416,24 @@ def report_output(self) -> str: class SaturationSpectroscopy(ProtocolOperation): _DUMMY_F_Q = 5e9 - _DUMMY_GAMMA = 1e6 - _DUMMY_A = 0.5 + _DUMMY_F_R = 7e9 + _DUMMY_DELTA = _DUMMY_F_R - _DUMMY_F_Q + + _DUMMY_P_IN = 1e-16 + _DUMMY_G = 50e6 + _DUMMY_KAPPA_R = 0.2e6 + + _DUMMY_T1 = 50e-6 + _DUMMY_T2 = 50e-6 + _DUMMY_GAMMA_1 = 1 / _DUMMY_T1 + _DUMMY_GAMMA_2 = 1 / (np.pi * _DUMMY_T2) + + _DUMMY_OMEGA = 2 * (_DUMMY_G / _DUMMY_DELTA) * np.sqrt(_DUMMY_KAPPA_R * _DUMMY_P_IN / (h * _DUMMY_F_Q)) + _DUMMY_GAMMA_Q = np.sqrt( (1 / _DUMMY_T2) ** 2 + ((2 * np.pi * _DUMMY_OMEGA) ** 2 * _DUMMY_T1 / _DUMMY_T2) ) / np.pi # blais eq 127 + _DUMMY_NOISE_AMP = 0.05 + _DUMMY_ANGLE = np.pi / 4 + def __init__(self, params): @@ -474,10 +539,10 @@ def _load_data_qick(self): def _measure_dummy(self) -> Path: logger.info("Starting dummy saturation spectroscopy measurement") frequencies = np.linspace(self.start_freq(), self.end_freq(), int(self.steps())) - generator = LorentzianDataGen(x0=self._DUMMY_F_Q, gamma=self._DUMMY_GAMMA, A=self._DUMMY_A, of=0, noise_std=self._DUMMY_NOISE_AMP) - sweep = sweep_parameter('frequencies', frequencies) * Sweep(record_as(generator.generate(frequencies), 'signal')) + generator = SatSpec(fq=self._DUMMY_F_Q, f_rabi=self._DUMMY_OMEGA, gamma1=self._DUMMY_GAMMA_1, gamma2=self._DUMMY_GAMMA_2, angle=self._DUMMY_ANGLE, noise_std=self._DUMMY_NOISE_AMP) + sweep = sweep_parameter("frequencies", frequencies, record_as(lambda frequencies: generator.generate(np.atleast_1d(frequencies)), "signal")) loc, _ = run_and_save_sweep(sweep, "data", self.name) - logger.info("Dummy measurement complete") + logger.info("Dummy saturation spectroscopy measurement complete.") return loc def _load_data_dummy(self): diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/t1.py b/src/cqedtoolbox/protocols/operations/single_qubit/t1.py index 7771c1b..a0496bd 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/t1.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/t1.py @@ -10,7 +10,7 @@ from labcore.analysis import DatasetAnalysis from labcore.analysis.fitfuncs.generic import ExponentialDecay from labcore.measurement.storage import run_and_save_sweep -from labcore.measurement.sweep import sweep_parameter, Sweep +from labcore.measurement.sweep import sweep_parameter from labcore.measurement.record import record_as from labcore.data.datagen import ExponentialDecay as ExponentialDecayDataGen from labcore.data.datadict_storage import datadict_from_hdf5 @@ -43,6 +43,8 @@ class SNRMinThreshold(CorrectionParameter): name: str = field(default="t1_snr_min_threshold", init=False) description: str = field(default="Minimum SNR for a valid T1 fit component", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.t1.snr_min() def _qick_setter(self, v): self.params.corrections.t1.snr_min(v) @@ -52,6 +54,8 @@ class MaxFitParamError(CorrectionParameter): name: str = field(default="t1_max_fit_param_error", init=False) description: str = field(default="Maximum allowed fractional fit parameter error (e.g. 1.0 = 100%)", init=False) + def _dummy_getter(self): return 1.0 + def _qick_getter(self): return self.params.corrections.t1.max_fit_param_error() def _qick_setter(self, v): self.params.corrections.t1.max_fit_param_error(v) @@ -61,6 +65,8 @@ class DelayIncreaseFactor(CorrectionParameter): name: str = field(default="t1_delay_factor", init=False) description: str = field(default="Factor by which to increase delay between shots", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.t1.delay_factor() def _qick_setter(self, v): self.params.corrections.t1.delay_factor(v) @@ -70,6 +76,8 @@ class MaxDelayIncreases(CorrectionParameter): name: str = field(default="t1_max_delay_increases", init=False) description: str = field(default="Maximum number of delay increases to try", init=False) + def _dummy_getter(self): return 3 + def _qick_getter(self): return int(self.params.corrections.t1.max_delay_increases()) def _qick_setter(self, v): self.params.corrections.t1.max_delay_increases(v) @@ -79,6 +87,8 @@ class AveragingIncreaseFactor(CorrectionParameter): name: str = field(default="t1_averaging_factor", init=False) description: str = field(default="Factor by which to increase repetitions", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.t1.averaging_factor() def _qick_setter(self, v): self.params.corrections.t1.averaging_factor(v) @@ -88,6 +98,8 @@ class MaxAveragingIncreases(CorrectionParameter): name: str = field(default="t1_max_averaging_increases", init=False) description: str = field(default="Maximum number of averaging increases to try", init=False) + def _dummy_getter(self): return 3 + def _qick_getter(self): return int(self.params.corrections.t1.max_averaging_increases()) def _qick_setter(self, v): self.params.corrections.t1.max_averaging_increases(v) @@ -237,7 +249,7 @@ def _measure_dummy(self) -> Path: logger.info("Starting dummy T1 measurement") delays = np.linspace(0, 5 * self._SIM_T1, int(self.steps())) generator = ExponentialDecayDataGen(A=self._SIM_AMP, tau=self._SIM_T1, of=0, noise_std=self._SIM_NOISE_AMP) - sweep = sweep_parameter("delays", delays) * Sweep(record_as(generator.generate(delays), "signal")) + sweep = sweep_parameter("delays", delays, record_as(lambda delays: generator.generate(np.atleast_1d(delays)), "signal")) loc, _ = run_and_save_sweep(sweep, "data", self.name) logger.info("Dummy measurement complete") return loc diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/t2e.py b/src/cqedtoolbox/protocols/operations/single_qubit/t2e.py index 3e062b3..d80e769 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/t2e.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/t2e.py @@ -10,7 +10,7 @@ from labcore.analysis import DatasetAnalysis from labcore.analysis.fitfuncs.generic import ExponentiallyDecayingSine from labcore.measurement.storage import run_and_save_sweep -from labcore.measurement.sweep import sweep_parameter, Sweep +from labcore.measurement.sweep import sweep_parameter from labcore.measurement.record import record_as from labcore.data.datagen import ExponentialDecayingSine from labcore.data.datadict_storage import datadict_from_hdf5 @@ -43,6 +43,8 @@ class SNRMinThreshold(CorrectionParameter): name: str = field(default="t2e_snr_min_threshold", init=False) description: str = field(default="Minimum SNR for a valid T2E fit component", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.t2e.snr_min() def _qick_setter(self, v): self.params.corrections.t2e.snr_min(v) @@ -52,6 +54,8 @@ class MaxFitParamError(CorrectionParameter): name: str = field(default="t2e_max_fit_param_error", init=False) description: str = field(default="Maximum allowed fractional fit parameter error (e.g. 1.0 = 100%)", init=False) + def _dummy_getter(self): return 1.0 + def _qick_getter(self): return self.params.corrections.t2e.max_fit_param_error() def _qick_setter(self, v): self.params.corrections.t2e.max_fit_param_error(v) @@ -61,6 +65,8 @@ class MaxEchos(CorrectionParameter): name: str = field(default="t2e_max_echos", init=False) description: str = field(default="Maximum number of echo pulses to try", init=False) + def _dummy_getter(self): return 3 + def _qick_getter(self): return int(self.params.corrections.t2e.max_echos()) def _qick_setter(self, v): self.params.corrections.t2e.max_echos(v) @@ -70,6 +76,8 @@ class AveragingIncreaseFactor(CorrectionParameter): name: str = field(default="t2e_averaging_factor", init=False) description: str = field(default="Factor by which to increase repetitions", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.t2e.averaging_factor() def _qick_setter(self, v): self.params.corrections.t2e.averaging_factor(v) @@ -79,6 +87,8 @@ class MaxAveragingIncreases(CorrectionParameter): name: str = field(default="t2e_max_averaging_increases", init=False) description: str = field(default="Maximum number of averaging increases to try", init=False) + def _dummy_getter(self): return 3 + def _qick_getter(self): return int(self.params.corrections.t2e.max_averaging_increases()) def _qick_setter(self, v): self.params.corrections.t2e.max_averaging_increases(v) @@ -223,7 +233,7 @@ def _measure_dummy(self) -> Path: logger.info("Starting dummy T2 Echo measurement") delays = np.linspace(0, 5 * self._SIM_T2E, int(self.steps())) generator = ExponentialDecayingSine(A=self._SIM_AMP, f=self._SIM_DETUNING, phi=0, tau=self._SIM_T2E, of=0, noise_std=self._SIM_NOISE_AMP) - sweep = sweep_parameter("delays", delays) * Sweep(record_as(generator.generate(delays), "signal")) + sweep = sweep_parameter("delays", delays, record_as(lambda delays: generator.generate(np.atleast_1d(delays)), "signal")) loc, _ = run_and_save_sweep(sweep, "data", self.name) logger.info("Dummy measurement complete") return loc diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/t2r.py b/src/cqedtoolbox/protocols/operations/single_qubit/t2r.py index 413bf38..c8efa15 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/t2r.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/t2r.py @@ -10,7 +10,7 @@ from labcore.analysis import DatasetAnalysis from labcore.analysis.fitfuncs.generic import ExponentiallyDecayingSine from labcore.measurement.storage import run_and_save_sweep -from labcore.measurement.sweep import sweep_parameter, Sweep +from labcore.measurement.sweep import sweep_parameter from labcore.measurement.record import record_as from labcore.data.datagen import ExponentialDecayingSine from labcore.data.datadict_storage import datadict_from_hdf5 @@ -43,6 +43,8 @@ class SNRMinThreshold(CorrectionParameter): name: str = field(default="t2r_snr_min_threshold", init=False) description: str = field(default="Minimum SNR for a valid T2R fit component", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.t2r.snr_min() def _qick_setter(self, v): self.params.corrections.t2r.snr_min(v) @@ -52,6 +54,8 @@ class MaxFitParamError(CorrectionParameter): name: str = field(default="t2r_max_fit_param_error", init=False) description: str = field(default="Maximum allowed fractional fit parameter error (e.g. 1.0 = 100%)", init=False) + def _dummy_getter(self): return 1.0 + def _qick_getter(self): return self.params.corrections.t2r.max_fit_param_error() def _qick_setter(self, v): self.params.corrections.t2r.max_fit_param_error(v) @@ -61,6 +65,8 @@ class AveragingIncreaseFactor(CorrectionParameter): name: str = field(default="t2r_averaging_factor", init=False) description: str = field(default="Factor by which to increase repetitions", init=False) + def _dummy_getter(self): return 2.0 + def _qick_getter(self): return self.params.corrections.t2r.averaging_factor() def _qick_setter(self, v): self.params.corrections.t2r.averaging_factor(v) @@ -70,6 +76,8 @@ class MaxAveragingIncreases(CorrectionParameter): name: str = field(default="t2r_max_averaging_increases", init=False) description: str = field(default="Maximum number of averaging increases to try", init=False) + def _dummy_getter(self): return 3 + def _qick_getter(self): return int(self.params.corrections.t2r.max_averaging_increases()) def _qick_setter(self, v): self.params.corrections.t2r.max_averaging_increases(v) @@ -172,7 +180,7 @@ def _measure_dummy(self) -> Path: logger.info("Starting dummy T2 Ramsey measurement") delays = np.linspace(0, 5 * self._SIM_T2R, int(self.steps())) generator = ExponentialDecayingSine(A=self._SIM_AMP, f=self._SIM_DETUNING, phi=0, tau=self._SIM_T2R, of=0, noise_std=self._SIM_NOISE_AMP) - sweep = sweep_parameter("delays", delays) * Sweep(record_as(generator.generate(delays), "signal")) + sweep = sweep_parameter("delays", delays, record_as(lambda delays: generator.generate(np.atleast_1d(delays)), "signal")) loc, _ = run_and_save_sweep(sweep, "data", self.name) logger.info("Dummy measurement complete") return loc From f58f888787ac5c24d3d0b36234a827833ac848d4 Mon Sep 17 00:00:00 2001 From: olivers3uiuc Date: Thu, 23 Apr 2026 22:30:37 -0500 Subject: [PATCH 3/3] progress commit --- src/cqedtoolbox/fitfuncs/resonators.py | 1 - .../operations/single_qubit/pi_spec.py | 24 ++--- .../operations/single_qubit/power_rabi.py | 46 +++++----- .../operations/single_qubit/res_spec.py | 45 ++++++---- .../single_qubit/res_spec_after_pi.py | 26 +++--- .../single_qubit/res_spec_vs_gain.py | 32 ++++--- .../operations/single_qubit/sat_spec.py | 87 +++++++++---------- .../protocols/operations/single_qubit/t1.py | 28 +++--- .../protocols/operations/single_qubit/t2e.py | 30 ++++--- .../protocols/operations/single_qubit/t2r.py | 26 +++--- src/cqedtoolbox/protocols/parameters.py | 8 ++ 11 files changed, 196 insertions(+), 157 deletions(-) diff --git a/src/cqedtoolbox/fitfuncs/resonators.py b/src/cqedtoolbox/fitfuncs/resonators.py index 3ba69e7..497aaf2 100644 --- a/src/cqedtoolbox/fitfuncs/resonators.py +++ b/src/cqedtoolbox/fitfuncs/resonators.py @@ -157,7 +157,6 @@ def guess(coordinates, data) -> Dict[str, Any]: transmission_slope = guess_transmission_slope, ) - # return dict( # A = 1, # f_0 = 1, diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/pi_spec.py b/src/cqedtoolbox/protocols/operations/single_qubit/pi_spec.py index 290a4e5..8f5844f 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/pi_spec.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/pi_spec.py @@ -38,56 +38,60 @@ class PiSpecSNRThreshold(CorrectionParameter): name: str = field(default="pi_spec_snr_threshold", init=False) description: str = field(default="Minimum SNR for a successful pi spectroscopy fit", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.pi_spec.snr() def _qick_setter(self, value): self.params.corrections.pi_spec.snr(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class PiSpecMaxFitParamError(CorrectionParameter): name: str = field(default="pi_spec_max_fit_param_error", init=False) description: str = field(default="Max allowed fractional fit parameter error (e.g. 1.0 = 100%)", init=False) - def _dummy_getter(self): return 1.0 - def _qick_getter(self): return self.params.corrections.pi_spec.max_fit_param_error() def _qick_setter(self, value): self.params.corrections.pi_spec.max_fit_param_error(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class PiSpecAveragingFactor(CorrectionParameter): name: str = field(default="pi_spec_averaging_factor", init=False) description: str = field(default="Factor by which to multiply repetitions on retry", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.pi_spec.averaging_factor() def _qick_setter(self, value): self.params.corrections.pi_spec.averaging_factor(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class PiSpecMaxAveragingIncreases(CorrectionParameter): name: str = field(default="pi_spec_max_averaging_increases", init=False) description: str = field(default="Maximum number of repetition increases to attempt", init=False) - def _dummy_getter(self): return 3 - def _qick_getter(self): return int(self.params.corrections.pi_spec.max_averaging_increases()) def _qick_setter(self, value): self.params.corrections.pi_spec.max_averaging_increases(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + class IncreaseAveragingCorrection(Correction): name = "increase_averaging" @@ -171,8 +175,8 @@ def _measure_dummy(self) -> Path: frequencies = np.linspace(self.start_freq(), self.end_freq(), int(self.steps())) center = (self.start_freq() + self.end_freq()) / 2 + self._SIM_CENTER - generator = GaussianDataGen(x0=center, sigma=self._SIM_SIGMA, A=self._SIM_AMP, of=0, noise_std=self._SIM_NOISE_AMP) - sweep = sweep_parameter("frequencies", frequencies, record_as(lambda frequencies: generator.generate(np.atleast_1d(frequencies)), "signal")) + generator = GaussianDataGen(x0=center, sigma=self._SIM_SIGMA, A=self._SIM_AMP, of=0, noise_std=self._SIM_NOISE_AMP, imaginary=True) + sweep = sweep_parameter("frequencies", frequencies, record_as(lambda frequencies: np.atleast_1d(generator.generate(np.atleast_1d(frequencies)))[0], "signal")) loc, _ = run_and_save_sweep(sweep, "data", self.name) logger.info("Dummy measurement complete") return loc diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/power_rabi.py b/src/cqedtoolbox/protocols/operations/single_qubit/power_rabi.py index 6f965ad..2aba544 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/power_rabi.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/power_rabi.py @@ -11,7 +11,7 @@ from labcore.analysis.fitfuncs.generic import Cosine from labcore.measurement.storage import run_and_save_sweep from labcore.measurement import sweep_parameter, record_as -from labcore.data.datagen import PowerRabi as PowerRabiDataGen +from labcore.data.datagen import Sine from labcore.data.datadict_storage import datadict_from_hdf5 from labcore.protocols.base import ( @@ -41,10 +41,10 @@ class SNRThreshold(CorrectionParameter): name: str = field(default="power_rabi_snr_threshold", init=False) description: str = field(default="SNR threshold for power rabi quality check", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.power_rabi.snr() def _qick_setter(self, v): self.params.corrections.power_rabi.snr(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -52,10 +52,10 @@ class MaxFitParamError(CorrectionParameter): name: str = field(default="power_rabi_max_fit_param_error", init=False) description: str = field(default="Maximum allowed fractional fit parameter error (e.g. 1.0 = 100%)", init=False) - def _dummy_getter(self): return 1.0 - def _qick_getter(self): return self.params.corrections.power_rabi.max_fit_param_error() def _qick_setter(self, v): self.params.corrections.power_rabi.max_fit_param_error(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -63,10 +63,10 @@ class AveragingIncreaseFactor(CorrectionParameter): name: str = field(default="power_rabi_averaging_factor", init=False) description: str = field(default="Factor by which to increase repetitions", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.power_rabi.averaging_factor() def _qick_setter(self, v): self.params.corrections.power_rabi.averaging_factor(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -74,10 +74,10 @@ class MaxAveragingIncreases(CorrectionParameter): name: str = field(default="power_rabi_max_averaging_increases", init=False) description: str = field(default="Maximum number of averaging increases to try", init=False) - def _dummy_getter(self): return 3 - def _qick_getter(self): return int(self.params.corrections.power_rabi.max_averaging_increases()) def _qick_setter(self, v): self.params.corrections.power_rabi.max_averaging_increases(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -85,10 +85,10 @@ class SamplingIncreaseFactor(CorrectionParameter): name: str = field(default="power_rabi_sampling_factor", init=False) description: str = field(default="Factor by which to increase gain steps", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.power_rabi.sampling_factor() def _qick_setter(self, v): self.params.corrections.power_rabi.sampling_factor(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -96,10 +96,10 @@ class MaxSamplingIncreases(CorrectionParameter): name: str = field(default="power_rabi_max_sampling_increases", init=False) description: str = field(default="Maximum number of step count increases to try", init=False) - def _dummy_getter(self): return 3 - def _qick_getter(self): return int(self.params.corrections.power_rabi.max_sampling_increases()) def _qick_setter(self, v): self.params.corrections.power_rabi.max_sampling_increases(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -107,10 +107,10 @@ class DelayIncreaseFactor(CorrectionParameter): name: str = field(default="power_rabi_delay_factor", init=False) description: str = field(default="Factor by which to increase delay between shots", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.power_rabi.delay_factor() def _qick_setter(self, v): self.params.corrections.power_rabi.delay_factor(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -118,10 +118,10 @@ class MaxDelayIncreases(CorrectionParameter): name: str = field(default="power_rabi_max_delay_increases", init=False) description: str = field(default="Maximum number of delay increases to try", init=False) - def _dummy_getter(self): return 3 - def _qick_getter(self): return int(self.params.corrections.power_rabi.max_delay_increases()) def _qick_setter(self, v): self.params.corrections.power_rabi.max_delay_increases(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -129,10 +129,10 @@ class GainRangeShrinkFactor(CorrectionParameter): name: str = field(default="power_rabi_gain_shrink_factor", init=False) description: str = field(default="Factor by which to divide the gain half-span on each shrink", init=False) - def _dummy_getter(self): return 0.5 - def _qick_getter(self): return self.params.corrections.power_rabi.gain_shrink_factor() def _qick_setter(self, v): self.params.corrections.power_rabi.gain_shrink_factor(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -140,10 +140,10 @@ class MaxGainRangeShrinks(CorrectionParameter): name: str = field(default="power_rabi_max_gain_shrinks", init=False) description: str = field(default="Maximum number of gain range shrink steps to try", init=False) - def _dummy_getter(self): return 3 - def _qick_getter(self): return int(self.params.corrections.power_rabi.max_gain_shrinks()) def _qick_setter(self, v): self.params.corrections.power_rabi.max_gain_shrinks(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter # --------------------------------------------------------------------------- @@ -378,8 +378,8 @@ def _measure_qick(self) -> Path: def _measure_dummy(self): logger.info("Starting dummy power rabi measurement") gains = np.linspace(self.start_gain(), self.end_gain(), int(self.steps_gain())) - generator = PowerRabiDataGen(pi_amp=self._SIM_PI_AMP, noise_std=self._SIM_NOISE_AMP) - sweep = sweep_parameter("gains", gains, record_as(lambda gains: generator.generate(np.atleast_1d(gains)), "signal")) + generator = Sine(f= 1 / (2 * self._SIM_PI_AMP), phi = np.pi / 2, noise_std=self._SIM_NOISE_AMP) + sweep = sweep_parameter("gains", gains, record_as(lambda gains: np.atleast_1d(generator.generate(np.atleast_1d(gains)) - 1j * generator.generate(np.atleast_1d(gains)))[0], "signal")) loc, _ = run_and_save_sweep(sweep, "data", self.name) logger.info("Dummy measurement complete") return loc diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/res_spec.py b/src/cqedtoolbox/protocols/operations/single_qubit/res_spec.py index a8ad4cd..85ec483 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/res_spec.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/res_spec.py @@ -45,98 +45,105 @@ class SNRThreshold(CorrectionParameter): name: str = field(default="resonator_spec_SNR_threshold", init=False) description: str = field(default="SNR threshold", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.res_spec.snr() def _qick_setter(self, value): self.params.corrections.res_spec.snr(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class MaxWindowShifts(CorrectionParameter): name: str = field(default="res_spec_max_window_shifts", init=False) description: str = field(default="Number of ±n window shifts to try", init=False) - def _dummy_getter(self): return 3 - def _qick_getter(self): return int(self.params.corrections.res_spec.max_window_shifts()) def _qick_setter(self, value): self.params.corrections.res_spec.max_window_shifts(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class SamplingIncreaseFactor(CorrectionParameter): name: str = field(default="res_spec_sampling_increase_factor", init=False) description: str = field(default="Factor by which to increase frequency steps", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.res_spec.sampling_factor() def _qick_setter(self, value): self.params.corrections.res_spec.sampling_factor(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class MaxSamplingIncreases(CorrectionParameter): name: str = field(default="res_spec_max_sampling_increases", init=False) description: str = field(default="Maximum number of sampling rate increases to try", init=False) - def _dummy_getter(self): return 3 - def _qick_getter(self): return int(self.params.corrections.res_spec.max_sampling_increases()) def _qick_setter(self, value): self.params.corrections.res_spec.max_sampling_increases(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class AveragingIncreaseFactor(CorrectionParameter): name: str = field(default="res_spec_averaging_increase_factor", init=False) description: str = field(default="Factor by which to increase repetitions", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.res_spec.averaging_factor() def _qick_setter(self, value): self.params.corrections.res_spec.averaging_factor(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class MaxAveragingIncreases(CorrectionParameter): name: str = field(default="res_spec_max_averaging_increases", init=False) description: str = field(default="Maximum number of averaging increases to try", init=False) - def _dummy_getter(self): return 3 - def _qick_getter(self): return int(self.params.corrections.res_spec.max_averaging_increases()) def _qick_setter(self, value): self.params.corrections.res_spec.max_averaging_increases(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class MaxFitParamError(CorrectionParameter): name: str = field(default="res_spec_max_fit_param_error", init=False) description: str = field(default="Maximum allowed fractional fit parameter error (e.g. 1.0 = 100%)", init=False) - def _dummy_getter(self): return 1.0 - def _qick_getter(self): return self.params.corrections.res_spec.max_fit_param_error() def _qick_setter(self, value): self.params.corrections.res_spec.max_fit_param_error(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + class WindowShiftCorrection(Correction): name = "window_shift" @@ -282,9 +289,8 @@ def model( class ResonatorSpectroscopy(ProtocolOperation): - _SIM_F0 = 7e9 - _SIM_QI = 20e3 - _SIM_QC = 20e3 + _SIM_QI = 500 + _SIM_QC = 500 _SIM_A = 4.0 _SIM_PHI = 0.0 _SIM_NOISE_AMP = 0.05 @@ -380,8 +386,9 @@ def _measure_qick(self) -> Path: def _measure_dummy(self): logger.info("Starting dummy resonator spectroscopy measurement") frequencies = np.linspace(self.start_frequency(), self.end_frequency(), int(self.steps())) - generator = HangerResonator(f0=self._SIM_F0, Qc=self._SIM_QC, Qi=self._SIM_QI, A=self._SIM_A, phi=self._SIM_PHI, noise_std=self._SIM_NOISE_AMP) - sweep = sweep_parameter("frequencies", frequencies, record_as(lambda frequencies: generator.generate(np.atleast_1d(frequencies)), "signal")) + f0 = (self.start_frequency() + self.end_frequency()) / 2 + generator = HangerResonator(f0=f0, Qc=self._SIM_QC, Qi=self._SIM_QI, A=self._SIM_A, phi=self._SIM_PHI, noise_std=self._SIM_NOISE_AMP) + sweep = sweep_parameter("frequencies", frequencies, record_as(lambda frequencies: np.atleast_1d(generator.generate(np.atleast_1d(frequencies)))[0], "signal")) loc, _ = run_and_save_sweep(sweep, "data", self.name) logger.info("Dummy measurement complete") return loc diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_after_pi.py b/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_after_pi.py index 37e49df..87b8fd9 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_after_pi.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_after_pi.py @@ -41,42 +41,45 @@ class ResSpecAfterPiSNRThreshold(CorrectionParameter): name: str = field(default="res_spec_after_pi_snr_threshold", init=False) description: str = field(default="Minimum SNR for a successful resonator fit", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.res_spec_after_pi.snr() def _qick_setter(self, value): self.params.corrections.res_spec_after_pi.snr(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class ResSpecAfterPiMaxFitParamError(CorrectionParameter): name: str = field(default="res_spec_after_pi_max_fit_param_error", init=False) description: str = field(default="Max allowed fractional fit parameter error (e.g. 1.0 = 100%)", init=False) - def _dummy_getter(self): return 1.0 - def _qick_getter(self): return self.params.corrections.res_spec_after_pi.max_fit_param_error() def _qick_setter(self, value): self.params.corrections.res_spec_after_pi.max_fit_param_error(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class DetuningThreshold(CorrectionParameter): name: str = field(default="res_spec_after_pi_detuning_threshold", init=False) description: str = field(default="Minimum Chi to consider the dispersive shift valid (MHz)", init=False) - def _dummy_getter(self): return 1.0e6 - def _qick_getter(self): return self.params.corrections.res_spec_after_pi.detuning_threshold() def _qick_setter(self, value): self.params.corrections.res_spec_after_pi.detuning_threshold(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + class ResonatorSpectroscopyAfterPi(ProtocolOperation): @@ -129,18 +132,19 @@ def __init__(self, params): # Detuning value self.chi = None - _SIM_CHI = 2.0 # MHz dispersive shift between ground and excited state + _SIM_CHI = 2e6 # Hz dispersive shift between ground and excited state def _measure_dummy(self) -> Path: logger.info("Starting dummy resonator spectroscopy before/after pi measurement") frequencies = np.linspace(self.start_freq(), self.end_freq(), int(self.steps())) - gen_before = HangerResonator(f0=_RS._SIM_F0, Qc=_RS._SIM_QC, Qi=_RS._SIM_QI, A=_RS._SIM_A, phi=_RS._SIM_PHI, noise_std=_RS._SIM_NOISE_AMP) - sweep_before = sweep_parameter("frequencies", frequencies, record_as(lambda frequencies: gen_before.generate(np.atleast_1d(frequencies)), "signal")) + f0 = (self.start_freq() + self.end_freq()) / 2 + gen_before = HangerResonator(f0=f0, Qc=_RS._SIM_QC, Qi=_RS._SIM_QI, A=_RS._SIM_A, phi=_RS._SIM_PHI, noise_std=_RS._SIM_NOISE_AMP) + sweep_before = sweep_parameter("frequencies", frequencies, record_as(lambda frequencies: np.atleast_1d(gen_before.generate(np.atleast_1d(frequencies)))[0], "signal")) loc_before, _ = run_and_save_sweep(sweep_before, "data", f"{self.name}_before") - gen_after = HangerResonator(f0=_RS._SIM_F0 + self._SIM_CHI, Qc=_RS._SIM_QC, Qi=_RS._SIM_QI, A=_RS._SIM_A, phi=_RS._SIM_PHI, noise_std=_RS._SIM_NOISE_AMP) - sweep_after = sweep_parameter("frequencies", frequencies, record_as(lambda frequencies: gen_after.generate(np.atleast_1d(frequencies)), "signal")) + gen_after = HangerResonator(f0=f0 + self._SIM_CHI, Qc=_RS._SIM_QC, Qi=_RS._SIM_QI, A=_RS._SIM_A, phi=_RS._SIM_PHI, noise_std=_RS._SIM_NOISE_AMP) + sweep_after = sweep_parameter("frequencies", frequencies, record_as(lambda frequencies: np.atleast_1d(gen_after.generate(np.atleast_1d(frequencies)))[0], "signal")) loc_after, _ = run_and_save_sweep(sweep_after, "data", f"{self.name}_after") self.data_loc_before = loc_before diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_vs_gain.py b/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_vs_gain.py index f91f535..51bfa28 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_vs_gain.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/res_spec_vs_gain.py @@ -36,70 +36,75 @@ class ResSpecVsGainSNRThreshold(CorrectionParameter): name: str = field(default="res_spec_vs_gain_snr_threshold", init=False) description: str = field(default="SNR threshold for low-gain quality check", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.res_spec_vs_gain.snr() def _qick_setter(self, v): self.params.corrections.res_spec_vs_gain.snr(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class ResSpecVsGainMaxFitParamError(CorrectionParameter): name: str = field(default="res_spec_vs_gain_max_fit_param_error", init=False) description: str = field(default="Max fractional fit parameter error (e.g. 1.0 = 100%)", init=False) - def _dummy_getter(self): return 1.0 - def _qick_getter(self): return self.params.corrections.res_spec_vs_gain.max_fit_param_error() def _qick_setter(self, v): self.params.corrections.res_spec_vs_gain.max_fit_param_error(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class ResSpecVsGainHighSNRThreshold(CorrectionParameter): name: str = field(default="res_spec_vs_gain_high_snr_threshold", init=False) description: str = field(default="High SNR threshold — at least one trace must exceed this", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.res_spec_vs_gain.high_snr() def _qick_setter(self, v): self.params.corrections.res_spec_vs_gain.high_snr(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class ResSpecVsGainRepetitionFactor(CorrectionParameter): name: str = field(default="res_spec_vs_gain_repetition_factor", init=False) description: str = field(default="Factor by which repetitions are increased on retry", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.res_spec_vs_gain.rep_factor() def _qick_setter(self, v): self.params.corrections.res_spec_vs_gain.rep_factor(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class ResSpecVsGainMaxRepetitionIncreases(CorrectionParameter): name: str = field(default="res_spec_vs_gain_max_rep_increases", init=False) description: str = field(default="Maximum number of repetition increases to attempt", init=False) - def _dummy_getter(self): return 3 - def _qick_getter(self): return int(self.params.corrections.res_spec_vs_gain.max_rep_increases()) def _qick_setter(self, v): self.params.corrections.res_spec_vs_gain.max_rep_increases(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + class IncreaseRepetitionsCorrection(Correction): name = "increase_repetitions" @@ -204,7 +209,8 @@ def _load_data_qick(self): def _measure_dummy(self) -> Path: - freq_shift_per_gain_unit = -.5e6 # MHz per gain unit + freq_shift_per_gain_unit = -5e6 # Hz per gain unit + f0_center = (self.start_frequency() + self.end_frequency()) / 2 @recording( indep("gains"), @@ -214,9 +220,9 @@ def generate_signals(frequencies): gains = np.linspace(self.start_gain(), self.end_gain(), self._SIM_N_GAIN_STEPS) ret_signal = [] for i in range(self._SIM_N_GAIN_STEPS): - shifted_center = _RS._SIM_F0 + freq_shift_per_gain_unit * i + shifted_center = f0_center + freq_shift_per_gain_unit * i generator = HangerResonator(f0=shifted_center, Qc=_RS._SIM_QC, Qi=_RS._SIM_QI, A=_RS._SIM_A, phi=_RS._SIM_PHI, noise_std=_RS._SIM_NOISE_AMP) - ret_signal.append(generator.generate(np.atleast_1d(frequencies))) + ret_signal.append(np.atleast_1d(generator.generate(np.atleast_1d(frequencies)))[0]) return gains, ret_signal diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/sat_spec.py b/src/cqedtoolbox/protocols/operations/single_qubit/sat_spec.py index 9950ea6..f7c9fe1 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/sat_spec.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/sat_spec.py @@ -6,8 +6,6 @@ import numpy as np import matplotlib.pyplot as plt from numpy.typing import NDArray -from scipy.constants import h - plt.switch_backend("agg") @@ -43,168 +41,180 @@ class SNRThreshold(CorrectionParameter): name: str = field(default="sat_spec_snr_threshold", init=False) description: str = field(default="SNR threshold for saturation spectroscopy fit quality", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.sat_spec.snr() def _qick_setter(self, value): self.params.corrections.sat_spec.snr(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class MaxFitParamError(CorrectionParameter): name: str = field(default="sat_spec_max_fit_param_error", init=False) description: str = field(default="Maximum allowed fractional fit parameter error (e.g. 1.0 = 100%)", init=False) - def _dummy_getter(self): return 1.0 - def _qick_getter(self): return self.params.corrections.sat_spec.max_fit_param_error() def _qick_setter(self, value): self.params.corrections.sat_spec.max_fit_param_error(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class MaxWindowShifts(CorrectionParameter): name: str = field(default="sat_spec_max_window_shifts", init=False) description: str = field(default="Number of ±n window shifts to try", init=False) - def _dummy_getter(self): return 3 - def _qick_getter(self): return int(self.params.corrections.sat_spec.max_window_shifts()) def _qick_setter(self, value): self.params.corrections.sat_spec.max_window_shifts(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class AveragingIncreaseFactor(CorrectionParameter): name: str = field(default="sat_spec_averaging_increase_factor", init=False) description: str = field(default="Factor by which to increase repetitions", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.sat_spec.averaging_factor() def _qick_setter(self, value): self.params.corrections.sat_spec.averaging_factor(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class MaxAveragingIncreases(CorrectionParameter): name: str = field(default="sat_spec_max_averaging_increases", init=False) description: str = field(default="Maximum number of averaging increases to try", init=False) - def _dummy_getter(self): return 3 - def _qick_getter(self): return int(self.params.corrections.sat_spec.max_averaging_increases()) def _qick_setter(self, value): self.params.corrections.sat_spec.max_averaging_increases(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class SamplingIncreaseFactor(CorrectionParameter): name: str = field(default="sat_spec_sampling_increase_factor", init=False) description: str = field(default="Factor by which to increase frequency steps", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.sat_spec.sampling_factor() def _qick_setter(self, value): self.params.corrections.sat_spec.sampling_factor(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class MaxSamplingIncreases(CorrectionParameter): name: str = field(default="sat_spec_max_sampling_increases", init=False) description: str = field(default="Maximum number of sampling rate increases to try", init=False) - def _dummy_getter(self): return 3 - def _qick_getter(self): return int(self.params.corrections.sat_spec.max_sampling_increases()) def _qick_setter(self, value): self.params.corrections.sat_spec.max_sampling_increases(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class MaxPowerIncreases(CorrectionParameter): name: str = field(default="sat_spec_max_power_increases", init=False) description: str = field(default="Maximum number of drive power increases to try", init=False) - def _dummy_getter(self): return 3 - def _qick_getter(self): return int(self.params.corrections.sat_spec.max_power_increases()) def _qick_setter(self, value): self.params.corrections.sat_spec.max_power_increases(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class PowerIncreaseFactor(CorrectionParameter): name: str = field(default="sat_spec_power_increase_factor", init=False) description: str = field(default="Multiplicative factor for increasing drive gain (e.g. 1.1 = +10%)", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.sat_spec.power_increase_factor() def _qick_setter(self, value): self.params.corrections.sat_spec.power_increase_factor(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class SinglePeakSNRThreshold(CorrectionParameter): name: str = field(default="sat_spec_single_peak_snr", init=False) description: str = field(default="SNR threshold for detecting a second peak in the fit residuals", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.sat_spec.single_peak_snr() def _qick_setter(self, value): self.params.corrections.sat_spec.single_peak_snr(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class SinglePeakMaxPowerReductions(CorrectionParameter): name: str = field(default="sat_spec_single_peak_max_reductions", init=False) description: str = field(default="Maximum number of drive power reductions to try", init=False) - def _dummy_getter(self): return 3 - def _qick_getter(self): return int(self.params.corrections.sat_spec.single_peak_max_reductions()) def _qick_setter(self, value): self.params.corrections.sat_spec.single_peak_max_reductions(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class PowerReductionFactor(CorrectionParameter): name: str = field(default="sat_spec_power_reduction_factor", init=False) description: str = field(default="Multiplicative factor for reducing drive gain (e.g. 0.9 = -10%)", init=False) - def _dummy_getter(self): return 0.5 - def _qick_getter(self): return self.params.corrections.sat_spec.power_reduction_factor() def _qick_setter(self, value): self.params.corrections.sat_spec.power_reduction_factor(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + # --------------------------------------------------------------------------- # Correction strategies @@ -415,24 +425,12 @@ def model(frequencies: NDArray[Any], fq: float, f_rabi: float, gamma1: float, ga class SaturationSpectroscopy(ProtocolOperation): - _DUMMY_F_Q = 5e9 - _DUMMY_F_R = 7e9 - _DUMMY_DELTA = _DUMMY_F_R - _DUMMY_F_Q - - _DUMMY_P_IN = 1e-16 - _DUMMY_G = 50e6 - _DUMMY_KAPPA_R = 0.2e6 - - _DUMMY_T1 = 50e-6 - _DUMMY_T2 = 50e-6 - _DUMMY_GAMMA_1 = 1 / _DUMMY_T1 - _DUMMY_GAMMA_2 = 1 / (np.pi * _DUMMY_T2) - - _DUMMY_OMEGA = 2 * (_DUMMY_G / _DUMMY_DELTA) * np.sqrt(_DUMMY_KAPPA_R * _DUMMY_P_IN / (h * _DUMMY_F_Q)) - _DUMMY_GAMMA_Q = np.sqrt( (1 / _DUMMY_T2) ** 2 + ((2 * np.pi * _DUMMY_OMEGA) ** 2 * _DUMMY_T1 / _DUMMY_T2) ) / np.pi # blais eq 127 - + _DUMMY_GAMMA_1 = 2.5e6 # Hz — T1 ~ 400 ns, gives visible peak in a 200 MHz sweep + _DUMMY_GAMMA_2 = 5e6 # Hz — T2 ~ 200 ns + _DUMMY_OMEGA = 5e6 # Hz — Rabi frequency comparable to linewidth _DUMMY_NOISE_AMP = 0.05 _DUMMY_ANGLE = np.pi / 4 + _DUMMY_F_Q = 5e9 # Hz — qubit frequency used as peak center in dummy mode @@ -539,8 +537,9 @@ def _load_data_qick(self): def _measure_dummy(self) -> Path: logger.info("Starting dummy saturation spectroscopy measurement") frequencies = np.linspace(self.start_freq(), self.end_freq(), int(self.steps())) - generator = SatSpec(fq=self._DUMMY_F_Q, f_rabi=self._DUMMY_OMEGA, gamma1=self._DUMMY_GAMMA_1, gamma2=self._DUMMY_GAMMA_2, angle=self._DUMMY_ANGLE, noise_std=self._DUMMY_NOISE_AMP) - sweep = sweep_parameter("frequencies", frequencies, record_as(lambda frequencies: generator.generate(np.atleast_1d(frequencies)), "signal")) + fq = (self.start_freq() + self.end_freq()) / 2 + generator = SatSpec(fq=fq, f_rabi=self._DUMMY_OMEGA, gamma1=self._DUMMY_GAMMA_1, gamma2=self._DUMMY_GAMMA_2, angle=self._DUMMY_ANGLE, noise_std=self._DUMMY_NOISE_AMP) + sweep = sweep_parameter("frequencies", frequencies, record_as(lambda frequencies: np.atleast_1d(generator.generate(np.atleast_1d(frequencies)))[0], "signal")) loc, _ = run_and_save_sweep(sweep, "data", self.name) logger.info("Dummy saturation spectroscopy measurement complete.") return loc diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/t1.py b/src/cqedtoolbox/protocols/operations/single_qubit/t1.py index a0496bd..29d6cb9 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/t1.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/t1.py @@ -43,10 +43,10 @@ class SNRMinThreshold(CorrectionParameter): name: str = field(default="t1_snr_min_threshold", init=False) description: str = field(default="Minimum SNR for a valid T1 fit component", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.t1.snr_min() def _qick_setter(self, v): self.params.corrections.t1.snr_min(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -54,10 +54,10 @@ class MaxFitParamError(CorrectionParameter): name: str = field(default="t1_max_fit_param_error", init=False) description: str = field(default="Maximum allowed fractional fit parameter error (e.g. 1.0 = 100%)", init=False) - def _dummy_getter(self): return 1.0 - def _qick_getter(self): return self.params.corrections.t1.max_fit_param_error() def _qick_setter(self, v): self.params.corrections.t1.max_fit_param_error(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -65,10 +65,10 @@ class DelayIncreaseFactor(CorrectionParameter): name: str = field(default="t1_delay_factor", init=False) description: str = field(default="Factor by which to increase delay between shots", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.t1.delay_factor() def _qick_setter(self, v): self.params.corrections.t1.delay_factor(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -76,10 +76,10 @@ class MaxDelayIncreases(CorrectionParameter): name: str = field(default="t1_max_delay_increases", init=False) description: str = field(default="Maximum number of delay increases to try", init=False) - def _dummy_getter(self): return 3 - def _qick_getter(self): return int(self.params.corrections.t1.max_delay_increases()) def _qick_setter(self, v): self.params.corrections.t1.max_delay_increases(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -87,10 +87,10 @@ class AveragingIncreaseFactor(CorrectionParameter): name: str = field(default="t1_averaging_factor", init=False) description: str = field(default="Factor by which to increase repetitions", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.t1.averaging_factor() def _qick_setter(self, v): self.params.corrections.t1.averaging_factor(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -98,10 +98,10 @@ class MaxAveragingIncreases(CorrectionParameter): name: str = field(default="t1_max_averaging_increases", init=False) description: str = field(default="Maximum number of averaging increases to try", init=False) - def _dummy_getter(self): return 3 - def _qick_getter(self): return int(self.params.corrections.t1.max_averaging_increases()) def _qick_setter(self, v): self.params.corrections.t1.max_averaging_increases(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter # --------------------------------------------------------------------------- @@ -248,8 +248,8 @@ def __init__(self, params): def _measure_dummy(self) -> Path: logger.info("Starting dummy T1 measurement") delays = np.linspace(0, 5 * self._SIM_T1, int(self.steps())) - generator = ExponentialDecayDataGen(A=self._SIM_AMP, tau=self._SIM_T1, of=0, noise_std=self._SIM_NOISE_AMP) - sweep = sweep_parameter("delays", delays, record_as(lambda delays: generator.generate(np.atleast_1d(delays)), "signal")) + generator = ExponentialDecayDataGen(A=self._SIM_AMP, tau=self._SIM_T1, of=0, noise_std=self._SIM_NOISE_AMP, imaginary=True) + sweep = sweep_parameter("delays", delays, record_as(lambda delays: np.atleast_1d(generator.generate(np.atleast_1d(delays)))[0], "signal")) loc, _ = run_and_save_sweep(sweep, "data", self.name) logger.info("Dummy measurement complete") return loc diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/t2e.py b/src/cqedtoolbox/protocols/operations/single_qubit/t2e.py index d80e769..454d7a9 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/t2e.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/t2e.py @@ -43,10 +43,10 @@ class SNRMinThreshold(CorrectionParameter): name: str = field(default="t2e_snr_min_threshold", init=False) description: str = field(default="Minimum SNR for a valid T2E fit component", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.t2e.snr_min() def _qick_setter(self, v): self.params.corrections.t2e.snr_min(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -54,10 +54,10 @@ class MaxFitParamError(CorrectionParameter): name: str = field(default="t2e_max_fit_param_error", init=False) description: str = field(default="Maximum allowed fractional fit parameter error (e.g. 1.0 = 100%)", init=False) - def _dummy_getter(self): return 1.0 - def _qick_getter(self): return self.params.corrections.t2e.max_fit_param_error() def _qick_setter(self, v): self.params.corrections.t2e.max_fit_param_error(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -65,10 +65,10 @@ class MaxEchos(CorrectionParameter): name: str = field(default="t2e_max_echos", init=False) description: str = field(default="Maximum number of echo pulses to try", init=False) - def _dummy_getter(self): return 3 - def _qick_getter(self): return int(self.params.corrections.t2e.max_echos()) def _qick_setter(self, v): self.params.corrections.t2e.max_echos(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -76,10 +76,10 @@ class AveragingIncreaseFactor(CorrectionParameter): name: str = field(default="t2e_averaging_factor", init=False) description: str = field(default="Factor by which to increase repetitions", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.t2e.averaging_factor() def _qick_setter(self, v): self.params.corrections.t2e.averaging_factor(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -87,10 +87,10 @@ class MaxAveragingIncreases(CorrectionParameter): name: str = field(default="t2e_max_averaging_increases", init=False) description: str = field(default="Maximum number of averaging increases to try", init=False) - def _dummy_getter(self): return 3 - def _qick_getter(self): return int(self.params.corrections.t2e.max_averaging_increases()) def _qick_setter(self, v): self.params.corrections.t2e.max_averaging_increases(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter # --------------------------------------------------------------------------- @@ -231,9 +231,15 @@ def __init__(self, params): def _measure_dummy(self) -> Path: logger.info("Starting dummy T2 Echo measurement") - delays = np.linspace(0, 5 * self._SIM_T2E, int(self.steps())) + delays = np.linspace(0.5, 5 * self._SIM_T2E, int(self.steps())) generator = ExponentialDecayingSine(A=self._SIM_AMP, f=self._SIM_DETUNING, phi=0, tau=self._SIM_T2E, of=0, noise_std=self._SIM_NOISE_AMP) - sweep = sweep_parameter("delays", delays, record_as(lambda delays: generator.generate(np.atleast_1d(delays)), "signal")) + sweep = sweep_parameter("delays", delays, record_as( + lambda delays: ( + np.atleast_1d(generator.generate(np.atleast_1d(delays), phi=np.pi / 2))[0] + + 1j * np.atleast_1d(generator.generate(np.atleast_1d(delays)))[0] + ), + "signal" + )) loc, _ = run_and_save_sweep(sweep, "data", self.name) logger.info("Dummy measurement complete") return loc diff --git a/src/cqedtoolbox/protocols/operations/single_qubit/t2r.py b/src/cqedtoolbox/protocols/operations/single_qubit/t2r.py index c8efa15..e561d30 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/t2r.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/t2r.py @@ -43,10 +43,10 @@ class SNRMinThreshold(CorrectionParameter): name: str = field(default="t2r_snr_min_threshold", init=False) description: str = field(default="Minimum SNR for a valid T2R fit component", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.t2r.snr_min() def _qick_setter(self, v): self.params.corrections.t2r.snr_min(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -54,10 +54,10 @@ class MaxFitParamError(CorrectionParameter): name: str = field(default="t2r_max_fit_param_error", init=False) description: str = field(default="Maximum allowed fractional fit parameter error (e.g. 1.0 = 100%)", init=False) - def _dummy_getter(self): return 1.0 - def _qick_getter(self): return self.params.corrections.t2r.max_fit_param_error() def _qick_setter(self, v): self.params.corrections.t2r.max_fit_param_error(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -65,10 +65,10 @@ class AveragingIncreaseFactor(CorrectionParameter): name: str = field(default="t2r_averaging_factor", init=False) description: str = field(default="Factor by which to increase repetitions", init=False) - def _dummy_getter(self): return 2.0 - def _qick_getter(self): return self.params.corrections.t2r.averaging_factor() def _qick_setter(self, v): self.params.corrections.t2r.averaging_factor(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter @dataclass @@ -76,10 +76,10 @@ class MaxAveragingIncreases(CorrectionParameter): name: str = field(default="t2r_max_averaging_increases", init=False) description: str = field(default="Maximum number of averaging increases to try", init=False) - def _dummy_getter(self): return 3 - def _qick_getter(self): return int(self.params.corrections.t2r.max_averaging_increases()) def _qick_setter(self, v): self.params.corrections.t2r.max_averaging_increases(v) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter # --------------------------------------------------------------------------- @@ -178,9 +178,15 @@ def __init__(self, params): def _measure_dummy(self) -> Path: logger.info("Starting dummy T2 Ramsey measurement") - delays = np.linspace(0, 5 * self._SIM_T2R, int(self.steps())) + delays = np.linspace(0.5, 5 * self._SIM_T2R, int(self.steps())) generator = ExponentialDecayingSine(A=self._SIM_AMP, f=self._SIM_DETUNING, phi=0, tau=self._SIM_T2R, of=0, noise_std=self._SIM_NOISE_AMP) - sweep = sweep_parameter("delays", delays, record_as(lambda delays: generator.generate(np.atleast_1d(delays)), "signal")) + sweep = sweep_parameter("delays", delays, record_as( + lambda delays: ( + np.atleast_1d(generator.generate(np.atleast_1d(delays), phi=np.pi / 2))[0] + + 1j * np.atleast_1d(generator.generate(np.atleast_1d(delays)))[0] + ), + "signal" + )) loc, _ = run_and_save_sweep(sweep, "data", self.name) logger.info("Dummy measurement complete") return loc diff --git a/src/cqedtoolbox/protocols/parameters.py b/src/cqedtoolbox/protocols/parameters.py index 029347a..dbeb013 100644 --- a/src/cqedtoolbox/protocols/parameters.py +++ b/src/cqedtoolbox/protocols/parameters.py @@ -321,6 +321,14 @@ class SaturationSpecDriveGain(ProtocolParameterBase): name: str = field(default="sat_spec_drive_gain", init=False) description: str = field(default="Drive gain for the saturation spectroscopy pump pulse", init=False) + def _dummy_getter(self): + active_qubit = nestedAttributeFromString(self.params, "active.qubit")() + return nestedAttributeFromString(self.params, f"{active_qubit}.pulses.const.gain")() + + def _dummy_setter(self, value): + active_qubit = nestedAttributeFromString(self.params, "active.qubit")() + return nestedAttributeFromString(self.params, f"{active_qubit}.pulses.const.gain")(value) + def _qick_getter(self): active_qubit = nestedAttributeFromString(self.params, "active.qubit")() return nestedAttributeFromString(self.params, f"{active_qubit}.pulses.const.gain")()