A high-performance Rust implementation of the popular Python NetworkX library for graph processing and analysis. This library provides a robust, memory-efficient, and thread-safe implementation of graph data structures and algorithms.
- Multiple Graph Types: Support for directed graphs (
DiGraph), undirected graphs (Graph), and multigraphs (MultiGraph,MultiDiGraph) - Generic Data Types: Store any type of data in nodes and edges using Rust's type system
- Memory Efficient: Optimized data structures for storing and accessing graph components
- Thread Safety: Built-in support for parallel graph processing using Rayon
- Rich Algorithm Set: Comprehensive implementation of graph algorithms including:
- Graph traversal (DFS, BFS)
- Shortest path finding (Dijkstra's algorithm)
- Connected components
- Subgraph extraction
- Degree calculations
- And more...
- API Compatibility: Familiar API design matching NetworkX's Python interface
- Zero Dependencies: Core functionality requires no external dependencies
- Extensible: Easy to extend with custom graph algorithms and data structures
The library is designed for performance with:
- Efficient adjacency list representation
- Optimized memory usage
- Parallel processing capabilities
- Zero-copy operations where possible
- Minimal allocations
Add to your Cargo.toml:
[dependencies]
networkx-rs = "0.1.2"Basic usage:
use networkx_rs::Graph;
// Create a directed graph
let mut graph = Graph::<String, f64>::new(true);
// Add nodes
let n1 = graph.add_node("Node 1".to_string());
let n2 = graph.add_node("Node 2".to_string());
// Add an edge
graph.add_edge(n1, n2, 1.5);
// Find shortest path
let (path, cost) = graph.shortest_path(n1, n2, |&w| w).unwrap();This library includes comprehensive unit tests and integration tests to ensure correctness and reliability.
Run all unit tests:
cargo testRun tests for a specific module:
cargo test graph_tests
cargo test layout_tests
cargo test multigraph_testsRun a specific test:
cargo test test_spring_layout
cargo test test_spring_layout_with_json_weightsThe repository contains example files demonstrating various features of the NetworkX Rust library:
Demonstrates how to create a grid-based maze and find the shortest path through it.
Shows basic graph operations, graph traversals, and shortest path finding.
Demonstrates using MultiGraph and MultiDiGraph for representing transportation networks and network flows.
A more complex example showing how to model a subway network and find optimal routes based on different metrics (distance vs. travel time).
Benchmarks sequential vs. parallel implementations of various graph algorithms.
These example files demonstrate the library's capabilities and can serve as starting points for your own graph processing applications.
The examples in this repository demonstrate the following features of the NetworkX Rust library:
- Creating directed and undirected graphs
- Adding and removing nodes and edges
- Querying graph information (node count, edge count, degree, etc.)
- Traversing the graph (iterating over nodes, edges, neighbors)
- Depth-First Search (DFS)
- Breadth-First Search (BFS)
- Shortest path finding using Dijkstra's algorithm
- DiGraph (Directed Graph)
- MultiGraph (Undirected graph with multiple edges between nodes)
- MultiDiGraph (Directed graph with multiple edges between nodes)
- Parallel node search
- Parallel BFS traversal
- Parallel connected components calculation
- Parallel subgraph extraction
Demonstrates how to create a grid-based maze and find the shortest path through it.
Run with:
cargo run --bin maze_finderShows basic graph operations, graph traversals, and shortest path finding.
Run with:
cargo run --bin networkx_examplesDemonstrates using MultiGraph and MultiDiGraph for representing transportation networks and network flows.
Run with:
cargo run --bin multi_graph_exampleA more complex example showing how to model a subway network and find optimal routes based on different metrics (distance vs. travel time).
Run with:
cargo run --bin pathfinderBenchmarks sequential vs. parallel implementations of various graph algorithms.
Run with:
cargo run --bin parallel_graphGraph<T, E>: Basic graph structure supporting both directed and undirected graphsDiGraph<T, E>: Specialized directed graphMultiGraph<T, E>: Undirected graph supporting multiple edges between the same nodesMultiDiGraph<T, E>: Directed graph supporting multiple edges between the same nodes
// Create a directed graph
let directed_graph = Graph::<i32, f64>::new(true);
// Create an undirected graph
let undirected_graph = Graph::<String, u32>::new(false);
// Create a directed graph with a name
let named_graph = Graph::<String, f64>::with_name(true, "My Network");// Add a node
let node_id = graph.add_node(data);
// Remove a node
let removed_data = graph.remove_node(node_id);
// Check if a node exists
let exists = graph.has_node(node_id);
// Get node data
let data_ref = graph.get_node_data(node_id);
// Find nodes matching a criterion
let matching_nodes = graph.find_nodes(|&data| data > 50);// Add an edge
graph.add_edge(from_node, to_node, weight);
// Remove an edge
let weight = graph.remove_edge(from_node, to_node);
// Check if an edge exists
let exists = graph.has_edge(from_node, to_node);
// Get edge weight
let weight = graph.get_edge_data(from_node, to_node);// Depth-first search
let dfs_result = graph.dfs(start_node);
// Breadth-first search
let bfs_result = graph.bfs(start_node);
// Shortest path
let (path, cost) = graph.shortest_path(from_node, to_node, |&weight| weight as f64);// Add an edge (returns a unique edge ID)
let edge_id = multigraph.add_edge(from_node, to_node, weight);
// Remove a specific edge by ID
let removed = multigraph.remove_edge(from_node, to_node, &edge_id);
// Get all edges between two nodes
let edges = multigraph.edges_between(from_node, to_node);// Get successors (outgoing neighbors)
let successors = digraph.successors(node);
// Get predecessors (incoming neighbors)
let predecessors = digraph.predecessors(node);
// Get in-degree
let in_degree = digraph.in_degree(node);
// Get out-degree
let out_degree = digraph.out_degree(node);In our testing, the parallel implementations sometimes performed slower than their sequential counterparts for small to medium-sized graphs. This could be due to:
- The overhead of thread creation and management
- Running in debug mode without optimizations
- The specific hardware configuration
- Implementation details of the parallel algorithms
For large graphs with complex operations, the parallel implementations may show better performance.
During testing, we observed the following limitations:
- The
to_undirected()method on directed graphs appears to have an issue where edge counts don't match expectations - Some layout functions like
circular_layoutandspring_layouthave different parameter requirements than documented - Parallel versions of algorithms may have different behavior than their sequential counterparts