Skip to content

SynthesizeRZRotations pass #15641

Merged
alexanderivrii merged 57 commits intoQiskit:mainfrom
jan-an:jan_synthesize_rz
Mar 17, 2026
Merged

SynthesizeRZRotations pass #15641
alexanderivrii merged 57 commits intoQiskit:mainfrom
jan-an:jan_synthesize_rz

Conversation

@jan-an
Copy link
Copy Markdown
Member

@jan-an jan-an commented Feb 2, 2026

  • Addresses Issue Implement RZSynthesis pass with caching of Clifford+T synthesis results #15455
    This pass performs a two-fold optimization for implementing RZ synthesis:
  • For QFT-like circuits with repeating angles, we want to avoid doing redundant syntheses of the same angle multiple times. The mechanism for this is to first collect all the angles of RZ gates from the DAG and sort them. If we implement a cache-like mechanism where if the synthesis angle already exists, we reuse it, if it does not, then we synthesize them. We further extend this by allowing approximations of angles and doing the synthesis with a lower epsilon.
  • Because RZ is $$4\pi$$-cyclic, we can come up with canonical representations of angles from $$(-\infty,\infty) \to [0,4\pi)$$.
  • Further, we can exploit properties of $$RZ$$ such as:
    $$RZ(\theta+\pi/2)= RZ(\theta) \cdot S$$,
    $$RZ(\theta+\pi)= RZ(\theta) \cdot Z$$,
    $$RZ(\theta+2\pi)= - RZ(\theta) $$
    to partition the $$[0,4\pi)$$ domain as $$\cup [n\pi/2,(n+1)pi/2) , n \in [0,7], Z^+$$. The canonical angle representation reduces to range $$[0, \pi/2)$$ for $$RZ$$ synthesis, but we have a specific global phase updates and gates to add to the synthesised $$RZ$$
  • Hence, this mapping further reduces as $$[0, 4\pi) \to \{\{r: r \in Z^+ and [0,7]\} , (0,\pi/2]\} $$, where r corresponds to a specific phase and gate update from a static look up table made to the DAG.

@jan-an jan-an requested a review from a team as a code owner February 2, 2026 13:53
@qiskit-bot qiskit-bot added the Community PR PRs from contributors that are not 'members' of the Qiskit repo label Feb 2, 2026
@qiskit-bot
Copy link
Copy Markdown
Collaborator

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 following people are relevant to this code:

  • @Qiskit/terra-core

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Feb 2, 2026

CLA assistant check
All committers have signed the CLA.

@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@jan-an jan-an marked this pull request as draft February 2, 2026 13:54
@ShellyGarion ShellyGarion added fault tolerance related to fault tolerance compilation and removed Community PR PRs from contributors that are not 'members' of the Qiskit repo labels Feb 2, 2026
@ShellyGarion ShellyGarion added this to the 2.4.0 milestone Feb 2, 2026
@github-project-automation github-project-automation Bot moved this to Ready in Qiskit 2.4 Feb 2, 2026
@ShellyGarion ShellyGarion added the Changelog: Added Add an "Added" entry in the GitHub Release changelog. label Feb 2, 2026
@coveralls
Copy link
Copy Markdown

coveralls commented Feb 2, 2026

Pull Request Test Coverage Report for Build 22852088886

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 87 of 91 (95.6%) changed or added relevant lines in 7 files are covered.
  • 21 unchanged lines in 3 files lost coverage.
  • Overall coverage decreased (-0.007%) to 87.711%

Changes Missing Coverage Covered Lines Changed/Added Lines %
qiskit/transpiler/passes/synthesis/synthesize_rz_rotations.py 11 12 91.67%
crates/transpiler/src/passes/synthesize_rz_rotations.rs 67 70 95.71%
Files with Coverage Reduction New Missed Lines %
crates/circuit/src/parameter/parameter_expression.rs 1 86.99%
crates/qasm2/src/lex.rs 2 92.29%
crates/qasm2/src/parse.rs 18 95.73%
Totals Coverage Status
Change from base Build 22817757681: -0.007%
Covered Lines: 101165
Relevant Lines: 115339

💛 - Coveralls

Copy link
Copy Markdown
Member

@alexanderivrii alexanderivrii left a comment

Choose a reason for hiding this comment

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

Thanks Janani, this is an amazing start. After several preliminary experiments, the code seems to work and return correct results.

I have left a few mostly minor comments. The suggestions are for illustration purposes only, so do not commit them directly through the web interface 😄.

Comment thread crates/transpiler/src/passes/synthesize_rz_rotations.rs Outdated
Comment thread crates/transpiler/src/passes/synthesize_rz_rotations.rs Outdated
Comment thread crates/transpiler/src/passes/synthesize_rz_rotations.rs Outdated
Comment thread crates/transpiler/src/passes/synthesize_rz_rotations.rs Outdated
Comment thread crates/transpiler/src/passes/synthesize_rz_rotations.rs Outdated
Comment thread qiskit/transpiler/passes/synthesis/synthesize_rz_rotations.py Outdated
"""Replace RZ gates with Clifford+T decompositions using gridsynth.

This pass replaces all single-qubit RZ rotation gates with sequences
of Clifford+T gates (H, S, T gates) using the gridsynth algorithm.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also here: the (H, S, T) are specific to gridsynth.

Comment thread qiskit/transpiler/passes/synthesis/synthesize_rz_rotations.py Outdated
Comment thread releasenotes/notes/short-description-string-d22b1705f7cbbb23.yaml Outdated

# [TO DO] could combine tests for angle and epsilon

@data(1e-9, 1e-10, 1e-11)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe try lower values, like 1e-6? (If I remember correctly, the argument passed to the inner rust function is currently ignored).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Noted Sasha. As you mentioned, I also just noticed that it ignores the argument and used the constant EPSILON, which may also be why this test passes now. I have changed how it accepts epsilon, will need to check if this test will still pass for variable epsilon.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What is the difference between this and the following test?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

here you check that the operators are equal. in the following test it's up to an error.

Comment thread crates/transpiler/src/passes/synthesize_rz_rotations.rs Outdated
@alexanderivrii
Copy link
Copy Markdown
Member

alexanderivrii commented Feb 3, 2026

An additional random idea while looking at the code (but let's implement it only in a follow-up, if at all). When considering the set of angles that need to be synthesized, you are already exploiting the fact that $RZ(\theta)$ is $4\pi$-periodic, hence it's enough to consider $\theta$ modulo $4\pi$, reducing the set of distinct angles for the expensive gridsynth computation. In fact, you also already exploit that $RZ(\theta + 2\pi)$ = $-RZ(\theta)$, further reducing the set of distinct angles, which corresponds to adjusting the global phase of the computed circuit. We can take the idea a bit further, also exploiting that $RZ(\theta + \pi)$ = $RZ(\theta)\cdot Z$, and $RZ(\theta + \pi/2) = RZ(\theta) \cdot S$. That is, we only need to consider different values of $\theta$ in $[0, \pi/2)$, adjusting the Clifford gate and the phase of the circuit accordingly.

@jan-an jan-an marked this pull request as ready for review February 9, 2026 09:30
Copy link
Copy Markdown
Member

@ShellyGarion ShellyGarion left a comment

Choose a reason for hiding this comment

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

This PR looks almost ready now. I only have some minor comments and questions.
Thanks @jan-an and @alexanderivrii !

Comment thread crates/transpiler/src/passes/synthesize_rz_rotations.rs Outdated
Comment thread crates/transpiler/src/passes/synthesize_rz_rotations.rs Outdated
Comment thread crates/transpiler/src/passes/synthesize_rz_rotations.rs Outdated
Comment thread qiskit/transpiler/passes/synthesis/synthesize_rz_rotations.py Outdated

The synthesis accuracy can be controlled by either specifying an
``approximation_degree``, or alternatively by explicitly setting
both ``synthesis_error`` and ``cache_error``.
Copy link
Copy Markdown
Member

@ShellyGarion ShellyGarion Mar 17, 2026

Choose a reason for hiding this comment

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

I would suggest to give in the API docstring an example for using synthesis_error and cache_error

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I could do that, but I think we are no longer adding large examples to the release notes. I also feel that most users (except for Julien) will not be interested in controlling the two parts of the error budget separately, so I would prefer not to stress out this use-case.


# [TO DO] could combine tests for angle and epsilon

@data(1e-9, 1e-10, 1e-11)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

here you check that the operators are equal. in the following test it's up to an error.

@ddt
class TestSynthesizeRzRotations(QiskitTestCase):
"""Test Synthesize Rz rotations method"""

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

perhaps it's worth to add tests for the parameters synthesis_error and cache_error ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Added in 0fb9f5c.

@jan-an
Copy link
Copy Markdown
Member Author

jan-an commented Mar 17, 2026

Hi @alexanderivrii , thanks for the clippy fix, indeed it was complaining that there needed to be an indent on the etc in docstring, which is fixed now.

All changes look good to me, I just had one suggestion regarding the recent addition of the error budget; I think it would be good to have a test for calling the pass with the synthesis and cache errors. What do you think?

@alexanderivrii
Copy link
Copy Markdown
Member

here you check that the operators are equal. in the following test it's up to an error.

I have removed the test that checks Operator equality for approximation degree below 1e-8 since I thought it's redundant (and depends a bit too much on the inner knowledge of error handling in Operator equality).

In any case, we have plenty of tests that Operators are considered equal for the default value of approximation degree, and we have tests for the operator spectral norm when varying approximation degree.

ShellyGarion
ShellyGarion previously approved these changes Mar 17, 2026
Copy link
Copy Markdown
Member

@ShellyGarion ShellyGarion 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 @jan-an and @alexanderivrii !
@Cryoris - do you have any further comments?

@Cryoris
Copy link
Copy Markdown
Collaborator

Cryoris commented Mar 17, 2026

Yes, a few, I'm pushing a commit in a bit to address them.

@Cryoris Cryoris added the on hold Can not fix yet label Mar 17, 2026
- our derivations use operator norm, not frobenius norm
- use exact angle computation with arcsin
- reduce some of the tests and add a real-life QFT one
- use approximation_degree None instead of arithmetic in the signature
- use PyResult over anyhow since that was the only result type
Copy link
Copy Markdown
Member

@ShellyGarion ShellyGarion left a comment

Choose a reason for hiding this comment

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

Just a few questions:

Comment thread crates/transpiler/src/passes/synthesize_rz_rotations.rs
let total_error = if let Some(approximation_degree) = approximation_degree {
MINIMUM_EPSILON.max(1. - approximation_degree)
} else {
1e-10
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

should it be hard-coded here or set some constant?
why here it's 1e-10 while MINIMUM_EPSILON is 1e-12?

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.

I moved it into a hardcoded constant. 1e-10 is a heuristic choice and tradeoff between accuracy and runtime. There's some inherent approximation we're using here and "no approximation" doesn't really make sense anymore here..

Comment thread crates/transpiler/src/passes/synthesize_rz_rotations.rs
Comment thread test/python/transpiler/test_synthesize_rz_rotations.py Outdated
Comment thread test/python/transpiler/test_synthesize_rz_rotations.py Outdated
Copy link
Copy Markdown
Member

@alexanderivrii alexanderivrii left a comment

Choose a reason for hiding this comment

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

Thanks Julien. A few minor comments on my part.

Comment thread crates/transpiler/src/passes/synthesize_rz_rotations.rs
Comment thread crates/transpiler/src/passes/synthesize_rz_rotations.rs Outdated
Comment thread test/python/transpiler/test_synthesize_rz_rotations.py Outdated
Comment thread test/python/transpiler/test_synthesize_rz_rotations.py
Copy link
Copy Markdown
Member

@alexanderivrii alexanderivrii left a comment

Choose a reason for hiding this comment

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

Great work, everyone: Janani - for implementing this pass, and Julien, Shelly & myself - for helping to push it across the finishing line.

@alexanderivrii alexanderivrii added this pull request to the merge queue Mar 17, 2026
Merged via the queue into Qiskit:main with commit c688134 Mar 17, 2026
24 of 25 checks passed
@github-project-automation github-project-automation Bot moved this from Ready to Done in Qiskit 2.4 Mar 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Changelog: Added Add an "Added" entry in the GitHub Release changelog. fault tolerance related to fault tolerance compilation mod: transpiler Issues and PRs related to Transpiler on hold Can not fix yet

Projects

Status: Done
Status: Done

Development

Successfully merging this pull request may close these issues.

Implement RZSynthesis pass with caching of Clifford+T synthesis results

8 participants