Skip to content

Commit bcc39ec

Browse files
raynelfsskevinhartmanElePT
committed
Create a secure internal path for custom prefixes in registers. (Qiskit#14005)
* FIx: Create an internal path for custom prefixes in registers. Prior implementations would replace the Register's prefix attribute inplace which is an unsafe operation. The following commits add a secure path for a provisional replacement of a register's prefix name to fix changed unsafe behavior from Qiskit#13860. * Fix: Address review comments - Add test-case * Apply suggestions from code review Co-authored-by: Kevin Hartman <[email protected]> * Fix: Address more review comments * Fix: Lint error * Update test/python/circuit/test_circuit_operations.py Co-authored-by: Elena Peña Tapia <[email protected]> --------- Co-authored-by: Kevin Hartman <[email protected]> Co-authored-by: Elena Peña Tapia <[email protected]>
1 parent a994d47 commit bcc39ec

File tree

4 files changed

+49
-5
lines changed

4 files changed

+49
-5
lines changed

crates/circuit/src/bit.rs

+19
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,25 @@ macro_rules! create_bit_object {
771771
})
772772
}
773773

774+
/// Allows for the creation of a new register with a temporary prefix and the
775+
/// same instance counter.
776+
#[pyo3(signature=(size=None, name=None, bits=None))]
777+
#[staticmethod]
778+
fn _new_with_prefix(
779+
py: Python,
780+
size: Option<isize>,
781+
name: Option<String>,
782+
bits: Option<Vec<$bit_struct>>,
783+
) -> PyResult<Py<Self>> {
784+
let name =
785+
format!(
786+
"{}{}",
787+
name.unwrap_or(Self::prefix().to_string()),
788+
$reg_struct::anonymous_instance_count().fetch_add(1, Ordering::Relaxed)
789+
);
790+
Py::new(py, Self::py_new(size, Some(name), bits)?)
791+
}
792+
774793
#[classattr]
775794
fn prefix() -> &'static str {
776795
$pyreg_prefix

qiskit/circuit/quantumcircuit.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -3889,15 +3889,15 @@ def clear(self) -> None:
38893889
def _create_creg(self, length: int, name: str) -> ClassicalRegister:
38903890
"""Creates a creg, checking if ClassicalRegister with same name exists"""
38913891
if name in [creg.name for creg in self.cregs]:
3892-
new_creg = ClassicalRegister(length, name=f"{name}{ClassicalRegister.instance_count}")
3892+
new_creg = ClassicalRegister._new_with_prefix(length, name)
38933893
else:
38943894
new_creg = ClassicalRegister(length, name)
38953895
return new_creg
38963896

38973897
def _create_qreg(self, length: int, name: str) -> QuantumRegister:
38983898
"""Creates a qreg, checking if QuantumRegister with same name exists"""
38993899
if name in [qreg.name for qreg in self.qregs]:
3900-
new_qreg = QuantumRegister(length, name=f"{name}{QuantumRegister.instance_count}")
3900+
new_qreg = QuantumRegister._new_with_prefix(length, name)
39013901
else:
39023902
new_qreg = QuantumRegister(length, name)
39033903
return new_qreg

qiskit/transpiler/passes/layout/full_ancilla_allocation.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,8 @@ def run(self, dag):
9292

9393
if idle_physical_qubits:
9494
if self.ancilla_name in dag.qregs:
95-
qreg = QuantumRegister(
96-
len(idle_physical_qubits),
97-
name=f"{self.ancilla_name}{QuantumRegister.instances_count + 1}",
95+
qreg = QuantumRegister._new_with_prefix(
96+
len(idle_physical_qubits), self.ancilla_name
9897
)
9998
else:
10099
qreg = QuantumRegister(len(idle_physical_qubits), name=self.ancilla_name)

test/python/circuit/test_circuit_operations.py

+26
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,32 @@ def test_measure_all_repetition(self):
852852
self.assertEqual(len(circuit.cregs[0]), 2) # Both length 2
853853
self.assertEqual(len(circuit.cregs[1]), 2)
854854

855+
def test_measure_all_with_multiple_regs_creation(self):
856+
"""Test measure_all in a circuit where the method is called
857+
multiple times consecutively and checks that a register of
858+
a different name is created on each call."""
859+
860+
circuit = QuantumCircuit(1)
861+
862+
# First call should create a new register
863+
circuit.measure_all()
864+
self.assertEqual(len(circuit.cregs), 1) # One creg
865+
self.assertEqual(len(circuit.cregs[0]), 1) # Of length 1
866+
867+
# Second call should also create a new register
868+
circuit.measure_all()
869+
self.assertEqual(len(circuit.cregs), 2) # Now two cregs
870+
self.assertTrue(all(len(reg) == 1 for reg in circuit.cregs)) # All of length 1
871+
# Check that no name is the same
872+
self.assertEqual(len({reg.name for reg in circuit.cregs}), 2)
873+
874+
# Third call should also create a new register
875+
circuit.measure_all()
876+
self.assertEqual(len(circuit.cregs), 3) # Now three cregs
877+
self.assertTrue(all(len(reg) == 1 for reg in circuit.cregs)) # All of length 1
878+
# Check that no name is the same
879+
self.assertEqual(len({reg.name for reg in circuit.cregs}), 3)
880+
855881
def test_remove_final_measurements(self):
856882
"""Test remove_final_measurements
857883
Removes all measurements at end of circuit.

0 commit comments

Comments
 (0)