Skip to content

Commit 017bd5e

Browse files
committed
Refactored ultragraph's trait system and algo implementations for better maintainability.
Signed-off-by: Marvin Hansen <[email protected]>
1 parent 98e7c60 commit 017bd5e

14 files changed

+757
-543
lines changed

ultragraph/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ pub mod types;
1212
// Errors
1313
pub use crate::errors::graph_error::GraphError;
1414
// Traits
15-
pub use crate::traits::graph_algo::GraphAlgorithms;
15+
pub use crate::traits::graph_algo::*;
16+
pub use crate::traits::graph_algo_centrality::*;
17+
pub use crate::traits::graph_algo_pathfinder::*;
18+
pub use crate::traits::graph_algo_structural::*;
19+
pub use crate::traits::graph_algo_topological::*;
1620
pub use crate::traits::graph_freeze::Freezable;
1721
pub use crate::traits::graph_mut::GraphMut;
1822
pub use crate::traits::graph_traversal::GraphTraversal;

ultragraph/src/traits/graph_algo.rs

Lines changed: 12 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -3,73 +3,18 @@
33
* Copyright (c) "2025" . The DeepCausality Authors and Contributors. All Rights Reserved.
44
*/
55

6-
use crate::{GraphError, GraphView};
6+
use crate::traits::graph_algo_topological::TopologicalGraphAlgorithms;
7+
use crate::{CentralityGraphAlgorithms, PathfindingGraphAlgorithms, StructuralGraphAlgorithms};
78

8-
/// Defines a suite of high-performance, read-only analytical algorithms.
9+
/// A comprehensive suite of graph algorithms.
910
///
10-
/// This trait is intended for implementation on static, optimized graph structures
11-
/// like `next_graph::CsmGraph` to validate their structure and properties.
12-
pub trait GraphAlgorithms<N, W>: GraphView<N, W> {
13-
// --- Structural Validation Algorithms ---
14-
15-
/// Finds a single cycle in the graph and returns the path of nodes that form it.
16-
///
17-
/// This is the most powerful cycle detection method, as it not only confirms the
18-
/// presence of a cycle but also identifies the specific nodes involved. This is
19-
/// invaluable for debugging dynamically generated graphs.
20-
///
21-
/// # Returns
22-
/// `Some(Vec<usize>)` containing the sequence of node indices that form a cycle
23-
/// (e.g., `[0, 1, 0]`). Returns `None` if the graph is a DAG.
24-
fn find_cycle(&self) -> Result<Option<Vec<usize>>, GraphError>;
25-
26-
/// Checks if the graph contains any directed cycles.
27-
///
28-
/// This method should be implemented as a simple call to `self.find_cycle().is_some()`.
29-
fn has_cycle(&self) -> Result<bool, GraphError>;
30-
31-
/// Computes a topological sort of the graph, if it is a Directed Acyclic Graph (DAG).
32-
/// Returns `None` if the graph contains a cycle.
33-
fn topological_sort(&self) -> Result<Option<Vec<usize>>, GraphError>;
34-
35-
// --- Pathfinding and Reachability Algorithms ---
36-
37-
/// Checks if a path of any length exists from a start to a stop index.
38-
fn is_reachable(&self, start_index: usize, stop_index: usize) -> Result<bool, GraphError>;
39-
40-
/// Returns the length of the shortest path (in number of nodes) from a start to a stop index.
41-
fn shortest_path_len(
42-
&self,
43-
start_index: usize,
44-
stop_index: usize,
45-
) -> Result<Option<usize>, GraphError>;
46-
47-
/// Finds the complete shortest path from a start to a stop index.
48-
fn shortest_path(
49-
&self,
50-
start_index: usize,
51-
stop_index: usize,
52-
) -> Result<Option<Vec<usize>>, GraphError>;
53-
54-
/// Finds the shortest path in a weighted graph using Dijkstra's algorithm.
55-
///
56-
/// The edge weight type `W` must support addition, comparison, and have a zero value.
57-
///
58-
/// # Returns
59-
/// A tuple containing the sequence of node indices in the path and the total cost of that path.
60-
/// Returns `None` if no path exists.
61-
fn shortest_weighted_path(
62-
&self,
63-
start_index: usize,
64-
stop_index: usize,
65-
) -> Result<Option<(Vec<usize>, W)>, GraphError>
66-
where
67-
W: Copy + Ord + Default + std::ops::Add<Output = W>;
68-
69-
/// Finds all Strongly Connected Components in the graph using Tarjan's algorithm.
70-
///
71-
/// # Returns
72-
/// A vector of vectors, where each inner vector is a list of node indices
73-
/// belonging to a single SCC.
74-
fn strongly_connected_components(&self) -> Result<Vec<Vec<usize>>, GraphError>;
11+
/// This trait aggregates several focused algorithm traits into a single, convenient
12+
/// supertrait. A type that implements `GraphAlgorithms` has access to all methods
13+
/// from the component traits.
14+
pub trait GraphAlgorithms<N, W>:
15+
TopologicalGraphAlgorithms<N, W>
16+
+ PathfindingGraphAlgorithms<N, W>
17+
+ StructuralGraphAlgorithms<N, W>
18+
+ CentralityGraphAlgorithms<N, W>
19+
{
7520
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* SPDX-License-Identifier: MIT
3+
* Copyright (c) "2025" . The DeepCausality Authors and Contributors. All Rights Reserved.
4+
*/
5+
use crate::{GraphError, GraphView};
6+
7+
pub trait CentralityGraphAlgorithms<N, W>: GraphView<N, W> {
8+
/// Calculates the betweenness centrality of each node in the graph.
9+
///
10+
/// Betweenness centrality measures a node's importance by counting how often it
11+
/// appears on the shortest paths between all other pairs of nodes. It is a powerful
12+
/// metric for identifying bottlenecks, bridges, and critical control points in a network.
13+
///
14+
/// The implementation uses Brandes' algorithm, which is highly efficient.
15+
///
16+
/// # Arguments
17+
///
18+
/// * `directed`: If `true`, the calculation considers the graph's edge directions.
19+
/// If `false`, all edges are treated as bidirectional.
20+
/// * `normalized`: If `true`, the centrality scores are normalized by dividing by the
21+
/// number of possible pairs of nodes. This allows for comparison between graphs of
22+
/// different sizes. If `false`, the raw count of paths is returned.
23+
///
24+
/// # Returns
25+
///
26+
/// A `Result` containing a vector of `(node_index, centrality_score)` tuples.
27+
/// The centrality score is an `f64` representing the node's importance.
28+
/// The vector is unsorted.
29+
fn betweenness_centrality(
30+
&self,
31+
directed: bool,
32+
normalized: bool,
33+
) -> Result<Vec<(usize, f64)>, GraphError>;
34+
35+
/// Calculates betweenness centrality across a specific set of critical pathways.
36+
///
37+
/// This function is a highly efficient tool for targeted analysis, such as root cause
38+
/// investigation or identifying bottlenecks in specific set of graph pathways. Instead of
39+
/// analyzing all possible paths, it only considers the shortest paths between the
40+
/// start and end nodes of the pathways you provide.
41+
///
42+
/// The algorithm correctly routes these paths through the entire graph, ensuring that
43+
/// the results are accurate even if the shortest path temporarily leaves a conceptual
44+
/// "subgraph".
45+
///
46+
/// # Arguments
47+
/// * `pathways`: A slice of `(start_node, end_node)` tuples defining the pathways to analyze.
48+
/// * `directed`: If `true`, considers edge directions.
49+
/// * `normalized`: If `true`, normalizes scores by the number of provided pathways.
50+
///
51+
/// # Returns
52+
/// An unsorted vector of `(node_index, centrality_score)` tuples for nodes that lie on
53+
/// one or more of the specified pathways.
54+
///
55+
fn pathway_betweenness_centrality(
56+
&self,
57+
pathways: &[(usize, usize)],
58+
directed: bool,
59+
normalized: bool,
60+
) -> Result<Vec<(usize, f64)>, GraphError>;
61+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* SPDX-License-Identifier: MIT
3+
* Copyright (c) "2025" . The DeepCausality Authors and Contributors. All Rights Reserved.
4+
*/
5+
use crate::{GraphError, GraphView};
6+
7+
pub trait PathfindingGraphAlgorithms<N, W>: GraphView<N, W> {
8+
/// Checks if a path of any length exists from a start to a stop index.
9+
fn is_reachable(&self, start_index: usize, stop_index: usize) -> Result<bool, GraphError>;
10+
11+
/// Returns the length of the shortest path (in number of nodes) from a start to a stop index.
12+
fn shortest_path_len(
13+
&self,
14+
start_index: usize,
15+
stop_index: usize,
16+
) -> Result<Option<usize>, GraphError>;
17+
18+
/// Finds the complete shortest path from a start to a stop index.
19+
fn shortest_path(
20+
&self,
21+
start_index: usize,
22+
stop_index: usize,
23+
) -> Result<Option<Vec<usize>>, GraphError>;
24+
25+
/// Finds the shortest path in a weighted graph using Dijkstra's algorithm.
26+
///
27+
/// The edge weight type `W` must support addition, comparison, and have a zero value.
28+
///
29+
/// # Returns
30+
/// A tuple containing the sequence of node indices in the path and the total cost of that path.
31+
/// Returns `None` if no path exists.
32+
fn shortest_weighted_path(
33+
&self,
34+
start_index: usize,
35+
stop_index: usize,
36+
) -> Result<Option<(Vec<usize>, W)>, GraphError>
37+
where
38+
W: Copy + Ord + Default + std::ops::Add<Output = W>;
39+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
* SPDX-License-Identifier: MIT
3+
* Copyright (c) "2025" . The DeepCausality Authors and Contributors. All Rights Reserved.
4+
*/
5+
use crate::{GraphError, GraphView};
6+
7+
pub trait StructuralGraphAlgorithms<N, W>: GraphView<N, W> {
8+
/// Finds all Strongly Connected Components in the graph using Tarjan's algorithm.
9+
///
10+
/// # Returns
11+
/// A vector of vectors, where each inner vector is a list of node indices
12+
/// belonging to a single SCC.
13+
fn strongly_connected_components(&self) -> Result<Vec<Vec<usize>>, GraphError>;
14+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* SPDX-License-Identifier: MIT
3+
* Copyright (c) "2025" . The DeepCausality Authors and Contributors. All Rights Reserved.
4+
*/
5+
use crate::{GraphError, GraphView};
6+
7+
pub trait TopologicalGraphAlgorithms<N, W>: GraphView<N, W> {
8+
/// Finds a single cycle in the graph and returns the path of nodes that form it.
9+
///
10+
/// This is the most powerful cycle detection method, as it not only confirms the
11+
/// presence of a cycle but also identifies the specific nodes involved. This is
12+
/// invaluable for debugging dynamically generated graphs.
13+
///
14+
/// # Returns
15+
/// `Some(Vec<usize>)` containing the sequence of node indices that form a cycle
16+
/// (e.g., `[0, 1, 0]`). Returns `None` if the graph is a DAG.
17+
fn find_cycle(&self) -> Result<Option<Vec<usize>>, GraphError>;
18+
19+
/// Checks if the graph contains any directed cycles.
20+
///
21+
/// This method should be implemented as a simple call to `self.find_cycle().is_some()`.
22+
fn has_cycle(&self) -> Result<bool, GraphError>;
23+
24+
/// Computes a topological sort of the graph, if it is a Directed Acyclic Graph (DAG).
25+
/// Returns `None` if the graph contains a cycle.
26+
fn topological_sort(&self) -> Result<Option<Vec<usize>>, GraphError>;
27+
}

ultragraph/src/traits/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,9 @@ pub mod graph_mut;
99
pub mod graph_traversal;
1010
pub mod graph_view;
1111

12+
pub mod graph_algo_centrality;
13+
pub mod graph_algo_pathfinder;
14+
pub mod graph_algo_structural;
15+
pub mod graph_algo_topological;
1216
pub mod graph_freeze;
1317
pub mod graph_unfreeze;

0 commit comments

Comments
 (0)