Skip to content

Commit

Permalink
Enforce type-safety in NeighborTable
Browse files Browse the repository at this point in the history
This hides the internal `.neighbors` field in favour of instead
implemented `Index<PhysicalQubit>` on the entire `NeighborTable` struct.
This is done because indexing into `.neighbors` required calling
`PhysicalQubit::index` to retrieve a `usize`, but a similar method also
exists on `VirtualQubit`.  This meant that it was previously a normal
API pattern to do `neighbors.neighbors[qubit.index()]`, and this threw
away the type safety: it would compile without warning whether `qubit`
was physical or virtual.

To support the hiding of the `.neighbors` field, the constructor of the
coupling map is moved to be an associated function on `NeighborTable`,
so the unsafe access only happens within the context of all other unsafe
operations.
  • Loading branch information
jakelishman committed Sep 5, 2023
1 parent 8018716 commit 452ad44
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 17 deletions.
22 changes: 6 additions & 16 deletions crates/accelerate/src/sabre_swap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,12 @@ fn obtain_swaps<'a>(
layout: &'a NLayout,
) -> impl Iterator<Item = [VirtualQubit; 2]> + 'a {
front_layer.iter_active().flat_map(move |&v| {
neighbors.neighbors[v.to_phys(layout).index()]
neighbors[v.to_phys(layout)]
.iter()
.filter_map(move |&neighbor| {
let virtual_neighbor = neighbor.to_virt(layout);
if virtual_neighbor > v || !front_layer.is_active(virtual_neighbor) {
Some([v, virtual_neighbor])
.filter_map(move |p_neighbor| {
let neighbor = p_neighbor.to_virt(layout);
if neighbor > v || !front_layer.is_active(neighbor) {
Some([v, neighbor])
} else {
None
}
Expand Down Expand Up @@ -196,16 +196,6 @@ fn populate_extended_set(
}
}

fn cmap_from_neighor_table(neighbor_table: &NeighborTable) -> DiGraph<(), ()> {
DiGraph::<(), ()>::from_edges(neighbor_table.neighbors.iter().enumerate().flat_map(
|(u, targets)| {
targets
.iter()
.map(move |v| (NodeIndex::new(u), NodeIndex::new(v.index())))
},
))
}

/// Run sabre swap on a circuit
///
/// Returns:
Expand Down Expand Up @@ -271,7 +261,7 @@ pub fn build_swap_map_inner(
Some(run_in_parallel) => run_in_parallel,
None => getenv_use_multiple_threads() && num_trials > 1,
};
let coupling_graph: DiGraph<(), ()> = cmap_from_neighor_table(neighbor_table);
let coupling_graph = neighbor_table.coupling_graph();
let outer_rng = match seed {
Some(seed) => Pcg64Mcg::seed_from_u64(seed),
None => Pcg64Mcg::from_entropy(),
Expand Down
22 changes: 21 additions & 1 deletion crates/accelerate/src/sabre_swap/neighbor_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use ndarray::prelude::*;
use numpy::PyReadonlyArray2;
use pyo3::prelude::*;
use rayon::prelude::*;
use rustworkx_core::petgraph::prelude::*;

use crate::nlayout::PhysicalQubit;

Expand All @@ -31,7 +32,26 @@ use crate::nlayout::PhysicalQubit;
#[pyclass(module = "qiskit._accelerate.sabre_swap")]
#[derive(Clone, Debug)]
pub struct NeighborTable {
pub neighbors: Vec<Vec<PhysicalQubit>>,
neighbors: Vec<Vec<PhysicalQubit>>,
}

impl NeighborTable {
/// Regenerate a Rust-space coupling graph from the table.
pub fn coupling_graph(&self) -> DiGraph<(), ()> {
DiGraph::from_edges(self.neighbors.iter().enumerate().flat_map(|(u, targets)| {
targets
.iter()
.map(move |v| (NodeIndex::new(u), NodeIndex::new(v.index())))
}))
}
}

impl std::ops::Index<PhysicalQubit> for NeighborTable {
type Output = [PhysicalQubit];

fn index(&self, index: PhysicalQubit) -> &Self::Output {
&self.neighbors[index.index()]
}
}

#[pymethods]
Expand Down

0 comments on commit 452ad44

Please sign in to comment.