yog
Yog - A comprehensive graph algorithm library for Gleam.
Provides efficient implementations of classic graph algorithms with a clean, functional API.
Quick Start
import yog
import yog/pathfinding
import gleam/int
pub fn main() {
let graph =
yog.directed()
|> yog.add_node(1, "Start")
|> yog.add_node(2, "Middle")
|> yog.add_node(3, "End")
|> yog.add_edge(from: 1, to: 2, with: 5)
|> yog.add_edge(from: 2, to: 3, with: 3)
|> yog.add_edge(from: 1, to: 3, with: 10)
case pathfinding.shortest_path(
in: graph,
from: 1,
to: 3,
with_zero: 0,
with_add: int.add,
with_compare: int.compare
) {
Some(path) -> {
// Path(nodes: [1, 2, 3], total_weight: 8)
io.println("Shortest path found!")
}
None -> io.println("No path exists")
}
}
Modules
Core
-
yog/model- Graph data structures and basic operations- Create directed/undirected graphs
- Add nodes and edges
- Query successors, predecessors, neighbors
-
yog/builder/labeled- Build graphs with arbitrary labels- Use strings or any type as node identifiers
- Automatically maps labels to internal integer IDs
- Convert to standard Graph for use with all algorithms
Algorithms
-
yog/pathfinding- Shortest path algorithms- Dijkstra’s algorithm (non-negative weights)
- A* search (with heuristics)
- Bellman-Ford (negative weights, cycle detection)
-
yog/traversal- Graph traversal- Breadth-First Search (BFS)
- Depth-First Search (DFS)
- Early termination support
-
yog/mst- Minimum Spanning Tree- Kruskal’s algorithm with Union-Find
-
yog/topological_sort- Topological ordering- Kahn’s algorithm
- Lexicographical variant (heap-based)
-
yog/components- Connected components- Tarjan’s algorithm for Strongly Connected Components (SCC)
-
yog/connectivity- Graph connectivity analysis- Tarjan’s algorithm for bridges and articulation points
-
yog/min_cut- Minimum cut algorithms- Stoer-Wagner algorithm for global minimum cut
Data Structures
yog/disjoint_set- Union-Find / Disjoint Set- Path compression and union by rank
- O(α(n)) amortized operations (practically constant)
- Dynamic connectivity queries
- Generic over any type
Transformations
yog/transform- Graph transformations- Transpose (O(1) edge reversal!)
- Map nodes and edges (functor operations)
- Filter nodes with auto-pruning
- Merge graphs
Visualization
yog/render- Graph visualization- Mermaid diagram generation (GitHub/GitLab compatible)
- Path highlighting for algorithm results
- Customizable node and edge labels
Features
- Functional and Immutable: All operations return new graphs
- Generic: Works with any node/edge data types
- Type-Safe: Leverages Gleam’s type system
- Well-Tested: 454+ tests covering all algorithms and data structures
- Efficient: Optimal data structures (pairing heaps, union-find)
- Documented: Every function has examples
Types
pub type Graph(node_data, edge_data) =
model.Graph(node_data, edge_data)
pub type GraphType =
model.GraphType
Values
pub fn add_edge(
graph: model.Graph(n, e),
from src: Int,
to dst: Int,
with weight: e,
) -> model.Graph(n, e)
Adds an edge to the graph with the given weight.
For directed graphs, adds a single edge from src to dst.
For undirected graphs, adds edges in both directions.
Example
graph
|> yog.add_edge(from: 1, to: 2, with: 10)
pub fn add_node(
graph: model.Graph(n, e),
id: Int,
data: n,
) -> model.Graph(n, e)
Adds a node to the graph with the given ID and data. If a node with this ID already exists, its data will be replaced.
Example
graph
|> yog.add_node(1, "Node A")
|> yog.add_node(2, "Node B")
pub fn add_simple_edge(
graph: model.Graph(n, Int),
from src: Int,
to dst: Int,
) -> model.Graph(n, Int)
Adds a simple edge with weight 1.
This is a convenience function for graphs with integer weights where a default weight of 1 is appropriate (e.g., unweighted graphs, hop counts).
Example
graph
|> yog.add_simple_edge(from: 1, to: 2)
|> yog.add_simple_edge(from: 2, to: 3)
// Both edges have weight 1
pub fn add_unweighted_edge(
graph: model.Graph(n, Nil),
from src: Int,
to dst: Int,
) -> model.Graph(n, Nil)
Adds an unweighted edge to the graph.
This is a convenience function for graphs where edges have no meaningful weight.
Uses Nil as the edge data type.
Example
let graph: Graph(String, Nil) = yog.directed()
|> yog.add_node(1, "A")
|> yog.add_node(2, "B")
|> yog.add_unweighted_edge(from: 1, to: 2)
pub fn all_nodes(graph: model.Graph(n, e)) -> List(Int)
Returns all unique node IDs that have edges in the graph.
pub fn directed() -> model.Graph(n, e)
Creates a new empty directed graph.
This is a convenience function that’s equivalent to yog.new(Directed),
but requires only a single import.
Example
import yog
let graph =
yog.directed()
|> yog.add_node(1, "Start")
|> yog.add_node(2, "End")
|> yog.add_edge(from: 1, to: 2, with: 10)
pub fn neighbors(
graph: model.Graph(n, e),
id: Int,
) -> List(#(Int, e))
Gets all nodes connected to the given node, regardless of direction. For undirected graphs, this is equivalent to successors. For directed graphs, this combines successors and predecessors.
pub fn new(graph_type: model.GraphType) -> model.Graph(n, e)
Creates a new empty graph of the specified type.
Example
import yog
import yog/model.{Directed}
let graph = yog.new(Directed)
pub fn predecessors(
graph: model.Graph(n, e),
id: Int,
) -> List(#(Int, e))
Gets nodes you came FROM to reach the given node (predecessors). Returns a list of tuples containing the source node ID and edge data.
pub fn successor_ids(
graph: model.Graph(n, e),
id: Int,
) -> List(Int)
Returns just the NodeIds of successors (without edge data). Convenient for traversal algorithms that only need the IDs.
pub fn successors(
graph: model.Graph(n, e),
id: Int,
) -> List(#(Int, e))
Gets nodes you can travel TO from the given node (successors). Returns a list of tuples containing the destination node ID and edge data.
pub fn undirected() -> model.Graph(n, e)
Creates a new empty undirected graph.
This is a convenience function that’s equivalent to yog.new(Undirected),
but requires only a single import.
Example
import yog
let graph =
yog.undirected()
|> yog.add_node(1, "A")
|> yog.add_node(2, "B")
|> yog.add_edge(from: 1, to: 2, with: 5)