Graph library using adjacency list representation.
Please see the README on GitHub at https://github.com/tpapak/algraph#readme
algraph
A pure Haskell graph library using adjacency list representation, featuring the Tide algorithm — a level-synchronous push-pull-relabel solver for the maximum flow problem.
Quick start
import Data.Graph.AdjacencyList
import Data.Graph.AdjacencyList.Network
import Data.Graph.AdjacencyList.PushRelabel.Pure
import Data.Graph.AdjacencyList.PushRelabel.Internal (netFlow, stCut)
import qualified Data.Map.Strict as M
main :: IO ()
main = do
-- Build a directed graph: 1 -> 2 -> 4
-- 1 -> 3 -> 4
let g = graphFromEdges [Edge 1 2, Edge 1 3, Edge 2 4, Edge 3 4]
caps = M.fromList [ (Edge 1 2, 10), (Edge 1 3, 5)
, (Edge 2 4, 8), (Edge 3 4, 7) ]
net = Network { graph = g, source = 1, sink = 4
, capacities = caps, flow = M.empty }
case pushRelabel net of
Left err -> putStrLn $ "Error: " ++ err
Right rg -> do
putStrLn $ "Max flow: " ++ show (netFlow rg) -- 13
putStrLn $ "Min cut: " ++ show (stCut rg) -- s-t cut edges
Features
- Maximum flow via the Tide algorithm — the only push-relabel implementation in the Haskell ecosystem, and the only sub-O(VE^2) pure functional max-flow solver available
- Exact arithmetic — capacities and flows use
Rational, guaranteeing correct results for arbitrary inputs (no floating-point rounding) - s-t minimum cut extracted directly from the max-flow residual graph
- BFS and DFS with level maps, parent maps, spanning trees, topological sort, longest path, and connectivity queries
- Floyd-Warshall all-pairs shortest paths (weighted and unweighted)
- Graph metrics — eccentricity, radius, diameter, density
- d-dimensional lattice generator — cubic lattices with periodic boundary conditions (toroidal topology) in arbitrary dimension
- QuickCheck-verified — Tide tested against FGL on 10,000 random graphs
Modules
| Module | Description |
|---|---|
Data.Graph.AdjacencyList | Core types (Vertex, Edge, Graph, Neighbors, EdgeMap) and graph constructors |
Data.Graph.AdjacencyList.Network | Flow network type (Network, Capacities, Capacity = Rational) |
Data.Graph.AdjacencyList.PushRelabel.Pure | Tide algorithm — pushRelabel :: Network -> Either String ResidualGraph |
Data.Graph.AdjacencyList.PushRelabel.Internal | Residual graph types, netFlow, stCut, push/pull primitives |
Data.Graph.AdjacencyList.BFS | Breadth-first search |
Data.Graph.AdjacencyList.DFS | Depth-first search, topological sort, longest path |
Data.Graph.AdjacencyList.WFI | Floyd-Warshall all-pairs shortest paths |
Data.Graph.AdjacencyList.Metrics | Eccentricity, radius, diameter, density |
Data.Graph.AdjacencyList.Grid | d-dimensional cubic lattices with periodic boundary conditions |
The Tide algorithm
Each iteration ("tide") performs three global sweeps on the residual graph:
- globalRelabel — BFS from sink (and source) to recompute vertex heights
- globalPull — right fold over active vertices: pull flow on reverse edges
- globalPush — left fold over active vertices: push flow on forward edges
The algorithm terminates when both the net flow and the set of overflowing vertices stabilize. A skip-globalRelabel optimization tracks whether any edge crosses a saturation boundary during push/pull; when none do, the BFS is skipped (1.25--1.6x speedup in practice).
Complexity
| Worst case | Practical | |
|---|---|---|
| Tides | O(V^2) | O(V) |
| Per-tide | O((V+E) log V) | O((V+E) log V) |
| Total | O(V^2 (V+E) log V) | O(V(V+E) log V) |
The log V factor comes from IntMap lookups in the pure Haskell implementation. The O(V^2) worst case requires exponentially-varying capacities; on graphs with polynomially-bounded capacity ratios (covering virtually all practical inputs), the tide count is empirically O(V).
How it compares
| Algorithm | Best known complexity |
|---|---|
| Edmonds-Karp (FGL) | O(VE^2) |
| Dinic | O(V^2 E) |
| Highest-label push-relabel | O(V^2 sqrt(E)) |
| Tide (as implemented) | O(V(V+E) log V) practical |
A companion Rust implementation (tide-maxflow) achieves O(VE) practical complexity using O(1) array-based data structures and has been benchmarked against Hi-PR, Google OR-Tools, LEMON, and Boost on 63 DIMACS graph instances.
Context in the Haskell graph ecosystem
| Library | Max flow | Shortest paths | Metrics | Generators |
|---|---|---|---|---|
| containers | -- | -- | -- | -- |
| fgl | Edmonds-Karp O(VE^2) | Dijkstra, BF | -- | -- |
| algebraic-graphs | -- | -- | -- | -- |
| algraph | Tide O(V(V+E) log V) | Floyd-Warshall APSP | eccentricity, radius, diameter, density | d-dim lattices (PBC) |
Key differences from fgl:
- Faster max flow — Tide is asymptotically better than fgl's Edmonds-Karp at all graph densities (O(V(V+E) log V) vs O(VE^2))
- Exact arithmetic — fgl uses
Doublefor max flow; algraph usesRational, guaranteeing correct results for arbitrary capacity values - Faster traversals — algraph's BFS/DFS are O((V+E) log V) vs fgl's O(V^2) due to fgl's O(V)-per-vertex
matchdecomposition - APSP — Floyd-Warshall is built in; fgl only offers single-source algorithms
fgl has broader algorithm coverage (SCC, dominators, MST, Dijkstra, transitive closure) and supports labeled nodes/edges.
BFS performance vs fgl
fgl's BFS uses match at each vertex, making it O(V^2) instead of the textbook O(V+E). algraph's BFS is O((V+E) log V) using IntMap/IntSet. The gap widens with graph size:
| Graph | V | E | algraph | fgl | fgl/algraph |
|---|---|---|---|---|---|
| Grid 100x100 | 10K | 20K | 51 ms | 53 ms | 1.0x |
| Grid 200x200 | 40K | 80K | 258 ms | 337 ms | 1.3x |
| Grid 500x500 | 250K | 500K | 1675 ms | 2848 ms | 1.7x |
| Grid 1000x1000 | 1M | 2M | 6956 ms | 24316 ms | 3.5x |
| Layered 20x50 | 1K | 48K | 60 ms | 296 ms | 5.0x |
| Layered 50x100 | 5K | 490K | 695 ms | 1487 ms | 2.1x |
Building
Requires Stack:
git clone https://github.com/tpapak/algraph
cd algraph
stack build
Testing
stack test
The test suite includes:
- Unit tests for graph construction, BFS, DFS, grid lattices, Floyd-Warshall, and graph metrics
- Tide max-flow correctness test against FGL on a reference network
- QuickCheck property: Tide vs FGL max-flow agreement on 10,000 random graphs
License
LGPL-3 -- see LICENSE.