-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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 pulse gate transpiler pass to use target. #9587
Changes from 9 commits
c5078e5
c7f10c0
bfd33d4
a256743
dd4ad0c
5207b47
939ad61
d10fcf9
066d9d8
bdb790a
a722e5e
20977ed
f203f59
2c4b687
f217d01
d0b2109
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,11 +34,12 @@ class CalibrationEntry(metaclass=ABCMeta): | |
"""A metaclass of a calibration entry.""" | ||
|
||
@abstractmethod | ||
def define(self, definition: Any): | ||
def define(self, definition: Any, user_provided: bool): | ||
"""Attach definition to the calibration entry. | ||
|
||
Args: | ||
definition: Definition of this entry. | ||
user_provided: If this entry is defined by user. | ||
""" | ||
pass | ||
|
||
|
@@ -64,6 +65,12 @@ def get_schedule(self, *args, **kwargs) -> Union[Schedule, ScheduleBlock]: | |
""" | ||
pass | ||
|
||
@property | ||
@abstractmethod | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you do this without breaking compatibility? Wouldn't adding a new required abstract method break any downstream usage of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 33b76b3 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure this has been fixed (or if it was the fix got undone at some point), because this is still a new There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Previously I implemented an abstractmethod to return schedule duration. Note that schedule duration is computed here for added schedule from the inst map, but this requires Qobj parsing that is the biggest performance bottleneck for non-pulse users. So I have implemented a custom (complicated) method that computes the duration without parsing Qobj. New abstract method just returns whether it is provided by backend or by end-user. If your schedule is provided from backend, you should know duration of it and we don't need to update duration in the first place. This avoids unnecessary Qobj parsing for duration. If it is user-provided one, usually they don't define schedule with JSON format, and no parsing overhead. |
||
def user_provided(self) -> bool: | ||
"""Return if this entry is user defined.""" | ||
pass | ||
|
||
|
||
class ScheduleDef(CalibrationEntry): | ||
"""In-memory Qiskit Pulse representation. | ||
|
@@ -90,6 +97,11 @@ def __init__(self, arguments: Optional[Sequence[str]] = None): | |
|
||
self._definition = None | ||
self._signature = None | ||
self._user_provided = None | ||
|
||
@property | ||
def user_provided(self) -> bool: | ||
return self._user_provided | ||
|
||
def _parse_argument(self): | ||
"""Generate signature from program and user provided argument names.""" | ||
|
@@ -120,35 +132,48 @@ def _parse_argument(self): | |
) | ||
self._signature = signature | ||
|
||
def define(self, definition: Union[Schedule, ScheduleBlock]): | ||
def define( | ||
self, | ||
definition: Union[Schedule, ScheduleBlock], | ||
user_provided: bool = True, | ||
): | ||
self._definition = definition | ||
# add metadata | ||
if "publisher" not in definition.metadata: | ||
definition.metadata["publisher"] = CalibrationPublisher.QISKIT | ||
self._parse_argument() | ||
self._user_provided = user_provided | ||
|
||
def get_signature(self) -> inspect.Signature: | ||
return self._signature | ||
|
||
def get_schedule(self, *args, **kwargs) -> Union[Schedule, ScheduleBlock]: | ||
if not args and not kwargs: | ||
return self._definition | ||
try: | ||
to_bind = self.get_signature().bind_partial(*args, **kwargs) | ||
except TypeError as ex: | ||
raise PulseError("Assigned parameter doesn't match with schedule parameters.") from ex | ||
value_dict = {} | ||
for param in self._definition.parameters: | ||
# Schedule allows partial bind. This results in parameterized Schedule. | ||
out = self._definition | ||
else: | ||
try: | ||
value_dict[param] = to_bind.arguments[param.name] | ||
except KeyError: | ||
pass | ||
return self._definition.assign_parameters(value_dict, inplace=False) | ||
to_bind = self.get_signature().bind_partial(*args, **kwargs) | ||
except TypeError as ex: | ||
raise PulseError( | ||
"Assigned parameter doesn't match with schedule parameters." | ||
) from ex | ||
value_dict = {} | ||
for param in self._definition.parameters: | ||
# Schedule allows partial bind. This results in parameterized Schedule. | ||
try: | ||
value_dict[param] = to_bind.arguments[param.name] | ||
except KeyError: | ||
pass | ||
out = self._definition.assign_parameters(value_dict, inplace=False) | ||
if "publisher" not in out.metadata: | ||
if self.user_provided: | ||
out.metadata["publisher"] = CalibrationPublisher.QISKIT | ||
else: | ||
out.metadata["publisher"] = CalibrationPublisher.BACKEND_PROVIDER | ||
return out | ||
|
||
def __eq__(self, other): | ||
# This delegates equality check to Schedule or ScheduleBlock. | ||
return self._definition == other._definition | ||
if hasattr(other, "_definition"): | ||
return self._definition == other._definition | ||
return False | ||
|
||
def __str__(self): | ||
out = f"Schedule {self._definition.name}" | ||
|
@@ -171,10 +196,20 @@ def __init__(self): | |
"""Define an empty entry.""" | ||
self._definition = None | ||
self._signature = None | ||
self._user_provided = None | ||
|
||
@property | ||
def user_provided(self) -> bool: | ||
return self._user_provided | ||
|
||
def define(self, definition: Callable): | ||
def define( | ||
self, | ||
definition: Callable, | ||
user_provided: bool = True, | ||
): | ||
self._definition = definition | ||
self._signature = inspect.signature(definition) | ||
self._user_provided = user_provided | ||
|
||
def get_signature(self) -> inspect.Signature: | ||
return self._signature | ||
|
@@ -186,17 +221,20 @@ def get_schedule(self, *args, **kwargs) -> Union[Schedule, ScheduleBlock]: | |
to_bind.apply_defaults() | ||
except TypeError as ex: | ||
raise PulseError("Assigned parameter doesn't match with function signature.") from ex | ||
|
||
schedule = self._definition(**to_bind.arguments) | ||
# add metadata | ||
if "publisher" not in schedule.metadata: | ||
schedule.metadata["publisher"] = CalibrationPublisher.QISKIT | ||
return schedule | ||
out = self._definition(**to_bind.arguments) | ||
if "publisher" not in out.metadata: | ||
if self.user_provided: | ||
out.metadata["publisher"] = CalibrationPublisher.QISKIT | ||
else: | ||
out.metadata["publisher"] = CalibrationPublisher.BACKEND_PROVIDER | ||
return out | ||
|
||
def __eq__(self, other): | ||
# We cannot evaluate function equality without parsing python AST. | ||
# This simply compares wether they are the same object. | ||
return self._definition is other._definition | ||
# This simply compares weather they are the same object. | ||
if hasattr(other, "_definition"): | ||
return self._definition == other._definition | ||
return False | ||
|
||
def __str__(self): | ||
params_str = ", ".join(self.get_signature().parameters.keys()) | ||
|
@@ -237,14 +275,17 @@ def _build_schedule(self): | |
for qobj_inst in self._source: | ||
for qiskit_inst in self._converter._get_sequences(qobj_inst): | ||
schedule.insert(qobj_inst.t0, qiskit_inst, inplace=True) | ||
schedule.metadata["publisher"] = CalibrationPublisher.BACKEND_PROVIDER | ||
|
||
self._definition = schedule | ||
self._parse_argument() | ||
|
||
def define(self, definition: List[PulseQobjInstruction]): | ||
def define( | ||
self, | ||
definition: List[PulseQobjInstruction], | ||
user_provided: bool = False, | ||
): | ||
# This doesn't generate signature immediately, because of lazy schedule build. | ||
self._source = definition | ||
self._user_provided = user_provided | ||
|
||
def get_signature(self) -> inspect.Signature: | ||
if self._definition is None: | ||
|
@@ -261,9 +302,11 @@ def __eq__(self, other): | |
# If both objects are Qobj just check Qobj equality. | ||
return self._source == other._source | ||
if isinstance(other, ScheduleDef) and self._definition is None: | ||
# To compare with other scheudle def, this also generates schedule object from qobj. | ||
# To compare with other schedule def, this also generates schedule object from qobj. | ||
self._build_schedule() | ||
return self._definition == other._definition | ||
if hasattr(other, "_definition"): | ||
return self._definition == other._definition | ||
return False | ||
|
||
def __str__(self): | ||
if self._definition is None: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be a breaking API change for any downstream usage of
CalibrationEntry
right? Like if you have an existingCalibrationEntry
subclass in 0.23.x then when upgrading to 0.24.0 that class'sdefine
method signature will no long match the abstract interface definition.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, all subclasses provide default value for
user_provided
to conform to previous default behavior. Currently in 0.23.x, this flag is implicitly determined by type information and onlyPulseQobjDef
is recognized as a backend provided entry. However one may want to provide backend calibration asScheduleDef
, to bypass cumbersome JSON data generation. This could be a separate PR though.