Model Module
Directed Acyclic Graph (DAG) type and operations.
A DAG is a directed graph with no cycles. This module provides a type-safe wrapper around Graph that guarantees acyclicity through construction, enabling total functions for operations that would be partial on general graphs.
Core Concept
The DAG type uses the "smart constructor" pattern: - fromGraph validates acyclicity and wraps the graph - addEdge returns Result because it could create a cycle - addNode/removeNode/removeEdge are safe (cannot create cycles)
Once constructed, the DAG type guarantees no cycles exist, allowing algorithms like topological sort to be total functions.
When to Use
Use Case |
Example |
|---|---|
Task Scheduling |
Build systems (Make, Gradle), CI/CD pipelines |
Dependencies |
Package managers (npm, cargo), module imports |
Partial Orders |
Preference rankings, event causality |
Control Flow |
Compiler IRs, dataflow graphs |
Version Control |
Git commit history, merge bases |
Bayesian Networks |
Probabilistic graphical models |
Key Properties
- Guaranteed acyclic: Type system prevents cycles at construction
- Topological sort always succeeds: No need to handle cycle errors
- Longest path is well-defined: Can find critical paths in O(V+E)
- Shortest path is efficient: O(V+E) vs O((V+E) log V) for general graphs
Comparison with Graph
Aspect |
Dag |
Graph |
|---|---|---|
Cycles allowed |
❌ No |
✅ Yes |
addEdge result |
|
|
topologicalSort |
Always succeeds |
May fail |
longestPath |
O(V+E) |
NP-hard |
shortestPath |
O(V+E) |
O((V+E) log V) |
Example
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: |
|
References
<summary> Core graph data structures and basic operations for the yog library. This module defines the fundamental `Graph` type and provides all basic operations for creating and manipulating graphs. The graph uses an adjacency list representation with dual indexing (both outgoing and incoming edges) for efficient traversal in both directions. </summary>
<summary> Creates a new empty graph of the specified type. ## Example let graph = Model.empty Directed ## Parameters - `graphType`: The type of the graph. ## Returns A new empty graph. </summary>
<summary> 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 |> addNode 1 "Node A" |> addNode 2 "Node B" ## Parameters - `id`: The unique identifier for the node. - `data`: The data to store at the node. - `graph`: The graph to add the node to. ## Returns A new graph with the node added. </summary>
<summary> 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 |> addEdge 1 2 10 **Note:** If `src` or `dst` have not been added via `addNode`, an `ArgumentException` is thrown. To automatically create missing endpoints when adding an edge, use `addEdgeEnsured` or `addEdgeEnsuredWith`. ## Parameters - `src`: The source node ID. - `dst`: The destination node ID. - `weight`: The weight of the edge. - `graph`: The graph to add the edge to. ## Returns A new graph with the edge added. </summary>
<summary> Directed Acyclic Graph (DAG) type and operations. A DAG is a directed graph with no cycles. This module provides a type-safe wrapper around Graph that guarantees acyclicity through construction, enabling total functions for operations that would be partial on general graphs. ## Core Concept The DAG type uses the "smart constructor" pattern: - **fromGraph** validates acyclicity and wraps the graph - **addEdge** returns Result because it could create a cycle - **addNode/removeNode/removeEdge** are safe (cannot create cycles) Once constructed, the DAG type guarantees no cycles exist, allowing algorithms like topological sort to be total functions. ## When to Use | Use Case | Example | |----------|---------| | **Task Scheduling** | Build systems (Make, Gradle), CI/CD pipelines | | **Dependencies** | Package managers (npm, cargo), module imports | | **Partial Orders** | Preference rankings, event causality | | **Control Flow** | Compiler IRs, dataflow graphs | | **Version Control** | Git commit history, merge bases | | **Bayesian Networks** | Probabilistic graphical models | ## Key Properties - **Guaranteed acyclic**: Type system prevents cycles at construction - **Topological sort always succeeds**: No need to handle cycle errors - **Longest path is well-defined**: Can find critical paths in O(V+E) - **Shortest path is efficient**: O(V+E) vs O((V+E) log V) for general graphs ## Comparison with Graph | Aspect | Dag | Graph | |--------|-----|-------| | Cycles allowed | ❌ No | ✅ Yes | | addEdge result | `Result<Dag, error>` | `Graph` | | topologicalSort | Always succeeds | May fail | | longestPath | O(V+E) | NP-hard | | shortestPath | O(V+E) | O((V+E) log V) | ## Example open Yog.Dag // Build a task dependency DAG let dagResult = Yog.Model.empty Directed |> Yog.Model.addNode 0 "Compile" |> Yog.Model.addNode 1 "Link" |> Yog.Model.addNode 2 "Test" |> Yog.Model.addEdge 0 1 () |> Yog.Model.addEdge 1 2 () |> Model.fromGraph match dagResult with | Ok dag -> let order = Algorithms.topologicalSort dag printfn "Execution order: %A" order | Error CycleDetected -> printfn "Circular dependency detected!" ## References - [Wikipedia: Directed Acyclic Graph](https://en.wikipedia.org/wiki/Directed_acyclic_graph) - [Topological Sorting](https://en.wikipedia.org/wiki/Topological_sorting) - [Smart Constructors](https://wiki.haskell.org/Smart_constructors) </summary>
<summary> The Guard: Uses isAcyclic to validate the graph. ## Parameters - `graph`: The graph to validate ## Returns - `Ok dag`: If the graph is acyclic - `Error CycleDetected`: If the graph contains a cycle ## Example let result = Model.fromGraph myGraph match result with | Ok dag -> printfn "Valid DAG" | Error CycleDetected -> printfn "Graph has a cycle" </summary>
<summary> DAG-specific algorithms that leverage acyclicity guarantees. These algorithms are optimized for DAGs and would be incorrect or inefficient on general graphs with cycles. The DAG property enables linear-time solutions for problems that are NP-hard on general graphs. ## Algorithms Provided | Algorithm | Complexity | Use Case | |-----------|------------|----------| | `topologicalSort` | O(V+E) | Task scheduling, dependency ordering | | `longestPath` | O(V+E) | Critical path analysis (PERT/CPM) | | `shortestPath` | O(V+E) | Minimum cost path between nodes | | `transitiveClosure` | O(V²) | Reachability queries, indirect dependencies | | `transitiveReduction` | O(V×E) | Simplify graphs, remove implied edges | | `countReachability` | O(V+E) | Impact analysis, prerequisite counting | | `lowestCommonAncestors` | O(V×(V+E)) | Merge bases, shared dependencies | ## Why DAGs Are Special DAGs enable efficient algorithms for problems that are intractable on general graphs: - **Longest Path**: O(V+E) on DAGs vs NP-hard on general graphs - **Topological Sort**: Always succeeds on DAGs vs may fail with cycles - **Shortest Path**: O(V+E) on DAGs vs O((V+E) log V) with Dijkstra ## References - [Wikipedia: Directed Acyclic Graph](https://en.wikipedia.org/wiki/Directed_acyclic_graph) - [Longest Path Problem](https://en.wikipedia.org/wiki/Longest_path_problem) - [Transitive Closure](https://en.wikipedia.org/wiki/Transitive_closure) - [Transitive Reduction](https://en.wikipedia.org/wiki/Transitive_reduction) </summary>
<summary> Topological sort guaranteed to succeed for the Dag type. ## Complexity - **Time**: O(V + E) - **Space**: O(V) ## Returns List of node IDs in topological order (sources before targets). ## Note This function cannot fail because the Dag type guarantees acyclicity. If it somehow encounters a cycle, it raises an exception (indicating a bug). ## Example let order = Algorithms.topologicalSort dag // Process nodes in dependency order for node in order do executeTask node </summary>
Functions and values
| Function or value |
Description
|
||
Adds an edge. Returns Result because it could create a cycle.
Parameters
Example
union case Result.Ok: ResultValue: 'T -> Result<'T,'TError>
val dag': obj
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
union case Result.Error: ErrorValue: 'TError -> Result<'T,'TError>
val CycleDetected: obj
|
|||
|
|||
|
The Guard: Uses isAcyclic to validate the graph.
Parameters
Example
val result: Result<obj,obj>
union case Result.Ok: ResultValue: 'T -> Result<'T,'TError>
val dag: obj
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
union case Result.Error: ErrorValue: 'TError -> Result<'T,'TError>
val CycleDetected: obj
|
||
|
|||
|
|||
|