Skip to content

Fix: BasisTranslator processing of nested ControlFlowOp#15875

Merged
Cryoris merged 8 commits intoQiskit:mainfrom
raynelfss:fix_basis_again
Mar 26, 2026
Merged

Fix: BasisTranslator processing of nested ControlFlowOp#15875
Cryoris merged 8 commits intoQiskit:mainfrom
raynelfss:fix_basis_again

Conversation

@raynelfss
Copy link
Copy Markdown
Contributor

@raynelfss raynelfss commented Mar 25, 2026

Summary

Inspired by #15797
The following PR fixes a long standing bug in BasisTranslator by allowing all calculated transformations to be applied within a ControlFlowOp.

Details and comments

Fixes #15734
Fixes #14025
Fixes #13162

  • Remove the bool output is_updated from apply_translation helper method in BasisTranslator. The removal of this argument ensures that when a translation is found it is always applied.
    • The is_modified boolean output, while intended as a way of optimizing the operation of the BasisTranslator did not achieve much as a new DAGCircuit would be created each time.
    • During the ControlFlowOp handling step of this method we relied on is_updated to determine whether to use the original DAGCircuit or not, which in the end would erroneously skip certain transformations in the Operation's blocks.
    • By removing this tracking value, which was already ignored by the main BasisTranslator process. We end up with a more correct processing of the instructions at hand.
  • Update test test_inner_wire_map_control_op from the TestCircuitControlFlowOps as better outcome is now found for said circuit.

- Remove the bool output `is_updated` from `apply_translation` helper method in `BasisTranslator`.
    The removal of this argument ensures that when a translation is found it is always applied. The `is_modified` boolean output, while intended as a way of optimizing the operation of the `BasisTranslator` did not achieve much as a new `DAGCircuit would be created each time.
    During the `ControlFlowOp` handling step of this method we relied on `is_updated` to determine whether to use the original `DAGCircuit` or not, which in the end would erroneously skip certain transformations in the `Operation`'s blocks.
    By removing this tracking value, which was already ignored by the main `BasisTranslator` process. We end up with a more correct processing of the instructions at hand.
- Update test `test_inner_wire_map_control_op` from the `TestCircuitControlFlowOps` as better outcome is now found for said circuit.
- Inspired on [comment](Qiskit#13162 (comment)) from @jakelishman.
@raynelfss raynelfss added this to the 2.4.0 milestone Mar 25, 2026
@raynelfss raynelfss requested a review from a team as a code owner March 25, 2026 14:37
@raynelfss raynelfss added Changelog: Fixed Add a "Fixed" entry in the GitHub Release changelog. Rust This PR or issue is related to Rust code in the repository mod: transpiler Issues and PRs related to Transpiler labels Mar 25, 2026
@qiskit-bot
Copy link
Copy Markdown
Collaborator

One or more of the following people are relevant to this code:

@mtreinish mtreinish added the stable backport potential Make Mergify open a backport PR to the most recent stable branch on merge. label Mar 25, 2026
@rajnisht7
Copy link
Copy Markdown
Contributor

rajnisht7 commented Mar 25, 2026

thank you for sharing the PR, it clarified how control flow should be handles, great learning experience. Thanks alot

just wanted to ask is it covering all nested blocks like all nested blocks are always translated to target basis in single pass

Copy link
Copy Markdown
Collaborator

@Cryoris Cryoris left a comment

Choose a reason for hiding this comment

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

Some minor comments below otherwise LGTM, thanks Ray! I also double checked that this fixes the drawer error.

cqc = pm.run(qc)
self.assertEqual(Operator(qc), Operator.from_circuit(cqc))

def test_basis_nested_control_flow_op(self):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could we add a test that uses the AerSimulator from the bug reports, too, to ensure we have the full regression test?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I refrained from adding a test that does this basically because I'd need to change the dependency list for test to include qiskit_aer. I can add a test using a GenericBackendV2 and uses a PresetPassManager to run the transpiler pipeline. It should produce a very similar outcome.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We have several tests using Aer, that's what the optionals package is for. E.g. something like

    @unittest.skipUnless(HAS_AER, "Aer required for clifford simulation")
    def test_thing(self):
        from qiskit_aer import AerSimulator
         
        # test your thing

It's fine to use it 🙂

"""Test that the gates inside ControlFlowOps land on correct qubits when transpiled"""
expected = "\n".join(
[
" ",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

To document this: Ray and I checked this test and it's still the same equivalence since the inside of the control flow has a global phase shift of pi to account to the sign flip here.

control flow operations would have its blocks skip the calculated
transformations.

See `#13162 <https://github.com/Qiskit/qiskit/issues/13162>`__. No newline at end of file
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If we list the fixed issues, we probably should list them all 🙂

@jakelishman jakelishman modified the milestones: 2.4.0, 2.4.0rc2 Mar 26, 2026
- Add new test using a backend and the full transpiler pipeline.
- Add all issues to release note.
- Small language fix.
@raynelfss
Copy link
Copy Markdown
Contributor Author

just wanted to ask is it covering all nested blocks like all nested blocks are always translated to target basis in single pass

@rajnisht7 Yes, it always calculates transformations for each block recursively. The problem is that it would (in most cases) just skip the transformation entirely. Thankfully, with this fix, it behaves the way it is supposed to.

Co-authored-by: Julien Gacon <gaconju@gmail.com>
Copy link
Copy Markdown
Collaborator

@Cryoris Cryoris left a comment

Choose a reason for hiding this comment

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

LGTM thanks for the fix, Ray!

@Cryoris Cryoris enabled auto-merge March 26, 2026 14:18
@Cryoris Cryoris added this pull request to the merge queue Mar 26, 2026
Merged via the queue into Qiskit:main with commit 9c8f4fc Mar 26, 2026
25 checks passed
@github-project-automation github-project-automation bot moved this from Ready to Done in Qiskit 2.4 Mar 26, 2026
mergify bot pushed a commit that referenced this pull request Mar 26, 2026
* Fix: `BasisTranslator` processing of `ControlFlowOp`
- Remove the bool output `is_updated` from `apply_translation` helper method in `BasisTranslator`.
    The removal of this argument ensures that when a translation is found it is always applied. The `is_modified` boolean output, while intended as a way of optimizing the operation of the `BasisTranslator` did not achieve much as a new `DAGCircuit would be created each time.
    During the `ControlFlowOp` handling step of this method we relied on `is_updated` to determine whether to use the original `DAGCircuit` or not, which in the end would erroneously skip certain transformations in the `Operation`'s blocks.
    By removing this tracking value, which was already ignored by the main `BasisTranslator` process. We end up with a more correct processing of the instructions at hand.
- Update test `test_inner_wire_map_control_op` from the `TestCircuitControlFlowOps` as better outcome is now found for said circuit.

* Test: Add test
- Inspired on [comment](#13162 (comment)) from @jakelishman.

* Docs: Add release note

* Address review comments:
- Add new test using a backend and the full transpiler pipeline.
- Add all issues to release note.
- Small language fix.

* Lint: formatting

* Fix: Make new test use aer

* Update releasenotes/notes/fix_basis_control_flow-033ecba233d5f9ed.yaml

Co-authored-by: Julien Gacon <gaconju@gmail.com>

---------

Co-authored-by: Julien Gacon <gaconju@gmail.com>
(cherry picked from commit 9c8f4fc)
github-merge-queue bot pushed a commit that referenced this pull request Mar 26, 2026
…15884)

* Fix: `BasisTranslator` processing of `ControlFlowOp`
- Remove the bool output `is_updated` from `apply_translation` helper method in `BasisTranslator`.
    The removal of this argument ensures that when a translation is found it is always applied. The `is_modified` boolean output, while intended as a way of optimizing the operation of the `BasisTranslator` did not achieve much as a new `DAGCircuit would be created each time.
    During the `ControlFlowOp` handling step of this method we relied on `is_updated` to determine whether to use the original `DAGCircuit` or not, which in the end would erroneously skip certain transformations in the `Operation`'s blocks.
    By removing this tracking value, which was already ignored by the main `BasisTranslator` process. We end up with a more correct processing of the instructions at hand.
- Update test `test_inner_wire_map_control_op` from the `TestCircuitControlFlowOps` as better outcome is now found for said circuit.

* Test: Add test
- Inspired on [comment](#13162 (comment)) from @jakelishman.

* Docs: Add release note

* Address review comments:
- Add new test using a backend and the full transpiler pipeline.
- Add all issues to release note.
- Small language fix.

* Lint: formatting

* Fix: Make new test use aer

* Update releasenotes/notes/fix_basis_control_flow-033ecba233d5f9ed.yaml



---------


(cherry picked from commit 9c8f4fc)

Co-authored-by: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com>
Co-authored-by: Julien Gacon <gaconju@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Changelog: Fixed Add a "Fixed" entry in the GitHub Release changelog. mod: transpiler Issues and PRs related to Transpiler Rust This PR or issue is related to Rust code in the repository stable backport potential Make Mergify open a backport PR to the most recent stable branch on merge.

Projects

Status: Done

6 participants