Skip to content

Commit

Permalink
Update Target to use CalibrationEntry to create inst map (#9597) (#9610)
Browse files Browse the repository at this point in the history
* Bug fix for InstructionScheduleMap.has_custom_gate.

InstructionProperties._calibration now only takes CalibrationEntry and use this instance to create an inst map.

* Fix repr not to call .calibration

* Review comment

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>

* Remove getattr

---------

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
(cherry picked from commit ecb6f9a)

Co-authored-by: Naoki Kanazawa <nkanazawa1989@gmail.com>
  • Loading branch information
mergify[bot] and nkanazawa1989 committed Feb 17, 2023
1 parent b39807f commit 97f7bbf
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 17 deletions.
9 changes: 8 additions & 1 deletion qiskit/pulse/calibration_entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,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 @@ -185,7 +188,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 @@ -71,20 +71,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 @@ -530,8 +535,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 @@ -1008,7 +1017,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())

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

0 comments on commit 97f7bbf

Please sign in to comment.