Tutorial 07

June 15, 2026 · View on GitHub

This ties three pieces together: metastable-state labels (from SPIB or any clustering) → a roadmap graph of all-pairs pathways → an agentic controller that adapts the swarm. All runnable without MD.

Build a roadmap from a state-label trajectory

SPIBProgress.state_labels gives a per-frame metastable-state id; feed that sequence to a Roadmap. Here we use a synthetic label trajectory:

from pathgennie.search.roadmap import Roadmap

rm = Roadmap()
# e.g. progress.state_labels from a SPIB run; here a synthetic 3-state walk:
labels = [0, 0, 1, 1, 2, 2, 1, 0, 1, 2, 0, 2]
rm.observe_sequence(labels)

print("states:", rm.nodes)
cost, path = rm.min_free_energy_path(0, 2)          # Dijkstra
print("min-free-energy path 0->2:", path, "cost", round(cost, 3))

for cost, route in rm.k_shortest_paths(0, 2, k=2):   # competing routes (Yen)
    print("route", route, "cost", round(cost, 3))

report = rm.all_pairs_paths(k=1)                      # every ordered pair
print("pairs found:", sorted(report))

Edges are weighted by -log(transition fraction), so the cheapest path is the maximum-likelihood / minimum-free-energy route.

Drive the swarm with the agentic controller

from pathgennie.agent import RuleBasedController, SwarmParams

ctrl = RuleBasedController(SwarmParams(n_trial=8, tau1=4, tau2=8),
                           stall_window=5, stall_eps=1e-3,
                           escalate=1.5, relax=0.75, stop_patience=20, refresh_every=50)

metric_history = []
for cycle in range(200):
    p = ctrl.update(metric_history)        # adapt N / tau1 / tau2 from progress
    # ... run one driver cycle with p.n_trial, p.tau1, p.tau2 ...
    metric_history.append(get_latest_metric())
    if ctrl.should_refresh_cv(cycle):
        ...                                # retrain SPIB
    if ctrl.should_stop(metric_history):
        break

When progress stalls the controller enlarges the swarm and lengthens the segments; when it flows it relaxes the swarm to save compute; on a long plateau it recommends stopping. RuleBasedController.choose_frontier(visit_counts) picks the least-visited region to expand next (anti-trapping for RRT).

See roadmap-graph.md and agent.md.