Skip to content

Conversation

raynelfss
Copy link
Contributor

@raynelfss raynelfss commented Mar 11, 2025

Summary

The following commits replace the op_object attribute of NormalOperation with a OnceLock to allow the python version to be either built dynamically or cached from its original source.

Details and comments

As we plan to use PackedOperation to add instructions to a Target in Rust. We should not require having a python instance to exist within. The following commits fix that by ensuring that a python instruction is not required, and if so it can be built based on a PackedOperation's internal data.

This should not have any visible effects on the current implementations, but can be proven to work via the added unit-tests.

- As we plan to use `PackedOperation` to add instructions to a `Target` in Rust. We should not require having a python instance exists within. The following commits fix that by ensuring that a python instruction is not required, and if so it can be built based on a `PackedOperation`'s internal data.
- This should not have any visible effects on the current implementations, but can be proven to work via the added unit-tests.
@raynelfss raynelfss requested a review from a team as a code owner March 11, 2025 18:47
@qiskit-bot
Copy link
Collaborator

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

  • @Qiskit/terra-core

@raynelfss raynelfss changed the title Add: Use OnceLock to cache a NormalOperation in Target Use OnceLock to cache a NormalOperation in Target Mar 11, 2025
@raynelfss raynelfss added Changelog: None Do not include in changelog Rust This PR or issue is related to Rust code in the repository mod: transpiler Issues and PRs related to Transpiler labels Mar 11, 2025
@coveralls
Copy link

coveralls commented Mar 11, 2025

Pull Request Test Coverage Report for Build 14625475896

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

  • 43 of 64 (67.19%) changed or added relevant lines in 1 file are covered.
  • 555 unchanged lines in 16 files lost coverage.
  • Overall coverage decreased (-0.3%) to 87.9%

Changes Missing Coverage Covered Lines Changed/Added Lines %
crates/accelerate/src/target_transpiler/mod.rs 43 64 67.19%
Files with Coverage Reduction New Missed Lines %
crates/qasm2/src/expr.rs 1 94.23%
qiskit/qpy/binary_io/value.py 4 82.99%
crates/qasm2/src/lex.rs 5 91.48%
crates/circuit/src/lib.rs 7 86.67%
qiskit/circuit/classical/expr/visitors.py 7 93.22%
qiskit/transpiler/passes/layout/vf2_utils.py 9 93.2%
qiskit/circuit/_classical_resource_map.py 10 76.32%
qiskit/transpiler/passes/layout/vf2_layout.py 10 91.94%
crates/accelerate/src/vf2_layout.rs 11 83.43%
crates/qasm2/src/parse.rs 12 97.15%
Totals Coverage Status
Change from base Build 14389768512: -0.3%
Covered Lines: 74256
Relevant Lines: 84478

💛 - Coveralls

Copy link
Contributor

@kevinhartman kevinhartman left a 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 me.

My only issue really is with the IMPORT_LOCK mutex in the unit tests. See my comment there. I think we can likely avoid needing it, but it'd require a tweak to our test harness.


#[test]
fn add_standard_gate_without_python() -> PyResult<()> {
let _lock = IMPORT_LOCK
Copy link
Contributor

Choose a reason for hiding this comment

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

There was some discussion a while back about having qiskit get imported up front to avoid this sort of thing:

#13776 (comment)

I didn't bother submitting a PR to do that since there's also the issue of PyObjects not being extractable to Rust pyclass structs due to ABI compat issues (i.e. in the Rust tests, our pyclass structs come from the rlib, but the structs that back PyObjects coming from the actual Qiskit code originate from the cdylib linked into the Python process).

My assumption was that folks probably would just not really use PyO3 Rust unit testing for anything complex. But, it looks like you have a use case here. Should I make a PR to avoid the deadlock issue?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If it does solve the issue, I would love to have a PR that does that. It would be pretty helpful.

Copy link
Contributor

Choose a reason for hiding this comment

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

Done in #14200.

I tried it with this PR removing the mutex and it seemed to work 🙂/

Copy link
Member

Choose a reason for hiding this comment

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

I commented on #14200, but just to centralise: if we do this, I'd agree that #14200 is a better strategy. That said, I'm not convinced that we ought to use Python in this test. I'm not entirely sure what the use of Python is buying us - if we test the Rust objects from Rust, is there a really serious risk that "object created from Rust" might not produce the correct Python object later in a way that would not be caught by regular tests of the creation of Python-free StandardGate objects?

Even if this test can in theory work at this moment in time, I'm not crazy about doing more testing where _accelerate is imported from apython in Rust, because we know that that's not ABI compatible and feels like an accident waiting to happen.

Copy link
Contributor

Choose a reason for hiding this comment

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

IMO, #14200 is a nice-to-have if we're going to keep PyO3 unit testing around for the time being. The main use-case I'd see for it is as a stop-gap measure for cases where our internal Rust code relies on importing parts of our data model that aren't yet native to Rust.

But in this particular case, I'd agree that we're not getting much by testing that the Rust-backed operations from the target are equivalent to gates constructed via Python. I'd rather expect any testing here to be poking at the Target Rust instance to make sure the instructions were added successfully (no Python involved), if you're to have any testing here at all.

- Remove `From` implementations in favor of `from_packed_operation` in both `TargetOperation`, and `NormalOperation`.
Ok(self.op_object.bind(py).clone())
Ok(self
.op_object
.get_or_init(|| self.create_py_op(py, None).unwrap())
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we store op_object as a PyResult<PyObject> instead so we can propagate the error if create_py_op fails?

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 think it might not be necessary, I could just extract the operation before setting it instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 3547921

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After an internal conversation, we have gone with storing a PyResult. Fixed in cd8e63b.

@raynelfss raynelfss requested a review from kevinhartman April 23, 2025 17:05
Copy link
Contributor

@kevinhartman kevinhartman 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 changes!

@raynelfss raynelfss added this pull request to the merge queue Apr 23, 2025
Merged via the queue into Qiskit:main with commit b35a709 Apr 23, 2025
24 checks passed
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 mod: transpiler Issues and PRs related to Transpiler priority: high Rust This PR or issue is related to Rust code in the repository
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants