Expand description
§ferroplan
A fast, data-parallel PDDL planner in Rust — a deterministic planning core for the age of AI. The engine is a delete-relaxation FF heuristic over a data-oriented (bitset / structure-of-arrays) task representation, with enforced hill-climbing + best-first fallback and parallel grounding / heuristic evaluation.
PDDL coverage: STRIPS, typing, ADL (conditional/forall effects, equality),
numeric fluents, derived axioms, PDDL3 soft-goal preferences/metric, and
PDDL2.1 temporal durative actions (constant / parameter-dependent durations,
duration inequalities, timed initial literals). Plus an SGPlan-style
partition-and-resolve mode.
§The public API (all serde-serializable)
solve— plan a domain + problem; returns aSolution(mode auto-detected).decompose— split a temporal goal too big for one-shot search into ordered, individually-solvedContracts, stitched into one validated plan (Decomposition).parse— syntax-check PDDL and summarize its structure (ParseReport) without grounding or solving — fast feedback for an authoring loop.Session— ground once, replan many: hold a mutable world state (set_fact/set_fluent) and re-solve per tick paying only the search (~10x per-tick on small contracts) — the embedding API for games/simulations.plan::validate_plan— independently check a plan under ferroplan’s semantics.
§Quick start
let domain = std::fs::read_to_string("domain.pddl").unwrap();
let problem = std::fs::read_to_string("problem.pddl").unwrap();
// Catch syntax mistakes before solving.
let report = ferroplan::parse(&domain);
assert!(report.ok, "{:?}", report.error);
let solution = ferroplan::solve(&domain, &problem, &ferroplan::Options::default()).unwrap();
if let Some(plan) = solution.plan {
for step in &plan.steps { println!("{}", step.action); }
}The lower-level text-rendering entry points (run_planner, run_ff) produce
classic Metric-FF / IPC output and back the ff binary.
Re-exports§
pub use api::decompose;pub use api::parse;pub use api::solve;pub use api::Contract;pub use api::Decomposition;pub use api::DomainSummary;pub use api::Metric;pub use api::Mode;pub use api::Options;pub use api::ParseReport;pub use api::Plan;pub use api::ProblemSummary;pub use api::Search;pub use api::Solution;pub use api::SolveError;pub use api::Statistics;pub use api::Step;pub use planner::run_ff;pub use planner::run_planner;pub use session::Session;pub use trace::trace;pub use trace::StateSnapshot;pub use types::ParseError;
Modules§
- api
- The smart, serde-serializable public API.
- bitset
- Fixed-width bit set over
u64words — the fact layer of a state. Word-oriented so applicability/apply are tight bitwise loops and states are compact for hashing/dedup in parallel search. - constraints
- PDDL3 trajectory-constraint ENFORCEMENT (0.7, docs/roadmap-0.7.md).
- derived
- Derived predicates (
:derivedaxioms), compiled away before grounding. - espc
- ESPC — Extended Saddle-Point Condition penalty-resolution outer loop.
- features
- Process-global overrides for the env-gated planner features.
- ground
- Grounding into the data-oriented
PackedTask. - hash
- In-tree FxHash — a fast, deterministic hasher (the rustc-hash algorithm), so we drop SipHash for the hot visited-set and the grounding interner without a dependency. Deterministic: no random seed, so results are reproducible and thread-count-independent (bucket layout never affects the search order, which is driven by the heap, not set iteration).
- heuristic
- FF relaxed-plan heuristic over the packed task, allocation-free on the hot
path: all working buffers live in a reusable
Scratchthat a worker thread creates once and resets per evaluation (cleared, never re-allocated). This removes the per-state allocation churn — and the global-allocator contention it caused across worker threads — which was the main limit on both raw speed and parallel scaling. - invariants
- Mutex-group synthesis: Helmert-style monotonicity invariants (multi-predicate).
- lexer
- Hand-written PDDL tokenizer (mirrors
lex-ops_pddl.l/lex-fct_pddl.l). - output
- Output formatting. The plan block, the unsolvable/trivial messages, and the exit codes match Metric-FF (and metricff) so this binary is a drop-in: the planner crate’s regex and the differential harness consume it unchanged. The search-configuration / progress lines honestly reflect the parallel engine.
- packed
- Data-oriented grounded task (Structure-of-Arrays / CSR) and bitset state.
- par
- Minimal data-parallel primitives over
std::thread::scope(no external deps). The thread count comes fromFFDP_THREADS(if set) else available cores;threads <= 1falls back to a plain sequential map, so behaviour is deterministic regardless of parallelism. - parser
- Recursive-descent parser for the PDDL subset Metric-FF accepts.
Mirrors
scan-ops_pddl.y(domain) andscan-fct_pddl.y(problem). - partition
- Goal partitioning.
- pddl3
- PDDL3.0 soft-goal preferences + metric optimization (phase 1).
- plan
- Built-in plan validator — check a plan against ferroplan’s OWN semantics.
- planner
- report
- Output rendering. Default = classic Metric-FF format (drop-in for the planner
crate + the differential validator);
-ipc= SGPlan6’s IPC temporal format. - resolve
- Partition-and-resolve control loop (the SGPlan core, adapted to numeric STRIPS — see docs/sgplan6-spec.md §5,§9).
- resource
- Renewable “counter” resource detection for resource-aware search guidance.
- search
- Data-parallel weighted best-first search.
- selection
- Exact preference-subset SELECTION for the PDDL3 metric path.
- session
- Ground once, replan many — the embedding API for callers that re-solve the same world every tick (a game’s villagers, a simulation loop, an agent runtime).
- temporal
- PDDL2.1 temporal planning — durative actions (EPIC-Temporal).
- trace
- Plan trace: replay a plan and capture the world state before the first
action and after every action — the instrumentation a UI needs to animate a
plan. Mirrors
crate::verify’s replay loop but snapshots each intermediate state. Classic/numeric/PDDL3 plans (a sequential op list) only; temporal plans (overlapping durative actions) are not replayed this way. - tresolve
- Temporal partition-and-resolve decomposer (Phase B) — the SGPlan partition loop
(
crate::resolve) brought to the durative/numeric path, gated byFF_TDECOMP. - tsched
- Concurrent scheduling phase for temporal plans.
- types
- AST (parser output) and the numeric intermediate representation shared by
grounding and the heuristic. The grounded representation is data-oriented
and lives in
packed.rs. - verify
- Independent PDDL3 plan/metric verifier (conformance oracle).
- viz
- Visualization model: derive an abstract graph from a parsed domain + problem, plus helpers a GUI needs (per-snapshot positions, PDDL generation). This is pure, view-agnostic logic — no GUI/layout types — so any front-end (Bevy, egui, web) can consume it.