Skip to content

Commit

Permalink
Implement num_qubits and num_clbits in Rust
Browse files Browse the repository at this point in the history
* QuantumCircuit.num_qubits and num_clbits are twice as fast after
  this PR.
* These are used in several places. For example QuantumCircuit.width()
  is three times faster.
* num_qubits and num_clbits are introduced in circuit_data.rs. These
  functions are called by the corresponding Python methods.
  They are also used in circuit_data.rs itself.
  • Loading branch information
jlapeyre committed Jun 3, 2024
1 parent 9d03b4b commit b087eb4
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 24 deletions.
34 changes: 24 additions & 10 deletions crates/circuit/src/circuit_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,11 @@ impl CircuitData {
self.qubits.clone_ref(py)
}

#[getter]
pub fn num_qubits(&self) -> usize {
self.qubits_native.len()
}

/// Returns the current sequence of registered :class:`.Clbit`
/// instances as a list.
///
Expand All @@ -235,6 +240,15 @@ impl CircuitData {
self.clbits.clone_ref(py)
}

#[getter]
pub fn num_clbits(&self) -> usize {
self.clbits_native.len()
}

pub fn width(&self) -> usize {
self.num_qubits() + self.num_clbits()
}

/// Registers a :class:`.Qubit` instance.
///
/// Args:
Expand All @@ -246,13 +260,13 @@ impl CircuitData {
/// was provided.
#[pyo3(signature = (bit, *, strict=true))]
pub fn add_qubit(&mut self, py: Python, bit: &Bound<PyAny>, strict: bool) -> PyResult<()> {
if self.qubits_native.len() != self.qubits.bind(bit.py()).len() {
if self.num_qubits() != self.qubits.bind(bit.py()).len() {
return Err(PyRuntimeError::new_err(concat!(
"This circuit's 'qubits' list has become out of sync with the circuit data.",
" Did something modify it?"
)));
}
let idx: BitType = self.qubits_native.len().try_into().map_err(|_| {
let idx: BitType = self.num_qubits().try_into().map_err(|_| {
PyRuntimeError::new_err(
"The number of qubits in the circuit has exceeded the maximum capacity",
)
Expand Down Expand Up @@ -284,13 +298,13 @@ impl CircuitData {
/// was provided.
#[pyo3(signature = (bit, *, strict=true))]
pub fn add_clbit(&mut self, py: Python, bit: &Bound<PyAny>, strict: bool) -> PyResult<()> {
if self.clbits_native.len() != self.clbits.bind(bit.py()).len() {
if self.num_clbits() != self.clbits.bind(bit.py()).len() {
return Err(PyRuntimeError::new_err(concat!(
"This circuit's 'clbits' list has become out of sync with the circuit data.",
" Did something modify it?"
)));
}
let idx: BitType = self.clbits_native.len().try_into().map_err(|_| {
let idx: BitType = self.num_clbits().try_into().map_err(|_| {
PyRuntimeError::new_err(
"The number of clbits in the circuit has exceeded the maximum capacity",
)
Expand Down Expand Up @@ -460,11 +474,11 @@ impl CircuitData {
) -> PyResult<()> {
let mut temp = CircuitData::new(py, qubits, clbits, None, 0)?;
if qubits.is_some() {
if temp.qubits_native.len() < self.qubits_native.len() {
if temp.num_qubits() < self.num_qubits() {
return Err(PyValueError::new_err(format!(
"Replacement 'qubits' of size {:?} must contain at least {:?} bits.",
temp.qubits_native.len(),
self.qubits_native.len(),
temp.num_qubits(),
self.num_qubits(),
)));
}
std::mem::swap(&mut temp.qubits, &mut self.qubits);
Expand All @@ -475,11 +489,11 @@ impl CircuitData {
);
}
if clbits.is_some() {
if temp.clbits_native.len() < self.clbits_native.len() {
if temp.num_clbits() < self.num_clbits() {
return Err(PyValueError::new_err(format!(
"Replacement 'clbits' of size {:?} must contain at least {:?} bits.",
temp.clbits_native.len(),
self.clbits_native.len(),
temp.num_clbits(),
self.num_clbits(),
)));
}
std::mem::swap(&mut temp.clbits, &mut self.clbits);
Expand Down
28 changes: 14 additions & 14 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1919,10 +1919,10 @@ def replace_var(var: expr.Var, cache: Mapping[expr.Var, expr.Var]) -> expr.Var:
edge_map.update(zip(other.qubits, dest.qubits))
else:
mapped_qubits = dest.qbit_argument_conversion(qubits)
if len(mapped_qubits) != len(other.qubits):
if len(mapped_qubits) != other.num_qubits:
raise CircuitError(
f"Number of items in qubits parameter ({len(mapped_qubits)}) does not"
f" match number of qubits in the circuit ({len(other.qubits)})."
f" match number of qubits in the circuit ({other.num_qubits})."
)
if len(set(mapped_qubits)) != len(mapped_qubits):
raise CircuitError(
Expand All @@ -1935,10 +1935,10 @@ def replace_var(var: expr.Var, cache: Mapping[expr.Var, expr.Var]) -> expr.Var:
edge_map.update(zip(other.clbits, dest.clbits))
else:
mapped_clbits = dest.cbit_argument_conversion(clbits)
if len(mapped_clbits) != len(other.clbits):
if len(mapped_clbits) != other.num_clbits:
raise CircuitError(
f"Number of items in clbits parameter ({len(mapped_clbits)}) does not"
f" match number of clbits in the circuit ({len(other.clbits)})."
f" match number of clbits in the circuit ({other.num_clbits})."
)
if len(set(mapped_clbits)) != len(mapped_clbits):
raise CircuitError(
Expand Down Expand Up @@ -2917,7 +2917,7 @@ def add_register(self, *regs: Register | int | Sequence[Bit]) -> None:
else:
self._data.add_qubit(bit)
self._qubit_indices[bit] = BitLocations(
len(self._data.qubits) - 1, [(register, idx)]
self._data.num_qubits - 1, [(register, idx)]
)

elif isinstance(register, ClassicalRegister):
Expand All @@ -2929,7 +2929,7 @@ def add_register(self, *regs: Register | int | Sequence[Bit]) -> None:
else:
self._data.add_clbit(bit)
self._clbit_indices[bit] = BitLocations(
len(self._data.clbits) - 1, [(register, idx)]
self._data.num_clbits - 1, [(register, idx)]
)

elif isinstance(register, list):
Expand All @@ -2950,10 +2950,10 @@ def add_bits(self, bits: Iterable[Bit]) -> None:
self._ancillas.append(bit)
if isinstance(bit, Qubit):
self._data.add_qubit(bit)
self._qubit_indices[bit] = BitLocations(len(self._data.qubits) - 1, [])
self._qubit_indices[bit] = BitLocations(self._data.num_qubits - 1, [])
elif isinstance(bit, Clbit):
self._data.add_clbit(bit)
self._clbit_indices[bit] = BitLocations(len(self._data.clbits) - 1, [])
self._clbit_indices[bit] = BitLocations(self._data.num_clbits - 1, [])
else:
raise CircuitError(
"Expected an instance of Qubit, Clbit, or "
Expand Down Expand Up @@ -3393,12 +3393,12 @@ def width(self) -> int:
int: Width of circuit.
"""
return len(self.qubits) + len(self.clbits)
return self._data.width()

@property
def num_qubits(self) -> int:
"""Return number of qubits."""
return len(self.qubits)
return self._data.num_qubits

@property
def num_ancillas(self) -> int:
Expand All @@ -3408,7 +3408,7 @@ def num_ancillas(self) -> int:
@property
def num_clbits(self) -> int:
"""Return number of classical bits."""
return len(self.clbits)
return self._data.num_clbits

# The stringified return type is because OrderedDict can't be subscripted before Python 3.9, and
# typing.OrderedDict wasn't added until 3.7.2. It can be turned into a proper type once 3.6
Expand Down Expand Up @@ -3879,18 +3879,18 @@ def measure_all(
else:
circ = self.copy()
if add_bits:
new_creg = circ._create_creg(len(circ.qubits), "meas")
new_creg = circ._create_creg(circ.num_qubits, "meas")
circ.add_register(new_creg)
circ.barrier()
circ.measure(circ.qubits, new_creg)
else:
if len(circ.clbits) < len(circ.qubits):
if circ.num_clbits < circ.num_qubits:
raise CircuitError(
"The number of classical bits must be equal or greater than "
"the number of qubits."
)
circ.barrier()
circ.measure(circ.qubits, circ.clbits[0 : len(circ.qubits)])
circ.measure(circ.qubits, circ.clbits[0 : circ.num_qubits])

if not inplace:
return circ
Expand Down

0 comments on commit b087eb4

Please sign in to comment.