-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Add constructor to build target from legacy transpiler model #9255
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
Add constructor to build target from legacy transpiler model #9255
Conversation
Thank you for opening a new pull request. Before your PR can be merged it will first need to pass continuous integration tests and be reviewed. Sometimes the review process can be slow, so please be patient. While you're waiting, please feel free to review other open PRs. While only a subset of people are authorized to approve pull requests for merging, everyone is encouraged to review open pull requests. Doing reviews helps reduce the burden on the core team and helps make the project's code better for everyone. One or more of the the following people are requested to review this:
|
This commit adds a new constructor method from_configuration() to the Target class. This can be used to construct a new Target object from the legacy input types: basis_gates, coupling_map, etc. This can then be used to provide a conversion layer in transpile() in support of migrating to using the target everywhere internally.
af81562
to
4414598
Compare
Pull Request Test Coverage Report for Build 4677945711
💛 - Coveralls |
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.
A few comments after looking through test documentation and tests.
releasenotes/notes/new-constructor-target-from-configuration-91f7eb569d95b330.yaml
Outdated
Show resolved
Hide resolved
releasenotes/notes/new-constructor-target-from-configuration-91f7eb569d95b330.yaml
Outdated
Show resolved
Hide resolved
@@ -1008,6 +1015,202 @@ def __str__(self): | |||
output.write("".join(prop_str_pieces)) | |||
return output.getvalue() | |||
|
|||
@classmethod | |||
def from_configuration( |
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.
It looks like there's some overlap here with convert_to_target
https://github.com/Qiskit/qiskit-terra/blob/ea984fda21b8cef1ab9b60c0bdbc1a1340263e6c/qiskit/providers/backend_compat.py#L33 . Would it make sense to consolidate the two (from_configuration
looks to be more general), or do they serve different purposes?
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 constructor is more general and takes slightly different information. convert_to_target
is explicitly to go from a BackendV1
's model objects (BackendConfiguration
, BackendProperties
, and PulseDefaults
) while this is to go from the loose objects which are basically the input to transpile()
and build a Target
from those. There is definitely overlap as at the end of the day both the transpile()
arguments and BackendV1
are expressing the same data (with roughly the same limitations) just in different formats. I think a good follow up to this might be to update convert_to_target
to leverage this constructor instead of doing it manually. But I feel that's outside the scope of this PR (there is also potential overhead with doing that because we basically have to build a bunch of intermediate objects from the BackendConfiguration
to match the input here).
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 method seems to me like encouraging end-user to build Target
from these legacy data structure. Alternatively you can introduce private factory function in the transpile, so that end-user still cannot build Target
with these transpiler args, which will be deprecated in future.
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.
I think it's fine to still be user facing. For better or worse it'd be difficult to remove most of the legacy data structures considering how widely used they are. I think leaving this is as a public facing interface is fine. If we do decide to do something like deprecate InstructionDurations
or something we can do that case by case basis here too.
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.
Thanks Matthew. Seems like the logic you introduced is different from the conventional V1 converter and new factory method is useful to migrate from transpiler args to Target. I added several comments, mainly about handling of instmap.
@@ -1008,6 +1015,202 @@ def __str__(self): | |||
output.write("".join(prop_str_pieces)) | |||
return output.getvalue() | |||
|
|||
@classmethod | |||
def from_configuration( |
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 method seems to me like encouraging end-user to build Target
from these legacy data structure. Alternatively you can introduce private factory function in the transpile, so that end-user still cannot build Target
with these transpiler args, which will be deprecated in future.
global_ideal_variable_width_gates = [] # pylint: disable=invalid-name | ||
if num_qubits is None: | ||
num_qubits = len(coupling_map.graph) | ||
for gate in basis_gates: |
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 ignores gates defined in the inst map. This must be something like union(set(basis_gates), set(inst_map.instructions))
. You could also add gates in backend properties, but likely this is an edge case (basis_gates must be superset).
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 looks good to go now. I just need clarification of the policy for custom calibration handling. When we deal with inst map and custom pulse for publication, the most popular situation would be the following. A user may calibrate custom pulse for particular qubit or qubit pair, which is a part of the coupling map (because it's tough to manage chip-wide calibration without automation tool, though we have Qiskit Experiments calibrations now). What we used to do is
backend.configuration().basis_gates += ["my_gate"]
backend.defaults().instruction_schedule_map.add(
"my_gate", (0, 1), my_gate_schedule
)
qc = QuantumCircuit(3)
qc.append(Gate("my_gate", 2, []), [0, 1])
qc.cx(1, 2)
qc.measure_all()
qc_t = transpile(
qc,
backend, # coupling_map is [[0,1], [1, 0], [1, 2], [2, 1], ...]
initial_layout=[0, 1, 2],
)
with this my_gate
only appears in the (0, 1) node, and the calibration is attached to the node. If we use another initial layout (or we do not provide), transpiled circuit may contain undefined my_gate
and fails in the validation on provider. The problem here is coupling_map of [[0,1], [1, 0], [1, 2], [2, 1], ...]
is agnostic to gate, and this limits the capability of experiment.
Proposed implementation in this PR preserves this behavior, although Target
can support partial connectivity for a particular gate. In principle, you can write smarter logic to discover such partial connectivity based on defined calibrations. I'm fine if current implementation is intentional, especially from the backward compatibility point of view.
Yeah, that's a good question. For this I was opting mostly for backwards compatibility with how backend.target.add_instruction(
Gate(my_gate, 2, []),
{(0, 1): InstructionProperties(duration=my_gate_schedule.duration(), calibration=my_gate_schedule)}
)
qc = QuantumCircuit(3)
qc.append(Gate("my_gate", 2, []), [0, 1])
qc.cx(1, 2)
qc.measure_all()
qc_t = transpile(
qc,
backend,
initial_layout=[0, 1, 2],
) That should work fine. The only place this interface would be an issue for calibration would be if you did something like: inst_map = backend.instruction_schedule_map
inst_map.add("my_gate", (0, 1), my_gate_schedule)
target = Target.from_configuration(
backend.operation_names,
backend.num_qubits,
backend.coupling_map,
inst_map=inst_map
) (or in terra 0.25.0 called But, my assumption was if you wanted to work at the individual argument level like that you'd just do this instead: inst_map = backend.instruction_schedule_map
inst_map.add("my_gate", (0, 1), my_gate_schedule)
target = Target.from_configuration(
backend.operation_names,
backend.num_qubits,
backend.coupling_map,
)
target.update_from_instruction_schedule_map(inst_map, {"my_gate": Gate(my_gate", 2, [])}) But I want to make sure this works ok for how you were expecting calibrations to be used. |
Co-authored-by: Naoki Kanazawa <[email protected]>
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.
Thanks for clarification. That makes sense. Anyways current transpiler arg model does't express partly calibrated gate very well, and a user who wants to deal with such calibration should move to Target. I think it's not mandatory to support full capability of Target (to express partly calibrated gate) with legacy transpiler input. We need to provide a good documentation to encourage users to use Target
.
@kdk do you want to do another review? |
I'm just going to enqueue this for merging in the interest of time for the 0.24.0 release. kdk's on vacation this week but if there is any followup discussion on this we can revisit it in a follow up PR. |
…9255) * Add constructor to build target from legacy transpiler model This commit adds a new constructor method from_configuration() to the Target class. This can be used to construct a new Target object from the legacy input types: basis_gates, coupling_map, etc. This can then be used to provide a conversion layer in transpile() in support of migrating to using the target everywhere internally. * Add more test coverage * Apply suggestions from code review Co-authored-by: Kevin Krsulich <[email protected]> * Make num_qubits optional * Explain arg precedence in docstring * Use assertRaisesRegex * Fix lint * Fix rebase name mismatch * Fix lint * Update error message with multiqubit gates * Give instruction durations higher priority * Improve handling of instruction schedule map * Add comment about how connectivity is defined * Update test exception assertion regex for new error message * Update qiskit/transpiler/target.py Co-authored-by: Naoki Kanazawa <[email protected]> --------- Co-authored-by: Kevin Krsulich <[email protected]> Co-authored-by: Naoki Kanazawa <[email protected]>
…9255) * Add constructor to build target from legacy transpiler model This commit adds a new constructor method from_configuration() to the Target class. This can be used to construct a new Target object from the legacy input types: basis_gates, coupling_map, etc. This can then be used to provide a conversion layer in transpile() in support of migrating to using the target everywhere internally. * Add more test coverage * Apply suggestions from code review Co-authored-by: Kevin Krsulich <[email protected]> * Make num_qubits optional * Explain arg precedence in docstring * Use assertRaisesRegex * Fix lint * Fix rebase name mismatch * Fix lint * Update error message with multiqubit gates * Give instruction durations higher priority * Improve handling of instruction schedule map * Add comment about how connectivity is defined * Update test exception assertion regex for new error message * Update qiskit/transpiler/target.py Co-authored-by: Naoki Kanazawa <[email protected]> --------- Co-authored-by: Kevin Krsulich <[email protected]> Co-authored-by: Naoki Kanazawa <[email protected]>
Summary
This commit adds a new constructor method from_configuration() to the Target class. This can be used to construct a new Target object from the legacy input types: basis_gates, coupling_map, etc. This can then be used to provide a conversion layer in
transpile()
in support of migrating to using the target everywhere internally.Details and comments