Closed jakelishman closed 2 months ago
One or more of the following people are relevant to this code:
@Qiskit/terra-core
@kevinhartman
@mtreinish
Changes Missing Coverage | Covered Lines | Changed/Added Lines | % | ||
---|---|---|---|---|---|
crates/circuit/src/interner.rs | 40 | 44 | 90.91% | ||
crates/circuit/src/circuit_data.rs | 24 | 30 | 80.0% | ||
<!-- | Total: | 136 | 146 | 93.15% | --> |
Files with Coverage Reduction | New Missed Lines | % | ||
---|---|---|---|---|
crates/circuit/src/circuit_data.rs | 2 | 92.18% | ||
crates/qasm2/src/lex.rs | 3 | 92.98% | ||
crates/qasm2/src/parse.rs | 6 | 97.61% | ||
<!-- | Total: | 11 | --> |
Totals | |
---|---|
Change from base Build 10563485346: | 0.01% |
Covered Lines: | 71667 |
Relevant Lines: | 80320 |
Looks good, I'll approve once conflicts are resolved.
Summary
This rewrites the interner mechanisms be defined in terms of two generic structs (
Interner
andInterned
) that formalise the idea that the interned values are owned versions of a given reference type, and move the logically separate intern-related actions (get a key from the value, and get the value from the key) into separate functions (get
andinsert
, respectively).This has a few advantages:
we now have both
insert
andinsert_owned
methods, which was awkward to add within the trait-based structure. This allows a more efficient path when the owned variant has already necessarily been constructed.additionally, the standard
insert
path now takes only a reference type. For large circuits, most intern lookups will, in general, find a pre-existing key, so in situations where the interned value is sufficiently small that it can be within a static allocation (which is the case for almost allqargs
andcargs
), it's more efficient not to construct the owned type on the heap.the type of the values retrieved from an interner are no longer indirected through the owned type that's stored. For example, where
IndexedInterner<Vec<Qubit>>
gave out&Vec<Qubit>
s as its lookups,Interner<[Qubit]>
returns the more standard&[Qubit]
, which is only singly indirect rather than double.The following replacements are made:
The
IndexedInterner
struct from before is now just calledInterner
(and internally, it uses anIndexSet
rather than manually tracking the indices). Its generic type is related to the references it returns, rather than the owned value it stores, soIndexedInterner<Vec<Qubit>>
becomesInterner<[Qubit]>
.The
Interner
trait is now gone. Everything is just accessed as concrete methods on theInterner
struct.<&IndexedInterner as Interner>::intern
(lookup of the value from an interner key) is now calledInterner::get
.<&mut IndexedInterner as Interner>::intern
(conversion of an owned value to an interner key) is now calledInterner::insert_owned
.A new method,
Interner::insert
, can now be used when one need not have an owned allocation of the storage type; the correct value will be allocated if required (which is expected to be less frequent).The intern key is no longer called
interner::Index
, butInterned<T>
, where the generic parameterT
matches the generic of theInterner<T>
that gave out the key.Details and comments
This is sufficiently generic now that it can probably be used directly for the purposes of #12917, though I didn't make it generic over the key width. It probably wouldn't be too hard to do that too, if we want it very much.
I'm intending to follow this PR with one that makes the key corresponding to the "empty" state constructible without any hash lookup, since we end up needing that quite a lot (think the cargs key when pushing a standard gate). (edit: now #13035.)