Skip to content

Commit

Permalink
Added 'proxy::EdgeWeightMap'
Browse files Browse the repository at this point in the history
'DijkstraShortestPaths', 'Spfs', and 'Connected::*' now only take graphs
with integer weights.
EdgeWeightMap is used to wrap other graphs.

Simplified 'Graph' (and other traits) method lifetime requirements.
  • Loading branch information
Emoun committed Nov 11, 2024
1 parent fb5742b commit 1f361ae
Show file tree
Hide file tree
Showing 18 changed files with 378 additions and 303 deletions.
78 changes: 23 additions & 55 deletions src/algo/dijkstra_shortest_paths.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,38 @@
use crate::core::{property::HasVertex, Edge, Graph};
use num_traits::{PrimInt, Unsigned};
use num_traits::{PrimInt, Unsigned, Zero};
use std::borrow::Borrow;

/// [Dijkstra's shortest paths algorithm](https://mathworld.wolfram.com/DijkstrasAlgorithm.html)
pub struct DijkstraShortestPaths<'a, G, W>
pub struct DijkstraShortestPaths<'a, G>
where
G: 'a + Graph,
W: PrimInt + Unsigned,
G::EdgeWeight: PrimInt + Unsigned,
{
graph: &'a G,
visited: Vec<G::Vertex>,
// We keep it sorted with the lowest weight at the end for efficiency.
queue: Vec<(W, (G::Vertex, G::Vertex, G::EdgeWeightRef<'a>))>,
get_distance: fn(&G::EdgeWeight) -> W,
queue: Vec<(G::EdgeWeight, (G::Vertex, G::Vertex, G::EdgeWeightRef<'a>))>,
}

impl<'a, G, W> DijkstraShortestPaths<'a, G, W>
impl<'a, G> DijkstraShortestPaths<'a, G>
where
G: 'a + Graph,
W: PrimInt + Unsigned,
G::EdgeWeight: PrimInt + Unsigned,
{
pub fn new(graph: &'a G, get_distance: fn(&G::EdgeWeight) -> W) -> Self
pub fn new(graph: &'a G) -> Self
where
G: HasVertex,
{
let mut dijk = Self {
graph,
visited: Vec::new(),
queue: Vec::new(),
get_distance,
};
dijk.visit(graph.get_vertex(), W::zero());
dijk.visit(graph.get_vertex(), G::EdgeWeight::zero());
dijk
}

fn visit(&mut self, v: G::Vertex, w: W)
fn visit(&mut self, v: G::Vertex, w: G::EdgeWeight)
{
self.visited.push(v);
let visited = &self.visited;
Expand All @@ -44,7 +42,7 @@ where

for (sink, weight) in edges
{
let new_weight = w + (self.get_distance)(weight.borrow());
let new_weight = w + *weight.borrow();
if let Some((old_weight, old_edge)) = self
.queue
.iter_mut()
Expand All @@ -66,42 +64,25 @@ where

/// Returns the vertices reachable from the designated vertex and the
/// weighted distance to them
pub fn distances(
graph: &'a G,
get_distance: fn(&G::EdgeWeight) -> W,
) -> impl 'a + Iterator<Item = (G::Vertex, W)>
pub fn distances(graph: &'a G) -> impl 'a + Iterator<Item = (G::Vertex, G::EdgeWeight)>
where
G: HasVertex,
W: 'a,
{
let mut distances = vec![(graph.get_vertex(), W::zero())];
let mut distances = vec![(graph.get_vertex(), G::EdgeWeight::zero())];

DijkstraShortestPaths::new(graph, get_distance).map(move |(so, si, w)| {
DijkstraShortestPaths::new(graph).map(move |(so, si, w)| {
let dist = distances.iter().find(|(v, _)| so == *v).unwrap().1;
let new_dist = dist + get_distance(w.borrow());
let new_dist = dist + *w.borrow();
distances.push((si, new_dist));
(si, new_dist)
})
}
}

impl<'a, G> DijkstraShortestPaths<'a, G, G::EdgeWeight>
impl<'a, G> Iterator for DijkstraShortestPaths<'a, G>
where
G: 'a + Graph,
G::EdgeWeight: PrimInt + Unsigned,
{
pub fn new_simple(graph: &'a G) -> Self
where
G: HasVertex,
{
Self::new(graph, Clone::clone)
}
}

impl<'a, G, W> Iterator for DijkstraShortestPaths<'a, G, W>
where
G: 'a + Graph,
W: PrimInt + Unsigned,
{
type Item = (G::Vertex, G::Vertex, G::EdgeWeightRef<'a>);

Expand All @@ -118,46 +99,33 @@ where
/// Shortest-Path-First search
///
/// next() doesn't return the starting vertex.
pub struct Spfs<'a, G, W>
pub struct Spfs<'a, G>
where
G: 'a + Graph,
W: PrimInt + Unsigned,
G::EdgeWeight: PrimInt + Unsigned,
{
dijk: DijkstraShortestPaths<'a, G, W>,
dijk: DijkstraShortestPaths<'a, G>,
}

impl<'a, G, W> Spfs<'a, G, W>
impl<'a, G> Spfs<'a, G>
where
G: 'a + Graph,
W: PrimInt + Unsigned,
G::EdgeWeight: PrimInt + Unsigned,
{
pub fn new(graph: &'a G, get_weight: fn(&G::EdgeWeight) -> W) -> Self
pub fn new(graph: &'a G) -> Self
where
G: HasVertex,
{
Self {
dijk: DijkstraShortestPaths::new(graph, get_weight),
dijk: DijkstraShortestPaths::new(graph),
}
}
}

impl<'a, G> Spfs<'a, G, G::EdgeWeight>
impl<'a, G> Iterator for Spfs<'a, G>
where
G: 'a + Graph,
G::EdgeWeight: PrimInt + Unsigned,
{
pub fn new_simple(graph: &'a G) -> Self
where
G: HasVertex,
{
Self::new(graph, Clone::clone)
}
}

impl<'a, G, W> Iterator for Spfs<'a, G, W>
where
G: 'a + Graph,
W: PrimInt + Unsigned,
{
type Item = G::Vertex;

Expand Down
22 changes: 11 additions & 11 deletions src/common/adjacency_list/impl_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ where
self.vertices.iter().enumerate().map(|(v, (w, _))| (v, w))
}

fn edges_between<'a: 'b, 'b>(
&'a self,
source: impl 'b + Borrow<Self::Vertex>,
sink: impl 'b + Borrow<Self::Vertex>,
) -> impl 'b + Iterator<Item = Self::EdgeWeightRef<'a>>
fn edges_between(
&self,
source: impl Borrow<Self::Vertex>,
sink: impl Borrow<Self::Vertex>,
) -> impl Iterator<Item = Self::EdgeWeightRef<'_>>
{
let source = source.borrow().clone();
let sink = sink.borrow().clone();
Expand Down Expand Up @@ -75,19 +75,19 @@ where
{
fn all_vertices_weighted_mut(
&mut self,
) -> impl '_ + Iterator<Item = (Self::Vertex, &mut Self::VertexWeight)>
) -> impl Iterator<Item = (Self::Vertex, &mut Self::VertexWeight)>
{
self.vertices
.iter_mut()
.enumerate()
.map(|(v, (w, _))| (v, w))
}

fn edges_between_mut<'a: 'b, 'b>(
&'a mut self,
source: impl 'b + Borrow<Self::Vertex>,
sink: impl 'b + Borrow<Self::Vertex>,
) -> impl 'b + Iterator<Item = &'a mut Self::EdgeWeight>
fn edges_between_mut(
&mut self,
source: impl Borrow<Self::Vertex>,
sink: impl Borrow<Self::Vertex>,
) -> impl Iterator<Item = &mut Self::EdgeWeight>
{
let source = source.borrow().clone();
let sink = sink.borrow().clone();
Expand Down
57 changes: 27 additions & 30 deletions src/core/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,18 +91,18 @@ pub trait Graph
///
/// If the graph is undirected, returns all edges connecting the two
/// vertices. I.e. all edges where e == (v1,v2,_) or e == (v2,v1,_)
fn edges_between<'a: 'b, 'b>(
&'a self,
source: impl 'b + Borrow<Self::Vertex>,
sink: impl 'b + Borrow<Self::Vertex>,
) -> impl 'b + Iterator<Item = Self::EdgeWeightRef<'a>>;
fn edges_between(
&self,
source: impl Borrow<Self::Vertex>,
sink: impl Borrow<Self::Vertex>,
) -> impl Iterator<Item = Self::EdgeWeightRef<'_>>;

// Optional methods

/// Returns copies of all current edges in the graph.
fn all_edges(
&self,
) -> impl '_ + Iterator<Item = (Self::Vertex, Self::Vertex, Self::EdgeWeightRef<'_>)>
) -> impl Iterator<Item = (Self::Vertex, Self::Vertex, Self::EdgeWeightRef<'_>)>
{
let mut finished = Vec::new();
self.all_vertices()
Expand All @@ -124,7 +124,7 @@ pub trait Graph
})
}

fn all_vertices(&self) -> impl '_ + Iterator<Item = Self::Vertex>
fn all_vertices(&self) -> impl Iterator<Item = Self::Vertex>
{
self.all_vertices_weighted().map(|(v, _)| v)
}
Expand All @@ -146,7 +146,7 @@ pub trait Graph
iter.into_iter().all(|v| self.contains_vertex(v))
}

fn all_vertex_weights(&self) -> impl '_ + Iterator<Item = &Self::VertexWeight>
fn all_vertex_weights(&self) -> impl Iterator<Item = &Self::VertexWeight>
{
self.all_vertices_weighted().map(|(_, w)| w)
}
Expand All @@ -156,10 +156,10 @@ pub trait Graph
///
/// If the graph is undirected, is semantically equivalent to
/// `edges_incident_on`.
fn edges_sourced_in<'a: 'b, 'b>(
&'a self,
v: impl 'b + Borrow<Self::Vertex>,
) -> impl 'b + Iterator<Item = (Self::Vertex, Self::EdgeWeightRef<'a>)>
fn edges_sourced_in(
&self,
v: impl Borrow<Self::Vertex>,
) -> impl Iterator<Item = (Self::Vertex, Self::EdgeWeightRef<'_>)>
{
self.all_vertices().flat_map(move |v2| {
self.edges_between(v.borrow().clone(), v2.borrow().clone())
Expand All @@ -172,10 +172,10 @@ pub trait Graph
///
/// If the graph is undirected, is semantically equivalent to
/// `edges_incident_on`.
fn edges_sinked_in<'a: 'b, 'b>(
&'a self,
v: impl 'b + Borrow<Self::Vertex>,
) -> impl 'b + Iterator<Item = (Self::Vertex, Self::EdgeWeightRef<'a>)>
fn edges_sinked_in(
&self,
v: impl Borrow<Self::Vertex>,
) -> impl Iterator<Item = (Self::Vertex, Self::EdgeWeightRef<'_>)>
{
self.all_vertices().flat_map(move |v2| {
self.edges_between(v2.borrow().clone(), v.borrow().clone())
Expand All @@ -187,10 +187,10 @@ pub trait Graph
/// on the given vertex.
///
/// If the graph is directed, edge directions are ignored.
fn edges_incident_on<'a: 'b, 'b>(
&'a self,
v: impl 'b + Borrow<Self::Vertex>,
) -> impl 'b + Iterator<Item = (Self::Vertex, Self::EdgeWeightRef<'a>)>
fn edges_incident_on(
&self,
v: impl Borrow<Self::Vertex>,
) -> impl Iterator<Item = (Self::Vertex, Self::EdgeWeightRef<'_>)>
{
self.edges_sourced_in(v.borrow().clone()).chain(
self.edges_sinked_in(v.borrow().clone())
Expand All @@ -200,10 +200,7 @@ pub trait Graph

/// Returns any vertices connected to the given one with an edge regardless
/// of direction.
fn vertex_neighbors<'a: 'b, 'b>(
&'a self,
v: impl 'b + Borrow<Self::Vertex>,
) -> impl 'b + Iterator<Item = Self::Vertex>
fn vertex_neighbors(&self, v: impl Borrow<Self::Vertex>) -> impl Iterator<Item = Self::Vertex>
{
self.all_vertices()
.filter(move |other| self.neighbors(v.borrow(), other.borrow()))
Expand Down Expand Up @@ -234,13 +231,13 @@ pub trait GraphMut: Graph
{
fn all_vertices_weighted_mut(
&mut self,
) -> impl '_ + Iterator<Item = (Self::Vertex, &mut Self::VertexWeight)>;
) -> impl Iterator<Item = (Self::Vertex, &mut Self::VertexWeight)>;

fn edges_between_mut<'a: 'b, 'b>(
&'a mut self,
source: impl 'b + Borrow<Self::Vertex>,
sink: impl 'b + Borrow<Self::Vertex>,
) -> impl 'b + Iterator<Item = &'a mut Self::EdgeWeight>;
fn edges_between_mut(
&mut self,
source: impl Borrow<Self::Vertex>,
sink: impl Borrow<Self::Vertex>,
) -> impl Iterator<Item = &mut Self::EdgeWeight>;

// Optional methods

Expand Down
Loading

0 comments on commit 1f361ae

Please sign in to comment.