def add_parameter_value( cls, cal: Calibrations, exp_data: ExperimentData, value: ParameterValueType, param: Union[Parameter, str], schedule: Union[ScheduleBlock, str] = None, group: str = "default", ): """Update the calibrations with the given value. Args: cal: The Calibrations instance to update. exp_data: The ExperimentData instance that contains the result and the experiment data. value: The value extracted by the subclasses in the :meth:`update` method. param: The name of the parameter, or the parameter instance, which will receive an updated value. schedule: The ScheduleBlock instance or the name of the instance to which the parameter is attached. group: The calibrations group to update. """ qubits = exp_data.metadata["physical_qubits"] param_value = ParameterValue( value=value, date_time=cls._time_stamp(exp_data), group=group, exp_id=exp_data.experiment_id, ) cal.add_parameter_value(param_value, param, qubits, schedule)
def update( cls, calibrations: Calibrations, exp_data: ExperimentData, parameter: str, schedule: Union[ScheduleBlock, str], result_index: Optional[int] = -1, group: str = "default", target_angle: float = np.pi, **options, ): """Update the value of a drag parameter measured by the FineDrag experiment. Args: calibrations: The calibrations to update. exp_data: The experiment data from which to update. parameter: The name of the parameter in the calibrations to update. schedule: The ScheduleBlock instance or the name of the instance to which the parameter is attached. result_index: The result index to use. By default search entry by name. group: The calibrations group to update. Defaults to "default." target_angle: The target rotation angle of the pulse. options: Trailing options. Raises: CalibrationError: If we cannot get the pulse's standard deviation from the schedule. """ qubits = exp_data.metadata["physical_qubits"] if isinstance(schedule, str): schedule = calibrations.get_schedule(schedule, qubits) # Obtain sigma as it is needed for the fine DRAG update rule. sigma = None for block in schedule.blocks: if isinstance(block, Play) and hasattr(block.pulse, "sigma"): sigma = getattr(block.pulse, "sigma") if sigma is None: raise CalibrationError(f"Could not infer sigma from {schedule}.") d_theta = BaseUpdater.get_value(exp_data, "d_theta", result_index) # See the documentation in fine_drag.py for the derivation of this rule. d_beta = -np.sqrt(np.pi) * d_theta * sigma / target_angle**2 old_beta = calibrations.get_parameter_value(parameter, qubits, schedule, group=group) new_beta = old_beta + d_beta cls.add_parameter_value(calibrations, exp_data, new_beta, parameter, schedule, group)
class TestAmplitudeUpdate(QiskitExperimentsTestCase): """Test the update functions in the update library.""" def setUp(self): """Setup amplitude values.""" super().setUp() self.cals = Calibrations() self.qubit = 1 axp = Parameter("amp") chan = Parameter("ch0") with pulse.build(name="xp") as xp: pulse.play(pulse.Gaussian(duration=160, amp=axp, sigma=40), pulse.DriveChannel(chan)) ax90p = Parameter("amp") with pulse.build(name="x90p") as x90p: pulse.play(pulse.Gaussian(duration=160, amp=ax90p, sigma=40), pulse.DriveChannel(chan)) self.x90p = x90p self.cals.add_schedule(xp, num_qubits=1) self.cals.add_schedule(x90p, num_qubits=1) self.cals.add_parameter_value(0.2, "amp", self.qubit, "xp") self.cals.add_parameter_value(0.1, "amp", self.qubit, "x90p")
def test_frequency(self): """Test calibrations update from spectroscopy.""" qubit = 1 peak_offset = 5.0e6 backend = SpectroscopyBackend(line_width=2e6, freq_offset=peak_offset) freq01 = backend.defaults().qubit_freq_est[qubit] frequencies = np.linspace(freq01 - 10.0e6, freq01 + 10.0e6, 21) spec = QubitSpectroscopy(qubit, frequencies) spec.set_run_options(meas_level=MeasLevel.CLASSIFIED) exp_data = spec.run(backend) self.assertExperimentDone(exp_data) result = exp_data.analysis_results(1) value = result.value.n self.assertTrue( freq01 + peak_offset - 2e6 < value < freq01 + peak_offset + 2e6) self.assertEqual(result.quality, "good") # Test the integration with the Calibrations cals = Calibrations.from_backend(FakeAthens()) self.assertNotEqual( cals.get_parameter_value(cals.__drive_freq_parameter__, qubit), value) Frequency.update(cals, exp_data) self.assertEqual( cals.get_parameter_value(cals.__drive_freq_parameter__, qubit), value)
def setUp(self): """Initialize some cals.""" super().setUp() library = FixedFrequencyTransmon() self.cals = Calibrations.from_backend(FakeArmonk(), libraries=[library])
def update( cls, calibrations: Calibrations, exp_data: ExperimentData, result_index: Optional[int] = -1, group: str = "default", angles_schedules: List[Tuple[float, str, Union[str, ScheduleBlock]]] = None, **options, ): """Update the amplitude of pulses. The value of the amplitude must be derived from the fit so the base method cannot be used. Args: calibrations: The calibrations to update. exp_data: The experiment data from which to update. result_index: The result index to use which defaults to -1. group: The calibrations group to update. Defaults to "default." angles_schedules: A list of tuples specifying which angle to update for which pulse schedule. Each tuple is of the form: (angle, parameter_name, schedule). Here, angle is the rotation angle for which to extract the amplitude, parameter_name is the name of the parameter whose value is to be updated, and schedule is the schedule or its name that contains the parameter. options: Trailing options. Raises: CalibrationError: If the experiment is not of the supported type. """ from qiskit_experiments.library.calibration.rabi import Rabi if angles_schedules is None: angles_schedules = [(np.pi, "amp", "xp")] if isinstance(exp_data.experiment, Rabi): rate = 2 * np.pi * BaseUpdater.get_value(exp_data, "rabi_rate", result_index) for angle, param, schedule in angles_schedules: qubits = exp_data.metadata["physical_qubits"] prev_amp = calibrations.get_parameter_value(param, qubits, schedule, group=group) value = np.round(angle / rate, decimals=8) * np.exp( 1.0j * np.angle(prev_amp)) cls.add_parameter_value(calibrations, exp_data, value, param, schedule, group) else: raise CalibrationError( f"{cls.__name__} updates from {type(Rabi.__name__)}.")
def setUp(self): """Setup amplitude values.""" super().setUp() self.cals = Calibrations(coupling_map=[]) self.qubit = 1 axp = Parameter("amp") chan = Parameter("ch0") with pulse.build(name="xp") as xp: pulse.play(pulse.Gaussian(duration=160, amp=axp, sigma=40), pulse.DriveChannel(chan)) ax90p = Parameter("amp") with pulse.build(name="x90p") as x90p: pulse.play(pulse.Gaussian(duration=160, amp=ax90p, sigma=40), pulse.DriveChannel(chan)) self.x90p = x90p self.cals.add_schedule(xp, num_qubits=1) self.cals.add_schedule(x90p, num_qubits=1) self.cals.add_parameter_value(0.2, "amp", self.qubit, "xp") self.cals.add_parameter_value(0.1, "amp", self.qubit, "x90p")
def test_frequency(self): """Test calibrations update from spectroscopy.""" qubit = 1 peak_offset = 5.0e6 backend = MockIQBackend( experiment_helper=SpectroscopyHelper(freq_offset=peak_offset), iq_cluster_centers=[((-1.0, -1.0), (1.0, 1.0))], iq_cluster_width=[0.2], ) backend._configuration.basis_gates = ["x"] backend._configuration.timing_constraints = {"granularity": 16} freq01 = backend.defaults().qubit_freq_est[qubit] frequencies = np.linspace(freq01 - 10.0e6, freq01 + 10.0e6, 21) spec = QubitSpectroscopy(qubit, frequencies) spec.set_run_options(meas_level=MeasLevel.CLASSIFIED) exp_data = spec.run(backend) self.assertExperimentDone(exp_data) result = exp_data.analysis_results(1) value = result.value.n self.assertTrue( freq01 + peak_offset - 2e6 < value < freq01 + peak_offset + 2e6) self.assertEqual(result.quality, "good") # Test the integration with the Calibrations cals = Calibrations.from_backend(FakeAthens()) self.assertNotEqual( cals.get_parameter_value(cals.__drive_freq_parameter__, qubit), value) Frequency.update(cals, exp_data) self.assertEqual( cals.get_parameter_value(cals.__drive_freq_parameter__, qubit), value)
class TestAmplitudeUpdate(QiskitTestCase): """Test the update functions in the update library.""" def setUp(self): """Setup amplitude values.""" super().setUp() self.cals = Calibrations() self.qubit = 1 axp = Parameter("amp") chan = Parameter("ch0") with pulse.build(name="xp") as xp: pulse.play(pulse.Gaussian(duration=160, amp=axp, sigma=40), pulse.DriveChannel(chan)) ax90p = Parameter("amp") with pulse.build(name="x90p") as x90p: pulse.play(pulse.Gaussian(duration=160, amp=ax90p, sigma=40), pulse.DriveChannel(chan)) self.x90p = x90p self.cals.add_schedule(xp, num_qubits=1) self.cals.add_schedule(x90p, num_qubits=1) self.cals.add_parameter_value(0.2, "amp", self.qubit, "xp") self.cals.add_parameter_value(0.1, "amp", self.qubit, "x90p") def test_amplitude(self): """Test amplitude update from Rabi.""" rabi = Rabi(self.qubit) rabi.set_experiment_options(amplitudes=np.linspace(-0.95, 0.95, 21)) exp_data = rabi.run(RabiBackend()) exp_data.block_for_results() with self.assertRaises(CalibrationError): self.cals.get_schedule("xp", qubits=0) to_update = [(np.pi, "amp", "xp"), (np.pi / 2, "amp", self.x90p)] self.assertEqual(len(self.cals.parameters_table()), 2) Amplitude.update(self.cals, exp_data, angles_schedules=to_update) with self.assertRaises(CalibrationError): self.cals.get_schedule("xp", qubits=0) self.assertEqual(len(self.cals.parameters_table()["data"]), 4) # Now check the corresponding schedules result = exp_data.analysis_results(1) rate = 2 * np.pi * result.value.value amp = np.round(np.pi / rate, decimals=8) with pulse.build(name="xp") as expected: pulse.play(pulse.Gaussian(160, amp, 40), pulse.DriveChannel(self.qubit)) self.assertEqual(self.cals.get_schedule("xp", qubits=self.qubit), expected) amp = np.round(0.5 * np.pi / rate, decimals=8) with pulse.build(name="xp") as expected: pulse.play(pulse.Gaussian(160, amp, 40), pulse.DriveChannel(self.qubit)) self.assertEqual(self.cals.get_schedule("x90p", qubits=self.qubit), expected)
def __init__(self): """A dummy class for parent order testing.""" super().__init__(Calibrations(coupling_map=[]), 0, [0, 1, 2])