Skip to content

Oxidize UnitarySynthesis path using basis_gates #13704

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

Merged
merged 6 commits into from
Feb 17, 2025

Conversation

ElePT
Copy link
Contributor

@ElePT ElePT commented Jan 21, 2025

Summary

This PR is a compilation of small improvements to the UnitarySynthesis pass, including:

  1. Performance -> Addition of fast path (Rust) for the use case where target is None, all input combinations can now be run using the fast path.

  2. Fixes -> Fixed an oversight where the pulse_optimize argument wasn't added to the fast path and therefore ignored (discovered after expanding the unit tests).

These set the stage for future refactorings of the UnitarySynthesis class to take better advantage of the plugin model (the default plugin is currently ignored in the fast path).

Note that this PR touches on code that is currently actively worked on in:

And builds on top of #13706 (will need to be rebased after it's merged).

Details and comments

@ElePT ElePT added this to the 2.0.0 milestone Jan 21, 2025
@ElePT ElePT added the on hold Can not fix yet label Jan 21, 2025
@ElePT ElePT changed the title Oxidize UnitarySynthesis path using basis_gates, remove use of BackendV1 in tests Oxidize UnitarySynthesis path using basis_gates Jan 21, 2025
@coveralls
Copy link

coveralls commented Jan 22, 2025

Pull Request Test Coverage Report for Build 13369594575

Details

  • 553 of 800 (69.13%) changed or added relevant lines in 3 files are covered.
  • 31 unchanged lines in 6 files lost coverage.
  • Overall coverage decreased (-0.07%) to 88.187%

Changes Missing Coverage Covered Lines Changed/Added Lines %
crates/accelerate/src/unitary_synthesis.rs 430 444 96.85%
qiskit/transpiler/passes/synthesis/default_unitary_synth_plugin.py 118 351 33.62%
Files with Coverage Reduction New Missed Lines %
crates/accelerate/src/two_qubit_decompose.rs 1 91.82%
crates/circuit/src/dag_node.rs 3 76.59%
crates/accelerate/src/unitary_synthesis.rs 4 94.39%
crates/qasm2/src/lex.rs 5 92.48%
qiskit/transpiler/passes/synthesis/unitary_synthesis.py 6 74.05%
crates/qasm2/src/parse.rs 12 97.15%
Totals Coverage Status
Change from base Build 13336190323: -0.07%
Covered Lines: 78742
Relevant Lines: 89290

💛 - Coveralls

Copy link
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.

I have taken a quick look at the PR, and it seems to be in a very good shape. I have left a few minor questions, and will take a deeper look once the PRs it's based on are merged. And of course you are missing release notes.

)?;
let out_qargs = dag.get_qargs(packed_instr.qubits);
apply_synth_dag(py, &mut out_dag, out_qargs, &synth_dag)?;
if basis_gates.is_empty() && matches!(target, None) {
Copy link
Member

Choose a reason for hiding this comment

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

Minor question: is there a benefit of writing matched!(target, None) over target.is_none()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think so, this was me just being rusty in Rust when I wrote it :) I actually prefer target.is_none()

@1ucian0 1ucian0 removed the on hold Can not fix yet label Feb 10, 2025
@1ucian0
Copy link
Member

1ucian0 commented Feb 10, 2025

Removing on hold as #13706 merged.

@ElePT ElePT force-pushed the revamp-unitary-synth branch from 68740c5 to 20fb3ae Compare February 10, 2025 22:24
@ElePT ElePT force-pushed the revamp-unitary-synth branch from 20fb3ae to f9b93cc Compare February 10, 2025 22:25
@ElePT ElePT marked this pull request as ready for review February 10, 2025 22:26
@ElePT ElePT requested a review from a team as a code owner February 10, 2025 22:26
@qiskit-bot
Copy link
Collaborator

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

  • @Qiskit/terra-core

@ElePT ElePT force-pushed the revamp-unitary-synth branch from 0200fd2 to 35c24a2 Compare February 11, 2025 12:20
…te logic from UnitarySynthesis pass. This plugin is currently ONLY USED as a fallback method if a custom unitary synthesis plugin is provided and fails. We don't have any unit tests currently covering this scenario. For more context, the _run_main_loop method in UnitarySynthesis has been replaced by a Rust function when the plugin is set to default.
@ElePT ElePT force-pushed the revamp-unitary-synth branch from 35c24a2 to 7a1914c Compare February 11, 2025 12:31
@ElePT
Copy link
Contributor Author

ElePT commented Feb 11, 2025

Sorry @alexanderivrii if I have complicated the review with the last couple of commits. It might look like a lot of changes but I mostly moved around existing code (the one you already reviewed). As a summary:

  • I restructured the Rust file to try to make it more understandable and reduce code repetition, I also added comments.
  • On the Python side, I moved the DefaultUnitarySynthesis plugin code to an independent file. This plugin duplicates the Rust logic, which unfortunately was developed in parallel to the plugin infrastructure. The Rust code combines the pass run_main_loop logic with the plugin run and currently cannot simply replace the plugin run. Ideally, we could yet again refactor the code to match the plugin infrastructure, but I don't think this will be possible before 2.0 (in terms of time).

For reference, the Python DefaultUnitarySynthesis class is still set as a fallback for the case where a custom plugin fails, which is the reason why I haven't removed it either. At least separating it from UnitarySynthesis might explain a bit better the situation.

Copy link
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.

@ElePT - thanks for refactoring the unitary_synthesis.rs code, as well as separating unitary_synthesis.py and the DefaultUnitarySynthesis plugin.
So it seems that the plugin is not being tested, and also behaves differently than the UnitarySynthesis pass?

if euler_basis_set.basis_supported(EulerBasis::U3)
&& euler_basis_set.basis_supported(EulerBasis::U321)
{
euler_basis_set.remove(EulerBasis::U3);
Copy link
Member

Choose a reason for hiding this comment

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

what happens if a user provides both 'U' and 'U3'?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They are both included in the target euler_basis_set and given to the 1q decomposer to deal with.

Copy link
Member

Choose a reason for hiding this comment

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

if euler_basis_set.basis_supported(EulerBasis::U3) == true and if euler_basis_set.basis_supported(EulerBasis::U) == true
should we remove one of them?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This part of the code literally follows the logic from the 1q euler decomposer... I am not sure if there was an initial motivation not to remove one of the two (U/U3) or if it was an oversight. I can't think of a reason from the top of my head.

@ElePT
Copy link
Contributor Author

ElePT commented Feb 12, 2025

Thanks @ShellyGarion, I guess that the strictly correct thing here would be to duplicate the synthesis unit tests for the plugin. I can do that.

alexanderivrii
alexanderivrii previously approved these changes Feb 12, 2025
Copy link
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 @ElePT, I have carefully reviewed the code and it looks perfect to me, either because it truly is or because I lack the expertise to spot any issues, probably both are true :).

One question (and I have a feeling that I may have already asked it at some point, but then forgot the answer) is why we need some code duplication between rust and python sides, i.e. why can't DefaultSynthesisPlugin just call the relevant functionality implemented in rust? I might be missing something even more basis here, doesn't UnitarySynthesis pass by default call the DefaultSynthesisPlugin?

And special thanks adding code comments, this helped me a lot to understand what each different function does.

I am approving it, but I think there were also some review comments from @ShellyGarion.

@ElePT
Copy link
Contributor Author

ElePT commented Feb 17, 2025

Thanks @alexanderivrii! To answer your question, the code shouldn't be duplicated. The reason why it is is that at the time the unitary synthesis code was originally oxidized, we didn't have any reference of how to interact with the plugin infrastructure, and because we only had 1 unitary synthesis plugin (the infrastructure wasn't really taken advantage of), we went for the path of replicating the whole flow in rust. In hindsight it would be way more modular to have only the plugin run method in rust, but implementing this now requires a refactoring that I didn't have the time to prioritize yet. Once you finish the HLS oxidization it might also be a good idea to try to make unitary synthesis is pluggable in the HLS workflow. This is something I briefly discussed with @Cryoris. The TL:DR is: not enough information at the time, not enough time to fix it when the information was clear.

@ElePT
Copy link
Contributor Author

ElePT commented Feb 17, 2025

Adding the plugin unit tests will probably take a bit more effort than originally planned. If you are fine with merging the PR as-is, I can address the test situation in a follow-up (it's anyways a non user-facing change).

Copy link
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!

@ElePT ElePT added this pull request to the merge queue Feb 17, 2025
Merged via the queue into Qiskit:main with commit 26906d3 Feb 17, 2025
17 checks passed
@ElePT ElePT added the Changelog: None Do not include in changelog label Mar 31, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: None Do not include in changelog
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants