Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor encapsulation of Bit storage #13377

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ pub fn barrier_before_final_measurements(
#[cfg(not(feature = "cache_pygates"))]
instruction: new_barrier.unbind(),
};
let qargs: Vec<Qubit> = (0..dag.num_qubits() as u32).map(Qubit).collect();
let qargs: Vec<Qubit> = (0..dag.num_qubits()).map(Qubit::new).collect();
#[cfg(feature = "cache_pygates")]
{
dag.apply_operation_back(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ pub(crate) fn basis_search(
equivs: vec![],
key: Key {
name: "key".to_string(),
num_qubits: u32::MAX,
num_qubits: usize::MAX,
},
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use smallvec::SmallVec;
use crate::equivalence::CircuitFromPython;

// Custom types
pub type GateIdentifier = (String, u32);
pub type GateIdentifier = (String, usize);
pub type BasisTransformIn = (SmallVec<[Param; 3]>, CircuitFromPython);
pub type BasisTransformOut = (SmallVec<[Param; 3]>, DAGCircuit);

Expand Down Expand Up @@ -62,7 +62,7 @@ pub(super) fn compose_transforms<'a>(
.collect::<SmallVec<[Param; 3]>>(),
))?;
let gate_obj: OperationFromPython = gate.extract()?;
let qubits: Vec<Qubit> = (0..dag.num_qubits() as u32).map(Qubit).collect();
let qubits: Vec<Qubit> = (0..dag.num_qubits()).map(Qubit::new).collect();
dag.apply_operation_back(
py,
gate_obj.operation,
Expand Down
29 changes: 20 additions & 9 deletions crates/accelerate/src/basis/basis_translator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ fn extract_basis_target(
// true for > 2q ops too (so for 4q operations we need to check for 3q, 2q,
// and 1q operations in the same manner)
let physical_qargs: SmallVec<[PhysicalQubit; 2]> =
qargs.iter().map(|x| PhysicalQubit(x.0)).collect();
qargs.iter().cloned().map(PhysicalQubit::from_bit).collect();
let physical_qargs_as_set: HashSet<PhysicalQubit> =
HashSet::from_iter(physical_qargs.iter().copied());
if qargs_with_non_global_operation.contains_key(&Some(physical_qargs))
Expand Down Expand Up @@ -374,7 +374,7 @@ fn extract_basis_target_circ(
// true for > 2q ops too (so for 4q operations we need to check for 3q, 2q,
// and 1q operations in the same manner)
let physical_qargs: SmallVec<[PhysicalQubit; 2]> =
qargs.iter().map(|x| PhysicalQubit(x.0)).collect();
qargs.iter().cloned().map(PhysicalQubit::from_bit).collect();
let physical_qargs_as_set: HashSet<PhysicalQubit> =
HashSet::from_iter(physical_qargs.iter().copied());
if qargs_with_non_global_operation.contains_key(&Some(physical_qargs))
Expand Down Expand Up @@ -509,8 +509,13 @@ fn apply_translation(
}
continue;
}
let node_qarg_as_physical: Option<Qargs> =
Some(node_qarg.iter().map(|x| PhysicalQubit(x.0)).collect());
let node_qarg_as_physical: Option<Qargs> = Some(
node_qarg
.iter()
.cloned()
.map(PhysicalQubit::from_bit)
.collect(),
);
if qargs_with_non_global_operation.contains_key(&node_qarg_as_physical)
&& qargs_with_non_global_operation[&node_qarg_as_physical].contains(node_obj.op.name())
{
Expand Down Expand Up @@ -563,7 +568,13 @@ fn apply_translation(
let unique_qargs: Option<Qargs> = if qubit_set.is_empty() {
None
} else {
Some(qubit_set.iter().map(|x| PhysicalQubit(x.0)).collect())
Some(
qubit_set
.iter()
.cloned()
.map(PhysicalQubit::from_bit)
.collect(),
)
};
if extra_inst_map.contains_key(&unique_qargs) {
replace_node(
Expand Down Expand Up @@ -614,12 +625,12 @@ fn replace_node(
let new_qubits: Vec<Qubit> = target_dag
.get_qargs(inner_node.qubits)
.iter()
.map(|qubit| old_qargs[qubit.0 as usize])
.map(|qubit| old_qargs[qubit.index()])
.collect();
let new_clbits: Vec<Clbit> = target_dag
.get_cargs(inner_node.clbits)
.iter()
.map(|clbit| old_cargs[clbit.0 as usize])
.map(|clbit| old_cargs[clbit.index()])
.collect();
let new_op = if inner_node.op.try_standard_gate().is_none() {
inner_node.op.py_copy(py)?
Expand Down Expand Up @@ -675,12 +686,12 @@ fn replace_node(
let new_qubits: Vec<Qubit> = target_dag
.get_qargs(inner_node.qubits)
.iter()
.map(|qubit| old_qargs[qubit.0 as usize])
.map(|qubit| old_qargs[qubit.index()])
.collect();
let new_clbits: Vec<Clbit> = target_dag
.get_cargs(inner_node.clbits)
.iter()
.map(|clbit| old_cargs[clbit.0 as usize])
.map(|clbit| old_cargs[clbit.index()])
.collect();
let new_op = if inner_node.op.try_standard_gate().is_none() {
inner_node.op.py_copy(py)?
Expand Down
14 changes: 7 additions & 7 deletions crates/accelerate/src/check_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ use qiskit_circuit::Qubit;
fn recurse<'py>(
py: Python<'py>,
dag: &'py DAGCircuit,
edge_set: &'py HashSet<[u32; 2]>,
edge_set: &'py HashSet<[usize; 2]>,
wire_map: Option<&'py [Qubit]>,
) -> PyResult<Option<(String, [u32; 2])>> {
) -> PyResult<Option<(String, [usize; 2])>> {
let check_qubits = |qubits: &[Qubit]| -> bool {
match wire_map {
Some(wire_map) => {
let mapped_bits = [wire_map[qubits[0].index()], wire_map[qubits[1].index()]];
edge_set.contains(&[mapped_bits[0].into(), mapped_bits[1].into()])
edge_set.contains(&[mapped_bits[0].index(), mapped_bits[1].index()])
}
None => edge_set.contains(&[qubits[0].into(), qubits[1].into()]),
None => edge_set.contains(&[qubits[0].index(), qubits[1].index()]),
}
};
for node in dag.op_nodes(false) {
Expand Down Expand Up @@ -72,7 +72,7 @@ fn recurse<'py>(
{
return Ok(Some((
inst.op.name().to_string(),
[qubits[0].0, qubits[1].0],
[qubits[0].index(), qubits[1].index()],
)));
}
}
Expand All @@ -84,8 +84,8 @@ fn recurse<'py>(
pub fn check_map(
py: Python,
dag: &DAGCircuit,
edge_set: HashSet<[u32; 2]>,
) -> PyResult<Option<(String, [u32; 2])>> {
edge_set: HashSet<[usize; 2]>,
) -> PyResult<Option<(String, [usize; 2])>> {
recurse(py, dag, &edge_set, None)
}

Expand Down
22 changes: 11 additions & 11 deletions crates/accelerate/src/circuit_library/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ impl BlockOperation {
#[pyclass]
pub struct Block {
pub operation: BlockOperation,
pub num_qubits: u32,
pub num_qubits: usize,
pub num_parameters: usize,
}

Expand All @@ -77,16 +77,16 @@ impl Block {
Block {
operation: BlockOperation::Standard { gate },
num_qubits: gate.num_qubits(),
num_parameters: gate.num_params() as usize,
num_parameters: gate.num_params(),
}
}

#[staticmethod]
#[pyo3(signature = (num_qubits, num_parameters, builder,))]
pub fn from_callable(
py: Python,
num_qubits: i64,
num_parameters: i64,
num_qubits: usize,
num_parameters: usize,
builder: &Bound<PyAny>,
) -> PyResult<Self> {
if !builder.is_callable() {
Expand All @@ -98,8 +98,8 @@ impl Block {
operation: BlockOperation::PyCustom {
builder: builder.to_object(py),
},
num_qubits: num_qubits as u32,
num_parameters: num_parameters as usize,
num_qubits,
num_parameters,
};

Ok(block)
Expand All @@ -108,11 +108,11 @@ impl Block {

// We introduce typedefs to make the types more legible. We can understand the hierarchy
// as follows:
// Connection: Vec<u32> -- indices that the multi-qubit gate acts on
// Connection: Vec<usize> -- indices that the multi-qubit gate acts on
// BlockEntanglement: Vec<Connection> -- entanglement for single block
// LayerEntanglement: Vec<BlockEntanglement> -- entanglements for all blocks in the layer
// Entanglement: Vec<LayerEntanglement> -- entanglement for every layer
type BlockEntanglement = Vec<Vec<u32>>;
type BlockEntanglement = Vec<Vec<usize>>;
pub(super) type LayerEntanglement = Vec<BlockEntanglement>;

/// Represent the entanglement in an n-local circuit.
Expand All @@ -127,7 +127,7 @@ pub struct Entanglement {
impl Entanglement {
/// Create an entanglement from the input of an n_local circuit.
pub fn from_py(
num_qubits: u32,
num_qubits: usize,
reps: usize,
entanglement: &Bound<PyAny>,
entanglement_blocks: &[&Block],
Expand Down Expand Up @@ -158,15 +158,15 @@ impl Entanglement {
}

fn unpack_entanglement(
num_qubits: u32,
num_qubits: usize,
layer: usize,
entanglement: &Bound<PyList>,
entanglement_blocks: &[&Block],
) -> PyResult<LayerEntanglement> {
entanglement_blocks
.iter()
.zip(entanglement.iter())
.map(|(block, ent)| -> PyResult<Vec<Vec<u32>>> {
.map(|(block, ent)| -> PyResult<Vec<Vec<usize>>> {
get_entanglement(num_qubits, block.num_qubits, &ent, layer)?.collect()
})
.collect()
Expand Down
52 changes: 26 additions & 26 deletions crates/accelerate/src/circuit_library/entanglement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ use crate::QiskitError;

/// Get all-to-all entanglement. For 4 qubits and block size 2 we have:
/// [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]
fn full(num_qubits: u32, block_size: u32) -> impl Iterator<Item = Vec<u32>> {
(0..num_qubits).combinations(block_size as usize)
fn full(num_qubits: usize, block_size: usize) -> impl Iterator<Item = Vec<usize>> {
(0..num_qubits).combinations(block_size)
}

/// Get a linear entanglement structure. For ``n`` qubits and block size ``m`` we have:
/// [(0..m-1), (1..m), (2..m+1), ..., (n-m..n-1)]
fn linear(num_qubits: u32, block_size: u32) -> impl DoubleEndedIterator<Item = Vec<u32>> {
fn linear(num_qubits: usize, block_size: usize) -> impl DoubleEndedIterator<Item = Vec<usize>> {
(0..num_qubits - block_size + 1)
.map(move |start_index| (start_index..start_index + block_size).collect())
}
Expand All @@ -37,15 +37,15 @@ fn linear(num_qubits: u32, block_size: u32) -> impl DoubleEndedIterator<Item = V
/// [(n-m..n-1), ..., (1..m), (0..m-1)]
/// This is particularly interesting, as CX+"full" uses n(n-1)/2 gates, but operationally equals
/// CX+"reverse_linear", which needs only n-1 gates.
fn reverse_linear(num_qubits: u32, block_size: u32) -> impl Iterator<Item = Vec<u32>> {
fn reverse_linear(num_qubits: usize, block_size: usize) -> impl Iterator<Item = Vec<usize>> {
linear(num_qubits, block_size).rev()
}

/// Return the qubit indices for circular entanglement. This is defined as tuples of length ``m``
/// starting at each possible index ``(0..n)``. Historically, Qiskit starts with index ``n-m+1``.
/// This is probably easiest understood for a concerete example of 4 qubits and block size 3:
/// [(2,3,0), (3,0,1), (0,1,2), (1,2,3)]
fn circular(num_qubits: u32, block_size: u32) -> Box<dyn Iterator<Item = Vec<u32>>> {
fn circular(num_qubits: usize, block_size: usize) -> Box<dyn Iterator<Item = Vec<usize>>> {
if block_size == 1 || num_qubits == block_size {
Box::new(linear(num_qubits, block_size))
} else {
Expand All @@ -61,7 +61,7 @@ fn circular(num_qubits: u32, block_size: u32) -> Box<dyn Iterator<Item = Vec<u32
/// Get pairwise entanglement. This is typically used on 2 qubits and only has a depth of 2, as
/// first all odd pairs, and then even pairs are entangled. For example on 6 qubits:
/// [(0, 1), (2, 3), (4, 5), /* now the even pairs */ (1, 2), (3, 4)]
fn pairwise(num_qubits: u32) -> impl Iterator<Item = Vec<u32>> {
fn pairwise(num_qubits: usize) -> impl Iterator<Item = Vec<usize>> {
// for Python-folks (like me): pairwise is equal to linear[::2] + linear[1::2]
linear(num_qubits, 2)
.step_by(2)
Expand All @@ -77,16 +77,16 @@ fn pairwise(num_qubits: u32) -> impl Iterator<Item = Vec<u32>> {
/// Offset 2 -> [(0,1,2), (1,2,3), (2,3,0), (3,0,1)]
/// ...
fn shift_circular_alternating(
num_qubits: u32,
block_size: u32,
num_qubits: usize,
block_size: usize,
offset: usize,
) -> Box<dyn Iterator<Item = Vec<u32>>> {
) -> Box<dyn Iterator<Item = Vec<usize>>> {
// index at which we split the circular iterator -- we use Python-like indexing here,
// and define ``split`` as equivalent to a Python index of ``-offset``
let split = (num_qubits - (offset as u32 % num_qubits)) % num_qubits;
let split = (num_qubits - (offset % num_qubits)) % num_qubits;
let shifted = circular(num_qubits, block_size)
.skip(split as usize)
.chain(circular(num_qubits, block_size).take(split as usize));
.skip(split)
.chain(circular(num_qubits, block_size).take(split));
if offset % 2 == 0 {
Box::new(shifted)
} else {
Expand All @@ -109,11 +109,11 @@ fn shift_circular_alternating(
/// The entangler map using mode ``entanglement`` to scatter a block of ``block_size``
/// qubits on ``num_qubits`` qubits.
pub fn get_entanglement_from_str(
num_qubits: u32,
block_size: u32,
num_qubits: usize,
block_size: usize,
entanglement: &str,
offset: usize,
) -> PyResult<Box<dyn Iterator<Item = Vec<u32>>>> {
) -> PyResult<Box<dyn Iterator<Item = Vec<usize>>>> {
if num_qubits == 0 || block_size == 0 {
return Ok(Box::new(std::iter::empty()));
}
Expand Down Expand Up @@ -157,11 +157,11 @@ pub fn get_entanglement_from_str(
/// The entangler map using mode ``entanglement`` to scatter a block of ``block_size``
/// qubits on ``num_qubits`` qubits.
pub fn get_entanglement<'a>(
num_qubits: u32,
block_size: u32,
num_qubits: usize,
block_size: usize,
entanglement: &'a Bound<PyAny>,
offset: usize,
) -> PyResult<Box<dyn Iterator<Item = PyResult<Vec<u32>>> + 'a>> {
) -> PyResult<Box<dyn Iterator<Item = PyResult<Vec<usize>>> + 'a>> {
// unwrap the callable, if it is one
let entanglement = if entanglement.is_callable() {
entanglement.call1((offset,))?
Expand Down Expand Up @@ -191,12 +191,12 @@ pub fn get_entanglement<'a>(

fn _check_entanglement_list<'a>(
list: Bound<'a, PyList>,
block_size: u32,
) -> PyResult<Box<dyn Iterator<Item = PyResult<Vec<u32>>> + 'a>> {
block_size: usize,
) -> PyResult<Box<dyn Iterator<Item = PyResult<Vec<usize>>> + 'a>> {
let entanglement_iter = list.iter().map(move |el| {
let connections: Vec<u32> = el.extract()?;
let connections: Vec<usize> = el.extract()?;

if connections.len() != block_size as usize {
if connections.len() != block_size {
return Err(QiskitError::new_err(format!(
"Entanglement {:?} does not match block size {}",
connections, block_size
Expand Down Expand Up @@ -235,14 +235,14 @@ fn _check_entanglement_list<'a>(
#[pyo3(signature = (num_qubits, block_size, entanglement, offset=0))]
pub fn get_entangler_map<'py>(
py: Python<'py>,
num_qubits: u32,
block_size: u32,
num_qubits: usize,
block_size: usize,
entanglement: &Bound<PyAny>,
offset: usize,
) -> PyResult<Vec<Bound<'py, PyTuple>>> {
// The entanglement is Result<impl Iterator<Item = Result<Vec<u32>>>>, so there's two
// The entanglement is Result<impl Iterator<Item = Result<Vec<usize>>>>, so there's two
// levels of errors we must handle: the outer error is handled by the outer match statement,
// and the inner (Result<Vec<u32>>) is handled upon the PyTuple creation.
// and the inner (Result<Vec<usize>>) is handled upon the PyTuple creation.
match get_entanglement(num_qubits, block_size, entanglement, offset) {
Ok(entanglement) => entanglement
.into_iter()
Expand Down
Loading