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 0d28af5..8f5844f 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/pi_spec.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/pi_spec.py @@ -12,6 +12,7 @@ from labcore.measurement.storage import run_and_save_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 from labcore.protocols.base import (ProtocolOperation, OperationStatus, serialize_fit_params, @@ -43,6 +44,9 @@ def _qick_getter(self): def _qick_setter(self, value): self.params.corrections.pi_spec.snr(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class PiSpecMaxFitParamError(CorrectionParameter): @@ -55,6 +59,9 @@ def _qick_getter(self): 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): @@ -67,6 +74,9 @@ def _qick_getter(self): 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): @@ -79,6 +89,9 @@ def _qick_getter(self): 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" @@ -162,11 +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 - 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, 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 4bd5211..2aba544 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/power_rabi.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/power_rabi.py @@ -11,6 +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 Sine from labcore.data.datadict_storage import datadict_from_hdf5 from labcore.protocols.base import ( @@ -42,6 +43,8 @@ class SNRThreshold(CorrectionParameter): 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 @@ -51,6 +54,8 @@ class MaxFitParamError(CorrectionParameter): 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 @@ -60,6 +65,8 @@ class AveragingIncreaseFactor(CorrectionParameter): 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 @@ -69,6 +76,8 @@ class MaxAveragingIncreases(CorrectionParameter): 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 @@ -78,6 +87,8 @@ class SamplingIncreaseFactor(CorrectionParameter): 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 @@ -87,6 +98,8 @@ class MaxSamplingIncreases(CorrectionParameter): 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 @@ -96,6 +109,8 @@ class DelayIncreaseFactor(CorrectionParameter): 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 @@ -105,6 +120,8 @@ class MaxDelayIncreases(CorrectionParameter): 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 @@ -114,6 +131,8 @@ class GainRangeShrinkFactor(CorrectionParameter): 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 @@ -123,6 +142,8 @@ class MaxGainRangeShrinks(CorrectionParameter): 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 # --------------------------------------------------------------------------- @@ -258,20 +279,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 +378,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 = 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 383706e..85ec483 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/res_spec.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/res_spec.py @@ -1,15 +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 +from labcore.data.datagen import DataGen from labcore.protocols.base import (ProtocolOperation, OperationStatus, serialize_fit_params, ParamImprovement, CorrectionParameter, CheckResult, Correction) @@ -36,20 +38,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 @@ -63,6 +51,9 @@ def _qick_getter(self): def _qick_setter(self, value): self.params.corrections.res_spec.snr(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class MaxWindowShifts(CorrectionParameter): @@ -75,6 +66,9 @@ def _qick_getter(self): 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): @@ -87,6 +81,9 @@ def _qick_getter(self): 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): @@ -99,6 +96,9 @@ def _qick_getter(self): 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): @@ -111,6 +111,9 @@ def _qick_getter(self): 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): @@ -123,6 +126,9 @@ def _qick_getter(self): 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): @@ -135,6 +141,9 @@ def _qick_getter(self): 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" @@ -252,11 +261,36 @@ 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 - _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 @@ -352,18 +386,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")) + 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 88eea2d..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 @@ -11,6 +11,7 @@ from labcore.measurement.storage import run_and_save_sweep from labcore.measurement.sweep import sweep_parameter from labcore.measurement.record import record_as +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, @@ -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__) @@ -46,6 +47,9 @@ def _qick_getter(self): 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): @@ -58,6 +62,9 @@ def _qick_getter(self): 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): @@ -70,6 +77,9 @@ def _qick_getter(self): 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): @@ -122,27 +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") - 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")) + 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 = 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=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 67335a2..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 @@ -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 cqedtoolbox.protocols.operations.single_qubit.res_spec 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 @@ -41,6 +42,9 @@ def _qick_getter(self): 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): @@ -53,6 +57,9 @@ def _qick_getter(self): 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): @@ -65,6 +72,9 @@ def _qick_getter(self): 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): @@ -77,6 +87,9 @@ def _qick_getter(self): 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): @@ -89,6 +102,9 @@ def _qick_getter(self): 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" @@ -193,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"), @@ -203,16 +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 = 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 = 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(np.atleast_1d(generator.generate(np.atleast_1d(frequencies)))[0]) return gains, ret_signal @@ -304,7 +314,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 +328,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..f7c9fe1 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/sat_spec.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/sat_spec.py @@ -1,11 +1,11 @@ import logging from pathlib import Path from dataclasses import dataclass, field +from typing import Any import numpy as np -from numpy.typing import ArrayLike import matplotlib.pyplot as plt -from scipy.constants import h +from numpy.typing import NDArray plt.switch_backend("agg") @@ -15,6 +15,7 @@ from labcore.data.datadict_storage import datadict_from_hdf5 from labcore.measurement.sweep import sweep_parameter from labcore.measurement.record import record_as +from labcore.data.datagen import DataGen from labcore.protocols.base import (ProtocolOperation, OperationStatus, serialize_fit_params, ParamImprovement, CorrectionParameter, CheckResult, Correction, @@ -46,6 +47,9 @@ def _qick_getter(self): def _qick_setter(self, value): self.params.corrections.sat_spec.snr(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + @dataclass class MaxFitParamError(CorrectionParameter): @@ -58,6 +62,9 @@ def _qick_getter(self): 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): @@ -70,6 +77,9 @@ def _qick_getter(self): 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): @@ -82,6 +92,9 @@ def _qick_getter(self): 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): @@ -94,6 +107,9 @@ def _qick_getter(self): 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): @@ -106,6 +122,9 @@ def _qick_getter(self): 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): @@ -118,6 +137,9 @@ def _qick_getter(self): 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): @@ -130,6 +152,9 @@ def _qick_getter(self): 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): @@ -142,6 +167,9 @@ def _qick_getter(self): 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): @@ -154,6 +182,9 @@ def _qick_getter(self): 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): @@ -166,6 +197,9 @@ def _qick_getter(self): 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): @@ -178,6 +212,9 @@ def _qick_getter(self): def _qick_setter(self, value): self.params.corrections.sat_spec.power_reduction_factor(value) + _dummy_getter = _qick_getter + _dummy_setter = _qick_setter + # --------------------------------------------------------------------------- # Correction strategies @@ -358,28 +395,27 @@ def apply(self) -> None: def report_output(self) -> str: return self._last_change - # --------------------------------------------------------------------------- -# Synthetic data helper +# SatSpec DataGen # --------------------------------------------------------------------------- @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: +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 * self.f_rabi**2 / ( - self.gamma1 * self.gamma2 + (frequencies - self.fq) ** 2 * self.gamma1 / self.gamma2 + self.f_rabi ** 2 + signal = 0.5 * f_rabi**2 / ( + gamma1 * gamma2 + (frequencies - fq) ** 2 * gamma1 / gamma2 + 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) + signal_re = signal * np.cos(angle) + signal_imag = signal * np.sin(angle) return signal_re + 1j * signal_imag @@ -389,24 +425,13 @@ 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_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 + def __init__(self, params): @@ -510,30 +535,13 @@ 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())) + 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 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..29d6cb9 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/t1.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/t1.py @@ -12,6 +12,7 @@ from labcore.measurement.storage import run_and_save_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 from labcore.protocols.base import ( @@ -44,6 +45,8 @@ class SNRMinThreshold(CorrectionParameter): 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 @@ -53,6 +56,8 @@ class MaxFitParamError(CorrectionParameter): 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 @@ -62,6 +67,8 @@ class DelayIncreaseFactor(CorrectionParameter): 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 @@ -71,6 +78,8 @@ class MaxDelayIncreases(CorrectionParameter): 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 @@ -80,6 +89,8 @@ class AveragingIncreaseFactor(CorrectionParameter): 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 @@ -89,6 +100,8 @@ class MaxAveragingIncreases(CorrectionParameter): 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 # --------------------------------------------------------------------------- @@ -235,9 +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())) - 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, 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 78772a1..454d7a9 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/t2e.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/t2e.py @@ -12,6 +12,7 @@ from labcore.measurement.storage import run_and_save_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 from labcore.protocols.base import ( @@ -44,6 +45,8 @@ class SNRMinThreshold(CorrectionParameter): 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 @@ -53,6 +56,8 @@ class MaxFitParamError(CorrectionParameter): 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 @@ -62,6 +67,8 @@ class MaxEchos(CorrectionParameter): 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 @@ -71,6 +78,8 @@ class AveragingIncreaseFactor(CorrectionParameter): 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 @@ -80,6 +89,8 @@ class MaxAveragingIncreases(CorrectionParameter): 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 # --------------------------------------------------------------------------- @@ -220,10 +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())) - 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")) + 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: ( + 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 53204b8..e561d30 100644 --- a/src/cqedtoolbox/protocols/operations/single_qubit/t2r.py +++ b/src/cqedtoolbox/protocols/operations/single_qubit/t2r.py @@ -12,6 +12,7 @@ from labcore.measurement.storage import run_and_save_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 from labcore.protocols.base import ( @@ -44,6 +45,8 @@ class SNRMinThreshold(CorrectionParameter): 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 @@ -53,6 +56,8 @@ class MaxFitParamError(CorrectionParameter): 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 @@ -62,6 +67,8 @@ class AveragingIncreaseFactor(CorrectionParameter): 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 @@ -71,6 +78,8 @@ class MaxAveragingIncreases(CorrectionParameter): 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 # --------------------------------------------------------------------------- @@ -169,10 +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())) - 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")) + 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: ( + 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")()