Reference
Traversal, reachability, and paths
Behavior
Need |
Function |
Caller supplies |
|---|---|---|
Explore outward |
|
Start IDs and |
Find the cheapest route |
|
Start, target, neighbors, and edge cost |
Bound context expansion |
|
Seeds, direction-specific neighbors, maximum depth |
Partition topology |
|
Node IDs and undirected neighbors |
How it works
Mari accepts IDs and callbacks. Algorithms operate on hashable IDs and return immutable results with visited-node counts and path cost. Traversal ordering uses ID type and representation. Prefer stable string or structural IDs whose representations remain consistent across runs.
from mari_components.graph import shortest_path
path = shortest_path(
"customer:42",
"policy:refunds",
neighbors=lambda node: store.outgoing_ids(node),
edge_cost=lambda left, right: 1.0 - store.confidence(left, right),
allowed=lambda node: can_read(node),
max_visited=500,
)
if path.found:
context.add(path.nodes)
Use build_adjacency to project caller edge iterables in the incoming,
outgoing, or both direction. predecessor_dag retains every predecessor on an
unweighted shortest path. Alternative explanations remain beside one
BFS parent.
from mari_components.graph import build_adjacency, predecessor_dag
outgoing = build_adjacency(
call_edges, endpoints=lambda edge: (edge.caller, edge.callee),
)
paths = predecessor_dag(
[changed_symbol], neighbors=outgoing.__getitem__, max_depth=5,
)
for entry in paths.entries:
print(entry.node, entry.predecessors, entry.shortest_path_count)
When edge payloads carry citation intent, relation version, time, or provenance,
use traverse_edges. The caller supplies edge enumeration, adjacency, node
authorization, and an edge rejection function. Results preserve accepted edges
and rejected edges with reasons.
from mari_components.graph import traverse_edges
trace = traverse_edges(
[review_id],
edges=citation_store.outgoing,
adjacent=lambda source, citation: citation.target,
reject_edge=lambda citation: "retracted" if citation.retracted else None,
max_depth=3,
)
Outgoing edges hidden by max_depth are returned as rejected edges with
reason="depth_limit", and truncated becomes true. This distinguishes a leaf
from an unexplored traversal boundary.
Breadth-first traversal gives minimum hop count in an unweighted graph. Weighted paths use Dijkstra’s algorithm and reject negative or non-finite costs. Authorization is checked before a node enters the frontier.
Warning
Weighted depth-bound limitation
shortest_path(max_depth=...) tracks one best cost per node. A cheaper route
that consumes more hops can displace a shallower route needed to reach the
target within the bound. This can omit a valid depth-constrained route.
Use an unbounded-depth search with a visited-node budget for weighted paths,
or use breadth-first traversal for minimum-hop questions. A reached search
budget requires treating the result as incomplete.
Keep neighbor callbacks within the intended tenant, revision, and time slice. Use incoming adjacency for downstream impact when stored edges point from a derived output to its inputs. Reachability identifies possible impact. The dependency planner compares completed input fingerprints to decide which reachable outputs need rebuilding.
Measures
Property |
Check |
|---|---|
Correctness |
Compare paths and components with NetworkX fixtures |
Determinism |
Shuffle neighbor order and require identical output |
Isolation |
Paths and visited traces exclude forbidden nodes |
Bounds |
Depth and visited-node budgets stop expansion exactly |
evaluate_path(predicted, expected) reports exact match plus node- and edge-level precision and recall. Edge scores distinguish a route that visits the right nodes in the wrong order from a correct path.
Papers and implementations
Dijkstra’s shortest pathNetworkX algorithmsBreadth-first search
NetworkX is BSD-3-Clause and serves as a differential reference. It remains outside the runtime dependency set.