Skip to content

Commit

Permalink
Update HamiltonianGate path and add tolerance to PulseBackend (#1280
Browse files Browse the repository at this point in the history
)

### Summary

After `qiskit.extensions` was deprecated in
Qiskit/qiskit#10725, this PR updates the path of
`HamiltonianGate` so tests against Qiskit main pass again. This also
adds `atol` and `rtol` tolerance parameters to `PulseBackend` for
speeding up slow tests.

(cherry picked from commit 73d0a03)

# Conflicts:
#	qiskit_experiments/test/__init__.py
#	test/library/calibration/test_half_angle.py
#	test/library/calibration/test_rough_amplitude.py
  • Loading branch information
coruscating authored and mergify[bot] committed Jan 23, 2024
1 parent 62d75a1 commit c83a214
Show file tree
Hide file tree
Showing 9 changed files with 111 additions and 15 deletions.
3 changes: 3 additions & 0 deletions qiskit_experiments/test/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@
MockIQParallelBackend
T2HahnBackend
NoisyDelayAerBackend
<<<<<<< HEAD
PulseBackend
SingleTransmonTestBackend
=======
>>>>>>> 73d0a03 (Update `HamiltonianGate` path and add tolerance to `PulseBackend` (#1280))
Helpers
=======
Expand Down
17 changes: 17 additions & 0 deletions qiskit_experiments/test/pulse_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ def __init__(
dt: float = 0.1 * 1e-9,
solver_method="RK23",
seed: int = 0,
atol: float = None,
rtol: float = None,
**kwargs,
):
"""Initialize a backend with model information.
Expand All @@ -86,6 +88,8 @@ def __init__(
methods. Defaults to "RK23".
seed: An optional seed given to the random number generator. If this argument is not
set then the seed defaults to 0.
atol: Absolute tolerance during solving.
rtol: Relative tolerance during solving.
"""
super().__init__(
None,
Expand All @@ -108,6 +112,12 @@ def __init__(

self.solver_method = solver_method

self.solve_kwargs = {}
if atol:
self.solve_kwargs["atol"] = atol
if rtol:
self.solve_kwargs["rtol"] = rtol

self.static_hamiltonian = static_hamiltonian
self.hamiltonian_operators = hamiltonian_operators
self.static_dissipators = static_dissipators
Expand Down Expand Up @@ -338,6 +348,7 @@ def solve(self, schedule: Union[ScheduleBlock, Schedule], qubits: Tuple[int]) ->
t_eval=[time_f],
signals=signal,
method=self.solver_method,
**self.solve_kwargs,
).y[0]

return unitary
Expand Down Expand Up @@ -452,6 +463,8 @@ def __init__(
lambda_2: float = 0.8e9,
gamma_1: float = 1e4,
noise: bool = True,
atol: float = None,
rtol: float = None,
**kwargs,
):
"""Initialise backend with hamiltonian parameters
Expand All @@ -464,6 +477,8 @@ def __init__(
gamma_1: Relaxation rate (1/T1) for 1-0. Defaults to 1e4.
noise: Defaults to True. If True then T1 dissipation is included in the pulse-simulation.
The strength is given by ``gamma_1``.
atol: Absolute tolerance during solving.
rtol: Relative tolerance during solving.
"""
qubit_frequency_02 = 2 * qubit_frequency + anharmonicity
ket0 = np.array([[1, 0, 0]]).T
Expand Down Expand Up @@ -505,6 +520,8 @@ def __init__(
rwa_cutoff_freq=1.9 * qubit_frequency,
rwa_carrier_freqs=[qubit_frequency],
evaluation_mode=evaluation_mode,
atol=atol,
rtol=rtol,
**kwargs,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ upgrade:
setting backend for just checking experiment sequence. The sequence with actual parameters
is generated after the backend is set. In addition, now experiments can take ``cr_gate``
in the constractor which is ``Gate`` type subclass taking a single parameter (flat-top width).
If one inputs a :class:`~qiskit.extensions.hamiltonian_gate.HamiltonianGate` subclass with
If one inputs a :class:`~qiskit.circuit.library.HamiltonianGate` subclass with
cross resonance Hamiltonian, experiment can be simulated with Aer QASM simulator.
62 changes: 62 additions & 0 deletions test/library/calibration/test_half_angle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2023.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Test rough amplitude calibration experiment classes."""

from test.base import QiskitExperimentsTestCase

from qiskit import pulse
from qiskit.circuit import Parameter

from qiskit_experiments.exceptions import CalibrationError
from qiskit_experiments.calibration_management.basis_gate_library import FixedFrequencyTransmon
from qiskit_experiments.calibration_management import Calibrations
from qiskit_experiments.library import HalfAngleCal
from qiskit_experiments.test.pulse_backend import SingleTransmonTestBackend


class TestHalfAngleCal(QiskitExperimentsTestCase):
"""A class to test the half angle calibration experiments."""

def setUp(self):
"""Setup the tests."""
super().setUp()
library = FixedFrequencyTransmon()

self.backend = SingleTransmonTestBackend(noise=False, atol=1e-3)
self.cals = Calibrations.from_backend(self.backend, libraries=[library])

def test_amp_parameter_error(self):
"""Test that setting cal_parameter_name to amp raises an error"""
with self.assertRaises(CalibrationError):
HalfAngleCal([0], self.cals, cal_parameter_name="amp")

def test_angle_parameter_missing_error(self):
"""Test that default cal_parameter_name with no matching parameter raises an error"""
cals_no_angle = Calibrations()
dur = Parameter("dur")
amp = Parameter("amp")
sigma = Parameter("σ")
beta = Parameter("β")
drive = pulse.DriveChannel(Parameter("ch0"))

with pulse.build(name="sx") as sx:
pulse.play(pulse.Drag(dur, amp, sigma, beta), drive)

cals_no_angle.add_schedule(sx, num_qubits=1)
with self.assertRaises(CalibrationError):
HalfAngleCal([0], cals_no_angle)

def test_circuits_roundtrip_serializable(self):
"""Test circuits serialization of the experiment."""
exp = HalfAngleCal([0], self.cals, backend=self.backend)
self.assertRoundTripSerializable(exp._transpiled_circuits())
17 changes: 9 additions & 8 deletions test/library/calibration/test_rabi.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,18 @@
class TestRabiEndToEnd(QiskitExperimentsTestCase):
"""Test the rabi experiment."""

def setUp(self):
@classmethod
def setUpClass(cls):
"""Setup the tests."""
super().setUp()
super().setUpClass()

self.qubit = 0
cls.qubit = 0

with pulse.build(name="x") as sched:
pulse.play(pulse.Drag(160, Parameter("amp"), 40, 0.4), pulse.DriveChannel(self.qubit))
pulse.play(pulse.Drag(160, Parameter("amp"), 40, 0.4), pulse.DriveChannel(cls.qubit))

self.sched = sched
self.backend = SingleTransmonTestBackend(noise=False)
cls.sched = sched
cls.backend = SingleTransmonTestBackend(noise=False, atol=1e-3)

# pylint: disable=no-member
def test_rabi_end_to_end(self):
Expand Down Expand Up @@ -100,7 +101,7 @@ def setUp(self):
super().setUp()

self.qubit = 0
self.backend = SingleTransmonTestBackend(noise=False)
self.backend = SingleTransmonTestBackend(noise=False, atol=1e-4)
self.anharmonicity = self.backend.anharmonicity
with pulse.build(name="x") as sched:
with pulse.frequency_offset(self.anharmonicity, pulse.DriveChannel(self.qubit)):
Expand All @@ -114,7 +115,7 @@ def setUp(self):
def test_ef_rabi_end_to_end(self):
"""Test the EFRabi experiment end to end."""

test_tol = 0.01
test_tol = 0.05

# Note that the backend is not sophisticated enough to simulate an e-f
# transition so we run the test with a tiny frequency shift, still driving the e-g transition.
Expand Down
7 changes: 6 additions & 1 deletion test/library/calibration/test_rough_amplitude.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def setUp(self):
super().setUp()
library = FixedFrequencyTransmon()

self.backend = SingleTransmonTestBackend(noise=False)
self.backend = SingleTransmonTestBackend(noise=False, atol=1e-3)
self.cals = Calibrations.from_backend(self.backend, libraries=[library])

def test_circuits(self):
Expand Down Expand Up @@ -100,8 +100,13 @@ def setUp(self):

library = FixedFrequencyTransmon()

<<<<<<< HEAD
self.backend = SingleTransmonTestBackend(noise=False)
self.cals = Calibrations.from_backend(self.backend, libraries=[library])
=======
cls.backend = SingleTransmonTestBackend(noise=False, atol=1e-3)
cls.cals = Calibrations.from_backend(cls.backend, libraries=[library])
>>>>>>> 73d0a03 (Update `HamiltonianGate` path and add tolerance to `PulseBackend` (#1280))

# Add some pulses on the 1-2 transition.
d0 = pulse.DriveChannel(0)
Expand Down
6 changes: 4 additions & 2 deletions test/library/calibration/test_rough_frequency.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class TestRoughFrequency(QiskitExperimentsTestCase):
def setUp(self):
"""Setup the tests."""
super().setUp()
self.backend = SingleTransmonTestBackend(noise=False)
self.backend = SingleTransmonTestBackend(noise=False, atol=1e-3)

def test_init(self):
"""Test that initialization."""
Expand All @@ -53,7 +53,9 @@ def test_update_calibrations(self):

freq01 = BackendData(self.backend).drive_freqs[0]

backend_5mhz = SingleTransmonTestBackend(qubit_frequency=freq01 + 5e6, noise=False)
backend_5mhz = SingleTransmonTestBackend(
qubit_frequency=freq01 + 5e6, noise=False, atol=1e-3
)

library = FixedFrequencyTransmon()
cals = Calibrations.from_backend(self.backend, libraries=[library])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@
from ddt import ddt, data, unpack
from qiskit import QuantumCircuit, pulse, quantum_info as qi
from qiskit.providers.fake_provider import FakeBogotaV2
from qiskit.extensions.hamiltonian_gate import HamiltonianGate

# TODO: remove old path after we stop supporting the relevant version of Qiskit
try:
from qiskit.circuit.library.hamiltonian_gate import HamiltonianGate
except ModuleNotFoundError:
from qiskit.extensions.hamiltonian_gate import HamiltonianGate

from qiskit_aer import AerSimulator
from qiskit_experiments.library.characterization import cr_hamiltonian

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def setUp(self):
"""Setup test variables."""
super().setUp()

self.backend = SingleTransmonTestBackend(noise=False)
self.backend = SingleTransmonTestBackend(noise=False, atol=1e-3)

# Build x12 schedule
self.qubit = 0
Expand Down Expand Up @@ -97,7 +97,7 @@ def test_discrimination_analysis(self, n_states):

fidelity = exp_data.analysis_results("fidelity").value

self.assertGreaterEqual(fidelity, 0.96)
self.assertGreaterEqual(fidelity, 0.93)

# check that the discriminator differentiates n different states
discrim_lbls = exp_data.analysis_results("discriminator_config").value["attributes"][
Expand Down

0 comments on commit c83a214

Please sign in to comment.