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

Check for gate parameter #12439

Open
wants to merge 6 commits into
base: stable/1.3
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion qiskit/transpiler/passes/calibration/pulse_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,4 @@ def get_calibration(self, node_op: CircuitInst, qubits: List) -> Union[Schedule,
Raises:
TranspilerError: When node is parameterized and calibration is raw schedule object.
"""
return self.target._get_calibration(node_op.name, tuple(qubits), *node_op.params)
return self.target.get_calibration(node_op.name, tuple(qubits), *node_op.params)
43 changes: 32 additions & 11 deletions qiskit/transpiler/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -655,37 +655,52 @@ def _get_instruction_schedule_map(self):
def has_calibration(
self,
operation_name: str,
qargs: tuple[int, ...],
qargs: tuple[int, ...] | list[int, ...],
operation_params: list[float] | float | None = None,
) -> bool:
"""Return whether the instruction (operation + qubits) defines a calibration.
"""Return whether the instruction (operation + operation_params + qubits) defines
a calibration.

Args:
operation_name: The name of the operation for the instruction.
qargs: The tuple of qubit indices for the instruction.
qargs: The qubit indices for the instruction.
operation_params: The parameters for the Instruction.

Returns:
Returns ``True`` if the calibration is supported and ``False`` if it isn't.
"""
return self._has_calibration(operation_name, qargs)
return self._has_calibration(operation_name, qargs, operation_params)

def _has_calibration(
self,
operation_name: str,
qargs: tuple[int, ...],
qargs: tuple[int, ...] | list[int, ...],
operation_params: list[float] | float | None = None,
) -> bool:
qargs = tuple(qargs)
if operation_params is not None and not isinstance(operation_params, list):
operation_params = [operation_params]

if operation_name not in self._gate_map:
return False

if qargs not in self._gate_map[operation_name]:
return False

if operation_params is not None and not (
operation_params == self._gate_name_map[operation_name].params
):
return False

return getattr(self._gate_map[operation_name][qargs], "_calibration", None) is not None

@deprecate_pulse_dependency
def get_calibration(
self,
operation_name: str,
qargs: tuple[int, ...],
qargs: tuple[int, ...] | list[int, ...],
*args: ParameterValueType,
operation_params: list[float] | float | None = None,
**kwargs: ParameterValueType,
) -> Schedule | ScheduleBlock:
"""Get calibrated pulse schedule for the instruction.
Expand All @@ -695,25 +710,31 @@ def get_calibration(

Args:
operation_name: The name of the operation for the instruction.
qargs: The tuple of qubit indices for the instruction.
qargs: The qubit indices for the instruction.
args: Parameter values to build schedule if any.
operation_params: The parameters for the Instruction.
kwargs: Parameter values with name to build schedule if any.

Returns:
Calibrated pulse schedule of corresponding instruction.
"""
return self._get_calibration(operation_name, qargs, *args, *kwargs)
return self._get_calibration(
operation_name, qargs, *args, operation_params=operation_params, **kwargs
)

def _get_calibration(
self,
operation_name: str,
qargs: tuple[int, ...],
qargs: tuple[int, ...] | list[int, ...],
*args: ParameterValueType,
operation_params: list[float] | float | None = None,
**kwargs: ParameterValueType,
) -> Schedule | ScheduleBlock:
if not self._has_calibration(operation_name, qargs):
qargs = tuple(qargs)
if not self._has_calibration(operation_name, qargs, operation_params):
raise KeyError(
f"Calibration of instruction {operation_name} for qubit {qargs} is not defined."
f"Calibration of instruction: `{operation_name}`, with params: "
f"`{operation_params}` for qubit: {qargs} is not defined."
)
cal_entry = getattr(self._gate_map[operation_name][qargs], "_calibration")
return cal_entry.get_schedule(*args, **kwargs)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
fixes:
- |
Fixed an issue with :func:`Target.has_calibration` and :func:`Target.get_calibration`
when passed with a parameterized Gate doesn't work as expected.
Refer to `qiskit/#11657 <https://github.com/Qiskit/qiskit/issues/11657>`__ and,
`qiskit/#11658 <https://github.com/Qiskit/qiskit/issues/11658>`__ for more information.

3 changes: 1 addition & 2 deletions test/python/transpiler/test_pulse_gate_pass.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,15 +159,14 @@ def test_transpile_with_custom_basis_gate_in_target(self):
qc.sx(1)
qc.measure_all()

transpiled_qc = transpile(qc, initial_layout=[0, 1], target=target)

ref_calibration = {
"sx": {
((0,), ()): self.custom_sx_q0,
((1,), ()): self.custom_sx_q1,
}
}
with self.assertWarns(DeprecationWarning):
transpiled_qc = transpile(qc, initial_layout=[0, 1], target=target)
self.assertDictEqual(transpiled_qc.calibrations, ref_calibration)

def test_transpile_with_instmap(self):
Expand Down
76 changes: 76 additions & 0 deletions test/python/transpiler/test_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -1483,6 +1483,82 @@ def test_loading_legacy_ugate_instmap(self):
self.assertSetEqual(names_before, names_after)


class TestHasCalibrationAndGetCalibration(QiskitTestCase):
"""
This test is to make sure :func:`Target.has_calibration` and
:func:`Target.get_schedule` works for parameterized gates.

Follow below mentiond issues for more information.
Refer to `#11657 <https://github.com/Qiskit/qiskit/issues/11657>`__
and, `#11658 <https://github.com/Qiskit/qiskit/issues/11658>`__
"""

def setUp(self):
super().setUp()
self.theta = Parameter("theta")
self.amp = Parameter("amp")
self.sigma = Parameter("sigma")
self.beta = Parameter("beta")

self.target = Target()

# Setting an arbitrary Pulse Schedule just for the sake of testing.
with self.assertWarns(DeprecationWarning):
self.calibration = pulse.Schedule(
pulse.Play(
pulse.Drag(
duration=1700, amp=self.amp, sigma=self.sigma, beta=self.beta, angle=0.3
),
pulse.channels.DriveChannel(0),
),
name="my_test_schedule",
)

rx_props = {
(0,): InstructionProperties(
duration=1700, error=1.2e-6, calibration=self.calibration
)
}
self.target.add_instruction(RXGate(self.theta), rx_props)

def test_has_calibration_for_oper_with_right_param(self):
with self.assertWarns(DeprecationWarning):
self.assertTrue(
self.target.has_calibration(
operation_name="rx", qargs=(0,), operation_params=self.theta
)
)

def test_has_calibration_for_oper_with_wrong_param(self):
with self.assertWarns(DeprecationWarning):
self.assertFalse(
self.target.has_calibration(operation_name="rx", qargs=(0,), operation_params=0.55)
)

def test_get_calibration_raise_oper_with_wrong_param(self):
with self.assertWarns(DeprecationWarning):
with self.assertRaisesRegex(KeyError, ".is not defined."):
self.target.get_calibration(operation_name="rx", qargs=(0,), operation_params=0.55)

def test_get_calibration_for_oper_with_param(self):
with self.assertWarns(DeprecationWarning):
self.assertEqual(
self.target.get_calibration(
operation_name="rx", qargs=(0,), operation_params=self.theta
).name,
"my_test_schedule",
)

def test_get_calibration_for_oper_with_param_pulse_with_param(self):
args = {0.23, 0.123, 0.2}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Considering theta maps to amp, sigma, and, beta. The emphasis here is just to prove that multiple *args could be successfully bind if equal number of parameter(variable) present in the actual Schedule

with self.assertWarns(DeprecationWarning):
self.assertFalse(
self.target.get_calibration(
"rx", (0,), operation_params=self.theta, *args
).is_parameterized()
)


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