From 1f361ae5b41b98518ad4940e1b2560d1081068c3 Mon Sep 17 00:00:00 2001 From: Emad Jacob Maroun Date: Mon, 11 Nov 2024 18:20:40 +0100 Subject: [PATCH] Added 'proxy::EdgeWeightMap' '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. --- src/algo/dijkstra_shortest_paths.rs | 78 +++++----------- src/common/adjacency_list/impl_graph.rs | 22 ++--- src/core/graph.rs | 57 ++++++----- src/core/property/connected.rs | 102 ++++++++++---------- src/core/property/directedness_ensurers.rs | 20 ++-- src/core/property/impl_ensurer.rs | 22 ++--- src/core/proxy/edge_proxy.rs | 22 ++--- src/core/proxy/edge_weight_map.rs | 104 +++++++++++++++++++++ src/core/proxy/mod.rs | 4 +- src/core/proxy/reverse_graph.rs | 22 ++--- src/core/proxy/subgraph_proxy.rs | 22 ++--- src/core/proxy/undirected_proxy.rs | 10 +- src/core/proxy/vertex_proxy.rs | 10 +- tests/algo/dijkstra_shortest_paths.rs | 30 ++++-- tests/algo/search.rs | 29 +++--- tests/core/ensure.rs | 48 +++++----- tests/core/property/connectedness.rs | 53 ++++++----- tests/mock_graph/mock_graph.rs | 26 +++--- 18 files changed, 378 insertions(+), 303 deletions(-) create mode 100644 src/core/proxy/edge_weight_map.rs diff --git a/src/algo/dijkstra_shortest_paths.rs b/src/algo/dijkstra_shortest_paths.rs index 40cd1b2..7b9d6ce 100644 --- a/src/algo/dijkstra_shortest_paths.rs +++ b/src/algo/dijkstra_shortest_paths.rs @@ -1,26 +1,25 @@ 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, // 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, { @@ -28,13 +27,12 @@ where 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; @@ -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() @@ -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 + pub fn distances(graph: &'a G) -> impl 'a + Iterator 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>); @@ -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; diff --git a/src/common/adjacency_list/impl_graph.rs b/src/common/adjacency_list/impl_graph.rs index b3e39aa..9455b0c 100644 --- a/src/common/adjacency_list/impl_graph.rs +++ b/src/common/adjacency_list/impl_graph.rs @@ -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, - sink: impl 'b + Borrow, - ) -> impl 'b + Iterator> + fn edges_between( + &self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator> { let source = source.borrow().clone(); let sink = sink.borrow().clone(); @@ -75,7 +75,7 @@ where { fn all_vertices_weighted_mut( &mut self, - ) -> impl '_ + Iterator + ) -> impl Iterator { self.vertices .iter_mut() @@ -83,11 +83,11 @@ where .map(|(v, (w, _))| (v, w)) } - fn edges_between_mut<'a: 'b, 'b>( - &'a mut self, - source: impl 'b + Borrow, - sink: impl 'b + Borrow, - ) -> impl 'b + Iterator + fn edges_between_mut( + &mut self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator { let source = source.borrow().clone(); let sink = sink.borrow().clone(); diff --git a/src/core/graph.rs b/src/core/graph.rs index d296b0f..f6a2025 100644 --- a/src/core/graph.rs +++ b/src/core/graph.rs @@ -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, - sink: impl 'b + Borrow, - ) -> impl 'b + Iterator>; + fn edges_between( + &self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator>; // Optional methods /// Returns copies of all current edges in the graph. fn all_edges( &self, - ) -> impl '_ + Iterator)> + ) -> impl Iterator)> { let mut finished = Vec::new(); self.all_vertices() @@ -124,7 +124,7 @@ pub trait Graph }) } - fn all_vertices(&self) -> impl '_ + Iterator + fn all_vertices(&self) -> impl Iterator { self.all_vertices_weighted().map(|(v, _)| v) } @@ -146,7 +146,7 @@ pub trait Graph iter.into_iter().all(|v| self.contains_vertex(v)) } - fn all_vertex_weights(&self) -> impl '_ + Iterator + fn all_vertex_weights(&self) -> impl Iterator { self.all_vertices_weighted().map(|(_, w)| w) } @@ -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, - ) -> impl 'b + Iterator)> + fn edges_sourced_in( + &self, + v: impl Borrow, + ) -> impl Iterator)> { self.all_vertices().flat_map(move |v2| { self.edges_between(v.borrow().clone(), v2.borrow().clone()) @@ -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, - ) -> impl 'b + Iterator)> + fn edges_sinked_in( + &self, + v: impl Borrow, + ) -> impl Iterator)> { self.all_vertices().flat_map(move |v2| { self.edges_between(v2.borrow().clone(), v.borrow().clone()) @@ -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, - ) -> impl 'b + Iterator)> + fn edges_incident_on( + &self, + v: impl Borrow, + ) -> impl Iterator)> { self.edges_sourced_in(v.borrow().clone()).chain( self.edges_sinked_in(v.borrow().clone()) @@ -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, - ) -> impl 'b + Iterator + fn vertex_neighbors(&self, v: impl Borrow) -> impl Iterator { self.all_vertices() .filter(move |other| self.neighbors(v.borrow(), other.borrow())) @@ -234,13 +231,13 @@ pub trait GraphMut: Graph { fn all_vertices_weighted_mut( &mut self, - ) -> impl '_ + Iterator; + ) -> impl Iterator; - fn edges_between_mut<'a: 'b, 'b>( - &'a mut self, - source: impl 'b + Borrow, - sink: impl 'b + Borrow, - ) -> impl 'b + Iterator; + fn edges_between_mut( + &mut self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator; // Optional methods diff --git a/src/core/property/connected.rs b/src/core/property/connected.rs index 0cbb772..7dba8d5 100644 --- a/src/core/property/connected.rs +++ b/src/core/property/connected.rs @@ -9,7 +9,7 @@ use crate::{ Ensure, Graph, GraphDerefMut, }, }; -use num_traits::{PrimInt, Unsigned}; +use num_traits::{PrimInt, Unsigned, Zero}; use std::borrow::Borrow; /// A marker trait for graphs that are connected. @@ -23,24 +23,25 @@ pub trait Connected: Unilateral /// Takes a closure that converts an edge's weight into a distance value. /// The distance between two vertices is equal to the distance of the /// edge(s) between them. - fn eccentricity_weighted( - &self, - get_distance: fn(&Self::EdgeWeight) -> W, - ) -> W + fn eccentricity(&self) -> Self::EdgeWeight where Self: EdgeCount + HasVertex + Sized, + Self::EdgeWeight: PrimInt + Unsigned, { // We search for all the shortest paths, the eccentricity is the longest one - DijkstraShortestPaths::distances(self, get_distance).fold(W::zero(), |max_dist, (_, d2)| { - if max_dist < d2 - { - d2 - } - else - { - max_dist - } - }) + DijkstraShortestPaths::distances(self).fold( + Self::EdgeWeight::zero(), + |max_dist, (_, d2)| { + if max_dist < d2 + { + d2 + } + else + { + max_dist + } + }, + ) } /// Calculates the maximum eccentricity of the graph ([the diameter](https://mathworld.wolfram.com/GraphDiameter.html)). @@ -48,25 +49,23 @@ pub trait Connected: Unilateral /// Takes a closure that converts an edge's weight into a distance value. /// The distance between two vertices is equal to the distance of the /// edge(s) between them. - fn diameter_weighted( - &self, - get_distance: fn(&Self::EdgeWeight) -> W, - ) -> W + fn diameter(&self) -> Self::EdgeWeight where Self: EdgeCount + Sized, + Self::EdgeWeight: PrimInt + Unsigned, { - self.all_vertices().fold(W::zero(), |max_ecc, v| { - let new_ecc = - VertexInGraph::ensure_unvalidated(self, v).eccentricity_weighted(get_distance); - if new_ecc > max_ecc - { - new_ecc - } - else - { - max_ecc - } - }) + self.all_vertices() + .fold(Self::EdgeWeight::zero(), |max_ecc, v| { + let new_ecc = VertexInGraph::ensure_unvalidated(self, v).eccentricity(); + if new_ecc > max_ecc + { + new_ecc + } + else + { + max_ecc + } + }) } /// Calculates the minimum eccentricity of the graph ([the radius](https://mathworld.wolfram.com/GraphDiameter.html)). @@ -74,22 +73,23 @@ pub trait Connected: Unilateral /// Takes a closure that converts an edge's weight into a distance value. /// The distance between two vertices is equal to the distance of the /// edge(s) between them. - fn radius_weighted(&self, get_distance: fn(&Self::EdgeWeight) -> W) -> W + fn radius(&self) -> Self::EdgeWeight where Self: EdgeCount + Sized, + Self::EdgeWeight: PrimInt + Unsigned, { - self.all_vertices().fold(W::zero(), |min_ecc, v| { - let new_ecc = - VertexInGraph::ensure_unvalidated(self, v).eccentricity_weighted(get_distance); - if new_ecc < min_ecc - { - new_ecc - } - else - { - min_ecc - } - }) + self.all_vertices() + .fold(Self::EdgeWeight::zero(), |min_ecc, v| { + let new_ecc = VertexInGraph::ensure_unvalidated(self, v).eccentricity(); + if new_ecc < min_ecc + { + new_ecc + } + else + { + min_ecc + } + }) } /// Returns the vertices with eccentricity equal to the radius ([the centers](https://mathworld.wolfram.com/GraphCenter.html)). @@ -97,18 +97,14 @@ pub trait Connected: Unilateral /// Takes a closure that converts an edge's weight into a distance value. /// The distance between two vertices is equal to the distance of the /// edge(s) between them. - fn centers_weighted( - &self, - get_distance: fn(&Self::EdgeWeight) -> W, - ) -> impl Iterator + fn centers(&self) -> impl Iterator where Self: EdgeCount + Sized, + Self::EdgeWeight: PrimInt + Unsigned, { - let radius = self.radius_weighted(get_distance); - self.all_vertices().filter(move |v| { - VertexInGraph::ensure_unvalidated(self, *v).eccentricity_weighted(get_distance) - == radius - }) + let radius = self.radius(); + self.all_vertices() + .filter(move |v| VertexInGraph::ensure_unvalidated(self, *v).eccentricity() == radius) } } diff --git a/src/core/property/directedness_ensurers.rs b/src/core/property/directedness_ensurers.rs index c294125..7363175 100644 --- a/src/core/property/directedness_ensurers.rs +++ b/src/core/property/directedness_ensurers.rs @@ -35,11 +35,11 @@ impl Graph for DirectedGraph &self, ) -> impl Iterator; - fn edges_between<'a: 'b, 'b>( - &'a self, - source: impl 'b + Borrow, - sink: impl 'b + Borrow, - ) -> impl 'b + Iterator>; + fn edges_between( + &self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator>; } } } @@ -82,11 +82,11 @@ impl Graph for UndirectedGraph &self, ) -> impl Iterator; - fn edges_between<'a: 'b, 'b>( - &'a self, - source: impl 'b + Borrow, - sink: impl 'b + Borrow, - ) -> impl 'b + Iterator>; + fn edges_between( + &self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator>; } } } diff --git a/src/core/property/impl_ensurer.rs b/src/core/property/impl_ensurer.rs index 009841a..5a27ced 100644 --- a/src/core/property/impl_ensurer.rs +++ b/src/core/property/impl_ensurer.rs @@ -254,11 +254,11 @@ macro_rules! impl_properties { &self, ) -> impl Iterator; - fn edges_between<'a: 'b, 'b>( - &'a self, - source: impl 'b + std::borrow::Borrow, - sink: impl 'b + std::borrow::Borrow, - ) -> impl 'b + Iterator>; + fn edges_between( + &self, + source: impl std::borrow::Borrow, + sink: impl std::borrow::Borrow, + ) -> impl Iterator>; } } } @@ -283,13 +283,13 @@ macro_rules! impl_properties { to $crate::core::GraphDerefMut::graph_mut(&mut self$($delegate)+) { fn all_vertices_weighted_mut( &mut self, - ) -> impl '_ + Iterator; + ) -> impl Iterator; - fn edges_between_mut<'a: 'b, 'b>( - &'a mut self, - source: impl 'b + std::borrow::Borrow, - sink: impl 'b + std::borrow::Borrow, - ) -> impl 'b + Iterator; + fn edges_between_mut( + &mut self, + source: impl std::borrow::Borrow, + sink: impl std::borrow::Borrow, + ) -> impl Iterator; } } } diff --git a/src/core/proxy/edge_proxy.rs b/src/core/proxy/edge_proxy.rs index 1b4ff7b..f3d749a 100644 --- a/src/core/proxy/edge_proxy.rs +++ b/src/core/proxy/edge_proxy.rs @@ -65,11 +65,11 @@ impl Graph for EdgeProxyGraph } } - fn edges_between<'a: 'b, 'b>( - &'a self, - source: impl 'b + Borrow, - sink: impl 'b + Borrow, - ) -> impl 'b + Iterator> + fn edges_between( + &self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator> { let applicable = |so, si| { (source.borrow() == so && sink.borrow() == si) @@ -100,15 +100,15 @@ where to self.graph.graph_mut() { fn all_vertices_weighted_mut( &mut self, - ) -> impl '_ + Iterator; + ) -> impl Iterator; } } - fn edges_between_mut<'a: 'b, 'b>( - &'a mut self, - source: impl 'b + Borrow, - sink: impl 'b + Borrow, - ) -> impl 'b + Iterator + fn edges_between_mut( + &mut self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator { // Safe because &mut () can't mutate anything self.edges_between(source, sink) diff --git a/src/core/proxy/edge_weight_map.rs b/src/core/proxy/edge_weight_map.rs new file mode 100644 index 0000000..7454626 --- /dev/null +++ b/src/core/proxy/edge_weight_map.rs @@ -0,0 +1,104 @@ +use crate::core::{Ensure, Graph, Release}; +use delegate::delegate; +use std::borrow::Borrow; + +/// Wraps a graph, mapping its edge weights to `Ew`. +/// +/// Useful for when a graph with specific edge weights are needed, but the graph +/// you have does not have the correct edge weight type. E.g., using this +/// struct, whatever edge weights a graph has can be mapped to integers and +/// provided to +/// [DijkstraShortestPaths](../../algo/struct.DijkstraShortestPaths.html), which +/// requires unsigned integers as weights. +#[derive(Clone, Debug)] +pub struct EdgeWeightMap( + C, + fn( + ::Vertex, + ::Vertex, + &::EdgeWeight, + ) -> Ew, +); + +impl EdgeWeightMap +{ + pub fn new( + c: ::Ensured, + map: fn( + ::Vertex, + ::Vertex, + &::EdgeWeight, + ) -> Ew, + ) -> Self + { + Self(c, map) + } +} + +impl Ensure for EdgeWeightMap +{ + fn ensure_unvalidated( + c: Self::Ensured, + map: fn( + ::Vertex, + ::Vertex, + &::EdgeWeight, + ) -> Ew, + ) -> Self + { + Self(c, map) + } + + fn validate( + _: &Self::Ensured, + _: &fn( + ::Vertex, + ::Vertex, + &::EdgeWeight, + ) -> Ew, + ) -> bool + { + true + } +} + +impl Graph for EdgeWeightMap +{ + type Directedness = ::Directedness; + type EdgeWeight = Ew; + type EdgeWeightRef<'a> + = Ew + where + Self: 'a; + type Vertex = ::Vertex; + type VertexWeight = ::VertexWeight; + + delegate! { + to self.0.graph() { + fn all_vertices_weighted(&self) + -> impl Iterator; + } + } + + fn edges_between( + &self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator> + { + self.0 + .graph() + .edges_between(*source.borrow(), *sink.borrow()) + .map(move |e| (self.1)(*source.borrow(), *sink.borrow(), e.borrow())) + } +} + +impl_ensurer! { + use EdgeWeightMap: Ensure, Graph, GraphMut, Reflexive, AddEdge, RemoveEdge + as (self.0) : C + as (self.1) : fn( + ::Vertex, + ::Vertex, + &::EdgeWeight, + ) -> Ew +} diff --git a/src/core/proxy/mod.rs b/src/core/proxy/mod.rs index 3683197..b6c9f16 100644 --- a/src/core/proxy/mod.rs +++ b/src/core/proxy/mod.rs @@ -1,9 +1,11 @@ mod edge_proxy; +mod edge_weight_map; mod reverse_graph; mod subgraph_proxy; mod undirected_proxy; mod vertex_proxy; pub use self::{ - edge_proxy::*, reverse_graph::*, subgraph_proxy::*, undirected_proxy::*, vertex_proxy::*, + edge_proxy::*, edge_weight_map::*, reverse_graph::*, subgraph_proxy::*, undirected_proxy::*, + vertex_proxy::*, }; diff --git a/src/core/proxy/reverse_graph.rs b/src/core/proxy/reverse_graph.rs index 769321a..fd59f76 100644 --- a/src/core/proxy/reverse_graph.rs +++ b/src/core/proxy/reverse_graph.rs @@ -41,11 +41,11 @@ where } } - fn edges_between<'a: 'b, 'b>( - &'a self, - source: impl 'b + Borrow, - sink: impl 'b + Borrow, - ) -> impl 'b + Iterator> + fn edges_between( + &self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator> { self.0.graph().edges_between(sink, source) } @@ -58,15 +58,15 @@ where delegate! { to self.0.graph_mut() { fn all_vertices_weighted_mut(&mut self) - -> impl '_ + Iterator; + -> impl Iterator; } } - fn edges_between_mut<'a: 'b, 'b>( - &'a mut self, - source: impl 'b + Borrow, - sink: impl 'b + Borrow, - ) -> impl 'b + Iterator + fn edges_between_mut( + &mut self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator { self.0.graph_mut().edges_between_mut(sink, source) } diff --git a/src/core/proxy/subgraph_proxy.rs b/src/core/proxy/subgraph_proxy.rs index 804067c..8060ea9 100644 --- a/src/core/proxy/subgraph_proxy.rs +++ b/src/core/proxy/subgraph_proxy.rs @@ -81,11 +81,11 @@ impl Graph for SubgraphProxy .filter(move |(v, _)| self.verts.contains(v)) } - fn edges_between<'a: 'b, 'b>( - &'a self, - source: impl 'b + Borrow, - sink: impl 'b + Borrow, - ) -> impl 'b + Iterator> + fn edges_between( + &self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator> { self.graph .graph() @@ -102,7 +102,7 @@ where { fn all_vertices_weighted_mut( &mut self, - ) -> impl '_ + Iterator + ) -> impl Iterator { let verts = &self.verts; let graph = self.graph.graph_mut(); @@ -112,11 +112,11 @@ where .filter(move |(v, _)| verts.contains(v)) } - fn edges_between_mut<'a: 'b, 'b>( - &'a mut self, - source: impl 'b + Borrow, - sink: impl 'b + Borrow, - ) -> impl 'b + Iterator + fn edges_between_mut( + &mut self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator { let return_any = self.contains_vertex(*source.borrow()) && self.contains_vertex(*sink.borrow()); diff --git a/src/core/proxy/undirected_proxy.rs b/src/core/proxy/undirected_proxy.rs index 934df25..47e43b4 100644 --- a/src/core/proxy/undirected_proxy.rs +++ b/src/core/proxy/undirected_proxy.rs @@ -41,11 +41,11 @@ where } } - fn edges_between<'a: 'b, 'b>( - &'a self, - source: impl 'b + Borrow, - sink: impl 'b + Borrow, - ) -> impl 'b + Iterator> + fn edges_between( + &self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator> { self.0 .graph() diff --git a/src/core/proxy/vertex_proxy.rs b/src/core/proxy/vertex_proxy.rs index 40c103f..9fdb0bb 100644 --- a/src/core/proxy/vertex_proxy.rs +++ b/src/core/proxy/vertex_proxy.rs @@ -64,11 +64,11 @@ impl Graph for VertexProxyGraph .chain((0..self.new_count).map(|v| (ProxyVertex::New(v), &()))) } - fn edges_between<'a: 'b, 'b>( - &'a self, - source: impl 'b + Borrow, - sink: impl 'b + Borrow, - ) -> impl 'b + Iterator> + fn edges_between( + &self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator> { match (source.borrow(), sink.borrow()) { diff --git a/tests/algo/dijkstra_shortest_paths.rs b/tests/algo/dijkstra_shortest_paths.rs index 24a5bbb..361e74b 100644 --- a/tests/algo/dijkstra_shortest_paths.rs +++ b/tests/algo/dijkstra_shortest_paths.rs @@ -6,6 +6,7 @@ use graphene::{ algo::DijkstraShortestPaths, core::{ property::{AddEdge, ConnectedGraph, HasVertex, VertexInGraph}, + proxy::EdgeWeightMap, Directed, Ensure, Graph, GraphDeref, Release, Undirected, }, }; @@ -29,7 +30,8 @@ mod __ let mut visited = HashSet::new(); visited.insert(mock.get_vertex().clone()); let mut visited_once = true; - DijkstraShortestPaths::new(mock.graph(), |w| w.value).for_each(|(_, v, _)| { + let e_map = EdgeWeightMap::new(mock.graph(), |_, _, w| w.value); + DijkstraShortestPaths::new(&e_map).for_each(|(_, v, _)| { visited_once &= visited.insert(v); }); @@ -51,8 +53,10 @@ mod __ let v_map = graph.join(&g2); // Ensure that no visited vertex comes from outside the start component - DijkstraShortestPaths::new(&VertexInGraph::ensure_unvalidated(graph, v), |w| w.value) - .all(|(_, v, _)| v_map.values().all(|&new_v| v != new_v)) + let e_map = EdgeWeightMap::new(VertexInGraph::ensure_unvalidated(graph, v), |_, _, w| { + w.value + }); + DijkstraShortestPaths::new(&e_map).all(|(_, v, _)| v_map.values().all(|&new_v| v != new_v)) } /// Tests that the paths returned are always increasing. @@ -63,10 +67,11 @@ mod __ path_weights.insert(g.get_vertex().clone(), 0); let mut len = 0; - for (source, sink, w) in DijkstraShortestPaths::new(&g, |w| w.value) + let e_map = EdgeWeightMap::new(g, |_, _, w| w.value); + for (source, sink, w) in DijkstraShortestPaths::new(&e_map) { let len_to_source = path_weights[&source]; - let len_to_sink = len_to_source + w.value; + let len_to_sink = len_to_source + w; if len_to_sink < len { return false; @@ -84,7 +89,8 @@ mod __ let mut seen = HashSet::new(); seen.insert(g.get_vertex().clone()); - for (source, sink, _) in DijkstraShortestPaths::new(&g, |w| w.value) + let e_map = EdgeWeightMap::new(g, |_, _, w| w.value); + for (source, sink, _) in DijkstraShortestPaths::new(&e_map) { if !seen.contains(&source) { @@ -126,9 +132,11 @@ fn directed_doesnt_visit_incoming_component( .unwrap(); } + let e_map = EdgeWeightMap::new(VertexInGraph::ensure_unvalidated(graph, v), |_, _, w| { + w.value + }); // Ensure that no visited vertex comes from outside the start component - DijkstraShortestPaths::new(&VertexInGraph::ensure_unvalidated(graph, v), |w| w.value) - .all(|(_, v, _)| v_map.values().all(|&new_v| v != new_v)) + DijkstraShortestPaths::new(&e_map).all(|(_, v, _)| v_map.values().all(|&new_v| v != new_v)) } /// Tests for directed graphs that any component with an edge to it from the @@ -164,6 +172,8 @@ fn directed_visits_outgoing_component( // Ensure that all vertices are visited let count = graph.all_vertices().count() - 1; - DijkstraShortestPaths::new(&VertexInGraph::ensure_unvalidated(graph, v), |w| w.value).count() - == count + let e_map = EdgeWeightMap::new(VertexInGraph::ensure_unvalidated(graph, v), |_, _, w| { + w.value + }); + DijkstraShortestPaths::new(&e_map).count() == count } diff --git a/tests/algo/search.rs b/tests/algo/search.rs index f4e7eb0..3aca6f3 100644 --- a/tests/algo/search.rs +++ b/tests/algo/search.rs @@ -6,24 +6,21 @@ use graphene::{ algo::{Bfs, Dfs, Spfs}, core::{ property::{AddEdge, ConnectedGraph, HasVertex, VertexInGraph}, + proxy::EdgeWeightMap, Directed, Ensure, Graph, GraphDeref, Release, Undirected, }, }; use std::collections::HashSet; -/// Constructs a new Spfs for graphs with MockEdgeWeight edge weights. -/// -/// Used in the duplicate to instantiate `Spfs` -fn spfs_new>(g: &G) -> Spfs -{ - Spfs::new(g, |v| v.value) -} - #[duplicate_item( - module search_algo_new; - [ dfs ] [ Dfs::new_simple ]; - [ bfs ] [ Bfs::new ]; - [ spfs ] [ spfs_new ] + module search_algo_new(graph); + [ dfs ] [ Dfs::new_simple(graph) ]; + [ bfs ] [ Bfs::new(graph) ]; + [ spfs ] [ + let ref_graph = graph; + let map_graph = EdgeWeightMap::new(ref_graph, |_,_,w| w.value); + Spfs::new(&map_graph) + ] )] mod module { @@ -51,7 +48,7 @@ mod module visited.insert(mock.get_vertex().clone()); let mut visited_once = true; - search_algo_new(mock.graph()).for_each(|v| { + search_algo_new([mock.graph()]).for_each(|v| { // Track whether we have seen the vertex before visited_once &= visited.insert(v); }); @@ -74,7 +71,7 @@ mod module let v_map = graph.join(&g2); // Ensure that no visited vertex comes from outside the start component - search_algo_new(&VertexInGraph::ensure_unvalidated(graph, v)) + search_algo_new([&VertexInGraph::ensure_unvalidated(graph, v)]) .all(|visit| v_map.values().all(|&new_v| visit != new_v)) } } @@ -110,7 +107,7 @@ mod module } // Ensure that no visited vertex comes from outside the start component - search_algo_new(&VertexInGraph::ensure_unvalidated(graph, v)) + search_algo_new([&VertexInGraph::ensure_unvalidated(graph, v)]) .all(|visit| v_map.values().all(|&new_v| visit != new_v)) } @@ -146,6 +143,6 @@ mod module // Ensure that all vertices are visited except the start let count = graph.all_vertices().count() - 1; - search_algo_new(&VertexInGraph::ensure_unvalidated(graph, v)).count() == count + search_algo_new([&VertexInGraph::ensure_unvalidated(graph, v)]).count() == count } } diff --git a/tests/core/ensure.rs b/tests/core/ensure.rs index ecfbf4a..5cf9a3c 100644 --- a/tests/core/ensure.rs +++ b/tests/core/ensure.rs @@ -80,13 +80,13 @@ impl Graph for MockEnsurer fn all_edges( &self, - ) -> impl '_ + Iterator)>; + ) -> impl Iterator)>; - fn edges_between<'a: 'b, 'b>( - &'a self, - source: impl 'b + Borrow, - sink: impl 'b + Borrow, - ) -> impl 'b + Iterator>; + fn edges_between( + &self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator>; } } } @@ -98,13 +98,13 @@ where to self.0.graph_mut() { fn all_vertices_weighted_mut( &mut self, - ) -> impl '_ + Iterator; + ) -> impl Iterator; - fn edges_between_mut<'a: 'b, 'b>( - &'a mut self, - source: impl 'b + Borrow, - sink: impl 'b + Borrow, - ) -> impl 'b + Iterator; + fn edges_between_mut( + &mut self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator; } } } @@ -185,13 +185,13 @@ impl Graph for MockUnloadedEnsurer fn all_edges( &self, - ) -> impl '_ + Iterator)>; + ) -> impl Iterator)>; - fn edges_between<'a: 'b, 'b>( - &'a self, - source: impl 'b + Borrow, - sink: impl 'b + Borrow, - ) -> impl 'b + Iterator>; + fn edges_between( + &self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator>; } } } @@ -201,16 +201,16 @@ where { fn all_vertices_weighted_mut( &mut self, - ) -> impl '_ + Iterator + ) -> impl Iterator { self.0.graph_mut().all_vertices_weighted_mut() } - fn edges_between_mut<'a: 'b, 'b>( - &'a mut self, - source: impl 'b + Borrow, - sink: impl 'b + Borrow, - ) -> impl 'b + Iterator + fn edges_between_mut( + &mut self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator { self.0.graph_mut().edges_between_mut(source, sink) } diff --git a/tests/core/property/connectedness.rs b/tests/core/property/connectedness.rs index 8526bde..c8a8e99 100644 --- a/tests/core/property/connectedness.rs +++ b/tests/core/property/connectedness.rs @@ -235,46 +235,49 @@ mod module mod __ { use super::*; - use graphene::core::Ensure; + use graphene::core::{proxy::EdgeWeightMap, Ensure}; - /// Tests `eccentricity_weighted` + /// Tests `eccentricity` #[quickcheck] - fn eccentricity_weighted( - Arb(g): Arb>>>, - ) -> bool + fn eccentricity(Arb(g): Arb>>>) -> bool { - let eccentricity = g.eccentricity_weighted(|w| w.value); - DijkstraShortestPaths::distances(&g, |w| w.value).all(|(_, dist)| dist <= eccentricity) + let e_map = EdgeWeightMap::ensure_unvalidated(&g, |_, _, w| w.value); + let eccentricity = e_map.eccentricity(); + let success = + DijkstraShortestPaths::distances(&e_map).all(|(_, dist)| dist <= eccentricity); + success } - /// Tests `diameter_weighted` + /// Tests `diameter` #[quickcheck] - fn diameter_weighted(Arb(g): Arb>>) -> bool + fn diameter(Arb(g): Arb>>) -> bool { - let diameter = g.diameter_weighted(|w| w.value); - g.all_vertices().all(|v| { - VertexInGraph::ensure_unvalidated(&g, v).eccentricity_weighted(|w| w.value) <= diameter - }) + let e_map = EdgeWeightMap::ensure_unvalidated(&g, |_, _, w| w.value); + let diameter = e_map.diameter(); + g.all_vertices() + .all(|v| VertexInGraph::ensure_unvalidated(&e_map, v).eccentricity() <= diameter) } - /// Tests `radius_weighted` + /// Tests `radius` #[quickcheck] - fn radius_weighted(Arb(g): Arb>>) -> bool + fn radius(Arb(g): Arb>>) -> bool { - let radius = g.radius_weighted(|w| w.value); - g.all_vertices().all(|v| { - VertexInGraph::ensure_unvalidated(&g, v).eccentricity_weighted(|w| w.value) >= radius - }) + let e_map = EdgeWeightMap::ensure_unvalidated(&g, |_, _, w| w.value); + let radius = e_map.radius(); + g.all_vertices() + .all(|v| VertexInGraph::ensure_unvalidated(&e_map, v).eccentricity() >= radius) } - /// Tests `centers_weighted` + /// Tests `centers` #[quickcheck] - fn centers_weighted(Arb(g): Arb>>) -> bool + fn centers(Arb(g): Arb>>) -> bool { - let radius = g.radius_weighted(|w| w.value); - g.centers_weighted(|w| w.value).all(|v| { - VertexInGraph::ensure_unvalidated(&g, v).eccentricity_weighted(|w| w.value) == radius - }) + let e_map = EdgeWeightMap::ensure_unvalidated(&g, |_, _, w| w.value); + let radius = e_map.radius(); + let success = e_map + .centers() + .all(|v| VertexInGraph::ensure_unvalidated(&e_map, v).eccentricity() == radius); + success } } diff --git a/tests/mock_graph/mock_graph.rs b/tests/mock_graph/mock_graph.rs index 2d5c381..5d03949 100644 --- a/tests/mock_graph/mock_graph.rs +++ b/tests/mock_graph/mock_graph.rs @@ -190,20 +190,18 @@ impl Graph for MockGraph .map(|(&v, w)| (MockVertex { value: v }, w)) } - fn all_edges( - &self, - ) -> impl '_ + Iterator + fn all_edges(&self) -> impl Iterator { self.edges .iter() .map(|(so, si, w)| (MockVertex { value: *so }, MockVertex { value: *si }, w)) } - fn edges_between<'a: 'b, 'b>( - &'a self, - source: impl 'b + Borrow, - sink: impl 'b + Borrow, - ) -> impl 'b + Iterator + fn edges_between( + &self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator { let source = source.borrow().value; let sink = sink.borrow().value; @@ -225,18 +223,18 @@ impl GraphMut for MockGraph { fn all_vertices_weighted_mut( &mut self, - ) -> impl '_ + Iterator + ) -> impl Iterator { self.vertices .iter_mut() .map(|(&v, w)| (MockVertex { value: v }, w)) } - fn edges_between_mut<'a: 'b, 'b>( - &'a mut self, - source: impl 'b + Borrow, - sink: impl 'b + Borrow, - ) -> impl 'b + Iterator + fn edges_between_mut( + &mut self, + source: impl Borrow, + sink: impl Borrow, + ) -> impl Iterator { let source = source.borrow().value; let sink = sink.borrow().value;