mirror of https://github.com/Qiskit/qiskit.git
582 lines
24 KiB
Python
582 lines
24 KiB
Python
# This code is part of Qiskit.
|
|
#
|
|
# (C) Copyright IBM 2020, 2024.
|
|
#
|
|
# 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.
|
|
|
|
# pylint: disable=missing-function-docstring
|
|
|
|
"""Test scheduled circuit (quantum circuit with duration)."""
|
|
from ddt import ddt, data
|
|
|
|
from qiskit import QuantumCircuit, QiskitError
|
|
from qiskit import transpile
|
|
from qiskit.circuit import Parameter
|
|
from qiskit.circuit.duration import convert_durations_to_dt
|
|
from qiskit.circuit.library import CXGate, HGate
|
|
from qiskit.circuit.delay import Delay
|
|
from qiskit.providers.fake_provider import GenericBackendV2
|
|
from qiskit.providers.basic_provider import BasicSimulator
|
|
from qiskit.transpiler import InstructionProperties, Target
|
|
from qiskit.transpiler.exceptions import TranspilerError
|
|
from test import QiskitTestCase # pylint: disable=wrong-import-order
|
|
|
|
|
|
@ddt
|
|
class TestScheduledCircuit(QiskitTestCase):
|
|
"""Test scheduled circuit (quantum circuit with duration)."""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.dt = 2.2222222222222221e-10
|
|
self.backend_with_dt = GenericBackendV2(2, seed=42, dt=self.dt)
|
|
self.backend_without_dt = GenericBackendV2(2, seed=42)
|
|
self.backend_without_dt.target.dt = None
|
|
self.simulator_backend = BasicSimulator()
|
|
|
|
def test_schedule_circuit_when_backend_tells_dt(self):
|
|
"""dt is known to transpiler by backend"""
|
|
qc = QuantumCircuit(2)
|
|
qc.delay(0.1, 0, unit="ms") # 450000[dt]
|
|
qc.delay(100, 0, unit="ns") # 450[dt]
|
|
qc.h(0) # 195[dt]
|
|
qc.h(1) # 210[dt]
|
|
|
|
backend = GenericBackendV2(2, seed=42)
|
|
|
|
sc = transpile(qc, backend, scheduling_method="alap", layout_method="trivial")
|
|
with self.assertWarns(DeprecationWarning):
|
|
self.assertEqual(sc.duration, 451095)
|
|
self.assertEqual(sc.unit, "dt")
|
|
self.assertEqual(sc.data[0].operation.name, "delay")
|
|
self.assertEqual(sc.data[0].operation.duration, 450900)
|
|
self.assertEqual(sc.data[0].operation.unit, "dt")
|
|
self.assertEqual(sc.data[1].operation.name, "rz")
|
|
self.assertEqual(sc.data[4].operation.name, "delay")
|
|
self.assertEqual(sc.data[4].operation.duration, 450885)
|
|
self.assertEqual(sc.data[4].operation.unit, "dt")
|
|
|
|
def test_schedule_circuit_when_transpile_option_tells_dt(self):
|
|
"""dt is known to transpiler by transpile option"""
|
|
qc = QuantumCircuit(2)
|
|
qc.delay(0.1, 0, unit="ms") # 450000[dt]
|
|
qc.delay(100, 0, unit="ns") # 450[dt]
|
|
qc.h(0) # duration: rz(0) + sx(195[dt]) + rz(0)
|
|
qc.h(1) # duration: rz(0)+ sx(210[dt]) + rz(0)
|
|
sc = transpile(
|
|
qc,
|
|
self.backend_without_dt,
|
|
scheduling_method="alap",
|
|
dt=self.dt,
|
|
layout_method="trivial",
|
|
seed_transpiler=20,
|
|
)
|
|
target_durations = self.backend_with_dt.target.durations()
|
|
with self.assertWarns(DeprecationWarning):
|
|
self.assertAlmostEqual(sc.duration, (450450 + target_durations.get("sx", 0)))
|
|
self.assertEqual(sc.unit, "dt")
|
|
self.assertEqual(sc.data[0].operation.name, "delay")
|
|
self.assertEqual(sc.data[0].operation.duration, 450450)
|
|
self.assertEqual(sc.data[0].operation.unit, "dt")
|
|
self.assertEqual(sc.data[1].operation.name, "rz")
|
|
self.assertEqual(sc.data[4].operation.name, "delay")
|
|
self.assertEqual(
|
|
sc.data[4].operation.duration,
|
|
450450 + target_durations.get("sx", 0) - target_durations.get("sx", 1),
|
|
)
|
|
self.assertEqual(sc.data[4].operation.unit, "dt")
|
|
|
|
def test_schedule_circuit_in_sec_when_no_one_tells_dt(self):
|
|
"""dt is unknown and all delays and gate times are in SI"""
|
|
qc = QuantumCircuit(2)
|
|
qc.delay(0.1, 0, unit="ms") # 450000[dt]
|
|
qc.delay(100, 0, unit="ns") # 450[dt]
|
|
qc.h(0) # duration: rz(0) + sx(195[dt]) + rz(0)
|
|
qc.h(1) # duration: rz(0)+ sx(210[dt]) + rz(0)
|
|
sc = transpile(
|
|
qc, self.backend_without_dt, scheduling_method="alap", layout_method="trivial"
|
|
)
|
|
target_durations = self.backend_with_dt.target.durations()
|
|
|
|
with self.assertWarns(DeprecationWarning):
|
|
self.assertAlmostEqual(sc.duration, (450450 + target_durations.get("sx", 0)) * self.dt)
|
|
self.assertEqual(sc.unit, "s")
|
|
self.assertEqual(sc.data[0].operation.name, "delay")
|
|
self.assertAlmostEqual(sc.data[0].operation.duration, 1.0e-4 + 1.0e-7)
|
|
self.assertEqual(sc.data[0].operation.unit, "s")
|
|
self.assertEqual(sc.data[1].operation.name, "rz")
|
|
self.assertEqual(sc.data[4].operation.name, "delay")
|
|
self.assertAlmostEqual(sc.data[4].operation.duration, 1.0e-4 + 1.0e-7)
|
|
self.assertEqual(sc.data[4].operation.unit, "s")
|
|
|
|
def test_cannot_schedule_circuit_with_mixed_SI_and_dt_when_no_one_tells_dt(self):
|
|
"""dt is unknown but delays and gate times have a mix of SI and dt"""
|
|
qc = QuantumCircuit(2)
|
|
qc.delay(100, 0, unit="ns") # 450[dt]
|
|
qc.delay(30, 0, unit="dt") # 30[dt]
|
|
qc.h(0) # duration: rz(0) + sx(195[dt]) + rz(0)
|
|
qc.h(1) # duration: rz(0)+ sx(210[dt]) + rz(0)
|
|
with self.assertRaises(QiskitError):
|
|
transpile(qc, self.backend_without_dt, scheduling_method="alap")
|
|
|
|
def test_transpile_single_delay_circuit(self):
|
|
qc = QuantumCircuit(1)
|
|
qc.delay(1234, 0)
|
|
sc = transpile(qc, backend=self.backend_with_dt, scheduling_method="alap")
|
|
with self.assertWarns(DeprecationWarning):
|
|
self.assertEqual(sc.duration, 1234)
|
|
self.assertEqual(sc.data[0].operation.name, "delay")
|
|
self.assertEqual(sc.data[0].operation.duration, 1234)
|
|
self.assertEqual(sc.data[0].operation.unit, "dt")
|
|
|
|
def test_transpile_t1_circuit(self):
|
|
qc = QuantumCircuit(1)
|
|
qc.x(0)
|
|
qc.delay(1000, 0, unit="ns") # 4500 [dt]
|
|
qc.measure_all()
|
|
scheduled = transpile(qc, backend=self.backend_with_dt, scheduling_method="alap")
|
|
# the x and measure gates get routed to qubit 1
|
|
target_durations = self.backend_with_dt.target.durations()
|
|
expected_scheduled = (
|
|
target_durations.get("x", 1) + 4500 + target_durations.get("measure", 1)
|
|
)
|
|
with self.assertWarns(DeprecationWarning):
|
|
self.assertEqual(scheduled.duration, expected_scheduled)
|
|
|
|
def test_transpile_delay_circuit_with_backend(self):
|
|
qc = QuantumCircuit(2)
|
|
qc.h(0) # 195 [dt]
|
|
qc.delay(100, 1, unit="ns") # 450 [dt]
|
|
qc.cx(0, 1) # 3169 [dt]
|
|
scheduled = transpile(
|
|
qc, backend=self.backend_with_dt, scheduling_method="alap", layout_method="trivial"
|
|
)
|
|
target_durations = self.backend_with_dt.target.durations()
|
|
with self.assertWarns(DeprecationWarning):
|
|
self.assertEqual(scheduled.duration, target_durations.get("cx", (0, 1)) + 450)
|
|
|
|
def test_transpile_circuit_with_custom_instruction(self):
|
|
"""See: https://github.com/Qiskit/qiskit-terra/issues/5154"""
|
|
bell = QuantumCircuit(2, name="bell")
|
|
bell.h(0)
|
|
bell.cx(0, 1)
|
|
bell_instr = bell.to_instruction()
|
|
qc = QuantumCircuit(2)
|
|
qc.delay(500, 1)
|
|
qc.append(bell_instr, [0, 1])
|
|
|
|
target = Target(num_qubits=2)
|
|
target.add_instruction(CXGate(), {(0, 1): InstructionProperties(0)})
|
|
target.add_instruction(
|
|
HGate(), {(0,): InstructionProperties(0), (1,): InstructionProperties(0)}
|
|
)
|
|
target.add_instruction(Delay(Parameter("t")), {(0,): None, (1,): None})
|
|
target.add_instruction(
|
|
bell_instr,
|
|
{
|
|
(0, 1): InstructionProperties(1000 * 1e-2),
|
|
(1, 0): InstructionProperties(1000 * 1e-2),
|
|
},
|
|
)
|
|
target.dt = 1e-2
|
|
scheduled = transpile(
|
|
qc,
|
|
scheduling_method="alap",
|
|
target=target,
|
|
dt=1e-2,
|
|
)
|
|
with self.assertWarns(DeprecationWarning):
|
|
self.assertEqual(scheduled.duration, 1500)
|
|
|
|
def test_transpile_delay_circuit_with_dt_but_without_scheduling_method(self):
|
|
qc = QuantumCircuit(1)
|
|
qc.delay(100, 0, unit="ns")
|
|
transpiled = transpile(qc, backend=self.backend_with_dt)
|
|
with self.assertWarns(DeprecationWarning):
|
|
self.assertEqual(transpiled.duration, None) # not scheduled
|
|
self.assertEqual(transpiled.data[0].operation.duration, 450) # unit is converted ns -> dt
|
|
|
|
def test_transpile_delay_circuit_without_scheduling_method_or_durs(self):
|
|
qc = QuantumCircuit(2)
|
|
qc.h(0)
|
|
qc.delay(500, 1)
|
|
qc.cx(0, 1)
|
|
not_scheduled = transpile(qc)
|
|
with self.assertWarns(DeprecationWarning):
|
|
self.assertEqual(not_scheduled.duration, None)
|
|
|
|
def test_raise_error_if_transpile_with_scheduling_method_but_without_durations(self):
|
|
qc = QuantumCircuit(2)
|
|
qc.h(0)
|
|
qc.delay(500, 1)
|
|
qc.cx(0, 1)
|
|
with self.assertRaises(TranspilerError):
|
|
transpile(qc, scheduling_method="alap")
|
|
|
|
def test_invalidate_schedule_circuit_if_new_instruction_is_appended(self):
|
|
qc = QuantumCircuit(2)
|
|
qc.h(0)
|
|
qc.delay(500 * self.dt, 1, "s")
|
|
qc.cx(0, 1)
|
|
scheduled = transpile(qc, backend=self.backend_with_dt, scheduling_method="alap")
|
|
# append a gate to a scheduled circuit
|
|
scheduled.h(0)
|
|
with self.assertWarns(DeprecationWarning):
|
|
self.assertEqual(scheduled.duration, None)
|
|
|
|
def test_unit_seconds_when_using_backend_durations(self):
|
|
qc = QuantumCircuit(2)
|
|
qc.h(0)
|
|
qc.delay(500 * self.dt, 1, "s")
|
|
qc.cx(0, 1)
|
|
# usual case
|
|
scheduled = transpile(
|
|
qc, backend=self.backend_with_dt, scheduling_method="alap", layout_method="trivial"
|
|
)
|
|
target_durations = self.backend_with_dt.target.durations()
|
|
with self.assertWarns(DeprecationWarning):
|
|
self.assertEqual(scheduled.duration, target_durations.get("cx", (0, 1)) + 500)
|
|
|
|
def test_per_qubit_durations(self):
|
|
"""Test target with custom instruction_durations"""
|
|
target = GenericBackendV2(
|
|
3,
|
|
coupling_map=[[0, 1], [1, 2]],
|
|
basis_gates=["cx", "h"],
|
|
seed=42,
|
|
).target
|
|
target.update_instruction_properties("cx", (0, 1), InstructionProperties(0.00001))
|
|
target.update_instruction_properties("cx", (1, 2), InstructionProperties(0.00001))
|
|
target.update_instruction_properties("h", (0,), InstructionProperties(0.000002))
|
|
target.update_instruction_properties("h", (1,), InstructionProperties(0.000002))
|
|
target.update_instruction_properties("h", (2,), InstructionProperties(0.000002))
|
|
|
|
qc = QuantumCircuit(3)
|
|
qc.h(0)
|
|
qc.delay(500, 1)
|
|
qc.cx(0, 1)
|
|
qc.h(1)
|
|
|
|
sc = transpile(qc, scheduling_method="alap", target=target)
|
|
self.assertEqual(sc.qubit_start_time(0), 500)
|
|
self.assertEqual(sc.qubit_stop_time(0), 54554)
|
|
self.assertEqual(sc.qubit_start_time(1), 9509)
|
|
self.assertEqual(sc.qubit_stop_time(1), 63563)
|
|
self.assertEqual(sc.qubit_start_time(2), 0)
|
|
self.assertEqual(sc.qubit_stop_time(2), 0)
|
|
self.assertEqual(sc.qubit_start_time(0, 1), 500)
|
|
self.assertEqual(sc.qubit_stop_time(0, 1), 63563)
|
|
|
|
qc.measure_all()
|
|
|
|
target.update_instruction_properties("measure", (0,), InstructionProperties(0.0001))
|
|
target.update_instruction_properties("measure", (1,), InstructionProperties(0.0001))
|
|
|
|
sc = transpile(qc, scheduling_method="alap", target=target)
|
|
q = sc.qubits
|
|
self.assertEqual(sc.qubit_start_time(q[0]), 500)
|
|
self.assertEqual(sc.qubit_stop_time(q[0]), 514013)
|
|
self.assertEqual(sc.qubit_start_time(q[1]), 9509)
|
|
self.assertEqual(sc.qubit_stop_time(q[1]), 514013)
|
|
self.assertEqual(sc.qubit_start_time(q[2]), 63563)
|
|
self.assertEqual(sc.qubit_stop_time(q[2]), 514013)
|
|
self.assertEqual(sc.qubit_start_time(*q), 500)
|
|
self.assertEqual(sc.qubit_stop_time(*q), 514013)
|
|
|
|
def test_convert_duration_to_dt(self):
|
|
"""Test that circuit duration unit conversion is applied only when necessary.
|
|
Tests fix for bug reported in PR #11782."""
|
|
|
|
backend = GenericBackendV2(num_qubits=3, seed=42)
|
|
|
|
circ = QuantumCircuit(2)
|
|
circ.cx(0, 1)
|
|
circ.measure_all()
|
|
|
|
circuit_dt = transpile(circ, backend, scheduling_method="asap")
|
|
with self.assertWarns(DeprecationWarning):
|
|
# reference duration and unit in dt
|
|
ref_duration = circuit_dt.duration
|
|
ref_unit = circuit_dt.unit
|
|
|
|
circuit_s = circuit_dt.copy()
|
|
circuit_s.duration *= backend.dt
|
|
circuit_s.unit = "s"
|
|
|
|
circuit_ms = circuit_s.copy()
|
|
circuit_ms.duration *= 1000
|
|
circuit_ms.unit = "ms"
|
|
|
|
for circuit in [circuit_dt, circuit_s, circuit_ms]:
|
|
with self.subTest(circuit=circuit):
|
|
converted_circ = convert_durations_to_dt(
|
|
circuit, dt_in_sec=2.22e-10, inplace=False
|
|
)
|
|
self.assertEqual(
|
|
converted_circ.duration,
|
|
ref_duration,
|
|
)
|
|
self.assertEqual(
|
|
converted_circ.unit,
|
|
ref_unit,
|
|
)
|
|
|
|
@data("s", "dt", "f", "p", "n", "u", "µ", "m", "k", "M", "G", "T", "P")
|
|
def test_estimate_duration(self, unit):
|
|
"""Test the circuit duration is computed correctly."""
|
|
backend = GenericBackendV2(num_qubits=3, seed=42)
|
|
|
|
circ = QuantumCircuit(2)
|
|
circ.cx(0, 1)
|
|
circ.measure_all()
|
|
|
|
circuit_dt = transpile(circ, backend, scheduling_method="asap")
|
|
duration = circuit_dt.estimate_duration(backend.target, unit=unit)
|
|
expected_in_sec = 1.815516e-06
|
|
expected_val = {
|
|
"s": expected_in_sec,
|
|
"dt": int(expected_in_sec / backend.target.dt),
|
|
"f": expected_in_sec / 1e-15,
|
|
"p": expected_in_sec / 1e-12,
|
|
"n": expected_in_sec / 1e-9,
|
|
"u": expected_in_sec / 1e-6,
|
|
"µ": expected_in_sec / 1e-6,
|
|
"m": expected_in_sec / 1e-3,
|
|
"k": expected_in_sec / 1e3,
|
|
"M": expected_in_sec / 1e6,
|
|
"G": expected_in_sec / 1e9,
|
|
"T": expected_in_sec / 1e12,
|
|
"P": expected_in_sec / 1e15,
|
|
}
|
|
self.assertEqual(duration, expected_val[unit])
|
|
|
|
@data("s", "dt", "f", "p", "n", "u", "µ", "m", "k", "M", "G", "T", "P")
|
|
def test_estimate_duration_with_long_delay(self, unit):
|
|
"""Test the circuit duration is computed correctly."""
|
|
backend = GenericBackendV2(num_qubits=3, seed=42)
|
|
|
|
circ = QuantumCircuit(3)
|
|
circ.cx(0, 1)
|
|
circ.measure_all()
|
|
circ.delay(1e15, 2)
|
|
|
|
circuit_dt = transpile(circ, backend, scheduling_method="asap")
|
|
duration = circuit_dt.estimate_duration(backend.target, unit=unit)
|
|
expected_in_sec = 222000.00000139928
|
|
expected_val = {
|
|
"s": expected_in_sec,
|
|
"dt": int(expected_in_sec / backend.target.dt),
|
|
"f": expected_in_sec / 1e-15,
|
|
"p": expected_in_sec / 1e-12,
|
|
"n": expected_in_sec / 1e-9,
|
|
"u": expected_in_sec / 1e-6,
|
|
"µ": expected_in_sec / 1e-6,
|
|
"m": expected_in_sec / 1e-3,
|
|
"k": expected_in_sec / 1e3,
|
|
"M": expected_in_sec / 1e6,
|
|
"G": expected_in_sec / 1e9,
|
|
"T": expected_in_sec / 1e12,
|
|
"P": expected_in_sec / 1e15,
|
|
}
|
|
self.assertEqual(duration, expected_val[unit])
|
|
|
|
@data("s", "dt", "f", "p", "n", "u", "µ", "m", "k", "M", "G", "T", "P")
|
|
def test_estimate_duration_with_dt_float(self, unit):
|
|
# This is not a valid use case, but it is still expressible currently
|
|
# since we don't disallow fractional dt values. This should not be assumed
|
|
# to be a part of an api contract. If there is a refactor and this test
|
|
# breaks remove the test it is not valid. This was only added to provide
|
|
# explicit test coverage for a rust code path.
|
|
backend = GenericBackendV2(num_qubits=3, seed=42)
|
|
|
|
circ = QuantumCircuit(3)
|
|
circ.cx(0, 1)
|
|
circ.measure_all()
|
|
circ.delay(1.23e15, 2, unit="dt")
|
|
circuit_dt = transpile(circ, backend, scheduling_method="asap")
|
|
duration = circuit_dt.estimate_duration(backend.target, unit=unit)
|
|
expected_in_sec = 273060.0000013993
|
|
expected_val = {
|
|
"s": expected_in_sec,
|
|
"dt": int(expected_in_sec / backend.target.dt),
|
|
"f": expected_in_sec / 1e-15,
|
|
"p": expected_in_sec / 1e-12,
|
|
"n": expected_in_sec / 1e-9,
|
|
"u": expected_in_sec / 1e-6,
|
|
"µ": expected_in_sec / 1e-6,
|
|
"m": expected_in_sec / 1e-3,
|
|
"k": expected_in_sec / 1e3,
|
|
"M": expected_in_sec / 1e6,
|
|
"G": expected_in_sec / 1e9,
|
|
"T": expected_in_sec / 1e12,
|
|
"P": expected_in_sec / 1e15,
|
|
}
|
|
self.assertEqual(duration, expected_val[unit])
|
|
|
|
def test_estimate_duration_invalid_unit(self):
|
|
backend = GenericBackendV2(num_qubits=3, seed=42)
|
|
|
|
circ = QuantumCircuit(2)
|
|
circ.cx(0, 1)
|
|
circ.measure_all()
|
|
|
|
circuit_dt = transpile(circ, backend, scheduling_method="asap")
|
|
with self.assertRaises(QiskitError):
|
|
circuit_dt.estimate_duration(backend.target, unit="jiffy")
|
|
|
|
def test_delay_circ(self):
|
|
backend = GenericBackendV2(num_qubits=3, seed=42)
|
|
|
|
circ = QuantumCircuit(2)
|
|
circ.delay(100, 0, unit="dt")
|
|
|
|
circuit_dt = transpile(circ, backend, scheduling_method="asap")
|
|
res = circuit_dt.estimate_duration(backend.target, unit="dt")
|
|
self.assertIsInstance(res, int)
|
|
self.assertEqual(res, 100)
|
|
|
|
def test_estimate_duration_control_flow(self):
|
|
backend = GenericBackendV2(num_qubits=3, seed=42, control_flow=True)
|
|
|
|
circ = QuantumCircuit(2)
|
|
circ.cx(0, 1)
|
|
circ.measure_all()
|
|
with circ.if_test((0, True)):
|
|
circ.x(0)
|
|
with self.assertRaises(QiskitError):
|
|
circ.estimate_duration(backend.target)
|
|
|
|
def test_estimate_duration_with_var(self):
|
|
backend = GenericBackendV2(num_qubits=3, seed=42, control_flow=True)
|
|
|
|
circ = QuantumCircuit(2)
|
|
circ.cx(0, 1)
|
|
circ.measure_all()
|
|
circ.add_var("a", False)
|
|
with self.assertRaises(QiskitError):
|
|
circ.estimate_duration(backend.target)
|
|
|
|
def test_estimate_duration_parameterized_delay(self):
|
|
backend = GenericBackendV2(num_qubits=3, seed=42, control_flow=True)
|
|
|
|
circ = QuantumCircuit(2)
|
|
circ.cx(0, 1)
|
|
circ.measure_all()
|
|
circ.delay(Parameter("t"), 0)
|
|
with self.assertRaises(QiskitError):
|
|
circ.estimate_duration(backend.target)
|
|
|
|
def test_estimate_duration_dt_delay_no_dt(self):
|
|
backend = GenericBackendV2(num_qubits=3, seed=42)
|
|
circ = QuantumCircuit(1)
|
|
circ.delay(100, 0)
|
|
backend.target.dt = None
|
|
with self.assertRaises(QiskitError):
|
|
circ.estimate_duration(backend.target)
|
|
|
|
def test_change_dt_in_transpile(self):
|
|
qc = QuantumCircuit(1, 1)
|
|
qc.x(0)
|
|
qc.measure(0, 0)
|
|
# default case
|
|
scheduled = transpile(
|
|
qc,
|
|
backend=GenericBackendV2(1, basis_gates=["x"], seed=2, dt=self.dt),
|
|
scheduling_method="asap",
|
|
)
|
|
with self.assertWarns(DeprecationWarning):
|
|
org_duration = scheduled.duration
|
|
# halve dt in sec = double duration in dt
|
|
scheduled = transpile(
|
|
qc,
|
|
backend=GenericBackendV2(1, basis_gates=["x"], seed=2, dt=self.dt),
|
|
scheduling_method="asap",
|
|
dt=self.dt / 2,
|
|
)
|
|
with self.assertWarns(DeprecationWarning):
|
|
self.assertEqual(scheduled.duration, org_duration * 2)
|
|
|
|
@data("asap", "alap")
|
|
def test_duration_on_same_instruction_instance(self, scheduling_method):
|
|
"""See: https://github.com/Qiskit/qiskit-terra/issues/5771"""
|
|
backend = GenericBackendV2(3, seed=42, dt=self.dt)
|
|
assert backend.target.durations().get(
|
|
"cx", qubits=(0, 1), unit="dt"
|
|
) != backend.target.durations().get("cx", qubits=(1, 2), unit="dt")
|
|
qc = QuantumCircuit(3)
|
|
qc.cz(0, 1)
|
|
qc.cz(1, 2)
|
|
sc = transpile(qc, backend=backend, scheduling_method=scheduling_method)
|
|
cxs = [inst.operation for inst in sc.data if inst.operation.name == "cx"]
|
|
self.assertEqual(cxs[0], cxs[1])
|
|
|
|
# Tests for circuits with parameterized delays
|
|
def test_can_transpile_circuits_after_assigning_parameters(self):
|
|
"""Check if not scheduled but duration is converted in dt"""
|
|
idle_dur = Parameter("t")
|
|
qc = QuantumCircuit(1, 1)
|
|
qc.x(0)
|
|
qc.delay(idle_dur, 0, "us")
|
|
qc.measure(0, 0)
|
|
qc = qc.assign_parameters({idle_dur: 0.1})
|
|
circ = transpile(qc, self.backend_with_dt)
|
|
with self.assertWarns(DeprecationWarning):
|
|
self.assertEqual(circ.duration, None) # not scheduled
|
|
self.assertEqual(circ.data[1].operation.duration, 450) # converted in dt
|
|
|
|
def test_can_transpile_circuits_with_assigning_parameters_inbetween(self):
|
|
idle_dur = Parameter("t")
|
|
qc = QuantumCircuit(1, 1)
|
|
qc.x(0)
|
|
qc.delay(idle_dur, 0, "us")
|
|
qc.measure(0, 0)
|
|
circ = transpile(qc, self.backend_with_dt)
|
|
circ = circ.assign_parameters({idle_dur: 0.1})
|
|
self.assertEqual(circ.data[1].name, "delay")
|
|
self.assertEqual(circ.data[1].params[0], 450)
|
|
|
|
def test_can_transpile_circuits_with_unbounded_parameters(self):
|
|
idle_dur = Parameter("t")
|
|
qc = QuantumCircuit(1, 1)
|
|
qc.x(0)
|
|
qc.delay(idle_dur, 0, "us")
|
|
qc.measure(0, 0)
|
|
# not assign parameter
|
|
circ = transpile(qc, self.backend_with_dt)
|
|
with self.assertWarns(DeprecationWarning):
|
|
self.assertEqual(circ.duration, None) # not scheduled
|
|
self.assertEqual(circ.data[1].operation.unit, "dt") # converted in dt
|
|
self.assertEqual(
|
|
circ.data[1].operation.duration, idle_dur * 1e-6 / self.dt
|
|
) # still parameterized
|
|
|
|
@data("asap", "alap")
|
|
def test_can_schedule_circuits_with_bounded_parameters(self, scheduling_method):
|
|
idle_dur = Parameter("t")
|
|
qc = QuantumCircuit(1, 1)
|
|
qc.x(0)
|
|
qc.delay(idle_dur, 0, "us")
|
|
qc.measure(0, 0)
|
|
qc = qc.assign_parameters({idle_dur: 0.1})
|
|
circ = transpile(qc, self.backend_with_dt, scheduling_method=scheduling_method)
|
|
with self.assertWarns(DeprecationWarning):
|
|
self.assertIsNotNone(circ.duration) # scheduled
|
|
|
|
@data("asap", "alap")
|
|
def test_fail_to_schedule_circuits_with_unbounded_parameters(self, scheduling_method):
|
|
idle_dur = Parameter("t")
|
|
qc = QuantumCircuit(1, 1)
|
|
qc.x(0)
|
|
qc.delay(idle_dur, 0, "us")
|
|
qc.measure(0, 0)
|
|
|
|
# unassigned parameter
|
|
with self.assertRaises(TranspilerError):
|
|
transpile(qc, self.backend_with_dt, scheduling_method=scheduling_method)
|