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

Update Target to use CalibrationEntry to create inst map #9597

Merged
merged 5 commits into from
Feb 17, 2023
Merged
Show file tree
Hide file tree
Changes from 4 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
9 changes: 8 additions & 1 deletion qiskit/pulse/calibration_entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ def _parse_argument(self):

def define(self, definition: Union[Schedule, ScheduleBlock]):
self._definition = definition
# add metadata
if "publisher" not in definition.metadata:
definition.metadata["publisher"] = CalibrationPublisher.QISKIT
self._parse_argument()

def get_signature(self) -> inspect.Signature:
Expand Down Expand Up @@ -184,7 +187,11 @@ def get_schedule(self, *args, **kwargs) -> Union[Schedule, ScheduleBlock]:
except TypeError as ex:
raise PulseError("Assigned parameter doesn't match with function signature.") from ex

return self._definition(**to_bind.arguments)
schedule = self._definition(**to_bind.arguments)
# add metadata
if "publisher" not in schedule.metadata:
schedule.metadata["publisher"] = CalibrationPublisher.QISKIT
return schedule

def __eq__(self, other):
# We cannot evaluate function equality without parsing python AST.
Expand Down
8 changes: 4 additions & 4 deletions qiskit/pulse/instruction_schedule_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

# pylint: disable=unused-import

"""
A convenient way to track reusable subschedules by name and qubit.

Expand All @@ -34,11 +36,12 @@
from qiskit.circuit.instruction import Instruction
from qiskit.circuit.parameterexpression import ParameterExpression
from qiskit.pulse.calibration_entries import (
CalibrationPublisher,
CalibrationEntry,
ScheduleDef,
CallableDef,
PulseQobjDef,
# for backward compatibility
CalibrationPublisher,
)
from qiskit.pulse.exceptions import PulseError
from qiskit.pulse.schedule import Schedule, ScheduleBlock
Expand Down Expand Up @@ -248,9 +251,6 @@ def add(
# generate signature
if isinstance(schedule, (Schedule, ScheduleBlock)):
entry = ScheduleDef(arguments)
# add metadata
if "publisher" not in schedule.metadata:
schedule.metadata["publisher"] = CalibrationPublisher.QISKIT
elif callable(schedule):
if arguments:
warnings.warn(
Expand Down
27 changes: 18 additions & 9 deletions qiskit/transpiler/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

from qiskit.circuit.parameter import Parameter
from qiskit.pulse.instruction_schedule_map import InstructionScheduleMap
from qiskit.pulse.calibration_entries import CalibrationEntry
from qiskit.pulse.calibration_entries import CalibrationEntry, ScheduleDef
from qiskit.pulse.schedule import Schedule, ScheduleBlock
from qiskit.transpiler.coupling import CouplingMap
from qiskit.transpiler.exceptions import TranspilerError
Expand Down Expand Up @@ -72,20 +72,25 @@ def __init__(
set of qubits.
calibration: The pulse representation of the instruction.
"""
self._calibration = None

self.duration = duration
self.error = error
self._calibration = calibration
self.calibration = calibration

@property
def calibration(self):
"""The pulse representation of the instruction."""
if isinstance(self._calibration, CalibrationEntry):
return self._calibration.get_schedule()
return self._calibration
return self._calibration.get_schedule()

@calibration.setter
def calibration(self, calibration: Union[Schedule, ScheduleBlock, CalibrationEntry]):
self._calibration = calibration
if isinstance(calibration, (Schedule, ScheduleBlock)):
new_entry = ScheduleDef()
new_entry.define(calibration)
else:
new_entry = calibration
self._calibration = new_entry

def __repr__(self):
return (
Expand Down Expand Up @@ -532,8 +537,12 @@ def instruction_schedule_map(self):
out_inst_schedule_map = InstructionScheduleMap()
for instruction, qargs in self._gate_map.items():
for qarg, properties in qargs.items():
if properties is not None and properties.calibration is not None:
out_inst_schedule_map.add(instruction, qarg, properties.calibration)
# Directly getting CalibrationEntry not to invoke .get_schedule().
# This keeps PulseQobjDef un-parsed.
cal_entry = getattr(properties, "_calibration", None)
if cal_entry is not None:
# Use fast-path to add entries to the inst map.
out_inst_schedule_map._add(instruction, qarg, cal_entry)
self._instruction_schedule_map = out_inst_schedule_map
return out_inst_schedule_map

Expand Down Expand Up @@ -1026,7 +1035,7 @@ def __str__(self):
error = getattr(props, "error", None)
if error is not None:
prop_str_pieces.append(f"\t\t\tError Rate: {error}\n")
schedule = getattr(props, "calibration", None)
schedule = getattr(props, "_calibration", None)
if schedule is not None:
prop_str_pieces.append("\t\t\tWith pulse schedule calibration\n")
extra_props = getattr(props, "properties", None)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
fixes:
- |
Fixed an issue with the :meth:`.InstructionScheduleMap.has_custom_gate` method,
where it would always return ``True`` when the :class:`~.InstructionScheduleMap`
object was created by :class:`.Target`.
Fixed `#9595 <https://github.com/Qiskit/qiskit-terra/issues/9595>`__
8 changes: 6 additions & 2 deletions test/python/pulse/test_instruction_schedule_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
ShiftPhase,
Constant,
)
from qiskit.pulse.instruction_schedule_map import CalibrationPublisher
from qiskit.pulse.calibration_entries import CalibrationPublisher
from qiskit.pulse.channels import DriveChannel
from qiskit.qobj import PulseQobjInstruction
from qiskit.qobj.converters import QobjToInstructionConverter
Expand Down Expand Up @@ -602,8 +602,12 @@ def test_has_custom_gate(self):

self.assertFalse(instmap.has_custom_gate())

# add something
# add custom schedule
some_sched = Schedule()
instmap.add("u3", (0,), some_sched)

self.assertTrue(instmap.has_custom_gate())

# delete custom schedule
instmap.remove("u3", (0,))
self.assertFalse(instmap.has_custom_gate())
33 changes: 32 additions & 1 deletion test/python/transpiler/test_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,15 @@
from qiskit.circuit.parameter import Parameter
from qiskit import pulse
from qiskit.pulse.instruction_schedule_map import InstructionScheduleMap
from qiskit.pulse.calibration_entries import CalibrationPublisher
from qiskit.transpiler.coupling import CouplingMap
from qiskit.transpiler.instruction_durations import InstructionDurations
from qiskit.transpiler.timing_constraints import TimingConstraints
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.transpiler import Target
from qiskit.transpiler import InstructionProperties
from qiskit.test import QiskitTestCase
from qiskit.providers.fake_provider import FakeBackendV2, FakeMumbaiFractionalCX
from qiskit.providers.fake_provider import FakeBackendV2, FakeMumbaiFractionalCX, FakeGeneva


class TestTarget(QiskitTestCase):
Expand Down Expand Up @@ -1228,6 +1229,36 @@ def test_timing_constraints(self):
f"{getattr(generated_constraints, i)}!={getattr(expected_constraints, i)}",
)

def test_default_instmap_has_no_custom_gate(self):
backend = FakeGeneva()
target = backend.target

# This copies .calibraiton of InstructionProperties of each instruction
# This must not convert PulseQobj to Schedule during this.
# See qiskit-terra/#9595
inst_map = target.instruction_schedule_map()
self.assertFalse(inst_map.has_custom_gate())

# Get pulse schedule. This generates Schedule provided by backend.
sched = inst_map.get("sx", (0,))
self.assertEqual(sched.metadata["publisher"], CalibrationPublisher.BACKEND_PROVIDER)
self.assertFalse(inst_map.has_custom_gate())

# Update target with custom instruction. This is user provided schedule.
new_prop = InstructionProperties(
duration=self.custom_sx_q0.duration,
error=None,
calibration=self.custom_sx_q0,
)
target.update_instruction_properties(instruction="sx", qargs=(0,), properties=new_prop)
inst_map = target.instruction_schedule_map()
self.assertTrue(inst_map.has_custom_gate())
nkanazawa1989 marked this conversation as resolved.
Show resolved Hide resolved

empty = InstructionProperties()
target.update_instruction_properties(instruction="sx", qargs=(0,), properties=empty)
inst_map = target.instruction_schedule_map()
self.assertFalse(inst_map.has_custom_gate())


class TestGlobalVariableWidthOperations(QiskitTestCase):
def setUp(self):
Expand Down