Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix broken codes #1465

Merged
merged 7 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/manuals/characterization/stark_experiment.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this backend (switched to V2) is removed because of Qiskit/qiskit#12608


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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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


Expand Down
99 changes: 38 additions & 61 deletions test/framework/test_backend_timing.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,51 +28,44 @@ 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):
timing.round_delay()

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):
Expand All @@ -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])
Expand All @@ -111,34 +101,30 @@ 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):
"""Test delay_time calculation"""
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):
"""Test delay_time calculation"""
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
)
Expand All @@ -148,36 +134,32 @@ 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):
"""Test pulse_time calculation"""
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):
"""Test pulse_time calculation"""
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

Expand All @@ -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)
106 changes: 37 additions & 69 deletions test/library/characterization/test_cross_resonance_hamiltonian.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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."""

Expand Down Expand Up @@ -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)
Loading
Loading