use crate::{Edge, Error, ErrorKind, Graph};
use nohash::IntSet;
use std::collections::HashSet;
use std::fmt::Debug;
use std::fmt::Display;
use std::hash::Hash;
use std::sync::Arc;
pub fn edge_boundary<'a, T, A>(
graph: &'a Graph<T, A>,
nbunch1: &[T],
nbunch2: Option<&[T]>,
) -> Result<Vec<&'a Arc<Edge<T, A>>>, Error>
where
T: Hash + Eq + Clone + Ord + Debug + Display + Send + Sync,
A: Clone + Send + Sync,
{
if !graph.has_nodes(nbunch1) {
return Err(Error {
kind: ErrorKind::NodeNotFound,
message: "One or more of `nbunch1` were not found in the graph.".to_string(),
});
}
if nbunch2.is_some() && !graph.has_nodes(nbunch2.unwrap()) {
return Err(Error {
kind: ErrorKind::NodeNotFound,
message: "One or more of `nbunch2` were not found in the graph.".to_string(),
});
}
let out_edges = match graph.specs.directed {
true => graph.get_out_edges_for_nodes(nbunch1),
false => graph.get_edges_for_nodes(nbunch1),
}
.unwrap();
let nset1 = nbunch1.iter().cloned().collect::<HashSet<T>>();
let nset2 = match nbunch2 {
Some(nbunch2) => nbunch2.iter().cloned().collect::<HashSet<T>>(),
None => graph
.get_all_node_names()
.into_iter()
.filter(|n| !nset1.contains(n))
.cloned()
.collect::<HashSet<T>>(),
};
return Ok(out_edges
.into_iter()
.filter(|e| {
(nset1.contains(&e.u) && nset2.contains(&e.v))
|| (nset2.contains(&e.u) && nset1.contains(&e.v))
})
.collect());
}
pub(crate) fn edge_boundary_by_indexes<'a, T, A>(
graph: &'a Graph<T, A>,
nbunch1: &[usize],
nbunch2: &[usize],
) -> Vec<(usize, usize, f64)>
where
T: Hash + Eq + Clone + Ord + Debug + Display + Send + Sync,
A: Clone + Send + Sync,
{
let out_edges = graph.get_out_edges_for_node_indexes(nbunch1);
let nset1 = nbunch1.iter().cloned().collect::<IntSet<usize>>();
let nset2 = nbunch2.iter().cloned().collect::<IntSet<usize>>();
out_edges
.into_iter()
.filter(|(u, v, _weight)| {
(nset1.contains(&u) && nset2.contains(&v)) || (nset2.contains(&u) && nset1.contains(&v))
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{generators, GraphSpecs};
use assert_unordered::assert_eq_unordered;
#[test]
fn test_edge_boundary_1() {
let edges = vec![
Edge::new("n1", "n2"),
Edge::new("n1", "n3"),
Edge::new("n2", "n1"),
Edge::new("n2", "n3"),
];
let specs = GraphSpecs::directed_create_missing();
let graph: Graph<&str, ()> = Graph::new_from_nodes_and_edges(vec![], edges, specs).unwrap();
let result = edge_boundary(&graph, &["n1"], None).unwrap();
assert_eq!(result.len(), 2);
let essence = result.iter().map(|e| (e.u, e.v)).collect::<Vec<_>>();
assert_eq_unordered!(essence, vec![("n1", "n2"), ("n1", "n3")]);
}
#[test]
fn test_edge_boundary_2() {
let edges = vec![
Edge::new("n1", "n2"),
Edge::new("n1", "n3"),
Edge::new("n2", "n1"),
Edge::new("n2", "n3"),
];
let specs = GraphSpecs::directed_create_missing();
let graph: Graph<&str, ()> = Graph::new_from_nodes_and_edges(vec![], edges, specs).unwrap();
let result = edge_boundary(&graph, &["n1"], Some(&["n2", "n3"])).unwrap();
let essence = result.iter().map(|e| (e.u, e.v)).collect::<Vec<_>>();
assert_eq_unordered!(essence, vec![("n1", "n2"), ("n1", "n3")]);
}
#[test]
fn test_edge_boundary_3() {
let edges = vec![
Edge::new("n1", "n3"),
Edge::new("n2", "n1"),
Edge::new("n2", "n3"),
];
let specs = GraphSpecs::directed_create_missing();
let graph: Graph<&str, ()> = Graph::new_from_nodes_and_edges(vec![], edges, specs).unwrap();
let result = edge_boundary(&graph, &["n1"], Some(&["n2", "n3"])).unwrap();
let essence = result.iter().map(|e| (e.u, e.v)).collect::<Vec<_>>();
assert_eq_unordered!(essence, vec![("n1", "n3")]);
}
#[test]
fn test_edge_boundary_4() {
let edges = vec![
Edge::new("n1", "n3"),
Edge::new("n2", "n1"),
Edge::new("n2", "n3"),
];
let specs = GraphSpecs::undirected_create_missing();
let graph: Graph<&str, ()> = Graph::new_from_nodes_and_edges(vec![], edges, specs).unwrap();
let result = edge_boundary(&graph, &["n1"], Some(&["n2", "n3"])).unwrap();
let essence = result.iter().map(|e| (e.u, e.v)).collect::<Vec<_>>();
assert_eq_unordered!(essence, vec![("n1", "n2"), ("n1", "n3")]);
}
#[test]
fn test_edge_boundary_5() {
let graph = generators::social::karate_club_graph();
let result = edge_boundary(&graph, &[0, 1, 2, 3], Some(&[4, 5, 6, 7])).unwrap();
let essence = result.iter().map(|e| (e.u, e.v)).collect::<Vec<_>>();
assert_eq_unordered!(
essence,
vec![(0, 4), (0, 5), (0, 6), (0, 7), (1, 7), (2, 7), (3, 7)]
);
}
}