Install
openclaw skills install transitive-closure-generatorCompute transitive closure on graphs to infer implicit relationships and expand graphs with logically implied connections. Supports multiple algorithms and cycle detection for dependency analysis, hierarchies, and reachability.
openclaw skills install transitive-closure-generatorCompute transitive closure on graphs to automatically infer implicit relationships and expand knowledge graphs.
This skill computes the transitive closure of relations, deriving all logically implied connections from existing relationships and materializing them as explicit edges.
A relation where if A→B and B→C then A→C.
Examples:
ancestor_of: A ancestor_of B ∧ B ancestor_of C ⇒ A ancestor_of C
depends_on: A depends B ∧ B depends C ⇒ A depends C
located_in: Paris located_in France ∧ France located_in Europe ⇒ Paris located_in Europe
subclass_of: Dog subclass_of Mammal ∧ Mammal subclass_of Animal ⇒ Dog subclass_of Animal
Relations where transitivity doesn't apply:
friend_of: A friend B ∧ B friend C ⇏ A friend C (not necessarily)
married_to: A married B ∧ B married C ⇏ A married C (false)
knows: A knows B ∧ B knows C ⇏ A knows C (uncertain)
The complete set of all pairs (a,b) where a can reach b.
Example:
Original edges:
A → B
B → C
C → D
Transitive closure:
Direct: A→B, B→C, C→D (3 edges)
Inferred: A→C, A→D, B→D (3 additional edges)
Total: 6 edges
Converting implicit paths into explicit edges.
Implicit chain: A ---> B ---> C ---> D
Materialized: A → C (2 hops), A → D (3 hops), etc.
Set of all nodes reachable from a given node.
From A: {B, C, D}
From B: {C, D}
From C: {D}
Find all reachable nodes from each source node.
Complexity: O(V * (V + E))
Space: O(V)
Best For: Small to medium graphs
For each node N:
Visited = {}
DFS(N, Visited)
Add all visited nodes as reachable
Level-by-level traversal to find all reachable nodes.
Complexity: O(V * (V + E))
Space: O(V)
Best For: Finding shortest paths, layered structures
For each node N:
Queue = {N}
While Queue not empty:
Current = Queue.pop()
For each neighbor of Current:
If not visited:
Mark visited
Add to closure
Queue.push(neighbor)
Compute all-pairs shortest paths and closure simultaneously.
Complexity: O(V³)
Space: O(V²)
Best For: Dense graphs, need all distances
D[i][j] = direct edge weight or ∞
For each intermediate k:
For each pair (i,j):
D[i][j] = min(D[i][j], D[i][k] + D[k][j])
If D[i][j] < ∞, add to closure
Specialized for transitive closure computation.
Complexity: O(V³)
Space: O(V²)
Best For: Dense graphs, pure closure (no distances)
TC[i][j] = 1 if edge exists, 0 otherwise
For each k in 0..V:
For each i in 0..V:
For each j in 0..V:
TC[i][j] = TC[i][j] OR (TC[i][k] AND TC[k][j])
Update closure incrementally when edges are added.
Complexity: O(added_edges * V)
Space: O(V²)
Best For: Dynamic graphs, continuous updates
On add edge (u, v):
Mark TC[u][v] = 1
For all (i,u) and (v,j) in TC:
Mark TC[i][j] = 1
Propagate transitively
Most transitive closure algorithms assume acyclic graphs (DAGs).
Cycles cause:
Colors: White (unvisited), Gray (visiting), Black (done)
If reach Gray node: cycle detected
If can't perform complete topological sort: contains cycle
If shortest path becomes negative: cycle in weight sense
Store all closure edges explicitly.
Pros: O(1) query, no computation needed
Cons: Storage overhead O(V²), update cost
Compute paths on-demand.
Pros: No storage overhead
Cons: Query time O(V+E), repeated computation
Materialize high-value closures, compute others on-demand.
Pros: Balanced cost/benefit
Cons: Complexity
Cache computed reachability sets.
reachable_cache = {}
def get_reachable(node):
if node in reachable_cache:
return reachable_cache[node]
result = compute_reachable_bfs(node)
reachable_cache[node] = result
return result
Update only affected paths when new edges added.
New edge (u, v):
- Find all nodes that can reach u
- Find all nodes reachable from v
- Add edges from reaching→v reachable
Search from both source and target.
Forward reach from source
Backward reach to target
Intersection = connected pairs
| Issue | Cause | Solution |
|---|---|---|
| Infinite loop | Cycles in graph | Detect and handle cycles |
| Memory overflow | Too many inferred edges | Lazy materialization, sampling |
| Performance timeout | O(V³) on large graph | Use faster algorithm, incremental |
| Incorrect results | Assuming non-transitive as transitive | Validate relation type |
| Duplicate edges | Not deduplicating | Track seen edges |
✓ Verify transitivity - Ensure relation is truly transitive
✓ Detect cycles early - Prevent infinite loops
✓ Choose right algorithm - Match to graph size and density
✓ Use memoization - Cache frequently computed paths
✓ Handle large graphs - Consider lazy evaluation
✓ Monitor performance - Track closure computation time
✓ Validate results - Check for correctness
✓ Consider updates - Plan for incremental changes
✓ Document assumptions - Clarify which relations are transitive
✓ Test with sample data - Verify on small graphs first
Compute closure with edge weights (e.g., confidence, cost).
Handle uncertain relationships with confidence scores.
Time-aware transitive closure considering timestamps.
Fast approximation for large graphs.
Compute closure across multiple interconnected graphs.
This skill integrates with:
networkx - DFS, BFS, topological sortscipy.sparse - Sparse matrix operationsnumpy - Matrix operations for Warshallfunctools.lru_cache - Memoizationcollections.deque - Queue for BFSigraph - Fast graph algorithmsgraph-tool - High-performance analysisVersion: 1.0.0