Skip to content

Commit dff9e81

Browse files
authored
Implement a capacity-allocated constructor for DAGCircuit in Rust. (#12975)
* Initial: implement fixed capacity constructor for DAGCircuit - Implement `DAGCircuit` with `with_capacity` to create an initially allocated instance, for rust only. - Implement `with_capacity` for `BitData` and `IndexInterner` that creates an initally allocated instance of each type. - Other small tweaks and fixes. - Add num_edges optional argument. If known, use `num_edges` argument to create a `DAGCircuit` with the exact number of edges. - Leverage usage of new method in `copy_empty_like`. - Use `..Default::default()` for `op_names` and `calibrations` fields in `DAGCircuit::with_capacity()` * Fix: Adapt to #13033 * Fix: Re-arrange the function arguments as per @mtreinish's review. - The order now follows: qubits, clbits, vars, num_ops, edges. As per [Matthew's comment](#12975 (comment)).
1 parent d3b8b91 commit dff9e81

File tree

3 files changed

+87
-1
lines changed

3 files changed

+87
-1
lines changed

crates/circuit/src/bit_data.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,15 @@ where
9595
}
9696
}
9797

98+
pub fn with_capacity(py: Python<'_>, description: String, capacity: usize) -> Self {
99+
BitData {
100+
description,
101+
bits: Vec::with_capacity(capacity),
102+
indices: HashMap::with_capacity(capacity),
103+
cached: PyList::empty_bound(py).unbind(),
104+
}
105+
}
106+
98107
/// Gets the number of bits.
99108
pub fn len(&self) -> usize {
100109
self.bits.len()

crates/circuit/src/dag_circuit.rs

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1556,7 +1556,14 @@ def _format(operand):
15561556
/// DAGCircuit: An empty copy of self.
15571557
#[pyo3(signature = (*, vars_mode="alike"))]
15581558
fn copy_empty_like(&self, py: Python, vars_mode: &str) -> PyResult<Self> {
1559-
let mut target_dag = DAGCircuit::new(py)?;
1559+
let mut target_dag = DAGCircuit::with_capacity(
1560+
py,
1561+
self.num_qubits(),
1562+
self.num_clbits(),
1563+
Some(self.num_vars()),
1564+
None,
1565+
None,
1566+
)?;
15601567
target_dag.name = self.name.as_ref().map(|n| n.clone_ref(py));
15611568
target_dag.global_phase = self.global_phase.clone();
15621569
target_dag.duration = self.duration.as_ref().map(|d| d.clone_ref(py));
@@ -6156,6 +6163,69 @@ impl DAGCircuit {
61566163
}
61576164
Ok(())
61586165
}
6166+
6167+
/// Alternative constructor, builds a DAGCircuit with a fixed capacity.
6168+
///
6169+
/// # Arguments:
6170+
/// - `py`: Python GIL token
6171+
/// - `num_qubits`: Number of qubits in the circuit
6172+
/// - `num_clbits`: Number of classical bits in the circuit.
6173+
/// - `num_vars`: (Optional) number of variables in the circuit.
6174+
/// - `num_ops`: (Optional) number of operations in the circuit.
6175+
/// - `num_edges`: (Optional) If known, number of edges in the circuit.
6176+
pub fn with_capacity(
6177+
py: Python,
6178+
num_qubits: usize,
6179+
num_clbits: usize,
6180+
num_vars: Option<usize>,
6181+
num_ops: Option<usize>,
6182+
num_edges: Option<usize>,
6183+
) -> PyResult<Self> {
6184+
let num_ops: usize = num_ops.unwrap_or_default();
6185+
let num_vars = num_vars.unwrap_or_default();
6186+
let num_edges = num_edges.unwrap_or(
6187+
num_qubits + // 1 edge between the input node and the output node or 1st op node.
6188+
num_clbits + // 1 edge between the input node and the output node or 1st op node.
6189+
num_vars + // 1 edge between the input node and the output node or 1st op node.
6190+
num_ops, // In Average there will be 3 edges (2 qubits and 1 clbit, or 3 qubits) per op_node.
6191+
);
6192+
6193+
let num_nodes = num_qubits * 2 + // One input + One output node per qubit
6194+
num_clbits * 2 + // One input + One output node per clbit
6195+
num_vars * 2 + // One input + output node per variable
6196+
num_ops;
6197+
6198+
Ok(Self {
6199+
name: None,
6200+
metadata: Some(PyDict::new_bound(py).unbind().into()),
6201+
calibrations: HashMap::default(),
6202+
dag: StableDiGraph::with_capacity(num_nodes, num_edges),
6203+
qregs: PyDict::new_bound(py).unbind(),
6204+
cregs: PyDict::new_bound(py).unbind(),
6205+
qargs_interner: Interner::with_capacity(num_qubits),
6206+
cargs_interner: Interner::with_capacity(num_clbits),
6207+
qubits: BitData::with_capacity(py, "qubits".to_string(), num_qubits),
6208+
clbits: BitData::with_capacity(py, "clbits".to_string(), num_clbits),
6209+
global_phase: Param::Float(0.),
6210+
duration: None,
6211+
unit: "dt".to_string(),
6212+
qubit_locations: PyDict::new_bound(py).unbind(),
6213+
clbit_locations: PyDict::new_bound(py).unbind(),
6214+
qubit_io_map: Vec::with_capacity(num_qubits),
6215+
clbit_io_map: Vec::with_capacity(num_clbits),
6216+
var_input_map: _VarIndexMap::new(py),
6217+
var_output_map: _VarIndexMap::new(py),
6218+
op_names: IndexMap::default(),
6219+
control_flow_module: PyControlFlowModule::new(py)?,
6220+
vars_info: HashMap::with_capacity(num_vars),
6221+
vars_by_type: [
6222+
PySet::empty_bound(py)?.unbind(),
6223+
PySet::empty_bound(py)?.unbind(),
6224+
PySet::empty_bound(py)?.unbind(),
6225+
],
6226+
})
6227+
}
6228+
61596229
/// Get qargs from an intern index
61606230
pub fn get_qargs(&self, index: Interned<[Qubit]>) -> &[Qubit] {
61616231
self.qargs_interner.get(index)

crates/circuit/src/interner.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@ where
125125
_type: PhantomData,
126126
}
127127
}
128+
129+
pub fn with_capacity(capacity: usize) -> Self {
130+
Self(IndexSet::with_capacity_and_hasher(
131+
capacity,
132+
::ahash::RandomState::new(),
133+
))
134+
}
128135
}
129136

130137
impl<T> Interner<T>

0 commit comments

Comments
 (0)