From 8031626b5b168df4504983b8038488204555b48d Mon Sep 17 00:00:00 2001 From: daehiff Date: Tue, 1 Apr 2025 00:12:52 +0200 Subject: [PATCH] Use petgraph at master branch Signed-off-by: daehiff --- Cargo.lock | 133 +++++++++++++++- Cargo.toml | 4 +- benches/clifford_tableau.rs | 5 +- benches/connectivity.rs | 57 +++++++ src/architecture.rs | 12 +- src/architecture/complete.rs | 155 ------------------- src/architecture/connectivity.rs | 250 +++++++++++++++++++++++++++---- src/architecture/line.rs | 241 ----------------------------- 8 files changed, 424 insertions(+), 433 deletions(-) create mode 100644 benches/connectivity.rs delete mode 100644 src/architecture/complete.rs delete mode 100644 src/architecture/line.rs diff --git a/Cargo.lock b/Cargo.lock index 7b23ccd6..8019160e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "anes" version = "0.1.6" @@ -29,6 +35,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + [[package]] name = "bitvec" version = "1.0.1" @@ -196,12 +208,30 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "funty" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi", +] + [[package]] name = "half" version = "2.4.1" @@ -217,6 +247,11 @@ name = "hashbrown" version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] [[package]] name = "hermit-abi" @@ -320,11 +355,14 @@ checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "petgraph" -version = "0.7.1" -source = "git+https://github.com/daehiff/petgraph?branch=add-mst-prim#705c658dd87d9f571453cff90871e1153d6f96fa" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96dc3f2709da98e228764d8f4c01c39a101dcc441547e8036372ee0522eb108" dependencies = [ "fixedbitset", + "hashbrown", "indexmap", + "serde", ] [[package]] @@ -355,6 +393,15 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.92" @@ -373,12 +420,48 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "radium" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha", + "rand_core", + "zerocopy", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] + [[package]] name = "rayon" version = "1.10.0" @@ -483,6 +566,8 @@ dependencies = [ "criterion", "itertools 0.13.0", "petgraph", + "rand", + "typenum", ] [[package]] @@ -512,6 +597,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-ident" version = "1.0.14" @@ -528,6 +619,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" version = "0.2.99" @@ -683,6 +783,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + [[package]] name = "wyz" version = "0.5.1" @@ -691,3 +800,23 @@ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] + +[[package]] +name = "zerocopy" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] diff --git a/Cargo.toml b/Cargo.toml index 18420aa8..5f747414 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,10 +9,12 @@ path = "src/lib.rs" [dependencies] bitvec = "1.0.1" itertools = "0.13.0" -petgraph = { git = "https://github.com/daehiff/petgraph", branch = "add-mst-prim" } +petgraph = { version = "0.8.0", features = ["stable_graph"]} +typenum = "1.17.0" [dev-dependencies] criterion = "0.5.1" +rand = "0.9.0" [[bench]] name = "clifford_tableau" diff --git a/benches/clifford_tableau.rs b/benches/clifford_tableau.rs index b7f2829f..c29746ca 100644 --- a/benches/clifford_tableau.rs +++ b/benches/clifford_tableau.rs @@ -1,3 +1,4 @@ +use crate::connectivity::connectivity_benchmark; use bitvec::bitvec; use bitvec::prelude::Lsb0; use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; @@ -8,6 +9,8 @@ use syn::ir::clifford_tableau::CallbackCliffordSynthesizer; use syn::ir::CliffordGates; use syn::ir::Synthesizer; +mod connectivity; + #[derive(Debug, Default)] pub struct MockCircuit { commands: Vec, @@ -162,4 +165,4 @@ pub fn ct_bench(c: &mut Criterion) { } criterion_group!(benches, ct_bench); -criterion_main!(benches); +criterion_main!(benches, connectivity_benchmark); diff --git a/benches/connectivity.rs b/benches/connectivity.rs new file mode 100644 index 00000000..6306e94c --- /dev/null +++ b/benches/connectivity.rs @@ -0,0 +1,57 @@ +use criterion::{black_box, criterion_group, Criterion}; +use petgraph::visit::Walker; +use rand::prelude::IndexedRandom; +use rand::seq::SliceRandom; +use rand::Rng; +use syn::architecture::connectivity::Connectivity; +use syn::architecture::Architecture; + +fn random_connected_connectivity( + num_nodes: usize, + extra_edges: usize, + subset_length: usize, +) -> (Connectivity, Vec, usize) { + let mut rng = rand::thread_rng(); + let mut edges = Vec::new(); + + let mut nodes: Vec = (0..num_nodes).collect(); + nodes.shuffle(&mut rng); + for i in 1..num_nodes { + edges.push((nodes[i - 1], nodes[i])); + } + + // Add extra random edges: + while edges.len() < (num_nodes - 1) + extra_edges { + let a = rng.gen_range(0..num_nodes); + let b = rng.gen_range(0..num_nodes); + if a != b && !edges.contains(&(a, b)) && !edges.contains(&(b, a)) { + edges.push((a, b)); + } + } + + let mut subset_nodes = nodes.clone(); + subset_nodes.shuffle(&mut rng); + let subset: Vec = subset_nodes.into_iter().take(subset_length).collect(); + + let random_element = *subset.choose(&mut rng).expect("Subset cannot be empty"); + + (Connectivity::from_edges(&edges), subset, random_element) +} + +fn get_cx_ladder_connectivity((connectivity, nodes, root): &(Connectivity, Vec, usize)) { + let _ = connectivity.get_cx_ladder(&nodes, &root); +} + +pub fn connectivity_bench(c: &mut Criterion) { + let input = random_connected_connectivity(30, 15, 10); + c.bench_function("get_cx_ladder_connectivity: 30, 15, 10", |b| { + b.iter(|| get_cx_ladder_connectivity(black_box(&input))) + }); + + let input = random_connected_connectivity(100, 50, 50); + c.bench_function("get_cx_ladder_connectivity: 100, 15, 10", |b| { + b.iter(|| get_cx_ladder_connectivity(black_box(&input))) + }); +} + +criterion_group!(connectivity_benchmark, connectivity_bench); diff --git a/src/architecture.rs b/src/architecture.rs index ba39e6b4..e046ffb4 100644 --- a/src/architecture.rs +++ b/src/architecture.rs @@ -1,14 +1,22 @@ -pub mod complete; pub mod connectivity; -pub mod line; type GraphIndex = usize; type EdgeWeight = usize; type NodeWeight = (); +#[derive(Debug, PartialEq)] +pub enum LadderError { + RootNotFound, +} + pub trait Architecture { fn best_path(&self, i: GraphIndex, j: GraphIndex) -> Vec; fn distance(&self, i: GraphIndex, j: GraphIndex) -> GraphIndex; fn neighbors(&self, i: GraphIndex) -> Vec; fn non_cutting(&mut self) -> &Vec; + fn get_cx_ladder( + &self, + nodes: &[GraphIndex], + root: &GraphIndex, + ) -> Result, LadderError>; } diff --git a/src/architecture/complete.rs b/src/architecture/complete.rs deleted file mode 100644 index 6319d4bc..00000000 --- a/src/architecture/complete.rs +++ /dev/null @@ -1,155 +0,0 @@ -use super::{Architecture, GraphIndex}; - -#[derive(Debug)] -pub struct Complete { - nodes: Vec, -} - -impl Complete { - pub fn new(num_qubits: usize) -> Self { - Complete { - nodes: (0..num_qubits).collect(), - } - } - - pub fn remove(&mut self, i: usize) { - assert!( - self.nodes.contains(&i), - "architecture does not contain node {i}" - ); - self.nodes.retain(|x| *x != i); - } - - pub fn insert(&mut self, i: usize) { - assert!( - !self.nodes.contains(&i), - "architecture already contains node {i}" - ); - self.nodes.push(i); - } -} - -impl Architecture for Complete { - fn best_path(&self, i: GraphIndex, j: GraphIndex) -> Vec { - assert!( - self.nodes.contains(&i), - "architecture does not contain node {i}" - ); - assert!( - self.nodes.contains(&j), - "architecture does not contain node {j}" - ); - vec![i, j] - } - - fn distance(&self, i: GraphIndex, j: GraphIndex) -> GraphIndex { - assert!( - self.nodes.contains(&i), - "architecture does not contain node {i}" - ); - assert!( - self.nodes.contains(&j), - "architecture does not contain node {j}" - ); - 1 - } - - fn neighbors(&self, i: GraphIndex) -> Vec { - assert!( - self.nodes.contains(&i), - "architecture does not contain node {i}" - ); - self.nodes.iter().filter(|x| **x != i).copied().collect() - } - - fn non_cutting(&mut self) -> &Vec { - &self.nodes - } -} - -#[cfg(test)] -mod tests { - use crate::architecture::Architecture; - - use super::Complete; - - #[test] - fn test_complete() { - let new_architecture = Complete::new(5); - - assert_eq!(new_architecture.nodes, vec![0, 1, 2, 3, 4]); - } - - #[test] - fn test_insert() { - let mut new_architecture = Complete::new(5); - new_architecture.insert(5); - assert_eq!(new_architecture.nodes, vec![0, 1, 2, 3, 4, 5]); - } - - #[test] - #[should_panic = "architecture already contains node 1"] - fn test_bad_insert() { - let mut new_architecture = Complete::new(5); - new_architecture.insert(1); - } - - #[test] - fn test_remove() { - let mut new_architecture = Complete::new(5); - new_architecture.remove(3); - assert_eq!(new_architecture.nodes, vec![0, 1, 2, 4]); - } - - #[test] - #[should_panic = "architecture does not contain node 4"] - fn test_bad_remove() { - let mut new_architecture = Complete::new(3); - new_architecture.remove(4); - } - - #[test] - fn test_best_path() { - let new_architecture = Complete::new(3); - assert_eq!(vec![1, 2], new_architecture.best_path(1, 2)); - } - - #[test] - #[should_panic = "architecture does not contain node 4"] - fn test_best_path_missing() { - let new_architecture = Complete::new(3); - new_architecture.best_path(4, 5); - } - - #[test] - fn test_distance() { - let new_architecture = Complete::new(3); - assert_eq!(1, new_architecture.distance(1, 2)); - } - - #[test] - #[should_panic = "architecture does not contain node 4"] - fn test_distance_missing() { - let new_architecture = Complete::new(3); - new_architecture.distance(4, 5); - } - - #[test] - fn test_neighbors() { - let new_architecture = Complete::new(4); - assert_eq!(vec![0, 1, 3], new_architecture.neighbors(2)); - } - - #[test] - #[should_panic = "architecture does not contain node 3"] - fn test_neighbor_missing() { - let new_architecture = Complete::new(3); - new_architecture.distance(2, 3); - } - - #[test] - fn test_non_cutting() { - let mut new_architecture = Complete::new(4); - assert_eq!(&vec![0, 1, 2, 3], new_architecture.non_cutting()); - } -} diff --git a/src/architecture/connectivity.rs b/src/architecture/connectivity.rs index eccec96d..a7fa1a52 100644 --- a/src/architecture/connectivity.rs +++ b/src/architecture/connectivity.rs @@ -1,18 +1,33 @@ -use super::{Architecture, EdgeWeight, GraphIndex, NodeWeight}; +use super::{Architecture, EdgeWeight, GraphIndex, LadderError, NodeWeight}; use petgraph::algo::floyd_warshall::floyd_warshall_path; +use petgraph::algo::steiner_tree::steiner_tree; +use petgraph::prelude::EdgeRef; +use petgraph::visit::{Bfs, Dfs, GraphBase, IntoNeighbors, VisitMap, Visitable, Walker}; use petgraph::{ algo::articulation_points::articulation_points, graph::{NodeIndex, UnGraph}, visit::{IntoNodeReferences, NodeIndexable, NodeRef}, }; +use std::borrow::Borrow; use std::collections::HashMap; +use std::ops::Index; + +/// Get all the vertices in a graph that are non-cutting (won't make the graph disconnected) +fn get_non_cutting_vertices( + graph: &UnGraph, +) -> Vec { + let art_points = articulation_points(&graph); + (0..graph.node_count()) + .filter(|node| !art_points.contains(&graph.from_index(*node))) + .collect() +} #[derive(Debug)] pub struct Connectivity { graph: UnGraph, non_cutting: Vec, prev: Vec>>, - distance: HashMap<(NodeIndex, NodeIndex), usize>, + distance: HashMap<(NodeIndex, NodeIndex), EdgeWeight>, } impl Connectivity { @@ -25,34 +40,51 @@ impl Connectivity { } } - pub fn from_edges(edges: &[(GraphIndex, GraphIndex)]) -> Self { - let graph = UnGraph::from_edges(edges); - let art_points = articulation_points(&graph); - - let non_cutting = (0..graph.node_count()) - .filter(|node| art_points.contains(&graph.from_index(*node))) - .collect(); + pub fn line(nr_qubits: usize) -> Self { + let edges: Vec<(usize, usize)> = (0..nr_qubits - 1).map(|i| (i, i + 1)).collect(); + Connectivity::from_edges(&edges) + } - let (distance, prev) = floyd_warshall_path(&graph, |e| *e.weight()).unwrap(); + pub fn grid(num_rows: usize, num_cols: usize) -> Self { + let mut edges = Vec::new(); + + for r in 0..num_rows { + for c in 0..num_cols { + if r < num_rows - 1 { + edges.push((num_cols * r + c, num_cols * (r + 1) + c)); + } + if c < num_cols - 1 { + edges.push((num_cols * r + c, num_cols * r + (c + 1))); + } + } + } + Connectivity::from_edges(&edges) + } - Connectivity { - graph, - non_cutting, - prev, - distance, + pub fn complete(num_qubits: usize) -> Self { + let mut edges = Vec::new(); + for i in 0..num_qubits { + for j in (i + 1)..num_qubits { + edges.push((i, j)); + } } + Connectivity::from_edges(&edges) } - pub fn from_weighted_edges(edges: &[(GraphIndex, GraphIndex, EdgeWeight)]) -> Self { + pub fn from_edges(edges: &[(GraphIndex, GraphIndex)]) -> Self { let graph = UnGraph::from_edges(edges); - let art_points = articulation_points(&graph); + Connectivity::from_graph(graph) + } - let non_cutting = (0..graph.node_count()) - .filter(|node| art_points.contains(&graph.from_index(*node))) - .collect(); + pub fn from_weighted_edges(edges: &[(GraphIndex, GraphIndex, EdgeWeight)]) -> Self { + let graph = UnGraph::from_edges(edges); + Connectivity::from_graph(graph) + } + pub fn from_graph(graph: UnGraph) -> Self { + let non_cutting = get_non_cutting_vertices(&graph); let (distance, prev) = floyd_warshall_path(&graph, |e| *e.weight()).unwrap(); - + let distance = distance.iter().map(|(k, v)| (*k, *v as usize)).collect(); Connectivity { graph, non_cutting, @@ -68,15 +100,27 @@ impl Connectivity { .collect() } - fn update(&mut self) { - let art_points = articulation_points(&self.graph); - - let non_cutting = (0..self.graph.node_count()) - .filter(|node| art_points.contains(&self.graph.from_index(*node))) + pub fn edges(&self) -> Vec<(GraphIndex, GraphIndex)> { + let graph_edges: Vec<(GraphIndex, GraphIndex)> = self + .graph + .edge_references() + .map(|node| { + ( + self.graph.to_index(node.source()), + self.graph.to_index(node.target()), + ) + }) .collect(); + graph_edges + } + + fn update(&mut self) { + let non_cutting = get_non_cutting_vertices(&self.graph); let (distance, prev) = floyd_warshall_path(&self.graph, |e| *e.weight()).unwrap(); - println!("prev: {:?}", prev); + + let distance = distance.iter().map(|(k, v)| (*k, *v as usize)).collect(); + self.non_cutting = non_cutting; self.distance = distance; self.prev = prev; @@ -144,7 +188,7 @@ impl Architecture for Connectivity { j < self.graph.node_count(), "architecture does not contain node {j}" ); - self.distance[&(self.graph.from_index(i), self.graph.from_index(j))] + self.distance[&(self.graph.from_index(i), self.graph.from_index(j))] as usize } fn neighbors(&self, i: GraphIndex) -> Vec { @@ -161,11 +205,45 @@ impl Architecture for Connectivity { fn non_cutting(&mut self) -> &Vec { &self.non_cutting } + + fn get_cx_ladder( + &self, + nodes: &[GraphIndex], + root: &GraphIndex, + ) -> Result, LadderError> { + let terminals: Vec<_> = self + .graph + .node_indices() + .filter(|node_index| nodes.contains(&node_index.index())) + .collect(); + + let tree = steiner_tree(&self.graph, &terminals); + + let root_node = tree + .node_indices() + .find(|item| item.index() == *root as usize) + .ok_or(LadderError::RootNotFound)?; + + let mut bfs = Bfs::new(&tree, root_node); + let mut edge_list = Vec::new(); + let mut visited = tree.visit_map(); + visited.visit(root_node); + + while let Some(node) = bfs.next(&tree) { + for neighbor in tree.neighbors(node) { + if !visited.is_visited(&neighbor) { + visited.visit(neighbor); + edge_list.push((node.index(), neighbor.index())); + } + } + } + Ok(edge_list) + } } #[cfg(test)] mod tests { - use crate::architecture::{Architecture, EdgeWeight, GraphIndex}; + use crate::architecture::{Architecture, EdgeWeight, GraphIndex, LadderError}; use super::Connectivity; fn setup_weighted() -> Vec<(GraphIndex, GraphIndex, EdgeWeight)> { @@ -197,11 +275,109 @@ mod tests { } #[test] - fn test_simple_constuctor() { + fn test_line_creation() { + let mut line_architecture = Connectivity::line(5); + assert_eq!( + line_architecture.edges(), + vec![(0, 1), (1, 2), (2, 3), (3, 4)] + ); + } + + #[test] + fn test_grid_creation() { + let mut line_architecture = Connectivity::grid(3, 3); + let mut edges = line_architecture.edges(); + edges.sort(); + assert_eq!( + edges, + vec![ + (0, 1), + (0, 3), + (1, 2), + (1, 4), + (2, 5), + (3, 4), + (3, 6), + (4, 5), + (4, 7), + (5, 8), + (6, 7), + (7, 8) + ] + ); + } + + #[test] + fn test_weight_is_considered() { + let new_architecture = Connectivity::from_weighted_edges(&setup_weighted()); + assert_eq!( + new_architecture + .get_cx_ladder(&vec![1, 2, 3, 4], &2) + .unwrap() + .len(), + 3 + ); + } + + #[test] + fn test_root_is_not_present() { + let new_architecture = Connectivity::from_edges(&setup_simple()); + assert_eq!( + new_architecture + .get_cx_ladder(&vec![1, 2, 3, 4], &42) + .expect_err("Should return a Error that the root was not found"), + LadderError::RootNotFound + ); + } + + #[test] + fn test_simple_constructor() { let new_architecture = Connectivity::from_edges(&setup_simple()); assert_eq!(new_architecture.nodes(), vec![0, 1, 2, 3, 4, 5]); } + #[test] + fn test_cx_ladder_line_setup() { + let new_architecture = Connectivity::from_edges(&setup_simple()); + assert_eq!( + new_architecture + .get_cx_ladder(&vec![0, 1, 2, 3], &0) + .unwrap(), + vec![(0, 1), (1, 2), (2, 3)] + ); + } + + #[test] + fn test_cx_ladder_extended_triangle() { + let new_architecture = Connectivity::from_edges(&setup_simple()); + assert_eq!( + new_architecture + .get_cx_ladder(&vec![0, 1, 2, 4, 5], &1) + .unwrap() + .len(), + 4 + ); + } + + #[test] + fn test_cx_ladder_small_triangle() { + let new_architecture = Connectivity::from_edges(&setup_simple()); + assert_eq!( + new_architecture + .get_cx_ladder(&vec![2, 3, 4], &2) + .unwrap() + .len(), + 2 + ); + assert_eq!( + new_architecture + .get_cx_ladder(&vec![2, 3, 4], &4) + .unwrap() + .len(), + 2 + ); + } + #[test] fn test_weighted_constructor() { let new_architecture = Connectivity::from_weighted_edges(&setup_weighted()); @@ -260,6 +436,18 @@ mod tests { #[test] fn test_non_cutting() { let mut new_architecture = Connectivity::from_edges(&setup_simple()); - assert_eq!(&Vec::::new(), new_architecture.non_cutting()); + assert_eq!(&new_architecture.nodes(), new_architecture.non_cutting()); + } + + #[test] + fn test_non_cutting_line() { + let mut line_architecture = Connectivity::line(5); + assert_eq!(*line_architecture.non_cutting(), vec![0, 4]); + } + + #[test] + fn test_non_cutting_complete() { + let mut line_architecture = Connectivity::complete(5); + assert_eq!(*line_architecture.non_cutting(), vec![0, 1, 2, 3, 4]); } } diff --git a/src/architecture/line.rs b/src/architecture/line.rs deleted file mode 100644 index 00d90146..00000000 --- a/src/architecture/line.rs +++ /dev/null @@ -1,241 +0,0 @@ -use super::{Architecture, GraphIndex}; - -/// Describes a line architecture. This ensures that consecutive numbers are adjacent. Allows for disjoint breaking of architecture in case this is required. -#[derive(Debug)] -pub struct Line { - nodes: Vec, - non_cutting: Vec, - updated: bool, -} - -impl Line { - pub fn new(num_qubits: usize) -> Self { - Line { - nodes: (0..num_qubits).collect(), - non_cutting: Vec::new(), - updated: false, - } - } - - pub fn remove(&mut self, i: GraphIndex) { - assert!( - self.nodes.contains(&i), - "architecture does not contain node {i}" - ); - self.updated = false; - self.nodes.retain(|x| *x != i); - } - - /// Ensures that strict ordering is always enforced - pub fn insert(&mut self, i: GraphIndex) { - match self.nodes.binary_search(&i) { - Ok(_) => panic!("architecture already contains node {i}"), - Err(pos) => self.nodes.insert(pos, i), - } - self.updated = false; - } -} - -impl Architecture for Line { - fn best_path(&self, i: GraphIndex, j: GraphIndex) -> Vec { - let (smaller, larger) = (i.min(j), i.max(j)); - assert!( - self.nodes.contains(&j), - "architecture does not contain node {i}" - ); - assert!( - self.nodes.contains(&j), - "architecture does not contain node {j}" - ); - assert!( - ((smaller + 1)..larger).all(|node| self.nodes.contains(&node)), - "no path exists between {i} and {j}" - ); - if i == smaller { - (smaller..=larger).collect() - } else { - (smaller..=larger).rev().collect() - } - } - - fn distance(&self, i: GraphIndex, j: GraphIndex) -> GraphIndex { - let (smaller, larger) = (i.min(j), i.max(j)); - assert!( - self.nodes.contains(&i), - "architecture does not contain node {i}" - ); - assert!( - self.nodes.contains(&j), - "architecture does not contain node {j}" - ); - assert!( - ((smaller + 1)..larger).all(|node| self.nodes.contains(&node)), - "no path exists between {i} and {j}" - ); - larger - smaller - } - - fn neighbors(&self, i: GraphIndex) -> Vec { - assert!( - self.nodes.contains(&i), - "architecture does not contain node {i}" - ); - let mut neighbors = Vec::new(); - if let Some(left_neighbor) = i.checked_sub(1) { - if self.nodes.contains(&left_neighbor) { - neighbors.push(left_neighbor); - } - } - - if let Some(right_neighbor) = i.checked_add(1) { - if self.nodes.contains(&right_neighbor) { - neighbors.push(right_neighbor); - } - } - - neighbors - } - - fn non_cutting(&mut self) -> &Vec { - if !self.updated { - let mut non_cutting = Vec::new(); - non_cutting.push(self.nodes[0]); - for nodes in self.nodes.windows(3) { - if let &[node1, node2, node3] = nodes { - // Since strict ordering is enforced during insertion and creation, adding 1 to smaller node should not cause overflow. - if node1 + 1 != node2 || node2 + 1 != node3 { - non_cutting.push(node2); - } - } - } - - if self.nodes.len() > 1 { - non_cutting.push(*self.nodes.last().unwrap()); - } - self.non_cutting = non_cutting; - self.updated = true; - } - &self.non_cutting - } -} - -#[cfg(test)] -mod tests { - use crate::architecture::Architecture; - - use super::Line; - - #[test] - fn test_complete() { - let new_architecture = Line::new(5); - - assert_eq!(new_architecture.nodes, vec![0, 1, 2, 3, 4]); - } - - #[test] - fn test_insert() { - let mut new_architecture = Line::new(5); - new_architecture.insert(5); - assert_eq!(new_architecture.nodes, vec![0, 1, 2, 3, 4, 5]); - } - - #[test] - #[should_panic = "architecture already contains node 1"] - fn test_bad_insert() { - let mut new_architecture = Line::new(5); - new_architecture.insert(1); - } - - #[test] - fn test_remove() { - let mut new_architecture = Line::new(5); - new_architecture.remove(3); - assert_eq!(new_architecture.nodes, vec![0, 1, 2, 4]); - } - - #[test] - fn test_remove_remove_insert() { - let mut new_architecture = Line::new(7); - new_architecture.remove(3); - new_architecture.remove(4); - new_architecture.remove(5); - new_architecture.insert(4); - assert_eq!(new_architecture.nodes, vec![0, 1, 2, 4, 6]); - } - - #[test] - #[should_panic = "architecture does not contain node 4"] - fn test_bad_remove() { - let mut new_architecture = Line::new(3); - new_architecture.remove(4); - } - - #[test] - fn test_best_path() { - let new_architecture = Line::new(6); - assert_eq!(vec![2, 3, 4], new_architecture.best_path(2, 4)); - } - - #[test] - #[should_panic = "architecture does not contain node 4"] - fn test_best_path_missing() { - let new_architecture = Line::new(3); - new_architecture.best_path(4, 5); - } - - #[test] - fn test_distance() { - let new_architecture = Line::new(6); - assert_eq!(2, new_architecture.distance(2, 4)); - } - - #[test] - #[should_panic = "architecture does not contain node 4"] - fn test_distance_missing() { - let new_architecture = Line::new(3); - new_architecture.distance(4, 5); - } - - #[test] - fn test_neighbors() { - let new_architecture = Line::new(4); - assert_eq!(vec![1, 3], new_architecture.neighbors(2)); - } - - #[test] - fn test_neighbors_0() { - let new_architecture = Line::new(4); - assert_eq!(vec![1], new_architecture.neighbors(0)); - } - - #[test] - #[should_panic = "architecture does not contain node 3"] - fn test_neighbor_missing() { - let new_architecture = Line::new(3); - new_architecture.distance(2, 3); - } - - #[test] - fn test_non_cutting() { - let mut new_architecture = Line::new(5); - assert_eq!(&vec![0, 4], new_architecture.non_cutting()); - } - - #[test] - fn test_non_cutting_complex() { - let mut new_architecture = Line::new(7); - new_architecture.remove(1); - new_architecture.remove(5); - assert_eq!(&vec![0, 2, 4, 6], new_architecture.non_cutting()); - } - - #[test] - fn test_non_cutting_cache() { - let mut new_architecture = Line::new(7); - assert_eq!(&vec![0, 6], new_architecture.non_cutting()); - new_architecture.remove(1); - assert_eq!(&vec![0, 2, 6], new_architecture.non_cutting()); - new_architecture.remove(5); - assert_eq!(&vec![0, 2, 4, 6], new_architecture.non_cutting()); - } -}