PDDL3 preferences
ferroplan compiles soft-goal preferences away (Keyder & Geffner, JAIR 2009) and
minimizes the :metric.
- Goal preferences, including
(forall (?x) (preference p phi)), are expanded into one instance per binding;(is-violated p)counts violated instances. - Precondition preferences become satisfied/violated action variants, so a violation is charged exactly once per application.
- The metric must be linear in
(is-violated …)and(total-cost)(the IPC-5 simple-preferences shape) — plus any monotone numeric term (e.g. rovers'sum-traverse-cost), which is folded intototal-cost; maximize / negative / scaled metrics fall back to a satisficing plan with a clear note.
The optimizer (0.4.0)
The default is an exact-closure metric optimizer: it searches real states with
metric-bounded acceptance and closes the compiled preference bookkeeping with a
provably-optimal collect/forgo phase tail. Three pieces make it scale:
- Static preference simplification at compile — statically-satisfied preference
instances are dropped before grounding (storage's 62k-instance quadratic
forallcollapses ~97%). - Barrier-free full-DNF guidance — the search sees a preference's forgone cost directly instead of behind a compilation barrier.
- A budget-escalating branch-and-bound — a tightening probe that hits its
per-iteration eval cap without improvement retries the same bound with the
remaining budget rather than giving up. The deterministic, thread-count-independent
budget is
FF_PREF_EVAL_BUDGET(default 2M evals) — a real quality dial. - Anytime sweeps + a diversified restart ladder — each sweep tightens its bound in place on every acceptance (a restart happens once per cap, not once per improvement), and a capped sweep that fails to improve rotates the open-list weights through a fixed profile ladder before the final full-budget escalation — a stuck h-ordering is a direction problem, not a budget problem. This is what broke the storage/tpp large-instance plateau (storage now beats SGPlan5 on p01–p07).
For resource-coupled domains a default-on ESPC penalty loop (opt out with FF_NO_ESPC; after
Hsu–Wah's extended-saddle-point method) prices a shared resource as a global
constraint across a partitioned search — the lever that puts openstacks ahead of
SGPlan5 on its larger instances. Every knob has a restore hatch (FF_PREF_COMPILED,
FF_PREF_NO_STATIC, FF_PREF_BARRIER, FF_PREF_NO_ESCALATE, FF_ESPC_MONO); see
the tuning reference.
Incumbent zero (0.28). Before the optimizer starts, the classical ladder
plans the HARD goals alone (soft trajectory constraints stripped — they cannot
invalidate a plan) and that plan is lifted into the compiled task as a
floor: the optimizer's own search is unchanged, and the seed is what comes
back, with a note, only when nothing cheaper was found inside the budget. Until
0.28 a run that ended before the optimizer's first plan reported solved: false with a valid plan milliseconds away. Two consequences worth knowing: on
a problem with no hard goal at all the floor is the empty plan (valid,
every preference violated), and when the compiled preference task does not
even ground inside the wall or the memory budget the hard-goal plan is
returned with metric: null and a NOT priced note. FF_PREF_NO_SEED=1
restores 0.27.
On the largest instances exact optimization may return a best-found plan (flagged
not proven optimal) within the budget. Full per-instance results vs SGPlan5:
benchmarks/ipc5-scoreboard.md.
Trajectory constraints ((:constraints ...)) — enforced since 0.7
The six untimed modal operators — always, sometime, at-most-once,
sometime-after, sometime-before, at end — are enforced on the
classical path by compiling each ground constraint instance into a small
monitor automaton: fresh 0-ary monitor facts transitioned by conditional
effects on every action, with acceptance checked at the goal. forall
outside a (preference ...) multiplies instances (so (is-violated name)
counts violated instances); and/forall inside a preference body stay
ONE instance, violated at most once — the PDDL3 instance boundary.
- Hard constraints gate the goal through a forced-terminal END action
(since 0.8): acceptance is latched by conditional effects on one
synthetic
TRAJ-ENDstep — stripped from reported plans — so the compiled goal stays literal-only and grounding cost is LINEAR in the monitor count (the 0.7 goal-conjunct compilation was exponential in the worst case;FF_NO_TRAJ_END=1restores it). A plan that violates a hard constraint is simply not a plan. - Soft
(preference name ...)constraints lower to ordinary goal preferences priced by the metric machinery above — the whole optimizer stack applies unchanged, and(is-violated name)works across goal and constraint preferences in one namespace. - The monitor transition block is ground ONCE and shared across all ground
actions (since 0.8,
FF_NO_COND_SHARE=1restores per-op copies) — the storage-scale instances that previously exhausted 15 GB during grounding now ground in well under a second at ~100–200 MB. - The independent verifier (
ferroplan::verify) replays the ORIGINAL constraint semantics over the trajectory — never the compiled monitors — so reported metrics are cross-checked by construction, andvalidate_planrejects constraint-violating plans. - Statically decidable instances are simplified away before grounding
(quadratic
forallconstraints over static relations stay tractable);FF_PREF_NO_STATIC=1restores the blind expansion.
Since 0.23, hard untimed constraints are enforced on durative-action
(temporal) domains too: the same monitor compile rides the snap-compiled
task, the emitted (ε-separated) schedule is re-audited against the monitors
before a constrained plan is returned, and the temporal validator folds the
original constraint semantics over its replay. The timed operators
(within, always-within, hold-during, hold-after) and soft
(preference ...) constraints on durative domains are rejected by
name — never silently dropped. FF_CONSTRAINTS_REJECT=1 restores the
pre-0.7 blanket rejection. Measured results on the IPC-5
qualitative-preferences track:
benchmarks/ipc5-qualitative-scoreboard.md.