Graph Topology

This module provides the Graph trait and various graph implementations for defining computational problems on different graph structures.

Design Philosophy

Following Julia's Graphs.jl pattern, problems are generic over graph type. This enables topology-aware reductions:

#![allow(unused)]
fn main() {
use problemreductions::topology::{Graph, SimpleGraph, UnitDiskGraph};
use problemreductions::models::graph::IndependentSetT;

// Standard problem on SimpleGraph (default)
let is_simple: IndependentSetT = IndependentSetT::new(4, vec![(0, 1), (1, 2)]);

// Same problem on UnitDiskGraph (for quantum hardware)
let udg = UnitDiskGraph::new(positions, radius);
let is_udg: IndependentSetT<UnitDiskGraph> = IndependentSetT::from_graph(udg);
}

Different reductions can target different topologies:

#![allow(unused)]
fn main() {
// Standard reduction -> arbitrary graph
impl ReduceTo<IndependentSetT<SimpleGraph>> for Satisfiability { ... }

// Topology-aware reduction -> unit disk graph (using geometric gadgets)
impl ReduceTo<IndependentSetT<UnitDiskGraph>> for Satisfiability { ... }
}

The Graph Trait

All graph types implement the Graph trait:

#![allow(unused)]
fn main() {
pub trait Graph: Clone + Send + Sync {
    fn num_vertices(&self) -> usize;
    fn num_edges(&self) -> usize;
    fn edges(&self) -> Vec<(usize, usize)>;
    fn has_edge(&self, u: usize, v: usize) -> bool;
    fn neighbors(&self, v: usize) -> Vec<usize>;
    fn degree(&self, v: usize) -> usize;  // default impl
    fn is_empty(&self) -> bool;            // default impl
}
}

SimpleGraph

The default graph type for most problems. Wraps petgraph's UnGraph with convenient constructors:

#![allow(unused)]
fn main() {
use problemreductions::topology::SimpleGraph;

// Create from vertices and edges
let g = SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)]);

// Factory methods
let complete = SimpleGraph::complete(4);    // K4
let path = SimpleGraph::path(5);            // 0-1-2-3-4
let cycle = SimpleGraph::cycle(4);          // 0-1-2-3-0
let star = SimpleGraph::star(5);            // 0 connected to 1,2,3,4
let grid = SimpleGraph::grid(3, 4);         // 3x4 grid graph
}

UnitDiskGraph

Geometric graphs where vertices have 2D positions and edges connect vertices within a distance threshold. Useful for:

  • Quantum computing (neutral atom arrays)
  • Wireless network problems
  • Geometric constraint satisfaction
#![allow(unused)]
fn main() {
use problemreductions::topology::UnitDiskGraph;

let positions = vec![
    (0.0, 0.0),
    (1.0, 0.0),   // Distance 1.0 from (0,0)
    (2.5, 0.0),   // Distance 2.5 from (0,0)
];

let udg = UnitDiskGraph::new(positions, 1.5);
// Edges: 0-1 (distance 1.0 <= 1.5)
// No edge 0-2 (distance 2.5 > 1.5)
// No edge 1-2 (distance 1.5 <= 1.5, so actually connected!)

// Grid factory
let grid_udg = UnitDiskGraph::grid(3, 4, 1.0, 1.5);
}

Using with Problems

#![allow(unused)]
fn main() {
use problemreductions::topology::UnitDiskGraph;
use problemreductions::models::graph::IndependentSetT;

// Create UDG representing quantum hardware connectivity
let udg = UnitDiskGraph::new(atom_positions, interaction_radius);

// Define Independent Set problem on this topology
let problem: IndependentSetT<UnitDiskGraph> = IndependentSetT::from_graph(udg);

// Solve for maximum independent set
let solutions = solver.find_best(&problem);
}

HyperGraph

Generalized graphs where edges (hyperedges) can connect any number of vertices. Note: HyperGraph does NOT implement the Graph trait since hyperedges are fundamentally different from 2-vertex edges.

#![allow(unused)]
fn main() {
use problemreductions::topology::HyperGraph;

let hg = HyperGraph::new(5, vec![
    vec![0, 1, 2],  // Hyperedge connecting 3 vertices
    vec![2, 3, 4],  // Another hyperedge
]);

assert_eq!(hg.num_vertices(), 5);
assert_eq!(hg.num_edges(), 2);

// Get neighbors (vertices sharing a hyperedge)
let neighbors = hg.neighbors(2);  // [0, 1, 3, 4]

// Check if it's actually a regular graph
if hg.is_regular_graph() {
    let edges = hg.to_graph_edges().unwrap();
}
}

API Reference

Graph Trait

#![allow(unused)]
fn main() {
pub trait Graph: Clone + Send + Sync {
    fn num_vertices(&self) -> usize;
    fn num_edges(&self) -> usize;
    fn edges(&self) -> Vec<(usize, usize)>;
    fn has_edge(&self, u: usize, v: usize) -> bool;
    fn neighbors(&self, v: usize) -> Vec<usize>;
    fn degree(&self, v: usize) -> usize;
    fn is_empty(&self) -> bool;
    fn for_each_edge<F>(&self, f: F) where F: FnMut(usize, usize);
}
}

SimpleGraph

#![allow(unused)]
fn main() {
impl SimpleGraph {
    pub fn new(num_vertices: usize, edges: Vec<(usize, usize)>) -> Self;
    pub fn empty(num_vertices: usize) -> Self;
    pub fn complete(num_vertices: usize) -> Self;
    pub fn path(num_vertices: usize) -> Self;
    pub fn cycle(num_vertices: usize) -> Self;
    pub fn star(num_vertices: usize) -> Self;
    pub fn grid(rows: usize, cols: usize) -> Self;
}
}

UnitDiskGraph

#![allow(unused)]
fn main() {
impl UnitDiskGraph {
    pub fn new(positions: Vec<(f64, f64)>, radius: f64) -> Self;
    pub fn unit(positions: Vec<(f64, f64)>) -> Self;  // radius = 1.0
    pub fn grid(rows: usize, cols: usize, spacing: f64, radius: f64) -> Self;
    pub fn radius(&self) -> f64;
    pub fn position(&self, v: usize) -> Option<(f64, f64)>;
    pub fn positions(&self) -> &[(f64, f64)];
    pub fn vertex_distance(&self, u: usize, v: usize) -> Option<f64>;
    pub fn bounding_box(&self) -> Option<((f64, f64), (f64, f64))>;
}
}

HyperGraph

#![allow(unused)]
fn main() {
impl HyperGraph {
    pub fn new(num_vertices: usize, edges: Vec<Vec<usize>>) -> Self;
    pub fn empty(num_vertices: usize) -> Self;
    pub fn num_vertices(&self) -> usize;
    pub fn num_edges(&self) -> usize;
    pub fn edges(&self) -> &[Vec<usize>];
    pub fn edge(&self, index: usize) -> Option<&Vec<usize>>;
    pub fn has_edge(&self, edge: &[usize]) -> bool;
    pub fn neighbors(&self, v: usize) -> Vec<usize>;
    pub fn degree(&self, v: usize) -> usize;
    pub fn edges_containing(&self, v: usize) -> Vec<&Vec<usize>>;
    pub fn max_edge_size(&self) -> usize;
    pub fn is_regular_graph(&self) -> bool;
    pub fn to_graph_edges(&self) -> Option<Vec<(usize, usize)>>;
}
}