Skip to content

Commit

Permalink
feat(algorithm): implement floyd-warshall
Browse files Browse the repository at this point in the history
  • Loading branch information
slavik-pastushenko committed Nov 4, 2024
1 parent d93cdbb commit 2eba5c3
Show file tree
Hide file tree
Showing 10 changed files with 353 additions and 43 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ documentation = "https://docs.rs/graph-algorithms-rs"
repository = "https://github.com/slavik-pastushenko/graph-algorithms-rs"

[features]
default = ["bellman_ford", "dijkstra"]
default = ["bellman_ford", "dijkstra", "floyd_warshall"]
bellman_ford = []
dijkstra = []
floyd_warshall = []

[lib]
name = "graph_algorithms"
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ Dijkstra's algorithm finds the shortest path from a starting node to all other n
### Bellman-Ford Algorithm
The Bellman-Ford algorithm computes shortest paths from a single source vertex to all of the other vertices in a weighted digraph. It can handle graphs with negative weight edges.

### Floyd-Warshall Algorithm
The Floyd-Warshall algorithm finds shortest paths between all pairs of vertices in a weighted graph. It can handle graphs with negative weights but no negative weight cycles.

### A* Algorithm (TODO)
A* is a pathfinding and graph traversal algorithm that is often used in many fields of computer science due to its completeness, optimality, and optimal efficiency.

Expand All @@ -31,9 +34,6 @@ BFS explores the graph level by level, starting from a given node. It is used fo
### Depth-First Search (DFS) (TODO)
DFS explores as far as possible along each branch before backtracking. It is used for pathfinding and topological sorting.

### Floyd-Warshall Algorithm (TODO)
The Floyd-Warshall algorithm finds shortest paths between all pairs of vertices in a weighted graph. It can handle graphs with negative weights but no negative weight cycles.

## Safety

This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in 100% safe Rust.
Expand Down
2 changes: 1 addition & 1 deletion examples/src/bellman_ford.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub fn run() -> Vec<i32> {
algorithm.add_edge(1, vec![(2, 1), (3, 2)]);
algorithm.add_edge(2, vec![(3, 5)]);

algorithm.run(0).unwrap_or_default()
algorithm.run(Some(0)).unwrap_or_default()
}

#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion examples/src/dijkstra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub fn run() -> Vec<usize> {
(2, vec![]),
]);

algorithm.run(0).unwrap_or_default()
algorithm.run(Some(0)).unwrap_or_default()
}

#[cfg(test)]
Expand Down
27 changes: 27 additions & 0 deletions examples/src/floyd_warshall.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use graph_algorithms::{FloydWarshallAlgorithm, GraphAlgorithm};

pub fn run() -> Vec<Vec<i32>> {
let mut algorithm = FloydWarshallAlgorithm::new();
algorithm.add_edge(0, 1, 1);
algorithm.add_edge(0, 2, 2);
algorithm.add_edge(1, 2, 1);
algorithm.add_edge(1, 0, 3);
algorithm.add_edge(2, 0, 4);
algorithm.add_edge(2, 1, 5);

algorithm.run(None).unwrap_or_default()
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_algorithm_run() {
let result = run();

assert_eq!(result[0][1], 1);
assert_eq!(result[0][2], 2);
assert_eq!(result[1][2], 1);
}
}
4 changes: 4 additions & 0 deletions examples/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
mod bellman_ford;
mod dijkstra;
mod floyd_warshall;

fn main() {
// Run the Dijkstra example
dijkstra::run();

// Run the Bellman-Ford example
bellman_ford::run();

// Run the Floyd-Warshall example
floyd_warshall::run();
}

#[cfg(test)]
Expand Down
46 changes: 29 additions & 17 deletions src/bellman_ford.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,18 +88,20 @@ impl GraphAlgorithm for BellmanFordAlgorithm {
type Node = isize;

/// Type of weight.
type Weight = i32;
type Weight = Vec<i32>;

/// Run Bellman-Ford Algorithm.
///
/// # Arguments
///
/// - `start`: The starting node.
/// - `start`: Starting node.
///
/// # Returns
///
/// Result containing a vector of shortest paths, or an error if applicable.
fn run(&self, start: Self::Node) -> Result<Vec<Self::Weight>, GraphError> {
fn run(&self, start: Option<Self::Node>) -> Result<Self::Weight, GraphError> {
let start = start.ok_or(GraphError::MissingStartNode)?;

let mut distances = vec![i32::MAX; self.total_vertices];
distances[start as usize] = 0;

Expand Down Expand Up @@ -149,6 +151,13 @@ mod tests {
assert_eq!(algorithm_default.edges.len(), 0);
}

#[test]
fn test_missing_start_node() {
let algorithm = BellmanFordAlgorithm::new();

assert_eq!(algorithm.run(None), Err(GraphError::MissingStartNode));
}

#[test]
fn test_run() {
let mut algorithm = BellmanFordAlgorithm {
Expand Down Expand Up @@ -215,7 +224,7 @@ mod tests {
algorithm.add_edge(source, vec![(destination, weight)]);
}

let result = algorithm.run(0).unwrap();
let result = algorithm.run(Some(0)).unwrap();

assert_eq!(result[0], 0);

Expand All @@ -233,7 +242,7 @@ mod tests {
let mut algorithm = BellmanFordAlgorithm::new();
algorithm.add_edge(0, vec![]);

assert_eq!(algorithm.run(0).unwrap(), vec![0]);
assert_eq!(algorithm.run(Some(0)).unwrap(), vec![0]);
}

#[test]
Expand All @@ -243,7 +252,7 @@ mod tests {
algorithm.add_edge(1, vec![(2, 1), (3, 2)]);
algorithm.add_edge(2, vec![(3, 5)]);

assert_eq!(algorithm.run(0).unwrap(), vec![0, 4, 3, 6]);
assert_eq!(algorithm.run(Some(0)).unwrap(), vec![0, 4, 3, 6]);
}

#[test]
Expand All @@ -253,7 +262,7 @@ mod tests {
algorithm.add_edge(1, vec![(2, -2), (3, 2)]);
algorithm.add_edge(2, vec![(3, 3)]);

assert_eq!(algorithm.run(0).unwrap(), vec![0, 4, 2, 5]);
assert_eq!(algorithm.run(Some(0)).unwrap(), vec![0, 4, 2, 5]);
}

#[test]
Expand All @@ -265,7 +274,7 @@ mod tests {
algorithm.add_edge(3, vec![]);

assert_eq!(
algorithm.run(0).unwrap(),
algorithm.run(Some(0)).unwrap(),
vec![0, i32::MAX, i32::MAX, i32::MAX]
);
}
Expand All @@ -277,7 +286,7 @@ mod tests {
algorithm.add_edge(1, vec![(2, 1), (3, 2)]);
algorithm.add_edge(2, vec![(3, 5)]);

assert_eq!(algorithm.run(1).unwrap(), vec![i32::MAX, 0, 1, 2]);
assert_eq!(algorithm.run(Some(1)).unwrap(), vec![i32::MAX, 0, 1, 2]);
}

#[test]
Expand All @@ -286,7 +295,10 @@ mod tests {
algorithm.add_edge(0, vec![(1, 4)]);
algorithm.add_edge(2, vec![(3, 5)]);

assert_eq!(algorithm.run(0).unwrap(), vec![0, 4, i32::MAX, i32::MAX]);
assert_eq!(
algorithm.run(Some(0)).unwrap(),
vec![0, 4, i32::MAX, i32::MAX]
);
}

#[test]
Expand All @@ -298,18 +310,18 @@ mod tests {
(2, vec![(0, -1)]),
]);

assert_eq!(algorithm.run(0), Err(GraphError::NegativeWeightCycle));
assert_eq!(algorithm.run(Some(0)), Err(GraphError::NegativeWeightCycle));
}

#[test]
fn test_early_exit_no_updates() {
fn test_run_early_exit_no_updates() {
let mut algorithm = BellmanFordAlgorithm::new();
algorithm.add_edge(0, vec![(1, 2)]);
algorithm.add_edge(1, vec![(2, 3)]);
algorithm.add_edge(2, vec![(3, 4)]);
algorithm.add_edge(3, vec![(4, 1)]);

let result = algorithm.run(0).unwrap();
let result = algorithm.run(Some(0)).unwrap();

assert_eq!(result[0], 0);
assert_eq!(result[1], 2);
Expand All @@ -319,14 +331,14 @@ mod tests {
}

#[test]
fn test_early_exit_with_no_negative_cycle() {
fn test_run_early_exit_with_no_negative_cycle() {
let mut algorithm = BellmanFordAlgorithm::new();
algorithm.add_edge(0, vec![(1, 2)]);
algorithm.add_edge(1, vec![(2, 3)]);
algorithm.add_edge(2, vec![(3, -5)]);
algorithm.add_edge(3, vec![(4, 1)]);

let result = algorithm.run(0).unwrap();
let result = algorithm.run(Some(0)).unwrap();

assert_eq!(result[0], 0);
assert_eq!(result[1], 2);
Expand All @@ -336,12 +348,12 @@ mod tests {
}

#[test]
fn test_early_exit_with_negative_cycle() {
fn test_run_early_exit_with_negative_cycle() {
let mut algorithm = BellmanFordAlgorithm::new();
algorithm.add_edge(0, vec![(1, 1)]);
algorithm.add_edge(1, vec![(2, -2)]);
algorithm.add_edge(2, vec![(0, -1)]); // Negative cycle here

assert_eq!(algorithm.run(0), Err(GraphError::NegativeWeightCycle));
assert_eq!(algorithm.run(Some(0)), Err(GraphError::NegativeWeightCycle));
}
}
Loading

0 comments on commit 2eba5c3

Please sign in to comment.