use super::{Edge, EdgeId, EdgeList, NetworkError, Vertex, VertexId};
use crate::algorithm::search::Direction;
use crate::model::network::EdgeListId;
use crate::model::network::GraphConfig;
use crate::util::fs::read_utils;
use indexmap::IndexMap;
use itertools::Itertools;
use kdam::tqdm;
use kdam::Bar;
#[derive(Debug)]
pub struct Graph {
pub vertices: Box<[Vertex]>,
pub edge_lists: Vec<EdgeList>,
pub adj: DenseAdjacencyList,
pub rev: DenseAdjacencyList,
}
pub type DenseAdjacencyList = Box<[IndexMap<(EdgeListId, EdgeId), VertexId>]>;
impl TryFrom<&GraphConfig> for Graph {
type Error = NetworkError;
fn try_from(config: &GraphConfig) -> Result<Self, Self::Error> {
let vertices: Box<[Vertex]> = read_utils::from_csv(
&config.vertex_list_input_file,
true,
Some(Bar::builder().desc(format!("graph vertices: {}", config.vertex_list_input_file))),
None,
)
.map_err(|e| NetworkError::CsvError { source: e })?;
let mut adj: Vec<IndexMap<(EdgeListId, EdgeId), VertexId>> =
vec![IndexMap::new(); vertices.len()];
let mut rev: Vec<IndexMap<(EdgeListId, EdgeId), VertexId>> =
vec![IndexMap::new(); vertices.len()];
let edge_lists = config
.edge_list
.iter()
.enumerate()
.map(|(idx, c)| EdgeList::new(&c.input_file, EdgeListId(idx)))
.collect::<Result<Vec<_>, _>>()?;
let total_edges = edge_lists.iter().map(|el| el.len()).sum::<usize>();
log::info!(
"loaded {} edge lists with a total of {} edges",
edge_lists.len(),
total_edges
);
let build_adjacencies_iter = tqdm!(
edge_lists.iter().flat_map(|el| el.edges()),
desc = "building adjacencies",
total = total_edges
);
let mut bad_refs: Vec<String> = vec![];
for edge in build_adjacencies_iter {
if let Err(e) = append_to_adjacency(edge, &mut adj, true) {
bad_refs.push(e);
}
if let Err(e) = append_to_adjacency(edge, &mut rev, false) {
bad_refs.push(e);
}
}
if !bad_refs.is_empty() {
let msg = format!("[{}]", bad_refs.iter().take(5).join("\n "));
return Err(NetworkError::DatasetError(format!(
"invalid edge lists for vertex set. (up to) first five errors:\n {msg}"
)));
}
let graph = Graph {
edge_lists,
vertices,
adj: adj.into_boxed_slice(),
rev: rev.into_boxed_slice(),
};
Ok(graph)
}
}
impl Graph {
pub fn get_edge_list(&self, edge_list_id: &EdgeListId) -> Result<&EdgeList, NetworkError> {
self.edge_lists
.get(edge_list_id.0)
.ok_or(NetworkError::EdgeListNotFound(*edge_list_id))
}
pub fn n_edge_lists(&self) -> usize {
self.edge_lists.len()
}
pub fn n_edges(&self) -> usize {
self.edge_lists.iter().map(|el| el.len()).sum::<usize>()
}
pub fn n_vertices(&self) -> usize {
self.vertices.len()
}
pub fn edge_ids(
&self,
edge_list_id: &EdgeListId,
) -> Result<Box<dyn Iterator<Item = EdgeId>>, NetworkError> {
self.get_edge_list(edge_list_id).map(|e| {
let iter: Box<dyn Iterator<Item = EdgeId>> = Box::new((0..e.len()).map(EdgeId));
iter
})
}
pub fn vertex_ids(&self) -> Box<dyn Iterator<Item = VertexId>> {
let range = (0..self.n_vertices()).map(VertexId);
Box::new(range)
}
pub fn edges<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Edge> + 'a> {
Box::new(self.edge_lists.iter().flat_map(|el| el.edges()))
}
pub fn get_edge(
&self,
edge_list_id: &EdgeListId,
edge_id: &EdgeId,
) -> Result<&Edge, NetworkError> {
match self.edge_lists.get(edge_list_id.0) {
None => Err(NetworkError::InternalError(format!(
"EdgeListId not found: {edge_list_id}"
))),
Some(edge_list) => match edge_list.get(edge_id) {
None => Err(NetworkError::EdgeNotFound(*edge_id)),
Some(edge) => Ok(edge),
},
}
}
pub fn get_vertex(&self, vertex_id: &VertexId) -> Result<&Vertex, NetworkError> {
match self.vertices.get(vertex_id.0) {
None => Err(NetworkError::VertexNotFound(*vertex_id)),
Some(vertex) => Ok(vertex),
}
}
pub fn out_edges(&self, src: &VertexId) -> Vec<(EdgeListId, EdgeId)> {
self.out_edges_iter(src).cloned().collect_vec()
}
pub fn out_edges_iter<'a>(
&'a self,
src: &'a VertexId,
) -> Box<dyn Iterator<Item = &'a (EdgeListId, EdgeId)> + 'a> {
match self.adj.get(src.0) {
Some(out_map) => Box::new(out_map.keys()),
None => Box::new(std::iter::empty()),
}
}
pub fn in_edges(&self, dst: &VertexId) -> Vec<(EdgeListId, EdgeId)> {
self.in_edges_iter(dst).cloned().collect_vec()
}
pub fn in_edges_iter<'a>(
&'a self,
src: &'a VertexId,
) -> Box<dyn Iterator<Item = &'a (EdgeListId, EdgeId)> + 'a> {
match self.rev.get(src.0) {
Some(in_map) => Box::new(in_map.keys()),
None => Box::new(std::iter::empty()),
}
}
pub fn src_vertex_id(
&self,
edge_list_id: &EdgeListId,
edge_id: &EdgeId,
) -> Result<VertexId, NetworkError> {
self.get_edge(edge_list_id, edge_id)
.map(|e| e.src_vertex_id)
}
pub fn dst_vertex_id(
&self,
edge_list_id: &EdgeListId,
edge_id: &EdgeId,
) -> Result<VertexId, NetworkError> {
self.get_edge(edge_list_id, edge_id)
.map(|e| e.dst_vertex_id)
}
pub fn incident_edges(
&self,
vertex_id: &VertexId,
direction: &Direction,
) -> Vec<(EdgeListId, EdgeId)> {
match direction {
Direction::Forward => self.out_edges(vertex_id),
Direction::Reverse => self.in_edges(vertex_id),
}
}
pub fn incident_edges_iter<'a>(
&'a self,
vertex_id: &'a VertexId,
direction: &Direction,
) -> Box<dyn Iterator<Item = &'a (EdgeListId, EdgeId)> + 'a> {
match direction {
Direction::Forward => self.out_edges_iter(vertex_id),
Direction::Reverse => self.in_edges_iter(vertex_id),
}
}
pub fn incident_vertex(
&self,
edge_list_id: &EdgeListId,
edge_id: &EdgeId,
direction: &Direction,
) -> Result<VertexId, NetworkError> {
match direction {
Direction::Forward => self.dst_vertex_id(edge_list_id, edge_id),
Direction::Reverse => self.src_vertex_id(edge_list_id, edge_id),
}
}
pub fn edge_triplet(
&self,
edge_list_id: &EdgeListId,
edge_id: &EdgeId,
) -> Result<(&Vertex, &Edge, &Vertex), NetworkError> {
let edge = self.get_edge(edge_list_id, edge_id)?;
let src = self.get_vertex(&edge.src_vertex_id)?;
let dst = self.get_vertex(&edge.dst_vertex_id)?;
Ok((src, edge, dst))
}
pub fn incident_triplet_ids(
&self,
vertex_id: &VertexId,
direction: &Direction,
) -> Result<Vec<(VertexId, EdgeListId, EdgeId, VertexId)>, NetworkError> {
self.incident_edges_iter(vertex_id, direction)
.map(|(edge_list_id, edge_id)| {
let terminal_vid = self.incident_vertex(edge_list_id, edge_id, direction)?;
Ok((*vertex_id, *edge_list_id, *edge_id, terminal_vid))
})
.collect()
}
pub fn incident_triplet_attributes(
&self,
vertex_id: &VertexId,
direction: &Direction,
) -> Result<Vec<(&Vertex, &Edge, &Vertex)>, NetworkError> {
self.incident_triplet_ids(vertex_id, direction)?
.iter()
.map(|(src_id, edge_list_id, edge_id, dst_id)| {
let src = self.get_vertex(src_id)?;
let edge = self.get_edge(edge_list_id, edge_id)?;
let dst = self.get_vertex(dst_id)?;
Ok((src, edge, dst))
})
.collect()
}
}
fn append_to_adjacency(
edge: &Edge,
adj: &mut [IndexMap<(EdgeListId, EdgeId), VertexId>],
forward: bool,
) -> Result<(), String> {
let vertex_idx = if forward {
edge.src_vertex_id.0
} else {
edge.dst_vertex_id.0
};
match adj.get_mut(vertex_idx) {
None => {
let direction = if forward { "forward" } else { "reverse" };
Err(format!(
"vertex {} not found in {} adjacencies for edge list, edge: {}, {}",
vertex_idx, direction, edge.edge_list_id.0, edge.edge_id.0
))
}
Some(out_links) => {
let target_vertex = if forward {
edge.dst_vertex_id
} else {
edge.src_vertex_id
};
out_links.insert((edge.edge_list_id, edge.edge_id), target_vertex);
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use uom::si::{f64::Length, length::meter};
fn create_test_edge(
edge_list_id: usize,
edge_id: usize,
src_vertex_id: usize,
dst_vertex_id: usize,
) -> Edge {
Edge::new(
edge_list_id,
edge_id,
src_vertex_id,
dst_vertex_id,
Length::new::<meter>(1.0),
)
}
#[test]
fn test_append_to_adjacency_forward_success() {
let mut adj: Vec<IndexMap<(EdgeListId, EdgeId), VertexId>> =
vec![IndexMap::new(), IndexMap::new(), IndexMap::new()];
let edge = create_test_edge(0, 0, 0, 2);
let result = append_to_adjacency(&edge, &mut adj, true);
assert!(result.is_ok());
let expected_key = (EdgeListId(0), EdgeId(0));
assert!(adj[0].contains_key(&expected_key));
assert_eq!(adj[0][&expected_key], VertexId(2));
assert!(adj[1].is_empty());
assert!(adj[2].is_empty());
}
#[test]
fn test_append_to_adjacency_reverse_success() {
let mut adj: Vec<IndexMap<(EdgeListId, EdgeId), VertexId>> =
vec![IndexMap::new(), IndexMap::new(), IndexMap::new()];
let edge = create_test_edge(0, 0, 0, 2);
let result = append_to_adjacency(&edge, &mut adj, false);
assert!(result.is_ok());
let expected_key = (EdgeListId(0), EdgeId(0));
assert!(adj[2].contains_key(&expected_key));
assert_eq!(adj[2][&expected_key], VertexId(0));
assert!(adj[0].is_empty());
assert!(adj[1].is_empty());
}
#[test]
fn test_append_to_adjacency_forward_invalid_vertex() {
let mut adj: Vec<IndexMap<(EdgeListId, EdgeId), VertexId>> =
vec![IndexMap::new(), IndexMap::new()];
let edge = create_test_edge(0, 5, 3, 1);
let result = append_to_adjacency(&edge, &mut adj, true);
assert!(result.is_err());
let error_msg = result.unwrap_err();
assert!(error_msg.contains("vertex 3 not found in forward adjacencies"));
assert!(error_msg.contains("edge list, edge: 0, 5"));
}
#[test]
fn test_append_to_adjacency_reverse_invalid_vertex() {
let mut adj: Vec<IndexMap<(EdgeListId, EdgeId), VertexId>> =
vec![IndexMap::new(), IndexMap::new()];
let edge = create_test_edge(1, 10, 0, 5);
let result = append_to_adjacency(&edge, &mut adj, false);
assert!(result.is_err());
let error_msg = result.unwrap_err();
assert!(error_msg.contains("vertex 5 not found in reverse adjacencies"));
assert!(error_msg.contains("edge list, edge: 1, 10"));
}
#[test]
fn test_append_to_adjacency_multiple_edges_same_vertex() {
let mut adj: Vec<IndexMap<(EdgeListId, EdgeId), VertexId>> =
vec![IndexMap::new(), IndexMap::new(), IndexMap::new()];
let edge1 = create_test_edge(0, 0, 0, 1);
let edge2 = create_test_edge(0, 1, 0, 2);
let edge3 = create_test_edge(1, 0, 0, 2);
assert!(append_to_adjacency(&edge1, &mut adj, true).is_ok());
assert!(append_to_adjacency(&edge2, &mut adj, true).is_ok());
assert!(append_to_adjacency(&edge3, &mut adj, true).is_ok());
assert_eq!(adj[0].len(), 3);
assert_eq!(adj[0][&(EdgeListId(0), EdgeId(0))], VertexId(1));
assert_eq!(adj[0][&(EdgeListId(0), EdgeId(1))], VertexId(2));
assert_eq!(adj[0][&(EdgeListId(1), EdgeId(0))], VertexId(2));
}
#[test]
fn test_append_to_adjacency_edge_overwrite() {
let mut adj: Vec<IndexMap<(EdgeListId, EdgeId), VertexId>> =
vec![IndexMap::new(), IndexMap::new(), IndexMap::new()];
let edge1 = create_test_edge(0, 0, 0, 1);
let edge2 = create_test_edge(0, 0, 0, 2);
assert!(append_to_adjacency(&edge1, &mut adj, true).is_ok());
assert_eq!(adj[0][&(EdgeListId(0), EdgeId(0))], VertexId(1));
assert!(append_to_adjacency(&edge2, &mut adj, true).is_ok());
assert_eq!(adj[0].len(), 1); assert_eq!(adj[0][&(EdgeListId(0), EdgeId(0))], VertexId(2)); }
}