pub mod adjacency;
use std::collections::HashMap;
use bit_set::BitSet;
pub type NodeId = usize;
pub type Weight = f64;
pub trait Graph {
fn num_nodes(&self) -> usize;
fn num_edges(&self) -> usize;
fn neighbors(&self, node: NodeId) -> Vec<(NodeId, Weight)>;
fn degree(&self, node: NodeId) -> Weight;
fn has_edge(&self, from: NodeId, to: NodeId) -> bool;
fn edge_weight(&self, from: NodeId, to: NodeId) -> Option<Weight>;
}
#[derive(Debug, Clone)]
pub struct CompressedSparseRow {
pub nrows: usize,
pub ncols: usize,
pub row_ptr: Vec<usize>,
pub col_indices: Vec<usize>,
pub values: Vec<f64>,
}
impl CompressedSparseRow {
pub fn new(nrows: usize, ncols: usize) -> Self {
Self {
nrows,
ncols,
row_ptr: vec![0; nrows + 1],
col_indices: Vec::new(),
values: Vec::new(),
}
}
pub fn nnz(&self) -> usize {
self.values.len()
}
pub fn row(&self, row: usize) -> impl Iterator<Item = (usize, f64)> + '_ {
let start = self.row_ptr[row];
let end = self.row_ptr[row + 1];
(start..end).map(move |idx| {
(self.col_indices[idx], self.values[idx])
})
}
pub fn row_sums(&self) -> Vec<f64> {
let mut sums = vec![0.0; self.nrows];
for row in 0..self.nrows {
for (_, value) in self.row(row) {
sums[row] += value;
}
}
sums
}
pub fn transpose(&self) -> Self {
let mut col_counts = vec![0; self.ncols];
for &col in &self.col_indices {
col_counts[col] += 1;
}
let mut new_row_ptr = vec![0; self.ncols + 1];
for i in 0..self.ncols {
new_row_ptr[i + 1] = new_row_ptr[i] + col_counts[i];
}
let mut new_col_indices = vec![0; self.nnz()];
let mut new_values = vec![0.0; self.nnz()];
let mut col_positions = new_row_ptr.clone();
for row in 0..self.nrows {
for (col, value) in self.row(row) {
let pos = col_positions[col];
new_col_indices[pos] = row;
new_values[pos] = value;
col_positions[col] += 1;
}
}
Self {
nrows: self.ncols,
ncols: self.nrows,
row_ptr: new_row_ptr,
col_indices: new_col_indices,
values: new_values,
}
}
}
#[derive(Debug)]
pub struct WorkQueue {
heap: std::collections::BinaryHeap<WorkItem>,
in_queue: BitSet,
threshold: f64,
}
#[derive(Debug, PartialEq, PartialOrd)]
struct WorkItem {
priority: OrderedFloat,
node_id: usize,
}
#[derive(Debug, PartialEq, PartialOrd)]
struct OrderedFloat(f64);
impl Eq for OrderedFloat {}
impl Ord for OrderedFloat {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.partial_cmp(other).unwrap_or(std::cmp::Ordering::Equal)
}
}
impl WorkQueue {
pub fn new(threshold: f64) -> Self {
Self {
heap: std::collections::BinaryHeap::new(),
in_queue: BitSet::new(),
threshold,
}
}
pub fn push_if_threshold(&mut self, node: usize, residual: f64, degree: f64) {
let priority = if degree > 0.0 { residual / degree } else { residual };
if priority >= self.threshold && !self.in_queue.contains(node) {
self.heap.push(WorkItem {
priority: OrderedFloat(priority),
node_id: node,
});
self.in_queue.insert(node);
}
}
pub fn pop(&mut self) -> Option<(usize, f64)> {
if let Some(item) = self.heap.pop() {
self.in_queue.remove(item.node_id);
Some((item.node_id, item.priority.0))
} else {
None
}
}
pub fn is_empty(&self) -> bool {
self.heap.is_empty()
}
pub fn len(&self) -> usize {
self.heap.len()
}
pub fn adaptive_threshold(&mut self, max_queue_size: usize, min_queue_size: usize) {
let current_size = self.len();
if current_size > max_queue_size {
self.threshold *= 1.1; } else if current_size < min_queue_size && self.threshold > 1e-12 {
self.threshold *= 0.9; }
}
}
#[derive(Debug)]
pub struct VisitedTracker {
visited: BitSet,
visit_order: Vec<usize>,
timestamps: Vec<u32>,
current_time: u32,
}
impl VisitedTracker {
pub fn new(n: usize) -> Self {
Self {
visited: BitSet::new(),
visit_order: Vec::new(),
timestamps: vec![0; n],
current_time: 1,
}
}
pub fn mark_visited(&mut self, node: usize) -> bool {
if !self.visited.contains(node) {
self.visited.insert(node);
self.visit_order.push(node);
if node < self.timestamps.len() {
self.timestamps[node] = self.current_time;
}
true
} else {
false
}
}
pub fn is_visited(&self, node: usize) -> bool {
self.visited.contains(node)
}
pub fn visited_nodes(&self) -> &[usize] {
&self.visit_order
}
pub fn reset_for_new_query(&mut self) {
self.current_time += 1;
self.visit_order.clear();
if self.current_time == u32::MAX {
self.full_reset();
}
}
fn full_reset(&mut self) {
self.visited.clear();
self.visit_order.clear();
self.timestamps.fill(0);
self.current_time = 1;
}
pub fn num_visited(&self) -> usize {
self.visit_order.len()
}
}
pub use adjacency::AdjacencyList;