use super::{Edge, EdgeId, NetworkError, Vertex, VertexId};
use crate::algorithm::search::Direction;
use crate::util::compact_ordered_hash_map::CompactOrderedHashMap;
use crate::util::fs::read_utils;
use allocative::Allocative;
use itertools::Itertools;
use kdam::Bar;
use std::collections::HashSet;
use std::path::Path;
#[derive(Debug, Allocative)]
pub struct Graph {
pub adj: Box<[CompactOrderedHashMap<EdgeId, VertexId>]>,
pub rev: Box<[CompactOrderedHashMap<EdgeId, VertexId>]>,
pub edges: Box<[Edge]>,
pub vertices: Box<[Vertex]>,
}
impl TryFrom<&serde_json::Value> for Graph {
type Error = NetworkError;
fn try_from(value: &serde_json::Value) -> Result<Self, Self::Error> {
let edge_list_value = value.get("edge_list_input_file").ok_or_else(|| {
NetworkError::DatasetError(String::from(
"configuration key edge_list_input_file missing",
))
})?;
let edge_list_str = edge_list_value
.as_str()
.ok_or_else(|| {
NetworkError::DatasetError(String::from(
"configuration value at key edge_list_input_file is not a string",
))
})?
.to_string();
let vertex_list_value = value.get("vertex_list_input_file").ok_or_else(|| {
NetworkError::DatasetError(String::from(
"configuration key edge_list_input_file missing",
))
})?;
let vertex_list_str = vertex_list_value
.as_str()
.ok_or_else(|| {
NetworkError::DatasetError(String::from(
"configuration value at key vertex_list_input_file is not a string",
))
})?
.to_string();
Self::from_files(&edge_list_str, &vertex_list_str)
}
}
impl Graph {
pub fn from_files<P: AsRef<Path>>(
edge_list_csv: &P,
vertex_list_csv: &P,
) -> Result<Graph, NetworkError> {
let vertices: Box<[Vertex]> = read_utils::from_csv(
&vertex_list_csv,
true,
Some(Bar::builder().desc("graph vertices")),
None,
)
.map_err(|e| NetworkError::CsvError { source: e })?;
let mut adj: Vec<CompactOrderedHashMap<EdgeId, VertexId>> =
vec![CompactOrderedHashMap::empty(); vertices.len()];
let mut rev: Vec<CompactOrderedHashMap<EdgeId, VertexId>> =
vec![CompactOrderedHashMap::empty(); vertices.len()];
let mut missing_vertices: HashSet<VertexId> = HashSet::new();
let cb = Box::new(|edge: &Edge| {
match adj.get_mut(edge.src_vertex_id.0) {
None => {
missing_vertices.insert(edge.src_vertex_id);
}
Some(out_links) => {
out_links.insert(edge.edge_id, edge.dst_vertex_id);
}
}
match rev.get_mut(edge.dst_vertex_id.0) {
None => {
missing_vertices.insert(edge.dst_vertex_id);
}
Some(in_links) => {
in_links.insert(edge.edge_id, edge.src_vertex_id);
}
}
});
let edges = read_utils::from_csv(
&edge_list_csv,
true,
Some(Bar::builder().desc("graph edges")),
Some(cb),
)
.map_err(|e| NetworkError::CsvError { source: e })?;
let graph = Graph {
adj: adj.into_boxed_slice(),
rev: rev.into_boxed_slice(),
edges,
vertices,
};
Ok(graph)
}
pub fn n_edges(&self) -> usize {
self.edges.len()
}
pub fn n_vertices(&self) -> usize {
self.vertices.len()
}
pub fn edge_ids(&self) -> Box<dyn Iterator<Item = EdgeId>> {
let range = (0..self.n_edges()).map(EdgeId);
Box::new(range)
}
pub fn vertex_ids(&self) -> Box<dyn Iterator<Item = VertexId>> {
let range = (0..self.n_vertices()).map(VertexId);
Box::new(range)
}
pub fn get_edge(&self, edge_id: &EdgeId) -> Result<&Edge, NetworkError> {
match self.edges.get(edge_id.0) {
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<EdgeId> {
self.out_edges_iter(src).cloned().collect_vec()
}
pub fn out_edges_iter<'a>(
&'a self,
src: &VertexId,
) -> Box<dyn Iterator<Item = &'a EdgeId> + 'a> {
match self.adj.get(src.0) {
None => Box::new(std::iter::empty()),
Some(out_map) => out_map.keys(),
}
}
pub fn in_edges(&self, dst: &VertexId) -> Vec<EdgeId> {
self.in_edges_iter(dst).cloned().collect_vec()
}
pub fn in_edges_iter<'a>(
&'a self,
dst: &VertexId,
) -> Box<dyn Iterator<Item = &'a EdgeId> + 'a> {
match self.rev.get(dst.0) {
None => Box::new(std::iter::empty()),
Some(out_map) => out_map.keys(),
}
}
pub fn src_vertex_id(&self, edge_id: &EdgeId) -> Result<VertexId, NetworkError> {
self.get_edge(edge_id).map(|e| e.src_vertex_id)
}
pub fn dst_vertex_id(&self, edge_id: &EdgeId) -> Result<VertexId, NetworkError> {
self.get_edge(edge_id).map(|e| e.dst_vertex_id)
}
pub fn incident_edges(&self, vertex_id: &VertexId, direction: &Direction) -> Vec<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: &VertexId,
direction: &Direction,
) -> Box<dyn Iterator<Item = &'a 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_id: &EdgeId,
direction: &Direction,
) -> Result<VertexId, NetworkError> {
match direction {
Direction::Forward => self.dst_vertex_id(edge_id),
Direction::Reverse => self.src_vertex_id(edge_id),
}
}
pub fn edge_triplet(
&self,
edge_id: &EdgeId,
) -> Result<(&Vertex, &Edge, &Vertex), NetworkError> {
let edge = self.get_edge(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, EdgeId, VertexId)>, NetworkError> {
self.incident_edges_iter(vertex_id, direction)
.map(|edge_id| {
let terminal_vid = self.incident_vertex(edge_id, direction)?;
Ok((*vertex_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_id, dst_id)| {
let src = self.get_vertex(src_id)?;
let edge = self.get_edge(edge_id)?;
let dst = self.get_vertex(dst_id)?;
Ok((src, edge, dst))
})
.collect()
}
}