use rustc_hash::{FxHashMap, FxHashSet};
use crate::Error;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Graph {
pub(crate) num_vertices: u32,
pub(crate) edges: Vec<(u32, u32)>,
}
impl Graph {
pub fn new(num_vertices: u32, edges: impl IntoIterator<Item = (u32, u32)>) -> Self {
Self::try_new(num_vertices, edges).unwrap_or_else(|error| panic!("{error}"))
}
pub fn try_new(
num_vertices: u32,
edges: impl IntoIterator<Item = (u32, u32)>,
) -> Result<Self, Error> {
let mut canonical = Vec::new();
for (left, right) in edges {
if left >= num_vertices || right >= num_vertices {
return Err(Error::InvalidInput(format!(
"graph edge ({left}, {right}) has an endpoint outside 0..{num_vertices}"
)));
}
canonical.push((left, right));
}
Ok(Graph {
num_vertices,
edges: canonical_edges(canonical),
})
}
pub fn num_vertices(&self) -> u32 {
self.num_vertices
}
pub fn edges(&self) -> &[(u32, u32)] {
&self.edges
}
pub fn induced_subgraph(&self, vertices: &[u32]) -> Result<Self, Error> {
if vertices.len() > u32::MAX as usize {
return Err(Error::InvalidInput(format!(
"induced subgraph has {} vertices, which does not fit in u32",
vertices.len()
)));
}
let mut seen = FxHashSet::default();
for &vertex in vertices {
if vertex >= self.num_vertices {
return Err(Error::InvalidInput(format!(
"induced-subgraph vertex {vertex} is outside 0..{}",
self.num_vertices
)));
}
if !seen.insert(vertex) {
return Err(Error::InvalidInput(format!(
"induced-subgraph vertex {vertex} occurs more than once"
)));
}
}
Ok(Graph::new(
vertices.len() as u32,
induced_edges(&self.edges, vertices),
))
}
}
pub(crate) fn canonical_edges(mut edges: Vec<(u32, u32)>) -> Vec<(u32, u32)> {
edges.retain(|&(u, v)| u != v);
for (u, v) in &mut edges {
if *u > *v {
std::mem::swap(u, v);
}
}
edges.sort_unstable();
edges.dedup();
edges
}
pub(crate) fn induced_edges(edges: &[(u32, u32)], subset: &[u32]) -> Vec<(u32, u32)> {
crate::meter::charge(edges.len() as u64);
let local = index_by_vertex(subset);
let mut out = Vec::new();
for &(u, v) in edges {
if let (Some(&lu), Some(&lv)) = (local.get(&u), local.get(&v)) {
out.push((lu, lv));
}
}
canonical_edges(out)
}
pub(crate) fn index_by_vertex(subset: &[u32]) -> FxHashMap<u32, u32> {
subset
.iter()
.enumerate()
.map(|(i, &v)| (v, i as u32))
.collect()
}
#[cfg(test)]
mod tests;