Skip to content

Oxidize VariableMapper and uses. #14361

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 18 commits into from
Jun 16, 2025
Merged

Conversation

kevinhartman
Copy link
Contributor

@kevinhartman kevinhartman commented May 13, 2025

Summary

Ports the private Python-space helper class VariableMapper to Rust.

Also introduces Rust-side iteration methods for classical expressions (Expr).

Details and comments

Note that we still will need the Python token in DAGCircuit::{py_substitute_node_with_dag, compose} until we (I) finish porting control flow.

To-do

  • Port DAGCircuit::compose to use new VariableMapper.
  • Make the existing Python-space variable mapper use the new one internally (we still need the Python interface for QuantumCircuit. Edit: I'm going to defer this to whoever ports QuantumCircuit.compose to Rust, rather than introducing some kind of PyVariableMapper wrapper that would go away during that process anyhow.
  • Rust-based unit testing for classical expression iterators and mutable walker.
  • Make sure Stretch is being mapped properly by DAGCircuit::py_substitute_node_with_dag Edit: only target and condition get mapped, and they'd never contain Stretch so this is all set.

@kevinhartman kevinhartman marked this pull request as ready for review May 14, 2025 21:15
@kevinhartman kevinhartman requested a review from a team as a code owner May 14, 2025 21:15
@qiskit-bot
Copy link
Collaborator

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

  • @Qiskit/terra-core

@mtreinish mtreinish added this to the 2.1.0 milestone May 14, 2025
@mtreinish mtreinish added performance Changelog: None Do not include in changelog Rust This PR or issue is related to Rust code in the repository labels May 14, 2025
@coveralls
Copy link

coveralls commented May 14, 2025

Pull Request Test Coverage Report for Build 15690052436

Details

  • 512 of 597 (85.76%) changed or added relevant lines in 4 files are covered.
  • 12 unchanged lines in 4 files lost coverage.
  • Overall coverage decreased (-0.006%) to 88.003%

Changes Missing Coverage Covered Lines Changed/Added Lines %
crates/circuit/src/bit.rs 4 9 44.44%
crates/circuit/src/classical/expr/expr.rs 290 298 97.32%
crates/circuit/src/dag_circuit.rs 114 122 93.44%
crates/circuit/src/variable_mapper.rs 104 168 61.9%
Files with Coverage Reduction New Missed Lines %
crates/circuit/src/symbol_expr.rs 1 73.9%
qiskit/circuit/_classical_resource_map.py 1 75.0%
crates/circuit/src/dag_circuit.rs 4 86.24%
crates/qasm2/src/lex.rs 6 92.23%
Totals Coverage Status
Change from base Build 15687720415: -0.006%
Covered Lines: 83520
Relevant Lines: 94906

💛 - Coveralls

Copy link
Member

@mtreinish mtreinish left a comment

Choose a reason for hiding this comment

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

Overall this looks really good thanks for doing that. I have a couple inline comments but nothing major.

The other question I have is there a reason to keep the python space mapper class around anymore? I'm guessing the answer is QuantumCircuit.compose() as that's the only usage I can find of it. That's probably blocked on #14299 so we might want to open a tracking issue for that.

Comment on lines +5349 to +5350
fn may_have_additional_wires(&self, op: OperationRef) -> bool {
let OperationRef::Instruction(inst) = op else {
Copy link
Member

Choose a reason for hiding this comment

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

Not that this is a big deal, but was there a reason for this change. It's probably better like this, but I'm just wondering if there was a motivation on this?

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 actually recall why I'd done this originally. FWIW, I believe this changes again in the control flow oxidation PR.


pub(crate) struct VariableMapper {
target_cregs: Vec<ClassicalRegister>,
register_map: RefCell<HashMap<String, ClassicalRegister>>,
Copy link
Member

Choose a reason for hiding this comment

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

I'm curious why does this need interior mutability? Are we returning references to registers and need to mutate it as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason is just that this is functionally a cache, only. Without the interior mutability, I'd need to make all of these map functions take a &mut self, and that had less than ideal implications when working with this struct. IIRC, caching is the official Rust docs example for interior mutability, so I feel good about the use of it here lol

@eliarbel eliarbel modified the milestones: 2.1.0, 2.2.0 Jun 5, 2025
@kevinhartman
Copy link
Contributor Author

The other question I have is there a reason to keep the python space mapper class around anymore? I'm guessing the answer is QuantumCircuit.compose() as that's the only usage I can find of it. That's probably blocked on #14299 so we might want to open a tracking issue for that.

Thanks for the initial review! Yeah, you're spot on—we need to port QuantumCircuit.compose to Rust to get rid of the Python variable mapper. I considered writing a Python wrapper around the new variable mapper, but I figured it'd be a waste of time if we'll soon port compose to Rust anyhow.

Copy link
Contributor

@raynelfss raynelfss left a comment

Choose a reason for hiding this comment

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

This LGTM! Just a minor nitpick and some appreciation :)

Comment on lines +2372 to +2381
let qargs_list: Vec<&ShareableQubit> = self
.qubits
.map_indices(self.qargs_interner.get(node.qubits))
.collect();
let mut cargs_list: Vec<&ShareableClbit> = self
.clbits
.map_indices(self.cargs_interner.get(node.clbits))
.collect();
let cargs_set: HashSet<&ShareableClbit> =
HashSet::from_iter(cargs_list.iter().cloned());
Copy link
Contributor

Choose a reason for hiding this comment

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

Thank you for this 😄

Comment on lines +123 to +126
}
// This is maintaining the legacy behavior of `DAGCircuit.compose`. We don't
// attempt to speed-up this lookup with a cache, since that would just make the more
// standard cases more annoying to deal with.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
}
// This is maintaining the legacy behavior of `DAGCircuit.compose`. We don't
// attempt to speed-up this lookup with a cache, since that would just make the more
// standard cases more annoying to deal with.
}
// This is maintaining the legacy behavior of `DAGCircuit.compose`. We don't
// attempt to speed-up this lookup with a cache, since that would just make the
// standard cases more annoying to deal with.

Saying "more" twice here seems a bit redundant.

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 will defer this since the comment is just ported from the Python version 🙂

@kevinhartman kevinhartman added this pull request to the merge queue Jun 16, 2025
Merged via the queue into Qiskit:main with commit f9caf2d Jun 16, 2025
26 checks passed
@kevinhartman kevinhartman deleted the ox-var-mapper branch June 16, 2025 21:51
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 performance 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.

Remove Python usage for VariableMapper and simplify DAGCircuit::substitute_node_with_dag
6 participants