Skip to content

Commit

Permalink
add utils methods to the topology
Browse files Browse the repository at this point in the history
  • Loading branch information
guissalustiano committed Jan 19, 2024
1 parent d852ee9 commit 1c8bab9
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 68 deletions.
36 changes: 1 addition & 35 deletions examples/all_paths.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
use petgraph::graph::NodeIndex;
use valley_free::{RelType, Topology};

type GraphPath = Vec<NodeIndex>;

fn main() {
let topo = Topology::from_edges(vec![
(1, 2, RelType::ProviderToCustomer),
Expand All @@ -15,38 +12,7 @@ fn main() {
let start = 4;
let topo = topo.paths_graph(start);

let start_idx = topo.index_of(start).unwrap(); // Node index
let mut stack: Vec<(NodeIndex, GraphPath)> = vec![(start_idx, vec![start_idx])];
let mut visited: Vec<NodeIndex> = vec![];
let mut all_paths: Vec<GraphPath> = vec![];

while !stack.is_empty() {
let (node_idx, path) = stack.pop().unwrap();

if visited.contains(&node_idx) {
continue;
}

visited.push(node_idx);
all_paths.push(path.clone());

let childrens = topo
.graph
.neighbors_directed(node_idx, petgraph::Direction::Outgoing)
.map(|child_idx| {
let mut path = path.clone();
path.push(child_idx);
(child_idx, path)
});
stack.extend(childrens);
}

for path in all_paths {
let path = path
.iter()
.map(|node_idx| topo.asn_of(*node_idx))
.collect::<Vec<_>>();

for path in topo.path_to_all_ases(start).unwrap() {
println!("{:?}", path);
}
}
18 changes: 4 additions & 14 deletions examples/all_paths_between_two_ases.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::fs::File;

use petgraph::algo::all_simple_paths;
use valley_free::Topology;

fn main() {
Expand All @@ -11,20 +10,11 @@ fn main() {
let universidade_de_sao_paulo_asn = 28571;
let ut_path = topo.paths_graph(university_of_twente_asn);

let paths = all_simple_paths::<Vec<_>, _>(
&ut_path.graph,
ut_path.index_of(university_of_twente_asn).unwrap(),
ut_path.index_of(universidade_de_sao_paulo_asn).unwrap(),
0,
None,
);

println!("Paths from UT to USP:");
for path in paths {
let path = path
.iter()
.map(|node| ut_path.asn_of(*node))
.collect::<Vec<_>>();
for path in ut_path
.all_paths_to(university_of_twente_asn, universidade_de_sao_paulo_asn)
.unwrap()
{
println!(" {:?}", path);
}
}
23 changes: 4 additions & 19 deletions examples/shortest_path_between_two_ases.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::fs::File;

use petgraph::algo::astar;
use valley_free::{RelType, Topology};
use valley_free::Topology;

fn main() {
let file = File::open("20231201.as-rel.txt").unwrap();
Expand All @@ -12,23 +11,9 @@ fn main() {
let ut_path = topo.paths_graph(university_of_twente_asn);

// Use A* to find the shortest path between two nodes
let (_len, path) = astar(
&ut_path.graph,
ut_path.index_of(university_of_twente_asn).unwrap(),
|finish| finish == ut_path.index_of(universidade_de_sao_paulo_asn).unwrap(),
|edge| match edge.weight() {
// priorize pearing
RelType::PearToPear => 0,
RelType::ProviderToCustomer => 1,
RelType::CustomerToProvider => 2,
},
|_| 0,
)
.unwrap();
let path = path
.iter()
.map(|node| ut_path.asn_of(*node))
.collect::<Vec<_>>();
let path = ut_path
.shortest_path_to(university_of_twente_asn, universidade_de_sao_paulo_asn)
.unwrap();

println!("Path from UT to USP: {:?}", path);
}
151 changes: 151 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::{
};

use petgraph::{
algo::{all_simple_paths, astar},
graph::{DiGraph, NodeIndex},
visit::EdgeRef,
};
Expand All @@ -25,10 +26,14 @@ impl Default for RelType {
}
}

#[derive(Debug, Clone)]
pub struct Topology {
pub graph: DiGraph<u32, RelType>,
}

pub type TopologyPath = Vec<u32>;
type TopologyPathIndex = Vec<NodeIndex>;

#[derive(Debug)]
pub enum TopologyError {
IoError(io::Error),
Expand Down Expand Up @@ -300,6 +305,92 @@ impl Topology {
// assert!(!is_cyclic_directed(&graph));
Topology { graph }
}

pub fn shortest_path_to(&self, source: u32, target: u32) -> Option<TopologyPath> {
let source_index = self.index_of(source)?;
let target_index = self.index_of(target)?;

// Use A* to find the shortest path between two nodes
let (_len, path) = astar(
&self.graph,
source_index,
|finish| finish == target_index,
|edge| match edge.weight() {
// priorize pearing
RelType::PearToPear => 0,
RelType::ProviderToCustomer => 1,
RelType::CustomerToProvider => 2,
},
|_| 0,
)
.unwrap();

let path = path.iter().map(|node| self.asn_of(*node)).collect();

Some(path)
}

pub fn all_paths_to(
&self,
source: u32,
target: u32,
) -> Option<impl Iterator<Item = TopologyPath> + '_> {
let source_index = self.index_of(source)?;
let target_index = self.index_of(target)?;

let paths = all_simple_paths::<TopologyPathIndex, _>(
&self.graph,
source_index,
target_index,
0,
None,
);

let paths = paths.map(move |path| {
path.iter()
.map(|node| self.asn_of(*node))
.collect::<Vec<u32>>()
});

Some(paths)
}

pub fn path_to_all_ases(&self, source: u32) -> Option<Vec<TopologyPath>> {
let source_index = self.index_of(source)?;

let mut stack: Vec<(NodeIndex, TopologyPathIndex)> =
vec![(source_index, vec![source_index])];
let mut visited: Vec<NodeIndex> = vec![];
let mut all_paths: Vec<TopologyPathIndex> = vec![];

while !stack.is_empty() {
let (node_idx, path) = stack.pop().unwrap();

if visited.contains(&node_idx) {
continue;
}

visited.push(node_idx);
all_paths.push(path.clone());

let childrens = self
.graph
.neighbors_directed(node_idx, petgraph::Direction::Outgoing)
.map(|child_idx| {
let mut path = path.clone();
path.push(child_idx);
(child_idx, path)
});
stack.extend(childrens);
}

let all_paths = all_paths
.into_iter()
.map(|path| path.iter().map(|node| self.asn_of(*node)).collect())
.collect();

Some(all_paths)
}
}

#[cfg(test)]
Expand Down Expand Up @@ -331,6 +422,29 @@ mod test {
])
}

/* ┌─────┐
* │ 1 │
* └──┬──┘
* ┌──────┴─────┐
* ┌──▼──┐ ┌──▼──┐
* │ 2 │ │ 3 │
* └──┬──┘ └──┬──┘
* ┌─────┴────┐ ┌────┴────┐
* ┌──▼──┐ ┌─▼──▼─┐ ┌──▼──┐
* │ 4 │ │ 05 │ │ 6 │
* └─────┘ └──────┘ └─────┘
*/
fn piramid_topology() -> Topology {
Topology::from_edges(vec![
(1, 2, RelType::ProviderToCustomer),
(1, 3, RelType::ProviderToCustomer),
(2, 4, RelType::ProviderToCustomer),
(2, 5, RelType::ProviderToCustomer),
(3, 5, RelType::ProviderToCustomer),
(3, 6, RelType::ProviderToCustomer),
])
}

#[test]
fn test_all_asns() {
let topo = diamond_topology();
Expand Down Expand Up @@ -441,4 +555,41 @@ mod test {
assert_eq!(topo.graph.edge_count(), 7);
assert!(!is_cyclic_directed(&topo.graph));
}

#[test]
fn test_shortest_path_to() {
let topo = piramid_topology();
let topo = topo.paths_graph(4);

let path = topo.shortest_path_to(4, 6).unwrap();
assert_eq!(path, vec![4, 2, 1, 3, 6]);
}

#[test]
fn test_all_paths_to() {
let topo = piramid_topology();
let topo = topo.paths_graph(4);

let paths = topo.all_paths_to(4, 5).unwrap().collect::<Vec<_>>();

assert!(paths.contains(&[4, 2, 5].into()));
assert!(paths.contains(&[4, 2, 1, 3, 5].into()));
assert_eq!(paths.len(), 2);
}

#[test]
fn test_path_to_all_ases() {
let topo = piramid_topology();
let topo = topo.paths_graph(4);

let paths = topo.path_to_all_ases(4).unwrap();

assert!(paths.contains(&[4].into()));
assert!(paths.contains(&[4, 2].into()));
assert!(paths.contains(&[4, 2, 5].into()) || paths.contains(&[4, 2, 1, 3, 5].into()));
assert!(paths.contains(&[4, 2, 1].into()));
assert!(paths.contains(&[4, 2, 1, 3].into()));
assert!(paths.contains(&[4, 2, 1, 3, 6].into()));
assert_eq!(paths.len(), 6);
}
}

0 comments on commit 1c8bab9

Please sign in to comment.