-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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 transpiler pipeline to only use target #12850
base: main
Are you sure you want to change the base?
Conversation
b587438
to
9078df2
Compare
As of 9078df2, there are 347 failing tests that must be classified into one of these 2 use cases depending on the different input combinations:
More updates will follow.
|
Status after df78b1c:
|
…is gates in the preset pass managers.
df78b1c
to
8eea4cf
Compare
Status after 8eea4cf
|
One or more of the following people are relevant to this code:
|
Pull Request Test Coverage Report for Build 12784141925Details
💛 - Coveralls |
15167a3
to
cbd0973
Compare
qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py
Outdated
Show resolved
Hide resolved
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 seems to me that this PR is probably premature for 1.3. For one, consider the following difference compared to main
:
qc = QuantumCircuit(2,2)
qc.cx(0,1)
pm = generate_preset_pass_manager(1, coupling_map=CouplingMap([[1,0]]))
tqc = pm.run(qc)
print(tqc)
The main
branch gives this:
┌───┐┌───┐┌───┐
q_0 -> 0 ┤ H ├┤ X ├┤ H ├
├───┤└─┬─┘├───┤
q_1 -> 1 ┤ H ├──■──┤ H ├
└───┘ └───┘
c: 2/═══════════════
But on transpile-only-target-pipeline
branch we get this:
q_0 -> 0 ──■──
┌─┴─┐
q_1 -> 1 ┤ X ├
└───┘
c: 2/═════
(GateDirection does not do what it's supposed to do here. See my line comments for more details).
The reason this is not caught by our unit testing is that we don't really have a way to directly compare transpilation results between versions AFAIK. Transpilation tests which do use pre-defined circuits as groundtruth (e.g. like in test_gate_direction.py
) could have catch this, but I guess none really use generate_preset_pass_manager
at all or
maybe use it but without just a coupling-map. I do see the FakeTarget
instantiation branch in target.py
reported as covered in the coverage report but probably not with those tests that compare output circuits and can flag a difference?
Anyway, once we have the above concern addressed, we should have explicit tests for the loose constraints and FakeTarget
execution path to make sure all passes are included as before and we get the expected output circuits.
qiskit/transpiler/target.py
Outdated
@@ -1303,3 +1324,29 @@ def target_to_backend_properties(target: Target): | |||
return BackendProperties.from_dict(properties_dict) | |||
else: | |||
return None | |||
|
|||
|
|||
class FakeTarget(Target): |
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.
Maybe _PartialTarget
is a better name? Also I would add to its docstring that this is an internal-only class and should not be used directly by the user. Though it's not in the API doc but still it wouldn't hurt to highlight.
with a `coupling_map` argument that allows to store connectivity constraints | ||
without 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.
When checking a FakeTarget
instance in a boolean context, e.g. like:
target = Target.from_configuration(basis_gates=None, coupling_map=CouplingMap([[0,1]]))
if target:
print("I'm alive")
The evaluation fails.
This would be problematic for all the passes that can work with partial target and do a check like self.target
. Overriding __len__
can mitigate this.
Also in the level_x..
pass managers (e.g. level_1_pass_managers
) functions, there are checks like this:
if (coupling_map and not coupling_map.is_symmetric) or (
target is not None and target.get_non_global_operation_names(strict_direction=True)
):
target is not None
will also always evaluate to False
with FakeTarget
.
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.
target is not None
does evaluate to True
because it's an explicit check for None
, so at least we can confirm that the pass manager generation logic was working as expected (else it would have been a bigger issue). However, it's true that the output of __len__
is 0
. I am wondering if it might make more sense not making FakeTarget
a subclass of Target
because it inherits all of these method that make little sense for the object we have.
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.
You're right, if target is None
is checking the reference itself. But if target:
is the boolean context where __len__
is used. There are a couple places in the codebase in which this pattern in used so it's still a potential issue.
Regarding whether or not to inherit from Target
- Ideally the best way going forward might be to support partial initialization of Target
itself. So, for example, in the case of calling Target.from_configuration
with only a coupling map, internally Target
will end up containing dummy instructions that will make constructing a coupling map and other functionality like instruction_supported
work as expected. This will probably require changing the Target
internals. Or maybe FakeTarget
can use Target.add_instruction
using an internal only instruction which might require changes also on the Rust side.
@raynelfss
Still I think we'll want to have a subclass like FakeTarget
or some other special factory anyway as a way to ensure partial target initialization is only supported for internal code callers.
def build_coupling_map(self, two_q_gate=None, filter_idle_qubits=False): | ||
return self._coupling_map | ||
|
||
def instruction_supported(self, *args, **kwargs): |
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.
Target.instruction_supported
is implemented in Rust and used in gates_in_bases.rs
and gate_direction.rs
. If these get a FakeTarget
from Python how would this overriden method is used?
Additionally, and even more important, returning True
here by default is not a good solution for the GateDirection
pass. We need to think about a way to solve this.
Hi @eliarbel! Thanks for taking such a detailed look into the PR. I think you are raising some very good points. I definitely agree with avoiding the user exposure to the class, and I will work on an alternative not to generate the As for the implementation in Rust and use in
|
After an offline discussion, we have concluded that:
The plan is to fix the individual issues raised in the review and expand the test base with some significant tests that could give us more assurance/ a better understanding of the differences in output this PR could cause. The deprecation warnings and a few fixes are elegible for 1.3 so they have been singled out in #13394. |
…ile-only-target-pipeline * Fix conflicts
b8e10e6
to
dafa66b
Compare
c34605d
to
bfd25d0
Compare
bfd25d0
to
ec6a46b
Compare
Summary
This PR is a step towards the target-only transpiler pipeline. It manages to almost exclusively use the target input to communicate transpilation constraints. To do this, I designed a (very simple) "FakeTarget" (name tbd) designed for internal use that augments the Target data model supporting the use-case of coupling map provided with no basis gates, and modified the preset pass manager creation mechanisms to contemplate this new use-case.
The creation of
FakeTarget
is not documented as part the public API, as it's designed for internal use.Details and comments
This PR addresses the following points from #9256:
Update preset pass manager construction to only pass a Target to passes. Stage plugins initialization will need to fill out all the fields in the PassManagerConfig from the Target for backwards compatibility. We can stop doing this as we deprecate non-target constraint fields in PassManagerConfig.
Deprecate anything we previously could represent that is lost in conversion to Target (or alternatively augment Target's data model to support these edge cases).
Note: There are 2 use cases where the Target is still skipped in the pass manager construction. Both are deprecated and will be removed in 2.0:
Whenever a custom basis gate is provided in
basis_gates
-> this use-case is now deprecated, the recommended alternative in these cases is to construct aTarget
user-side, which can be done through theTarget.from_configuration
method (there's acustom_name_mapping
argument that allows to use custom gate definitions). Until the deprecation period is over, we still allow custom basis gates by skipping the target.When
instruction_durations
are provided without a target or backend. The argument is deprecated, but we must support the use-case until the deprecation period is over.