Skip to content
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

[DAGCircuit Oxidation] Refactor bit management in CircuitData #12372

Merged
merged 14 commits into from
Jun 7, 2024

Conversation

kevinhartman
Copy link
Contributor

@kevinhartman kevinhartman commented May 8, 2024

Summary

This is part of the work we're doing to port DAGCircuit to Rust. It's work I've pulled out of a local branch where I'm doing that port, in an effort to reduce the size of that eventual PR.

The primary improvement that this brings is the introduction of BitData, which encapsulates keeping Python and native bit indices in sync. It provides methods like map_indices and map_bits which improve the ergonomics of translating between native and Python bits at PyO3 boundaries.

Details and comments

  • Added zero-cost struct types for our native Qubit and Clbit representation.
  • Added a new struct BitData which encapsulates maintaining a mapping between native Rust bit types and Python bit types. This significantly cleans up the code in CircuitData and makes it reusable for DAGCircuit.
  • Made InternContext into a more generic IndexedInterner data structure, which implements the new Interner trait with slotted interning for any T which is Hash and Eq.
  • Added Interner trait, for defining interner semantics for containers like IndexedInterner.

The one caveat to this PR is that we now no longer share interning between Qubit and Clbit indices, since this is more difficult from a typing perspective. If we want to share a single IndexedInterner for both Qubit and Clbit operand lists, this would be possible, but we should revisit it. For now I wanted to just focus on keeping things strongly typed so that our Rust code doesn't get overly messy at this early stage.

@kevinhartman kevinhartman added the Changelog: None Do not include in changelog label May 8, 2024
@kevinhartman kevinhartman requested a review from a team as a code owner May 8, 2024 20:36
@qiskit-bot
Copy link
Collaborator

One or more of the the following people are requested to review this:

@coveralls
Copy link

coveralls commented May 8, 2024

Pull Request Test Coverage Report for Build 9192883136

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

  • 237 of 264 (89.77%) changed or added relevant lines in 5 files are covered.
  • 3 unchanged lines in 1 file lost coverage.
  • Overall coverage increased (+0.006%) to 89.62%

Changes Missing Coverage Covered Lines Changed/Added Lines %
crates/circuit/src/circuit_data.rs 79 84 94.05%
crates/circuit/src/bit_data.rs 87 94 92.55%
crates/circuit/src/interner.rs 30 45 66.67%
Files with Coverage Reduction New Missed Lines %
crates/qasm2/src/lex.rs 3 93.64%
Totals Coverage Status
Change from base Build 9163549280: 0.006%
Covered Lines: 62362
Relevant Lines: 69585

💛 - Coveralls

Comment on lines +31 to +36
struct BitAsKey {
/// Python's `hash()` of the wrapped instance.
hash: isize,
/// The wrapped instance.
bit: PyObject,
}
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 wondering if we maybe should remove one of the layers of abstraction here. It's something I've been thinking about in #12205 is that the source of truth for bits is still python but for interaction from rust space this isn't super ergonomic. I'm wondering if instead of playing games like this we try flattening the two representations. I guess the complexity is things like register and index (although #11596 will take care of that)

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 not sure if this is what you were getting at, but I was thinking longer term that we'll want/need to make it possible to construct circuits from Rust space without creating any Python objects at all. We might not be at that point yet, but it might to be thinking about it.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, that's basically what I was getting at, having a pattern for making a circuit without any python interaction is what we'll want longer term. The example I have for making circuits from rust space right now still needs a py handle because we need to instantiate Qubit objects.

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'd agree as well. BitAsKey isn't new here, though. I've only moved it from CircuitData.

Also, the source of truth for bits is CircuitData. It owns its bits, and QuantumCircuit simply "references" them.

@mtreinish mtreinish added the Rust This PR or issue is related to Rust code in the repository label May 16, 2024
@kevinhartman
Copy link
Contributor Author

I'm rethinking a bit of this PR (specifically the stuff with packing and interning) so I'm going to put this into draft for now.

The BitData stuff is likely to stay, but I'm likely to reduce the traiting stuff around packing and interning a bit.

@kevinhartman kevinhartman marked this pull request as draft May 17, 2024 15:33
@kevinhartman kevinhartman marked this pull request as ready for review May 21, 2024 19:51
@qiskit-bot
Copy link
Collaborator

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

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.

This looks fine to me it's mostly moving code around, just a couple of questions inline.

crates/circuit/src/bit_data.rs Outdated Show resolved Hide resolved
#[pymethods]
impl CircuitInstruction {
#[new]
pub fn new(
pub fn py_new(
Copy link
Member

Choose a reason for hiding this comment

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

Oh, I like this pattern, this is something we need to do a lot and have a dedicated python space constructor like this works well.

crates/circuit/src/circuit_instruction.rs Outdated Show resolved Hide resolved
Comment on lines 35 to 38
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
struct Qubit(BitType);
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
struct Clbit(BitType);
Copy link
Member

Choose a reason for hiding this comment

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

Should these be public?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

At some point, yes. But, they aren't used in the public interface of the crate at the moment.

I also want to call out that I'm not especially keen on us implementing From<BitType> for Qubit and Clbit and From<Qubit|Clbit> for BitType, as I've done here. To me, that exposes what could be an implementation detail of BitData (how the bits are mapped between the native type and Python instances).

I have a separate branch where I explored removing those conversions. I thought it would be nice if Qubit and Clbit were defined in bit_data.rs, and the implementation of BitData encapsulated the awareness of these conversions to and from a u32 (BitType), and then we simply exposed Qubit and Clbit opaquely in lib.rs. I decided against this for now, since Python users already expect that bits are contiguous and ordered by insertion in a circuit (and hence convertible to and from an index). But I'd be curious to hear your thoughts, and perhaps what @jakelishman has to say when he's back from holiday.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Made public in 964d6fc.

Though I do suspect we may want to make the underlying representation more private. I'll likely explore that as part of porting DAGCircuit.

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.

This LGTM now, thanks for making the updates.

@mtreinish mtreinish added this pull request to the merge queue Jun 6, 2024
Merged via the queue into Qiskit:main with commit d1c8404 Jun 7, 2024
15 checks passed
Procatv pushed a commit to Procatv/qiskit-terra-catherines that referenced this pull request Aug 1, 2024
…it#12372)

* Checkpoint before rebase.

* Refactor bit management in CircuitData.

* Revert changes not for this PR.

* Add doc comment for InstructionPacker, rename Interner::lookup.

* Add CircuitData::map_* and refactor.

* Fix merge issue.

* CircuitInstruction::new returns Self.

* Use unbind.

* Make bit types pub.

* Fix merge.
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 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