# YogEx Cheatsheet
## Data Structure {:.col-2}
### Internal Representation
The core graph is a dual-map adjacency structure designed for efficiency.
* A graph can be `:directed` or `:undirected`.
* A Node ID can be any term.
* Node and Edge can hold any `term()` as data.
```elixir
# Internal structure visualization (Nodes: 1-6)
%Yog.Graph{
kind: :directed,
nodes: %{1 => "A", 2 => "B", 3 => "C", 4 => "D", 5 => "E", 6 => "F"},
out_edges: %{
1 => %{2 => 10, 3 => 5},
2 => %{4 => 8, 6 => 15},
3 => %{4 => 7, 5 => 12},
4 => %{5 => 10, 6 => 9},
5 => %{1 => 14},
6 => %{2 => 11}
},
in_edges: %{
2 => %{1 => 10, 6 => 11},
3 => %{1 => 5},
4 => %{2 => 8, 3 => 7},
5 => %{3 => 12, 4 => 10},
6 => %{2 => 15, 4 => 9},
1 => %{5 => 14}
}
}
```
### Representation
<div class="graphviz">
digraph G {
rankdir=LR;
bgcolor="transparent";
node [shape=circle, fontname="inherit"];
edge [fontname="inherit", fontsize=10];
1 [label="A"]; 2 [label="B"]; 3 [label="C"];
4 [label="D"]; 5 [label="E"]; 6 [label="F"];
1 -> 2 [label="10"]; 1 -> 3 [label="5"];
2 -> 4 [label="8"]; 2 -> 6 [label="15"];
3 -> 4 [label="7"]; 3 -> 5 [label="12"];
4 -> 5 [label="10"]; 4 -> 6 [label="9"];
5 -> 1 [label="14"];
6 -> 2 [label="11"];
}
</div>
### Performance Profile
- **Storage**: O(V + E) memory.
- **Node Access**: O(1) lookup in `nodes` map.
- **Edge Check**: O(1) lookup in `out_edges`.
- **Successors**: O(1) lookup of neighbor map in `out_edges`.
- **Predecessors**: O(1) lookup of parent map in `in_edges`.
- **Transpose**: O(1) swapping of in_edges and out_edges
## Core Graph Operations {:.col-2}
### Creation
We can create a graph directly or from edges.
`Yog` module contains helper functions to create graphs from edges and has
`:directed` or `:undirected` as first arg.
- `weighted` - Weight is provided
- `unweighted` - Weight is not provided (`nil`)
- `simple` - Weight is one
```elixir
# Create directed graph
Yog.directed()
# Create undirected graph
Yog.undirected()
# Custom graph type
Yog.new(:directed)
# From edges
Yog.from_edges(:directed, [{1, 2, 10}, {2, 3, 20}])
Yog.from_unweighted_edges(:undirected, [{1, 2}, {2, 3}])
Yog.from_simple_edges(:undirected, [{1, 2}, {2, 3}])
```
* Refer to `Yog` and `Yog.Model` for more information.
* See `Yog.Builder` modules for more ways to create graphs.
* See `Yog.IO` for more ways to import/export graphs.
* See `Yog.Generator` for more ways to generate graphs.
### Modification
A node ID can be any term but it is recommended to use numbers and keep the information
as node data.
`add_edge` and `add_edges` return `{:ok, graph}` or `{:error, reason}`. Use `add_edge!` and `add_edges!` to
raise an error if the edge cannot be added.
Please note that all modification functions return a new graph.
```elixir
# Add node with data
graph = Yog.add_node(graph, 1, %{name: "Center", lat: 0, lng: 0})
# Add edge - nodes must exist
{:ok, graph} = Yog.add_edge(graph, from: 1, to: 2, with: %{distance: 10, toll: false, speed_limit: 100})
{:ok, graph} = Yog.add_edges(graph, [{1, 2, 5}, {2, 3, 10}])
## Add edges - nodes are created if they don't exist
{:ok, graph} = Yog.add_edge_ensure(graph, from: 1, to: 0, with: 10, default: 0) # `nil` if default not provided
{:ok, graph} = Yog.add_edge_with(graph, 1, 0, 10, fn node_id -> DataStore.get(node_id) end)
# Remove node/edge
graph = Yog.remove_node(graph, 1)
graph = Yog.remove_edge(graph, 1, 2)
```
* See `Yog.Model` for more ways to add nodes and edges.
## Transform & Operations {:.col-2}
### Mapping & Filtering
Modify data or prune structure using functional primitives.
```elixir
# Transform node data
Yog.Transform.map_nodes(graph, &String.upcase/1)
# Transform edge weights
Yog.Transform.map_edges(graph, fn w -> w * 2 end)
# Prune structure
Yog.Transform.filter_nodes(graph, fn data -> data.active? end)
Yog.Transform.filter_edges(graph, fn u, v, _w -> u != v end)
```
### Graph Transformations
Topological mutations and re-orientations.
```elixir
# Reverse edge directions (O(1))
Yog.Transform.transpose(graph)
# Convert directionality
Yog.Transform.to_undirected(graph, &min/2)
Yog.Transform.to_directed(graph)
```
### Set Operations
Combine or compare graphs as sets of nodes and edges.
```elixir
# Operations from Yog.Operation
Yog.Operation.union(graph_a, graph_b)
Yog.Operation.intersection(graph_a, graph_b)
Yog.Operation.difference(graph_a, graph_b)
```
* See `Yog.Operation` for `composition` and `isomorphic?`.
## Pathfinding & Traversal {:.col-2}
### Pathfinding Options
Algorithms use **semiring** parameters for custom optimizations.
```elixir
# Dijkstra/A* options
opts = [
from: 1, to: 10,
zero: 0, # Identity
add: &+/2, # Accumulator
compare: &Yog.Utils.compare/2 # Relation
]
Yog.Pathfinding.shortest_path(graph, opts)
```
### Algorithms & Traversal
Standard search and exploration functions.
```elixir
# Standard Dijkstra / A*
Yog.Pathfinding.Dijkstra.shortest_path(graph, from: 1, to: 10)
Yog.Pathfinding.BFS.shortest_path(graph, from: 1, to: 10)
# Fold over structure (BFS/DFS)
Yog.Traversal.fold(graph, from: 1, initial: [], with: callback)
```
### Implicit Search & Traversal
Explore dynamic or infinite state spaces defined by logic.
```elixir
# Expansion-based search (A*)
Yog.Pathfinding.AStar.implicit_a_star(
from: start_state,
successors: fn s -> [{next, cost}] end,
is_goal: fn s -> s == target end
)
# Walk over a virtual graph
Yog.Traversal.implicit_fold(
from: 1, using: :breadth_first,
successors_of: fn n -> [n + 1, n * 2] end,
with: fn count, _id, _meta -> {:continue, count + 1} end
)
```
### Path Results & Hydration
Results return node IDs; "hydration" retrieves the associated data.
```elixir
# Result internal structure
%{nodes: [1, 5, 10], total_weight: 42.0, distance: 2}
# Map IDs to their associated data
path = Yog.Pathfinding.Dijkstra.shortest_path(graph, from: 1, to: 10)
Enum.map(path.nodes, &Yog.Model.node(graph, &1))
```
## Properties & Checks {:.col-2}
### Reachability
Check connectivity and extract components.
```elixir
# Path existence check
Yog.Connectivity.reachable?(graph, 1, 10)
# Strongly Connected Components
Yog.Connectivity.strongly_connected_components(graph)
```
### Invariants & Metrics
Verify structural properties and calculate health.
```elixir
# DAG & Bipartite checks
Yog.Property.Cyclicity.is_dag?(graph)
Yog.Property.Bipartite.bipartite?(graph)
# Walk-based analysis
Yog.Traversal.fold_walk(over: graph, from: 1, with: callback)
```
* See `Yog.Traversal` for lexicographical and acyclic-guaranteed walks.
## Algorithms & Metrics {:.col-2}
### Centrality & Rankings
Determine the relative importance of nodes within a network.
- **Betweenness**: Nodes that act as bridges.
- **Closeness**: Nodes that can reach others quickly.
- **PageRank**: Probability-based ranking.
```elixir
# Measure node importance
Yog.Centrality.Brandes.betweenness_centrality(graph)
# Direct neighbor counts
Yog.Model.successors(graph, 1)
Yog.Model.predecessors(graph, 1)
```
* See `Yog.Centrality` for advanced sorting and weighting.
### Network Health
Measure the global structural quality and efficiency of the graph.
- **Diameter**: Longest shortest path (max separation).
- **Radius**: Min eccentricity (best central hub).
- **Assortativity**: Do hubs connect to hubs or leaves?
```elixir
# Check graph compactness
Yog.Health.diameter(graph)
Yog.Health.radius(graph)
# Social (positive) vs Bio (negative) patterns
assort = Yog.Health.assortativity(graph)
```
* Use `Yog.Health` for average path lengths and eccentricity.
### Community Detection
Partition nodes into densely connected modules (clusters).
- **Louvain/Leiden**: Fast, quality-guaranteed modularity optimization.
- **Label Propagation**: Linear-time, near-instant detection.
- **Modularity (Q)**: Metric for "strength" of the grouping.
```elixir
# Auto-detect clusters
res = Yog.Community.Louvain.detect(graph)
# Get metrics
Yog.Community.modularity(graph, res)
Yog.Community.sizes(res) # %{comm_id => count}
```
* See `Yog.Community` for hierarchical and local detection.
### Minimum Spanning Trees (MST)
Find a subset of edges that connects all nodes with the minimum (or maximum) total weight.
- **Prim/Kruskal**: Standards for minimizing edge weight.
- **Maximum MST**: Connect nodes using the heaviest edges (for redundancy/capacity).
- **Uniform Forest**: Sample a random spanning tree from all possible trees.
```elixir
# Standard Minimum Tree (Kruskal)
Yog.MST.kruskal(graph)
# Maximum Spanning Tree (MaxST)
Yog.MST.kruskal_max(graph)
Yog.MST.prim_max(graph, from: 1)
# Random Spanning Forest (Wilson's)
Yog.MST.uniform_spanning_tree(graph)
```
* Refer to `Yog.MST` for algorithm selection and weighted/unweighted variations.
### Connectivity & SCC
Analyze how nodes are grouped together in directed and undirected graphs.
- **SCC (Strongly Connected)**: For directed graphs (Tarjan/Kosaraju).
- **Critical Points**: Bridges and Articulation Points.
```elixir
# Find all Strongly Connected Components
Yog.Connectivity.scc(directed_graph)
# Analyze bridges and articulation points
results = Yog.Connectivity.analyze(undirected_graph)
results.bridges # [{1, 2}, {3, 4}]
results.articulation_points # [2, 5]
```
* See `Yog.Connectivity` for k-core and weakly connected components.
## DAG {:.col-2}
### Type Safety
Validate and wrap a directed graph as a `DAG` to guarantee it has no cycles.
```elixir
# Wrap a graph and prove acyclicity
{:ok, dag} = Yog.DAG.from_graph(graph)
# Convert back to general graph
Yog.DAG.to_graph(dag)
```
* Use `Yog.DAG.Model` for operations that maintain DAG invariants.
### Total Functions
Specialized algorithms that are guaranteed to succeed on a DAG without checking for cycles internally.
```elixir
# Linear time topological sort (Infallible)
sorted_nodes = Yog.DAG.topological_sort(dag)
# Find the longest path (Critical Path analysis)
path = Yog.DAG.longest_path(dag)
```
* See `Yog.DAG.Algorithm` for more DAG-specific logic.
## Maze & Grid {:.col-2}
### Grid Construction
Build 2D structures from nested lists with movement constraints.
- **Topologies**: `rook` (4-way), `queen` (8-way), `bishop` (diagonal).
- **Predicates**: `walkable` (only specific values), `avoiding` (all but values).
```elixir
maze_data = [
[".", ".", "#"],
[".", "#", "."],
[".", ".", "G"]
]
# Create an undirected grid where "." is walkable
grid = Yog.Builder.Grid.from_2d_list(
maze_data,
:undirected,
Yog.Builder.Grid.walkable(".")
)
# Convert to standard graph for pathfinding
graph = Yog.Builder.GridGraph.to_graph(grid)
```
* Use `Yog.Builder.Grid` for coordinate-to-node-ID mapping.
### Navigation & Heuristics
Estimate costs between grid cells for informed search (A*).
- **Manhattan**: Sum of cardinal steps (for Rook).
- **Chebyshev**: Max of row/col diffs (for Queen/8-way).
- **Octile**: Precise 8-way cost using √2 for diagonals.
```elixir
# Convert coordinates back and forth
id = Yog.Builder.Grid.coord_to_id(row, col, cols)
{r, c} = Yog.Builder.Grid.id_to_coord(id, cols)
# Distance estimate for A*
heuristic = fn a, b ->
Yog.Builder.Grid.manhattan_distance(a, b, cols)
end
```
* Refer to `Yog.Builder.Grid` for all distance formulas.
### Maze Generation
Create procedurally generated "perfect mazes" (spanning trees on a grid).
- **Recursive Backtracker**: Classic, twisty corridors.
- **Prim**: Radial texture, many dead ends.
- **Kruskal**: Well-balanced, uniform look.
```elixir
# Generate a 20x20 twisty maze
maze = Yog.Generator.Maze.recursive_backtracker(20, 20, seed: 123)
# Render to string for CLI output
IO.puts(Yog.Render.ASCII.grid_to_string(maze))
```
* See `Yog.Generator.Maze` for algorithm selection guide.
## IO & Serialization {:.col-2}
### Constructors
Import graphs from standard formats or data structures.
```elixir
# From adjacency lists/matrices
Yog.from_adjacency_list(:directed, [{"A", ["B", "C"]}])
# Parse external formats
Yog.IO.JSON.decode(json)
Yog.IO.GraphML.decode(xml)
```
### Export & Render
Serialize graphs for storage or visualization.
```elixir
# Export to basic list
Yog.to_adjacency_list(graph)
# Render to diagram strings
Yog.Render.Dot.render(graph)
Yog.Render.Mermaid.render(graph)
```
* See `Yog.IO` for LEDA, Pajek, and GDF support.
## Documentation & Navigation {:.col-2}
### Browser Shortcuts
- **`s`**: Open quick-search bar
- **`g`**: Go to Hex package docs
- **`?`**: Toggle shortcut help
- **`/`**: Focus search bar
### Linking Syntax (Backticks)
- `` `Module` ``: Link to a module
- `` `mod.fun/2` ``: Link to a function
- `` `t:mod.type/0` ``: Link to a type
- `` `c:mod.fun/1` ``: Link to a callback
- `` `[txt](`fun/1`)` ``: Custom link text