Skip to content

Conversation

raynelfss
Copy link
Contributor

@raynelfss raynelfss commented Feb 17, 2025

Summary

The following commits finally bring Bits and Registers entirely to Rust by performing a couple of tasks:

  • Create native representations of Bit (a.k.a. ShareableQubit and ShareableClbit) and Register (a.k.a. QuantumRegister and ClassicalRegister) and move their python counterparts to be managed by rust PyO3.
  • Leverage the usage of native bits and registers in DAGCircuit and CircuitData.
  • Add helper structs RegisterData and BitLocator to represent the layout of bits/registers in the circuit.

All these changes supersede #13686.

Details and comments

As we move more of our core data model to Rust, the main drawback that we've still had is needing to use Python space to create bits and registers. Although simple on the outside, the Bit and Register structures in Python can be quite challenging due to cross referencing that can happen between each type to the other (Bit._register <-> Register[i]] which is not as easy to replicate in Rust space.

With the following commits we aim to move the representation of Bits and Registers to Rust in which the behavior is more similar to that of Python and allows us to share instances between circuits, rather than having the circuit have complete ownership of the bits it contains (which is the reason for preferring this over #13686).

Additions

Bit representation:

To represent a Qubit or Clbit in Rust, we have a couple of identifiers:

  • Within the circuit, Qubit or Clbit which is an index that the circuit can locally refer to when using a bit.
  • Globally, we would use one of two types of Bit:
    • ShareableQubit which represents a global bit.
    • ShareableClbit the classical counterpart to the ShareableQubit.

The base structure for a Bit in Rust, called BitInfo describes a global bit as being one of two things:

  • Owned where the bit is owned by a register.
  • Anonymous where the bit is independent, containing its own unique ID.

Each of these Bits also has an extra property called BitExtraInfo that acts as an identifier, and contains any extra information a Bit of a specific type should have. In the case of ShareableQubit whether the instance is an ancilla or not.

Python:
  • The python counterparts( PyBit, PyQubit, PyClbit and PyAncillaQubit ) have similar behavior and inheritance models as the originals, but should not be subclassed any further as it is no longer supported. See Deprecate subclassing of Register and Bit #13841.
  • The python counterparts may no longer be comparable via is() checks due to conversions in PyO3 not taking on the same addresses.
  • Each of the Rust native bits implements IntoPyObject<'_> so that they can be successfully converted and extracted from their Python counterparts.

Register representation:

To represent a QuantumRegister or ClassicalRegister in Rust, we can use the rust native counterparts of the same name:

  • A rust register is a wrapper to a RegisterInfo which classifies them into two categories:
    • Owning: which owns its bits, can generate them as needed. All of its bits are Owned.
    • Alias: A register that acts as a collection of bits independent from their type.
  • Registers within a circuit should be stored within a RegisterData struct.
Python:
  • Just as with bits, these structs maintain the same inheritance model and behavior as the originals, but should not be subclassed further. See Deprecate subclassing of Register and Bit #13841. They may also not be compared using is(), and the native implementations can be converted to and from Python using the `IntoPyObject<'_> trait.

CircuitData

CircuitData has been upgraded to contain registers, this is how it was achieved.

  • A helper struct called RegisterData was created just to save all of the registers stored in the CircuitData instance.
    • This struct contains a mapping between the Register names and instances.
    • Contains a cached PyDict to allow for a faster visit path from Python.
  • A mapping for qubit instances and its locations has been added in the shape of BitLocator it is essentially a wrapper around an IndexMap and emulates the behavior of QuantumCircuit._{cl, qu}bit_indices.
    • This mapping helps us leverage native types and avoid using Python to fetch the location of a bit.
    • Like RegisterData, it also contains a cached PyDict to enable faster access from python.
  • When processing bit identifiers from Python, we use rust versions of {q,c}bit_argument_conversions methods in QuantumCircuit which perform mappings with native bit types.

DAGCircuit

The DAGCircuit now uses RegisterData and BitLocator in the same way they are used for CircuitData, getting rid of the many PyDict objects being stored within. Said additions removed the need of most py tokens previously needed to modify the circuit's bits and registers.

Questions and Comments

Feel free to leave a review/comment addressing any questions you may have. 🚀

Co-authored-by: Jake Lishman jake.lishman@ibm.com

raynelfss and others added 5 commits February 13, 2025 16:46
Co-authored-by: Jake Lishman <jake.lishman@ibm.com>
- Use `macro_rules!` to create `Bit` and `Registers`.
- Add `new_owned` method for `BitInfo`.
@coveralls
Copy link

coveralls commented Feb 17, 2025

Pull Request Test Coverage Report for Build 13728587689

Details

  • 1631 of 1909 (85.44%) changed or added relevant lines in 81 files are covered.
  • 42 unchanged lines in 5 files lost coverage.
  • Overall coverage decreased (-0.1%) to 88.116%

Changes Missing Coverage Covered Lines Changed/Added Lines %
qiskit/circuit/library/blueprintcircuit.py 7 9 77.78%
crates/circuit/src/bit_locator.rs 52 55 94.55%
qiskit/circuit/quantumcircuit.py 30 35 85.71%
crates/circuit/src/dot_utils.rs 0 7 0.0%
crates/circuit/src/bit_data.rs 50 58 86.21%
qiskit/visualization/circuit/_utils.py 0 8 0.0%
crates/circuit/src/register_data.rs 109 149 73.15%
crates/circuit/src/circuit_data.rs 309 354 87.29%
crates/circuit/src/bit.rs 473 546 86.63%
crates/circuit/src/dag_circuit.rs 458 545 84.04%
Files with Coverage Reduction New Missed Lines %
crates/circuit/src/bit_data.rs 1 92.59%
qiskit/circuit/library/blueprintcircuit.py 1 94.21%
crates/qasm2/src/lex.rs 6 91.73%
crates/circuit/src/dag_circuit.rs 10 86.02%
crates/qasm2/src/parse.rs 24 96.22%
Totals Coverage Status
Change from base Build 13707387645: -0.1%
Covered Lines: 72624
Relevant Lines: 82419

💛 - Coveralls

@raynelfss raynelfss added this to the 2.0.0 milestone Feb 17, 2025
- Rebalance currently available api to allow for superclasses `Bit` and `Register` to also live in Rust.
- Remove: `ShareableBit` triat and structs that implement it. Replace them with new structs.
- Add prefix class attribute for python `Register` instances.
- Add `BitExtraInfo` as a soft identifier for `BitInfo`, can be null to identify a `Bit`.
- Add `RegisterInfo::get` method to retrieve the information of a `Bit`.
- Have the rust registers own their counters instead of having them be Python exclusive.
- Make subclassing of `Regster` and `Bit` a bit more effective by helping the subclasses inherit most methods.
raynelfss and others added 7 commits February 20, 2025 15:02
…GCircuit`.

- Modify `BitData` to accept an extra generic value specifying a "sharable" object type that can also be turned into a `PyObject` to allow for compatibility with `Var`.
- Rename `BitAsKey` to `VarAsKey` as it is currently only used for `Var` instances.
- Modify methods in `CommutationChecker` and `GateDirection` passes to use the newer methods.
- Other tweaks and fixes.
…bit`.

- Remove imports of `QUBIT` and `CLBIT` from `DAGCircuit` and `CircuitData`.
- Discarded old equality and hashing methods for `PyBit` and `PyRegister`.
- Fix `replace_bits` to accept any iterator over `Qubits` and `Clbits` from Python.
- Add `is_empty` methods to Register data, wherever `len` is available.
- Add additional parsing during creation of `Register` to more closely match previous behavior.
- Modify `Bit` tests due to mocking no longer operating correctly.
- Add method `register` to `BitData` to better represent a register reference in an owned bit.
- Add missing quotation marks in `Register` repr().
- Bits and registers that live in Rust are not guaranteed to pass the `is` equality check in Python.
- Restore type checking in `DAGCircuit` when adding bits.
- `Bits` should retain their hash value upon deserialization to avoid incorrect retrievals via hash or indexing. Fixed by implementing correct `__reduce__` method in `PyBit`, `PyClbit`, and `PyQubit`.
- Replace `__getnnewargs__` methods with `__reduce__` for more versatile serialization.
- Extend `SliceOrVec` to include a method that allows a vec with negative indices to correctly iterate based on a provided size.
- Modify `FullAncillaAllocation` to not replace the `QuantumRegister` prefix.
- Add `instances_count` getter attribute to all registers.
- `is` comparisons are no longer guaranteed to work between bits and registers. So some tests have been modified to reflect that.
- When `apply_operation` in the `DAGCircuit` receives a clbit in place of a qubit and viceversa, it will throw a type error, not a Key error. This is handled by PyO3 directly during extraction.
- Create `RegisterData` struct which, similarly to how `BitData` works, stores the registers and allows to access them by key, in this case by name or index.
- Tweak `CircuitData` and `QuantumCircuit` api to allow for access of registers from within `CircuitData`.
- Add `qubit_indices` and `clbit_indices` structs to keep track of the locations of bits and registers within the circuit.
- Modify `circuit_to_dag` to obtain the registers directly from `CircuitData`.
- Modify `BlueprintCircuit` to rely on `CircuitData` to access `QuantumRegister` instances.
- Add setters and getters for registers.
- Modify `circuit_to_instruction` to bring over the registers into the definition of the `Instruction`.
- Add `contains` method to `BitData`.
- Add rust native `BitLocations` that can be converted to a `Python` version if needed.
- Implemented `RegisterData` within the `DAGCircuit`.
- Modified methods of the `DAGCircuit` to utilize new native structures wherever possible.
- Modify `RegisterData` to not have a `description` attribute, and use an `IndexMap` to preserve insertion order of the registers.
- Use native initializers for Registers throughout the crate.
- Use native bits in initializer methods for `QuantumCircuit` and `ClassicalCircuit` instead of `BitData`.
- Modify additional `is` checks from tests where not needed.
- Modify `test_gate_definitions` to create owning registers when testing for definitions from the `EquivalenceLibrary`.
- Fix `remove` method to work more efficiently and add `remove_registers` for multiple removals.
- Store index mapping between register name and `RegisterIndex<u32>` which is copyable.
@mtreinish mtreinish requested a review from kevinhartman March 7, 2025 20:24
@mtreinish mtreinish enabled auto-merge March 7, 2025 20:34
@mtreinish mtreinish added this pull request to the merge queue Mar 7, 2025
Merged via the queue into Qiskit:main with commit 2fe67f0 Mar 7, 2025
20 checks passed
mtreinish added a commit to mtreinish/qiskit-core that referenced this pull request Mar 10, 2025
In preparation for providing a C API for building and interacting with
circuits this commit removes the required Python component from the
circuit construction APIs on CircuitData. After Qiskit#13860 the only use case
of Python in the circuit constructor is to manipulate a
ParameterExpression global phase as ParameterExpression is still a
Python only construct. This moves to using Python::with_gil() in those
places only when it's needed. This gives us a path for building a
circuit without the Python interpreter for the C API.
raynelfss added a commit to raynelfss/qiskit that referenced this pull request Mar 11, 2025
In an oversight after Qiskit#13860, when switching to use the pyo3 friendly `BitLocations` which is not an instance of `namedtuple` we did not extend it to include sequence methods `__getitem__` and `__len__`, which results in breaking api changes.

The following commits add those methods to `BitLocations` and also labels the class correctly as a `sequence` in PyO3.
raynelfss added a commit to raynelfss/qiskit that referenced this pull request Mar 12, 2025
Prior implementations would replace the Register's prefix attribute inplace which is an unsafe operation. The following commits add a secure path for a provisional replacement of a register's prefix name to fix changed unsafe behavior from Qiskit#13860.
github-merge-queue bot pushed a commit that referenced this pull request Mar 12, 2025
)

* Add: `__getitem__` and other attributes to `BitLocations`
In an oversight after #13860, when switching to use the pyo3 friendly `BitLocations` which is not an instance of `namedtuple` we did not extend it to include sequence methods `__getitem__` and `__len__`, which results in breaking api changes.

The following commits add those methods to `BitLocations` and also labels the class correctly as a `sequence` in PyO3.

* Fix: Address review comments

* Fix: More formatting issues in tests

* Apply suggestions from code review

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>

---------

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
mergify bot pushed a commit that referenced this pull request Mar 12, 2025
)

* Add: `__getitem__` and other attributes to `BitLocations`
In an oversight after #13860, when switching to use the pyo3 friendly `BitLocations` which is not an instance of `namedtuple` we did not extend it to include sequence methods `__getitem__` and `__len__`, which results in breaking api changes.

The following commits add those methods to `BitLocations` and also labels the class correctly as a `sequence` in PyO3.

* Fix: Address review comments

* Fix: More formatting issues in tests

* Apply suggestions from code review

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>

---------

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
(cherry picked from commit 3a80a81)
github-merge-queue bot pushed a commit that referenced this pull request Mar 12, 2025
) (#14008)

* Add: `__getitem__` and other attributes to `BitLocations`
In an oversight after #13860, when switching to use the pyo3 friendly `BitLocations` which is not an instance of `namedtuple` we did not extend it to include sequence methods `__getitem__` and `__len__`, which results in breaking api changes.

The following commits add those methods to `BitLocations` and also labels the class correctly as a `sequence` in PyO3.

* Fix: Address review comments

* Fix: More formatting issues in tests

* Apply suggestions from code review

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>

---------

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
(cherry picked from commit 3a80a81)

Co-authored-by: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com>
github-merge-queue bot pushed a commit that referenced this pull request Mar 14, 2025
* Remove Python dependency from circuit construction

In preparation for providing a C API for building and interacting with
circuits this commit removes the required Python component from the
circuit construction APIs on CircuitData. After #13860 the only use case
of Python in the circuit constructor is to manipulate a
ParameterExpression global phase as ParameterExpression is still a
Python only construct. This moves to using Python::with_gil() in those
places only when it's needed. This gives us a path for building a
circuit without the Python interpreter for the C API.

* Remove with_gil for calling extend() in new()
github-merge-queue bot pushed a commit that referenced this pull request Mar 18, 2025
* FIx: Create an internal path for custom prefixes in registers.

Prior implementations would replace the Register's prefix attribute inplace which is an unsafe operation. The following commits add a secure path for a provisional replacement of a register's prefix name to fix changed unsafe behavior from #13860.

* Fix: Address review comments
- Add test-case

* Apply suggestions from code review

Co-authored-by: Kevin Hartman <kevin@hart.mn>

* Fix: Address more review comments

* Fix: Lint error

* Update test/python/circuit/test_circuit_operations.py

Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com>

---------

Co-authored-by: Kevin Hartman <kevin@hart.mn>
Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com>
mergify bot pushed a commit that referenced this pull request Mar 18, 2025
* FIx: Create an internal path for custom prefixes in registers.

Prior implementations would replace the Register's prefix attribute inplace which is an unsafe operation. The following commits add a secure path for a provisional replacement of a register's prefix name to fix changed unsafe behavior from #13860.

* Fix: Address review comments
- Add test-case

* Apply suggestions from code review

Co-authored-by: Kevin Hartman <kevin@hart.mn>

* Fix: Address more review comments

* Fix: Lint error

* Update test/python/circuit/test_circuit_operations.py

Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com>

---------

Co-authored-by: Kevin Hartman <kevin@hart.mn>
Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com>
(cherry picked from commit 56a16ab)
github-merge-queue bot pushed a commit that referenced this pull request Mar 18, 2025
…) (#14048)

* FIx: Create an internal path for custom prefixes in registers.

Prior implementations would replace the Register's prefix attribute inplace which is an unsafe operation. The following commits add a secure path for a provisional replacement of a register's prefix name to fix changed unsafe behavior from #13860.

* Fix: Address review comments
- Add test-case

* Apply suggestions from code review

Co-authored-by: Kevin Hartman <kevin@hart.mn>

* Fix: Address more review comments

* Fix: Lint error

* Update test/python/circuit/test_circuit_operations.py

Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com>

---------

Co-authored-by: Kevin Hartman <kevin@hart.mn>
Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com>
(cherry picked from commit 56a16ab)

Co-authored-by: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com>
raynelfss added a commit to raynelfss/qiskit that referenced this pull request Mar 20, 2025
…kit#13997)

* Add: `__getitem__` and other attributes to `BitLocations`
In an oversight after Qiskit#13860, when switching to use the pyo3 friendly `BitLocations` which is not an instance of `namedtuple` we did not extend it to include sequence methods `__getitem__` and `__len__`, which results in breaking api changes.

The following commits add those methods to `BitLocations` and also labels the class correctly as a `sequence` in PyO3.

* Fix: Address review comments

* Fix: More formatting issues in tests

* Apply suggestions from code review

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>

---------

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
raynelfss pushed a commit to raynelfss/qiskit that referenced this pull request Mar 20, 2025
* Remove Python dependency from circuit construction

In preparation for providing a C API for building and interacting with
circuits this commit removes the required Python component from the
circuit construction APIs on CircuitData. After Qiskit#13860 the only use case
of Python in the circuit constructor is to manipulate a
ParameterExpression global phase as ParameterExpression is still a
Python only construct. This moves to using Python::with_gil() in those
places only when it's needed. This gives us a path for building a
circuit without the Python interpreter for the C API.

* Remove with_gil for calling extend() in new()
raynelfss added a commit to raynelfss/qiskit that referenced this pull request Mar 20, 2025
…it#14005)

* FIx: Create an internal path for custom prefixes in registers.

Prior implementations would replace the Register's prefix attribute inplace which is an unsafe operation. The following commits add a secure path for a provisional replacement of a register's prefix name to fix changed unsafe behavior from Qiskit#13860.

* Fix: Address review comments
- Add test-case

* Apply suggestions from code review

Co-authored-by: Kevin Hartman <kevin@hart.mn>

* Fix: Address more review comments

* Fix: Lint error

* Update test/python/circuit/test_circuit_operations.py

Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com>

---------

Co-authored-by: Kevin Hartman <kevin@hart.mn>
Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com>
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: circuit Related to the core of the `QuantumCircuit` class or the circuit library 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.

Create a rust representation of QuantumRegister and ClassicalRegister Make rust Qubit and Clbit the source of truth in a circuit
6 participants