Commit ef98c98
committed
Move
`ParameterVector` was previously a pure-Python concept, even though the
Rust-space `Symbol` necessarily had to track an internal backreference
to a vector Python object in order to convert itself to Python later.
This hidden dependence on the Python interpreter was the root cause of a
panicking bug in Qiskit 2.4[^1].
All `ParameterVectorElement`s in Python space have names and UUIDs that
are pure functions of the underlying vector and the element's index.
Now that this information is available in Rust space, it is far more
efficient to simply not store all this derived information, but
calculate it on the fly, as needed. This motivated the change in
`Symbol` to an `enum`: the logic was _already_ handling the two cases of
"standalone" and "element", but this formally separates them to avoid
storing unnecessary data. Doing this alone is already a performance
benefit: construction of a `list(ParameterVector("a", 10_000))` (to
ensure all the same Python-space objects are created) went from 7.7ms
to 1.4ms on my machine (~5x speedup).
This work here is far more aggressive at threading through shared `Arc`
references of both `Symbol` and `SymbolVector`. The `Arc<SymbolVector>`
is necessary for correctness in Python space, not just performance: we
must have `all(el.vector == els[0].vector for el in els)`, and we don't
want to have to cache more Python objects within `Symbol` to achieve
that. Unfortunately, `ParameterVector` never implemented true equality
(only the default referential equality that Python uses to make
mutable-state objects safely hashable by default), so we certainly need
some other stable reference that can be shared.
The previous Python-space constructions of `PyParameter` and
`PyParameterVectorElement` had not been done entirely correctly with
respect to subclassing; the same (cloned) `Symbol` was present in
multiple places, which was two more allocations than necessary (most
places in Rust use `Arc<Symbol>`, which can be near-freely cloned).
This corrects it so that `PyParameterVectorElement` is now just a simple
marker type, and the majority of the Rust logic needn't concern itself
with it at all.
The `AtomicUsize` used for the length of `SymbolVector` is to support
the unfortunate `ParameterVector.resize` operation from Python space.
The atomic operation makes the whole logic safe from data races, but
does nothing to solve the potential race condition of two places
querying a vector its length concurrently and getting different answers,
or the problem that shortening the vector can invalidate already-held
references to `ParameterVectorElement`s or `Symbol`s (a problem that was
pre-existing in Python). The `resize` in-place method is fairly
fundamentally unsound for these reasons, and we should consider removing
it; it's only used in the deprecated `NLocal` subclass of
`BlueprintCircuit`, and that use can probably be replaced by a simple
lazier construction of the vector, or a complete object replacement
rather than mutation.
[^1]: 16c8088: Fix panic in `RemoveIdentityEquivalent` with `ParameterVector` global phase (gh-16054)ParameterVector to Rust1 parent 8e4b783 commit ef98c98
20 files changed
Lines changed: 611 additions & 690 deletions
File tree
- crates
- cext/src
- transpiler
- circuit/src
- parameter
- qasm3/src
- qpy/src
- transpiler/src
- passes/basis_translator
- qiskit
- circuit
- qpy/binary_io
- releasenotes/notes
- test/python/circuit
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
50 | | - | |
| 50 | + | |
51 | 51 | | |
52 | 52 | | |
53 | 53 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
509 | 509 | | |
510 | 510 | | |
511 | 511 | | |
512 | | - | |
| 512 | + | |
513 | 513 | | |
514 | 514 | | |
515 | 515 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
240 | 240 | | |
241 | 241 | | |
242 | 242 | | |
| 243 | + | |
243 | 244 | | |
244 | 245 | | |
245 | 246 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
146 | 146 | | |
147 | 147 | | |
148 | 148 | | |
149 | | - | |
150 | | - | |
151 | | - | |
152 | | - | |
153 | | - | |
| 149 | + | |
154 | 150 | | |
155 | 151 | | |
156 | 152 | | |
| |||
0 commit comments