diff --git a/qiskit_experiments/library/calibration/fine_amplitude.py b/qiskit_experiments/library/calibration/fine_amplitude.py index 7e8c6ebc76..a96413aecd 100644 --- a/qiskit_experiments/library/calibration/fine_amplitude.py +++ b/qiskit_experiments/library/calibration/fine_amplitude.py @@ -17,6 +17,7 @@ from qiskit import QuantumCircuit from qiskit.circuit import Gate +from qiskit.circuit.library import XGate, SXGate from qiskit.providers import Backend from qiskit.pulse.schedule import ScheduleBlock @@ -103,6 +104,8 @@ def _default_experiment_options(cls) -> Options: Experiment Options: repetitions (List[int]): A list of the number of times that the gate is repeated. schedule (ScheduleBlock): The schedule attached to the gate that will be repeated. + gate_type (Type): This is a gate class such as XGate, so that one can obtain a gate + by doing :code:`options.gate_class()`. normalization (bool): If set to True the DataProcessor will normalized the measured signal to the interval [0, 1]. Defaults to True. add_sx (bool): If True then the circuits will start with an sx gate. This is typically @@ -115,6 +118,7 @@ def _default_experiment_options(cls) -> Options: options = super()._default_experiment_options() options.repetitions = list(range(15)) options.schedule = None + options.gate_type = None options.normalization = True options.add_sx = False options.add_xp_circuit = True @@ -197,26 +201,26 @@ def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: """ # Get the schedule and check assumptions. - schedule = self.experiment_options.get("schedule", None) + schedule = self.experiment_options.schedule if schedule is None: - raise CalibrationError("No schedule set for fine amplitude calibration.") + gate = self.experiment_options.gate_type() + else: + gate = Gate(name=schedule.name, num_qubits=1, params=[]) - if self.physical_qubits[0] not in set(ch.index for ch in schedule.channels): - raise CalibrationError( - f"User provided schedule {schedule.name} does not contain a channel " - "for the qubit on which to run the fine amplitude calibration." - ) + if self.physical_qubits[0] not in set(ch.index for ch in schedule.channels): + raise CalibrationError( + f"User provided schedule {schedule.name} does not contain a channel " + "for the qubit on which to run the fine amplitude calibration." + ) - if len(schedule.parameters) > 0: - raise CalibrationError( - "All parameters in a fine amplitude calibration schedule must be bound. " - f"Unbound parameters: {schedule.parameters}" - ) + if len(schedule.parameters) > 0: + raise CalibrationError( + "All parameters in a fine amplitude calibration schedule must be bound. " + f"Unbound parameters: {schedule.parameters}" + ) # Prepare the circuits. - gate = Gate(name=schedule.name, num_qubits=1, params=[]) - repetitions = self.experiment_options.get("repetitions") circuits = [] @@ -241,7 +245,7 @@ def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: circuit.metadata = { "experiment_type": self._type, - "qubits": (self.physical_qubits[0],), + "qubits": self.physical_qubits, "xval": (np.pi - phase_offset) / angle_per_gate, "unit": "gate number", } @@ -255,11 +259,13 @@ def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: circuit.append(gate, (0,)) circuit.measure_all() - circuit.add_calibration(gate, (self.physical_qubits[0],), schedule, params=[]) + + if schedule is not None: + circuit.add_calibration(gate, self.physical_qubits, schedule, params=[]) circuit.metadata = { "experiment_type": self._type, - "qubits": (self.physical_qubits[0],), + "qubits": self.physical_qubits, "xval": repetition, "unit": "gate number", } @@ -283,6 +289,7 @@ def _default_experiment_options(cls) -> Options: r"""Default values for the fine amplitude experiment. Experiment Options: + gate_type (Type): FineXAmplitude calibrates an XGate. add_sx (bool): This option is True by default when calibrating gates with a target angle per gate of :math:`\pi` as this increases the sensitivity of the experiment. @@ -290,6 +297,7 @@ def _default_experiment_options(cls) -> Options: a target angle per gate of :math:`\pi`. """ options = super()._default_experiment_options() + options.gate_type = XGate options.add_sx = True options.add_xp_circuit = True @@ -319,6 +327,7 @@ def _default_experiment_options(cls) -> Options: r"""Default values for the fine amplitude experiment. Experiment Options: + gate_type (Type): FineSXAmplitude calibrates an SXGate. add_sx (bool): This option is False by default when calibrating gates with a target angle per gate of :math:`\pi/2` as this increases the sensitivity of the experiment. @@ -330,6 +339,7 @@ def _default_experiment_options(cls) -> Options: plays the same role as including a circuit with an X gate. """ options = super()._default_experiment_options() + options.gate_type = SXGate options.add_sx = False options.add_xp_circuit = False options.repetitions = [1, 2, 3, 5, 7, 9, 11, 13, 15, 17, 21, 23, 25] diff --git a/qiskit_experiments/library/calibration/fine_drag.py b/qiskit_experiments/library/calibration/fine_drag.py index b2d11392bf..318b54acf2 100644 --- a/qiskit_experiments/library/calibration/fine_drag.py +++ b/qiskit_experiments/library/calibration/fine_drag.py @@ -17,6 +17,7 @@ from qiskit import QuantumCircuit from qiskit.circuit import Gate +from qiskit.circuit.library import XGate, SXGate from qiskit.providers import Backend from qiskit_experiments.framework import BaseExperiment, Options @@ -26,7 +27,7 @@ class FineDrag(BaseExperiment): - r"""Fine DRAG Calibration experiment. + r"""Fine DRAG experiment. # section: overview @@ -143,12 +144,13 @@ def _default_experiment_options(cls) -> Options: repetitions (List[int]): A list of the number of times that Rp - Rm gate sequence is repeated. schedule (ScheduleBlock): The schedule for the plus rotation. - normalization (bool): If set to True the DataProcessor will normalized the - measured signal to the interval [0, 1]. Defaults to True. + gate_type (Type[Gate]): This is a gate class such as XGate, so that one can obtain a gate + by doing :code:`options.gate_type()`. """ options = super()._default_experiment_options() options.repetitions = list(range(20)) options.schedule = None + options.gate_type = None return options @@ -195,21 +197,26 @@ def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: """ schedule, circuits = self.experiment_options.schedule, [] - drag_gate = Gate(name=schedule.name, num_qubits=1, params=[]) + if schedule is None: + gate = self.experiment_options.gate_type() + else: + gate = Gate(name=schedule.name, num_qubits=1, params=[]) for repetition in self.experiment_options.repetitions: circuit = self._pre_circuit() for _ in range(repetition): - circuit.append(drag_gate, (0,)) + circuit.append(gate, (0,)) circuit.rz(np.pi, 0) - circuit.append(drag_gate, (0,)) + circuit.append(gate, (0,)) circuit.rz(np.pi, 0) circuit.compose(self._post_circuit(), inplace=True) circuit.measure_all() - circuit.add_calibration(schedule.name, self._physical_qubits, schedule, params=[]) + + if schedule is not None: + circuit.add_calibration(schedule.name, self._physical_qubits, schedule, params=[]) circuit.metadata = { "experiment_type": self._type, @@ -230,6 +237,18 @@ class FineXDrag(FineDrag): qiskit_experiments.library.calibration.fine_drag.FineDrag """ + @classmethod + def _default_experiment_options(cls) -> Options: + r"""Default values for the FineXDrag experiment. + + Experiment Options: + gate_type (Type): FineXDrag calibrates an XGate. + """ + options = super()._default_experiment_options() + options.gate_type = XGate + + return options + @staticmethod def _pre_circuit() -> QuantumCircuit: """Return the quantum circuit done before the Rp - Rz - Rp - Rz gates.""" @@ -243,6 +262,18 @@ class FineSXDrag(FineDrag): qiskit_experiments.library.calibration.fine_drag.FineDrag """ + @classmethod + def _default_experiment_options(cls) -> Options: + r"""Default values for the FineSXDrag experiment. + + Experiment Options: + gate_type (Type): FineSXDrag calibrates an SXGate. + """ + options = super()._default_experiment_options() + options.gate_type = SXGate + + return options + @staticmethod def _pre_circuit() -> QuantumCircuit: """Return the quantum circuit with an sx gate before the Rp - Rz - Rp - Rz gates.""" diff --git a/qiskit_experiments/test/mock_iq_backend.py b/qiskit_experiments/test/mock_iq_backend.py index f86058635e..695d9b0fc0 100644 --- a/qiskit_experiments/test/mock_iq_backend.py +++ b/qiskit_experiments/test/mock_iq_backend.py @@ -176,13 +176,13 @@ def _compute_probability(self, circuit: QuantumCircuit) -> float: """Return the probability of being in the excited state.""" n_ops = circuit.count_ops().get(self._gate_name, 0) - n_sx_ops = circuit.count_ops().get("sx", 0) - n_x_ops = circuit.count_ops().get("x", 0) - angle = n_ops * (self._angle_per_gate + self.angle_error) - angle += np.pi / 2 * n_sx_ops - angle += np.pi * n_x_ops + if self._gate_name != "sx": + angle += np.pi / 2 * circuit.count_ops().get("sx", 0) + + if self._gate_name != "x": + angle += np.pi * circuit.count_ops().get("x", 0) return np.sin(angle / 2) ** 2 diff --git a/test/calibration/experiments/test_fine_amplitude.py b/test/calibration/experiments/test_fine_amplitude.py index 0e811481af..c5e1b47704 100644 --- a/test/calibration/experiments/test_fine_amplitude.py +++ b/test/calibration/experiments/test_fine_amplitude.py @@ -14,6 +14,7 @@ import numpy as np +from qiskit.circuit.library import XGate, SXGate from qiskit.test import QiskitTestCase from qiskit.pulse import DriveChannel, Drag import qiskit.pulse as pulse @@ -140,6 +141,7 @@ def test_fine_x_amp(self): self.assertTrue(exp.experiment_options.add_xp_circuit) self.assertEqual(exp.analysis_options.angle_per_gate, np.pi) self.assertEqual(exp.analysis_options.phase_offset, np.pi / 2) + self.assertEqual(exp.experiment_options.gate_type, XGate) def test_fine_sx_amp(self): """Test the fine SX amplitude.""" @@ -153,3 +155,22 @@ def test_fine_sx_amp(self): self.assertEqual(exp.experiment_options.repetitions, expected) self.assertEqual(exp.analysis_options.angle_per_gate, np.pi / 2) self.assertEqual(exp.analysis_options.phase_offset, 0) + self.assertEqual(exp.experiment_options.gate_type, SXGate) + + def test_end_to_end_no_schedule(self): + """Test the experiment end to end.""" + + amp_cal = FineXAmplitude(0) + amp_cal.set_analysis_options(number_guesses=11) + + backend = MockFineAmp(-np.pi * 0.07, np.pi, "x") + + expdata = amp_cal.run(backend).block_for_results() + result = expdata.analysis_results(1) + d_theta = result.value.value + + tol = 0.04 + + self.assertTrue(abs(d_theta - backend.angle_error) < tol) + self.assertEqual(result.quality, "good") + self.assertIsNone(amp_cal.experiment_options.schedule) diff --git a/test/calibration/experiments/test_fine_drag.py b/test/calibration/experiments/test_fine_drag.py index 92e46c909e..5eb7aa0ae6 100644 --- a/test/calibration/experiments/test_fine_drag.py +++ b/test/calibration/experiments/test_fine_drag.py @@ -19,7 +19,7 @@ from qiskit.test.mock import FakeArmonk import qiskit.pulse as pulse -from qiskit_experiments.library import FineDrag +from qiskit_experiments.library import FineDrag, FineXDrag from qiskit_experiments.test.mock_iq_backend import DragBackend @@ -64,3 +64,10 @@ def test_end_to_end(self): exp_data = drag.run(FineDragTestBackend()).block_for_results() self.assertEqual(exp_data.analysis_results(0).quality, "good") + + def test_end_to_end_no_schedule(self): + """Test that we can run without a schedule.""" + + exp_data = FineXDrag(0).run(FineDragTestBackend()).block_for_results() + + self.assertEqual(exp_data.analysis_results(0).quality, "good")