SNAPKITTYWEST's picture
chore: push full sov-kernel-monster content from local build
9425aed verified
Raw
History Blame Contribute Delete
9.71 kB
//! # Quantum Topology Module
//!
//! Coupling graph representation and native gate support queries.
//! Re-exports from backend_contract but provides specialized operations
//! for topology analysis.
use crate::backend_contract::{CouplingGraph, NativeGate, BackendError, Result};
/// Extended topology analysis
pub struct TopologyAnalyzer;
impl TopologyAnalyzer {
/// Check if graph is connected
pub fn is_connected(graph: &CouplingGraph) -> Result<bool> {
let n = graph.num_qubits();
if n == 0 {
return Err(BackendError::EmptyBackend);
}
let mut visited = vec![false; n];
let mut queue = std::collections::VecDeque::new();
queue.push_back(0);
visited[0] = true;
while let Some(node) = queue.pop_front() {
for neighbor in graph.neighbors(node)? {
if !visited[neighbor] {
visited[neighbor] = true;
queue.push_back(neighbor);
}
}
}
Ok(visited.iter().all(|&v| v))
}
/// Find connected components
pub fn connected_components(graph: &CouplingGraph) -> Result<Vec<Vec<usize>>> {
let n = graph.num_qubits();
let mut visited = vec![false; n];
let mut components = Vec::new();
for start in 0..n {
if !visited[start] {
let mut component = Vec::new();
let mut queue = std::collections::VecDeque::new();
queue.push_back(start);
visited[start] = true;
while let Some(node) = queue.pop_front() {
component.push(node);
for neighbor in graph.neighbors(node)? {
if !visited[neighbor] {
visited[neighbor] = true;
queue.push_back(neighbor);
}
}
}
components.push(component);
}
}
Ok(components)
}
/// Compute diameter (max distance between any two qubits)
pub fn diameter(graph: &CouplingGraph) -> Result<usize> {
let n = graph.num_qubits();
if n == 0 {
return Err(BackendError::EmptyBackend);
}
let mut max_dist = 0;
for i in 0..n {
for j in i + 1..n {
let dist = graph.distance(i, j)?;
if dist == usize::MAX {
return Ok(usize::MAX); // Not connected
}
max_dist = max_dist.max(dist);
}
}
Ok(max_dist)
}
/// Compute average degree
pub fn average_degree(graph: &CouplingGraph) -> Result<f64> {
let n = graph.num_qubits();
if n == 0 {
return Err(BackendError::EmptyBackend);
}
let mut total_degree = 0usize;
for i in 0..n {
let neighbors = graph.neighbors(i)?;
total_degree += neighbors.len();
}
Ok(total_degree as f64 / n as f64)
}
/// Check if topology is a line (path graph)
pub fn is_linear(graph: &CouplingGraph) -> Result<bool> {
let n = graph.num_qubits();
if n < 2 {
return Ok(n == 1 || n == 0);
}
for i in 0..n {
let neighbors = graph.neighbors(i)?;
let degree = neighbors.len();
// Endpoints should have degree 2 (neighbor + self-loop)
// Middle nodes should have degree 3 (2 neighbors + self-loop)
if i == 0 || i == n - 1 {
if degree != 2 {
return Ok(false);
}
} else {
if degree != 3 {
return Ok(false);
}
}
}
Ok(true)
}
/// Check if topology is fully connected
pub fn is_fully_connected(graph: &CouplingGraph) -> Result<bool> {
let n = graph.num_qubits();
for i in 0..n {
for j in 0..n {
if !graph.are_connected(i, j)? {
return Ok(false);
}
}
}
Ok(true)
}
/// Get all two-qubit connections that support a gate
pub fn get_two_qubit_gate_support(
graph: &CouplingGraph,
gate: &NativeGate,
) -> Result<Vec<(usize, usize)>> {
if !matches!(gate, NativeGate::CX | NativeGate::CZ | NativeGate::SWAP) {
return Err(BackendError::UnsupportedGate(gate.to_string()));
}
let n = graph.num_qubits();
let mut connections = Vec::new();
for i in 0..n {
for j in i + 1..n {
if graph.are_connected(i, j)? {
connections.push((i, j));
}
}
}
Ok(connections)
}
/// Find shortest path between two qubits
pub fn shortest_path(graph: &CouplingGraph, start: usize, end: usize) -> Result<Vec<usize>> {
let n = graph.num_qubits();
if start >= n || end >= n {
return Err(BackendError::InvalidQubit(if start >= n { start } else { end }));
}
if start == end {
return Ok(vec![start]);
}
let mut visited = vec![false; n];
let mut parent = vec![None; n];
let mut queue = std::collections::VecDeque::new();
queue.push_back(start);
visited[start] = true;
while let Some(node) = queue.pop_front() {
if node == end {
// Reconstruct path
let mut path = vec![end];
let mut current = end;
while let Some(Some(p)) = parent.get(current) {
path.push(*p);
current = *p;
}
path.reverse();
return Ok(path);
}
for neighbor in graph.neighbors(node)? {
if !visited[neighbor] {
visited[neighbor] = true;
parent[neighbor] = Some(node);
queue.push_back(neighbor);
}
}
}
Ok(Vec::new()) // Not connected
}
/// Identify bottleneck qubits (critical for connectivity)
pub fn articulation_points(graph: &CouplingGraph) -> Result<Vec<usize>> {
let n = graph.num_qubits();
let mut articulation = Vec::new();
for v in 0..n {
// Try removing vertex v
let mut visited = vec![false; n];
let components = Self::count_connected_components_excluding(graph, v, &mut visited)?;
// If removing v increases component count, v is articulation point
if components > 1 {
articulation.push(v);
}
}
Ok(articulation)
}
fn count_connected_components_excluding(
graph: &CouplingGraph,
excluded: usize,
visited: &mut Vec<bool>,
) -> Result<usize> {
let n = graph.num_qubits();
let mut components = 0;
for start in 0..n {
if start != excluded && !visited[start] {
components += 1;
let mut queue = std::collections::VecDeque::new();
queue.push_back(start);
visited[start] = true;
while let Some(node) = queue.pop_front() {
for neighbor in graph.neighbors(node)? {
if neighbor != excluded && !visited[neighbor] {
visited[neighbor] = true;
queue.push_back(neighbor);
}
}
}
}
}
Ok(components)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn linear_graph(n: usize) -> CouplingGraph {
let mut connectivity = vec![vec![false; n]; n];
for i in 0..n {
connectivity[i][i] = true; // self-loop
if i + 1 < n {
connectivity[i][i + 1] = true;
connectivity[i + 1][i] = true;
}
}
CouplingGraph::new(connectivity).unwrap()
}
fn fully_connected(n: usize) -> CouplingGraph {
let connectivity = vec![vec![true; n]; n];
CouplingGraph::new(connectivity).unwrap()
}
#[test]
fn test_is_connected() {
let graph = linear_graph(5);
assert!(TopologyAnalyzer::is_connected(&graph).unwrap());
}
#[test]
fn test_is_linear() {
let graph = linear_graph(5);
assert!(TopologyAnalyzer::is_linear(&graph).unwrap());
}
#[test]
fn test_is_fully_connected() {
let graph = fully_connected(4);
assert!(TopologyAnalyzer::is_fully_connected(&graph).unwrap());
}
#[test]
fn test_diameter() {
let graph = linear_graph(5);
assert_eq!(TopologyAnalyzer::diameter(&graph).unwrap(), 4);
}
#[test]
fn test_shortest_path() {
let graph = linear_graph(5);
let path = TopologyAnalyzer::shortest_path(&graph, 0, 4).unwrap();
assert_eq!(path, vec![0, 1, 2, 3, 4]);
}
#[test]
fn test_average_degree() {
let graph = linear_graph(5);
let avg = TopologyAnalyzer::average_degree(&graph).unwrap();
// Edges: 0-1, 1-2, 2-3, 3-4 + 5 self-loops = 9 edges / 5 = 1.8
assert!(avg > 1.0 && avg < 3.0);
}
#[test]
fn test_two_qubit_gate_support() {
let graph = linear_graph(3);
let connections = TopologyAnalyzer::get_two_qubit_gate_support(&graph, &NativeGate::CX)
.unwrap();
assert_eq!(connections.len(), 2); // (0,1) and (1,2)
}
}