diff --git a/docs/manuals/characterization/stark_experiment.rst b/docs/manuals/characterization/stark_experiment.rst index f34c83b981..fd4f11fbda 100644 --- a/docs/manuals/characterization/stark_experiment.rst +++ b/docs/manuals/characterization/stark_experiment.rst @@ -147,10 +147,10 @@ by a variant of the Hahn-echo pulse sequence [5]_. from qiskit_experiments.library import StarkRamseyXY from qiskit import schedule, pulse - from qiskit_ibm_runtime.fake_provider import FakeHanoi + from qiskit_ibm_runtime.fake_provider import FakeHanoiV2 from qiskit.visualization.pulse_v2 import IQXSimple - backend = FakeHanoi() + backend = FakeHanoiV2() exp = StarkRamseyXY( physical_qubits=[0], backend=backend, @@ -169,7 +169,7 @@ by a variant of the Hahn-echo pulse sequence [5]_. "formatter.label_offset.pulse_name": 0.1, "formatter.text_size.annotate": 14, } - ram_x_schedule.draw(time_range=(0, 1600), style=IQXSimple(**opt), backend=backend) + ram_x_schedule.draw(time_range=(0, 1600), style=IQXSimple(**opt)) The qubit is initialized in the :math:`Y`-eigenstate with the first half-pi pulse. This state may be visualized by a Bloch vector located on the equator of the Bloch sphere, diff --git a/qiskit_experiments/library/randomized_benchmarking/clifford_utils.py b/qiskit_experiments/library/randomized_benchmarking/clifford_utils.py index acf0935d4b..95dbeb610b 100644 --- a/qiskit_experiments/library/randomized_benchmarking/clifford_utils.py +++ b/qiskit_experiments/library/randomized_benchmarking/clifford_utils.py @@ -57,11 +57,9 @@ def _transpile_clifford_circuit( def _decompose_clifford_ops(circuit: QuantumCircuit) -> QuantumCircuit: # Simplified QuantumCircuit.decompose, which decomposes only Clifford ops - # Note that the resulting circuit depends on the input circuit, - # that means the changes on the input circuit may affect the resulting circuit. - # For example, the resulting circuit shares the parameter_table of the input circuit, res = circuit.copy_empty_like() - res._parameter_table = circuit._parameter_table + if hasattr(circuit, "_parameter_table"): + res._parameter_table = circuit._parameter_table for inst in circuit: if inst.operation.name.startswith("Clifford"): # Decompose rule = inst.operation.definition.data @@ -89,7 +87,8 @@ def _apply_qubit_layout(circuit: QuantumCircuit, physical_qubits: Sequence[int]) for reg in circuit.cregs: res.add_register(reg) _circuit_compose(res, circuit, qubits=physical_qubits) - res._parameter_table = circuit._parameter_table + if hasattr(circuit, "_parameter_table"): + res._parameter_table = circuit._parameter_table return res diff --git a/test/framework/test_backend_timing.py b/test/framework/test_backend_timing.py index a7951b2c21..c2384b405d 100644 --- a/test/framework/test_backend_timing.py +++ b/test/framework/test_backend_timing.py @@ -28,42 +28,36 @@ class TestBackendTiming(QiskitExperimentsTestCase): @classmethod def setUpClass(cls): super().setUpClass() - + cls.acquire_alignment = 16 + cls.dt = 1 / 4.5e9 + cls.granularity = 16 + cls.min_length = 64 + cls.pulse_alignment = 1 + + def setUp(self): + super().setUp() # Creating a complete fake backend is difficult so we use one from - # qiskit. Just to be safe, we check that the properties we care about - # for these tests are never changed from what the tests assume. - backend = FakeNairobiV2() - target = backend.target - assumptions = ( - (abs(target.dt * 4.5e9 - 1) < 1e-6) - and target.acquire_alignment == 16 - and target.pulse_alignment == 1 - and target.min_length == 64 - and target.granularity == 16 - ) - if not assumptions: # pragma: no cover - raise ValueError("FakeNairobiV2 properties have changed!") - - cls.acquire_alignment = target.acquire_alignment - cls.dt = target.dt - cls.granularity = target.granularity - cls.min_length = target.min_length - cls.pulse_alignment = target.pulse_alignment + # qiskit. Just to be safe, we override hardware properties + # with the values assumed for the unit tests. + self.backend = FakeNairobiV2() + self.backend.target.dt = self.dt + self.backend.target.acquire_alignment = self.acquire_alignment + self.backend.target.pulse_alignment = self.pulse_alignment + self.backend.target.min_length = self.min_length + self.backend.target.granularity = self.granularity @data((True, "s"), (False, "dt")) @unpack def test_delay_unit(self, null_dt, result): """Test delay unit matches dt""" - backend = FakeNairobiV2() if null_dt: - backend.target.dt = None - timing = BackendTiming(backend) + self.backend.target.dt = None + timing = BackendTiming(self.backend) self.assertEqual(timing.delay_unit, result) def test_round_delay_args(self): """Test argument checking in round_delay""" - backend = FakeNairobiV2() - timing = BackendTiming(backend) + timing = BackendTiming(self.backend) with self.assertRaises(QiskitError): timing.round_delay(time=self.dt * 16, samples=16) with self.assertRaises(QiskitError): @@ -71,8 +65,7 @@ def test_round_delay_args(self): def test_round_pulse_args(self): """Test argument checking in round_pulse""" - backend = FakeNairobiV2() - timing = BackendTiming(backend) + timing = BackendTiming(self.backend) with self.assertRaises(QiskitError): timing.round_pulse(time=self.dt * 64, samples=64) with self.assertRaises(QiskitError): @@ -84,25 +77,22 @@ def test_round_delay(self, samples_in, samples_out): """Test delay calculation with time input""" time = self.dt * samples_in - backend = FakeNairobiV2() - timing = BackendTiming(backend) + timing = BackendTiming(self.backend) self.assertEqual(timing.round_delay(time=time), samples_out) def test_round_delay_no_dt(self): """Test delay when dt is None""" time = self.dt * 16 - backend = FakeNairobiV2() - backend.target.dt = None - timing = BackendTiming(backend) + self.backend.target.dt = None + timing = BackendTiming(self.backend) self.assertEqual(timing.round_delay(time=time), time) @data([14, 16], [16, 16], [18, 16], [64.5, 64]) @unpack def test_round_delay_samples_in(self, samples_in, samples_out): """Test delay calculation with samples input""" - backend = FakeNairobiV2() - timing = BackendTiming(backend) + timing = BackendTiming(self.backend) self.assertEqual(timing.round_delay(samples=samples_in), samples_out) @data([12, 64], [65, 64], [79, 80], [83, 80]) @@ -111,16 +101,14 @@ def test_round_pulse(self, samples_in, samples_out): """Test round pulse calculation with time input""" time = self.dt * samples_in - backend = FakeNairobiV2() - timing = BackendTiming(backend) + timing = BackendTiming(self.backend) self.assertEqual(timing.round_pulse(time=time), samples_out) @data([12, 64], [65, 64], [79, 80], [83, 80], [80.5, 80]) @unpack def test_round_pulse_samples_in(self, samples_in, samples_out): """Test round pulse calculation with samples input""" - backend = FakeNairobiV2() - timing = BackendTiming(backend) + timing = BackendTiming(self.backend) self.assertEqual(timing.round_pulse(samples=samples_in), samples_out) def test_delay_time(self): @@ -128,8 +116,7 @@ def test_delay_time(self): time_in = self.dt * 16.1 time_out = self.dt * 16 - backend = FakeNairobiV2() - timing = BackendTiming(backend) + timing = BackendTiming(self.backend) self.assertAlmostEqual(timing.delay_time(time=time_in), time_out, delta=1e-6 * self.dt) def test_delay_time_samples_in(self): @@ -137,8 +124,7 @@ def test_delay_time_samples_in(self): samples_in = 16.1 time_out = self.dt * 16 - backend = FakeNairobiV2() - timing = BackendTiming(backend) + timing = BackendTiming(self.backend) self.assertAlmostEqual( timing.delay_time(samples=samples_in), time_out, delta=1e-6 * self.dt ) @@ -148,9 +134,8 @@ def test_delay_time_no_dt(self): time_in = self.dt * 16.1 time_out = time_in - backend = FakeNairobiV2() - backend.target.dt = None - timing = BackendTiming(backend) + self.backend.target.dt = None + timing = BackendTiming(self.backend) self.assertAlmostEqual(timing.delay_time(time=time_in), time_out, delta=1e-6 * self.dt) def test_pulse_time(self): @@ -158,8 +143,7 @@ def test_pulse_time(self): time_in = self.dt * 85.1 time_out = self.dt * 80 - backend = FakeNairobiV2() - timing = BackendTiming(backend) + timing = BackendTiming(self.backend) self.assertAlmostEqual(timing.pulse_time(time=time_in), time_out, delta=1e-6 * self.dt) def test_pulse_time_samples_in(self): @@ -167,17 +151,15 @@ def test_pulse_time_samples_in(self): samples_in = 85.1 time_out = self.dt * 80 - backend = FakeNairobiV2() - timing = BackendTiming(backend) + timing = BackendTiming(self.backend) self.assertAlmostEqual( timing.pulse_time(samples=samples_in), time_out, delta=1e-6 * self.dt ) def test_round_pulse_no_dt_error(self): """Test methods that don't work when dt is None raise exceptions""" - backend = FakeNairobiV2() - backend.target.dt = None - timing = BackendTiming(backend) + self.backend.target.dt = None + timing = BackendTiming(self.backend) time = self.dt * 81 @@ -186,19 +168,14 @@ def test_round_pulse_no_dt_error(self): def test_unexpected_pulse_alignment(self): """Test that a weird pulse_alignment parameter is caught""" - backend = FakeNairobiV2() - backend.target.pulse_alignment = 33 - timing = BackendTiming(backend) + self.backend.target.pulse_alignment = 33 + timing = BackendTiming(self.backend) with self.assertRaises(QiskitError): timing.round_pulse(samples=81) def test_unexpected_acquire_alignment(self): """Test that a weird acquire_alignment parameter is caught""" - backend = FakeNairobiV2() - try: - backend.target.acquire_alignment = 33 - except AttributeError: - backend.target.aquire_alignment = 33 - timing = BackendTiming(backend) + self.backend.target.acquire_alignment = 33 + timing = BackendTiming(self.backend) with self.assertRaises(QiskitError): timing.round_pulse(samples=81) diff --git a/test/library/characterization/test_cross_resonance_hamiltonian.py b/test/library/characterization/test_cross_resonance_hamiltonian.py index 00b574430a..3ed70b85e7 100644 --- a/test/library/characterization/test_cross_resonance_hamiltonian.py +++ b/test/library/characterization/test_cross_resonance_hamiltonian.py @@ -14,13 +14,15 @@ """Spectroscopy tests.""" from test.base import QiskitExperimentsTestCase -from test.extended_equality import is_equivalent import functools import io +from unittest.mock import patch + import numpy as np from ddt import ddt, data, unpack from qiskit import QuantumCircuit, pulse, qpy, quantum_info as qi +from qiskit.circuit import Gate # TODO: remove old path after we stop supporting the relevant version of Qiskit try: @@ -34,36 +36,6 @@ from qiskit_experiments.library.characterization import cr_hamiltonian -def is_equivalent_circuit(circ1: QuantumCircuit, circ2: QuantumCircuit) -> bool: - """ - Check if two circuits are structurally the same. - We use it due to the field 'operation' under 'circ.data[i]' wich its '__qe__' - method isn't good for reconstructed circuits (by using qpy) with custom pulse gates. - """ - check = ( - is_equivalent(circ1.calibrations, circ2.calibrations) - and is_equivalent(circ1.qregs, circ2.qregs) - and is_equivalent(circ1.qubits, circ2.qubits) - ) - - for data1, data2 in zip(circ1.data, circ2.data): - circ1_op = data1.operation - circ2_op = data2.operation - check = ( - check - and is_equivalent(data1.clbits, data2.clbits) - and is_equivalent(data1.qubits, data2.qubits) - and is_equivalent(circ1_op.definition, circ2_op.definition) - and is_equivalent(circ1_op.name, circ2_op.name) - and is_equivalent(circ1_op.params, circ2_op.params) - and is_equivalent(circ1_op.unit, circ2_op.unit) - and is_equivalent(circ1_op.num_clbits, circ2_op.num_clbits) - and is_equivalent(circ1_op.num_qubits, circ2_op.num_qubits) - ) - - return check - - class SimulatableCRGate(HamiltonianGate): """Hamiltonian Gate for simulation.""" @@ -302,48 +274,44 @@ def test_circuit_serialization(self): """Test generated circuits.""" backend = FakeBogotaV2() - expr = cr_hamiltonian.CrossResonanceHamiltonian( - physical_qubits=(0, 1), - amp=0.1, - sigma=64, - risefall=2, - ) - expr.backend = backend - - with pulse.build(default_alignment="left", name="cr") as _: - pulse.play( - pulse.GaussianSquare( - duration=1256, - amp=0.1, - sigma=64, - width=1000, - ), - pulse.ControlChannel(0), + with patch.object( + cr_hamiltonian.CrossResonanceHamiltonian.CRPulseGate, + "base_class", + Gate, + ): + # Monkey patching the Instruction.base_class property of the CRPulseGate. + # QPY loader is not aware of Gate subclasses defined outside Qiskit core, + # and a Gate subclass instance is reconstructed as a Gate class instance. + # This results in the failure in comparison of structurally same circuits. + # In this context, CRPulseGate looks like a Gate class. + expr = cr_hamiltonian.CrossResonanceHamiltonian( + physical_qubits=(0, 1), + amp=0.1, + sigma=64, + risefall=2, ) - pulse.delay(1256, pulse.DriveChannel(0)) - pulse.delay(1256, pulse.DriveChannel(1)) + expr.backend = backend - width_sec = 1000 * backend.dt - cr_gate = cr_hamiltonian.CrossResonanceHamiltonian.CRPulseGate(width=width_sec) - circuits = expr._transpiled_circuits() + width_sec = 1000 * backend.dt + cr_gate = cr_hamiltonian.CrossResonanceHamiltonian.CRPulseGate(width=width_sec) + circuits = expr._transpiled_circuits() - x0_circ = QuantumCircuit(2, 1) - x0_circ.append(cr_gate, [0, 1]) - x0_circ.rz(np.pi / 2, 1) - x0_circ.sx(1) - x0_circ.measure(1, 0) + x0_circ = QuantumCircuit(2, 1) + x0_circ.append(cr_gate, [0, 1]) + x0_circ.rz(np.pi / 2, 1) + x0_circ.sx(1) + x0_circ.measure(1, 0) - circuits.append(x0_circ) + circuits.append(x0_circ) - with io.BytesIO() as buff: - qpy.dump(circuits, buff) - buff.seek(0) - serialized_data = buff.read() + with io.BytesIO() as buff: + qpy.dump(circuits, buff) + buff.seek(0) + serialized_data = buff.read() - with io.BytesIO() as buff: - buff.write(serialized_data) - buff.seek(0) - decoded = qpy.load(buff) + with io.BytesIO() as buff: + buff.write(serialized_data) + buff.seek(0) + decoded = qpy.load(buff) - for circ1, circ2 in zip(circuits, decoded): - self.assertTrue(is_equivalent_circuit(circ1, circ2)) + self.assertListEqual(circuits, decoded) diff --git a/test/library/characterization/test_half_angle.py b/test/library/characterization/test_half_angle.py index 7e2359b292..9d0ebfd58f 100644 --- a/test/library/characterization/test_half_angle.py +++ b/test/library/characterization/test_half_angle.py @@ -17,7 +17,7 @@ from qiskit import pulse, transpile from qiskit.pulse import InstructionScheduleMap -from qiskit_ibm_runtime.fake_provider import FakeAthens +from qiskit_ibm_runtime.fake_provider import FakeAthensV2 from qiskit_experiments.test.mock_iq_backend import MockIQBackend from qiskit_experiments.test.mock_iq_helpers import MockIQHalfAngleHelper as HalfAngleHelper @@ -58,7 +58,7 @@ def test_circuits(self): # mimic what will happen in the experiment. transpile_opts = copy.copy(hac.transpile_options.__dict__) transpile_opts["initial_layout"] = list(hac._physical_qubits) - circuits = transpile(hac.circuits(), FakeAthens(), **transpile_opts) + circuits = transpile(hac.circuits(), FakeAthensV2(), **transpile_opts) for idx, circ in enumerate(circuits): self.assertEqual(circ.count_ops()["sx"], idx * 2 + 2) diff --git a/test/library/randomized_benchmarking/test_interleaved_rb.py b/test/library/randomized_benchmarking/test_interleaved_rb.py index f01fd6a764..47f46d4d07 100644 --- a/test/library/randomized_benchmarking/test_interleaved_rb.py +++ b/test/library/randomized_benchmarking/test_interleaved_rb.py @@ -23,7 +23,7 @@ from qiskit.transpiler import InstructionProperties from qiskit_aer import AerSimulator from qiskit_aer.noise import NoiseModel, depolarizing_error -from qiskit_ibm_runtime.fake_provider import FakeManila, FakeManilaV2, FakeWashington +from qiskit_ibm_runtime.fake_provider import FakeManilaV2 from qiskit_experiments.library import randomized_benchmarking as rb @@ -34,8 +34,7 @@ class TestInterleavedRB(QiskitExperimentsTestCase, RBTestMixin): def setUp(self): """Setup the tests.""" super().setUp() - self.backend = FakeManila() - self.backend_with_timing_constraint = FakeWashington() + self.backend = FakeManilaV2() # ### Tests for configuration ### def test_non_clifford_interleaved_element(self): @@ -56,7 +55,7 @@ def test_interleaving_delay_with_invalid_duration(self, duration, unit): interleaved_element=Delay(duration, unit=unit), physical_qubits=[0], lengths=[1, 2, 3], - backend=self.backend_with_timing_constraint, + backend=self.backend, ) def test_experiment_config(self): @@ -269,15 +268,14 @@ def test_interleaved_circuit_is_decomposed(self): def test_interleaving_cnot_gate_with_non_supported_direction(self): """Test if fails to interleave cx(1, 2) for backend that support only cx(2, 1).""" - my_backend = FakeManilaV2() - del my_backend.target["cx"][(1, 2)] # make support only cx(2, 1) + del self.backend.target["cx"][(1, 2)] # make support only cx(2, 1) exp = rb.InterleavedRB( interleaved_element=CXGate(), physical_qubits=(1, 2), lengths=[3], num_samples=4, - backend=my_backend, + backend=self.backend, seed=1234, ) with self.assertRaises(QiskitError): @@ -285,13 +283,12 @@ def test_interleaving_cnot_gate_with_non_supported_direction(self): def test_interleaving_three_qubit_gate_with_calibration(self): """Test if circuits for 3Q InterleavedRB contain custom calibrations supplied via target.""" - my_backend = FakeManilaV2() - with pulse.build(my_backend) as custom_3q_sched: # meaningless schedule + with pulse.build(self.backend) as custom_3q_sched: # meaningless schedule pulse.play(pulse.GaussianSquare(1600, 0.2, 64, 1300), pulse.drive_channel(0)) physical_qubits = (2, 1, 3) custom_3q_gate = self.ThreeQubitGate() - my_backend.target.add_instruction( + self.backend.target.add_instruction( custom_3q_gate, {physical_qubits: InstructionProperties(calibration=custom_3q_sched)} ) @@ -300,7 +297,7 @@ def test_interleaving_three_qubit_gate_with_calibration(self): physical_qubits=physical_qubits, lengths=[3], num_samples=1, - backend=my_backend, + backend=self.backend, seed=1234, ) circuits = exp._transpiled_circuits()