Skip to content

Commit 7b2d50c

Browse files
authored
Move interners from trait to generic structs (#13033)
* Move interners from trait to generic structs This rewrites the interner mechanisms be defined in terms of two generic structs (`Interner` and `Interned`) 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` and `insert`, respectively). This has a few advantages: 1. we now have both `insert` and `insert_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. 2. 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 all `qargs` and `cargs`), it's more efficient not to construct the owned type on the heap. 3. 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: 1. The `IndexedInterner` struct from before is now just called `Interner` (and internally, it uses an `IndexSet` rather than manually tracking the indices). Its generic type is related to the references it returns, rather than the owned value it stores, so `IndexedInterner<Vec<Qubit>>` becomes `Interner<[Qubit]>`. 2. The `Interner` trait is now gone. Everything is just accessed as concrete methods on the `Interner` struct. 3. `<&IndexedInterner as Interner>::intern` (lookup of the value from an interner key) is now called `Interner::get`. 4. `<&mut IndexedInterner as Interner>::intern` (conversion of an owned value to an interner key) is now called `Interner::insert_owned`. 5. 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). 6. The intern key is no longer called `interner::Index`, but `Interned<T>`, where the generic parameter `T` matches the generic of the `Interner<T>` that gave out the key. * Improve internal documentation * Match names of interners between `CircuitData` and` DAGCircuit`
1 parent cc87318 commit 7b2d50c

File tree

4 files changed

+242
-177
lines changed

4 files changed

+242
-177
lines changed

crates/circuit/src/circuit_data.rs

+34-36
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use std::cell::OnceCell;
1616
use crate::bit_data::BitData;
1717
use crate::circuit_instruction::{CircuitInstruction, OperationFromPython};
1818
use crate::imports::{ANNOTATED_OPERATION, CLBIT, QUANTUM_CIRCUIT, QUBIT};
19-
use crate::interner::{Index, IndexedInterner, Interner};
19+
use crate::interner::{Interned, Interner};
2020
use crate::operations::{Operation, OperationRef, Param, StandardGate};
2121
use crate::packed_instruction::{PackedInstruction, PackedOperation};
2222
use crate::parameter_table::{ParameterTable, ParameterTableError, ParameterUse, ParameterUuid};
@@ -91,9 +91,9 @@ pub struct CircuitData {
9191
/// The packed instruction listing.
9292
data: Vec<PackedInstruction>,
9393
/// The cache used to intern instruction bits.
94-
qargs_interner: IndexedInterner<Vec<Qubit>>,
94+
qargs_interner: Interner<[Qubit]>,
9595
/// The cache used to intern instruction bits.
96-
cargs_interner: IndexedInterner<Vec<Clbit>>,
96+
cargs_interner: Interner<[Clbit]>,
9797
/// Qubits registered in the circuit.
9898
qubits: BitData<Qubit>,
9999
/// Clbits registered in the circuit.
@@ -148,8 +148,8 @@ impl CircuitData {
148148
global_phase,
149149
)?;
150150
for (operation, params, qargs, cargs) in instruction_iter {
151-
let qubits = (&mut res.qargs_interner).intern(qargs)?;
152-
let clbits = (&mut res.cargs_interner).intern(cargs)?;
151+
let qubits = res.qargs_interner.insert_owned(qargs);
152+
let clbits = res.cargs_interner.insert_owned(cargs);
153153
let params = (!params.is_empty()).then(|| Box::new(params));
154154
res.data.push(PackedInstruction {
155155
op: operation,
@@ -199,9 +199,9 @@ impl CircuitData {
199199
instruction_iter.size_hint().0,
200200
global_phase,
201201
)?;
202-
let no_clbit_index = (&mut res.cargs_interner).intern(Vec::new())?;
202+
let no_clbit_index = res.cargs_interner.insert(&[]);
203203
for (operation, params, qargs) in instruction_iter {
204-
let qubits = (&mut res.qargs_interner).intern(qargs.to_vec())?;
204+
let qubits = res.qargs_interner.insert(&qargs);
205205
let params = (!params.is_empty()).then(|| Box::new(params));
206206
res.data.push(PackedInstruction {
207207
op: operation.into(),
@@ -227,8 +227,8 @@ impl CircuitData {
227227
) -> PyResult<Self> {
228228
let mut res = CircuitData {
229229
data: Vec::with_capacity(instruction_capacity),
230-
qargs_interner: IndexedInterner::new(),
231-
cargs_interner: IndexedInterner::new(),
230+
qargs_interner: Interner::new(),
231+
cargs_interner: Interner::new(),
232232
qubits: BitData::new(py, "qubits".to_string()),
233233
clbits: BitData::new(py, "clbits".to_string()),
234234
param_table: ParameterTable::new(),
@@ -258,9 +258,9 @@ impl CircuitData {
258258
params: &[Param],
259259
qargs: &[Qubit],
260260
) -> PyResult<()> {
261-
let no_clbit_index = (&mut self.cargs_interner).intern(Vec::new())?;
261+
let no_clbit_index = self.cargs_interner.insert(&[]);
262262
let params = (!params.is_empty()).then(|| Box::new(params.iter().cloned().collect()));
263-
let qubits = (&mut self.qargs_interner).intern(qargs.to_vec())?;
263+
let qubits = self.qargs_interner.insert(qargs);
264264
self.data.push(PackedInstruction {
265265
op: operation.into(),
266266
qubits,
@@ -351,8 +351,8 @@ impl CircuitData {
351351
) -> PyResult<Self> {
352352
let mut self_ = CircuitData {
353353
data: Vec::new(),
354-
qargs_interner: IndexedInterner::new(),
355-
cargs_interner: IndexedInterner::new(),
354+
qargs_interner: Interner::new(),
355+
cargs_interner: Interner::new(),
356356
qubits: BitData::new(py, "qubits".to_string()),
357357
clbits: BitData::new(py, "clbits".to_string()),
358358
param_table: ParameterTable::new(),
@@ -572,10 +572,10 @@ impl CircuitData {
572572
let qubits = PySet::empty_bound(py)?;
573573
let clbits = PySet::empty_bound(py)?;
574574
for inst in self.data.iter() {
575-
for b in self.qargs_interner.intern(inst.qubits) {
575+
for b in self.qargs_interner.get(inst.qubits) {
576576
qubits.add(self.qubits.get(*b).unwrap().clone_ref(py))?;
577577
}
578-
for b in self.cargs_interner.intern(inst.clbits) {
578+
for b in self.cargs_interner.get(inst.clbits) {
579579
clbits.add(self.clbits.get(*b).unwrap().clone_ref(py))?;
580580
}
581581
}
@@ -737,8 +737,8 @@ impl CircuitData {
737737
// Get a single item, assuming the index is validated as in bounds.
738738
let get_single = |index: usize| {
739739
let inst = &self.data[index];
740-
let qubits = self.qargs_interner.intern(inst.qubits);
741-
let clbits = self.cargs_interner.intern(inst.clbits);
740+
let qubits = self.qargs_interner.get(inst.qubits);
741+
let clbits = self.cargs_interner.get(inst.clbits);
742742
CircuitInstruction {
743743
operation: inst.op.clone(),
744744
qubits: PyTuple::new_bound(py, self.qubits.map_indices(qubits)).unbind(),
@@ -894,7 +894,7 @@ impl CircuitData {
894894
for inst in other.data.iter() {
895895
let qubits = other
896896
.qargs_interner
897-
.intern(inst.qubits)
897+
.get(inst.qubits)
898898
.iter()
899899
.map(|b| {
900900
Ok(self
@@ -905,7 +905,7 @@ impl CircuitData {
905905
.collect::<PyResult<Vec<Qubit>>>()?;
906906
let clbits = other
907907
.cargs_interner
908-
.intern(inst.clbits)
908+
.get(inst.clbits)
909909
.iter()
910910
.map(|b| {
911911
Ok(self
@@ -915,8 +915,8 @@ impl CircuitData {
915915
})
916916
.collect::<PyResult<Vec<Clbit>>>()?;
917917
let new_index = self.data.len();
918-
let qubits_id = Interner::intern(&mut self.qargs_interner, qubits)?;
919-
let clbits_id = Interner::intern(&mut self.cargs_interner, clbits)?;
918+
let qubits_id = self.qargs_interner.insert_owned(qubits);
919+
let clbits_id = self.cargs_interner.insert_owned(clbits);
920920
self.data.push(PackedInstruction {
921921
op: inst.op.clone(),
922922
qubits: qubits_id,
@@ -1113,14 +1113,12 @@ impl CircuitData {
11131113
}
11141114

11151115
fn pack(&mut self, py: Python, inst: &CircuitInstruction) -> PyResult<PackedInstruction> {
1116-
let qubits = Interner::intern(
1117-
&mut self.qargs_interner,
1118-
self.qubits.map_bits(inst.qubits.bind(py))?.collect(),
1119-
)?;
1120-
let clbits = Interner::intern(
1121-
&mut self.cargs_interner,
1122-
self.clbits.map_bits(inst.clbits.bind(py))?.collect(),
1123-
)?;
1116+
let qubits = self
1117+
.qargs_interner
1118+
.insert_owned(self.qubits.map_bits(inst.qubits.bind(py))?.collect());
1119+
let clbits = self
1120+
.cargs_interner
1121+
.insert_owned(self.clbits.map_bits(inst.clbits.bind(py))?.collect());
11241122
Ok(PackedInstruction {
11251123
op: inst.operation.clone(),
11261124
qubits,
@@ -1138,12 +1136,12 @@ impl CircuitData {
11381136
}
11391137

11401138
/// Returns an immutable view of the Interner used for Qargs
1141-
pub fn qargs_interner(&self) -> &IndexedInterner<Vec<Qubit>> {
1139+
pub fn qargs_interner(&self) -> &Interner<[Qubit]> {
11421140
&self.qargs_interner
11431141
}
11441142

11451143
/// Returns an immutable view of the Interner used for Cargs
1146-
pub fn cargs_interner(&self) -> &IndexedInterner<Vec<Clbit>> {
1144+
pub fn cargs_interner(&self) -> &Interner<[Clbit]> {
11471145
&self.cargs_interner
11481146
}
11491147

@@ -1162,14 +1160,14 @@ impl CircuitData {
11621160
&self.clbits
11631161
}
11641162

1165-
/// Unpacks from InternerIndex to `[Qubit]`
1166-
pub fn get_qargs(&self, index: Index) -> &[Qubit] {
1167-
self.qargs_interner().intern(index)
1163+
/// Unpacks from interned value to `[Qubit]`
1164+
pub fn get_qargs(&self, index: Interned<[Qubit]>) -> &[Qubit] {
1165+
self.qargs_interner().get(index)
11681166
}
11691167

11701168
/// Unpacks from InternerIndex to `[Clbit]`
1171-
pub fn get_cargs(&self, index: Index) -> &[Clbit] {
1172-
self.cargs_interner().intern(index)
1169+
pub fn get_cargs(&self, index: Interned<[Clbit]>) -> &[Clbit] {
1170+
self.cargs_interner().get(index)
11731171
}
11741172

11751173
fn assign_parameters_inner<I>(&mut self, py: Python, iter: I) -> PyResult<()>

0 commit comments

Comments
 (0)