miniplan 0.2.0

A PDDL planner library built around the pddl crate, with grounding and search utilities
Documentation

miniplan

A PDDL planner library built around the pddl crate.

CI Crates.io Crates.io docs.rs License Rust Edition unsafe forbidden

miniplan is a grounded, classical PDDL planner focused on simplicity and performance. It targets STRIPS-level domains with support for typing, negative preconditions, and conditional effects. Use it as a library in your own projects or as a CLI (miniplan) for quick planning experiments.

Features

  • PDDL 3.x parsing via pddl-rs
  • Grounding to a compact bitset-based internal representation
  • Planners:
    • bfs — blind breadth-first search (optimal)
    • astar — A* with pluggable heuristics (optimal)
    • gbfs — greedy best-first search (fast, non-optimal)
    • bidij — bidirectional Dijkstra (cost-aware, optimal)
    • bibfs-uc — bidirectional BFS (uniform-cost, optimal)
    • nbs — Near-Optimal Bidirectional Search (heuristic-guided, non-optimal with HFF)
    • bae — Bidirectional A* with Error / BAE* (heuristic-guided, non-optimal with HFF)
  • Bidirectional heuristic search: bidij, bibfs-uc, nbs (NBS), bae (BAE*)
  • Heuristics: hff (relaxed-plan), hadd, hmax, goal-count, blind
  • Output formats: plain, IPC, JSON
  • Search limits: time budget, node budget
  • Structured logging via tracing

Installation

Library

[dependencies]
miniplan = "0.1"

CLI

cargo install miniplan-cli --locked

The resulting binary is miniplan (not miniplan-cli).

Or install from the git repository:

cargo install --git https://github.com/sunsided/miniplan miniplan-cli

Build from source

git clone https://github.com/sunsided/miniplan
cd miniplan
cargo build --release

Quick Start (CLI)

Solve a bundled example with search statistics:

$ miniplan solve --stats examples/pddl/blocksworld.pddl
; cost = 8
; length = 8
(unstack(a,c))
(put-down(a))
(pick-up(c))
(stack(c,d))
(pick-up(b))
(stack(b,c))
(pick-up(a))
(stack(a,b))
Search stats:
  Nodes expanded: 27
  Nodes generated: 45
  Plan cost: 8.00
  Plan length: 8
  Time: 3.451169ms

Parse and ground a problem without solving:

miniplan check examples/pddl/gripper.pddl

List available planners and heuristics:

$ miniplan list-planners
Available planners:
  bfs          Breadth-first search
  astar        A* search with pluggable heuristic
  gbfs         Greedy best-first search
  bibfs-uc     Bidirectional BFS (uniform-cost, not cost-aware)
  bidij        Bidirectional Dijkstra (cost-aware)
  nbs        Near-Optimal Bidirectional Search (Chen et al. 2017)
  bae        Bidirectional A* with Error (BAE*, Sadhukhan 2013)

Available heuristics:
  goal-count
  hff
  blind
  hadd
  hmax

CLI Reference

Commands

Command Description
solve Solve a PDDL problem
check Parse and ground a problem (no search)
list-planners Show available planners and heuristics

solve Flags

Flag Default Description
-p, --planner astar Planner to use (bfs, astar, gbfs, bidij, bibfs-uc, nbs, bae)
-H, --heuristic ff Heuristic (hff, hadd, hmax, goal-count, blind)
-t, --timeout Time budget (e.g. 30s, 2m)
--max-nodes Maximum nodes to expand
-o, --output - Output file (- for stdout)
--format plain Output format (plain, ipc, json)
--stats Print search statistics to stderr
--domain Domain name (when input contains multiple)
--problem Problem name (when input contains multiple)
-v Verbosity (count-based: -v info, -vv debug, -vvv trace)

Exit Codes

Code Meaning
0 Plan found
1 Provably unsolvable
2 Search limit reached (time or nodes)
3 Unknown outcome

Library Usage

use std::path::PathBuf;

use miniplan::pddl_io::load_files_named;
use miniplan::ground::ground;
use miniplan::search::{Solver, PlannerChoice, PlannerKind, PlannerConfig, SearchLimits, SearchOutcome};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Load PDDL files (one combined, or domain + problem)
    let files = [PathBuf::from("examples/pddl/blocksworld.pddl")];
    let (domain, problem) = load_files_named(&files, None, None)?;

    // Ground the task
    let task = ground(&domain, &problem)?;

    // Configure and run the solver
    let solver = Solver::new();

    let mut config = PlannerConfig::default();
    config.opts.insert("heuristic".to_owned(), "hff".to_owned());

    let choice = PlannerChoice {
        kind: PlannerKind::Astar,
        heuristic: Some("hff".to_owned()),
        config,
    };

    let limits = SearchLimits::default();
    let outcome = solver.solve_task(&task, &choice, &limits)?;

    match outcome {
        SearchOutcome::Plan(plan, stats) => {
            println!("Plan found (cost={:.2}, length={}):", stats.plan_cost, stats.plan_length);
            println!("{}", plan);
        }
        SearchOutcome::Unsolvable(_) => {
            eprintln!("Problem is provably unsolvable.");
        }
        SearchOutcome::LimitReached(_) => {
            eprintln!("Search limit reached.");
        }
    }

    Ok(())
}

See docs.rs for the full API.

Supported PDDL Features

Feature Status
STRIPS Supported
Typing Supported
Negative preconditions Supported
Conditional effects Supported
Disjunctive preconditions Supported (via DNF grounding)
Quantified preconditions Partial
Derived predicates (static bodies) Supported
Numeric fluents Not yet
Durative actions Not yet

Examples

Five PDDL problems are bundled in examples/pddl/:

Problem Domain Recommended Planner
blocksworld.pddl Blocksworld astar
gripper-small.pddl Gripper bfs
gripper.pddl Gripper astar
logistics.pddl Logistics bfs
air-cargo.pddl Air Cargo astar / gbfs

Run all examples via the Taskfile:

task example        # Full demo sweep
task example:blocksworld   # Single example

Development

This project uses Task for automation.

task --list-all        # List all available tasks
task verify            # fmt + check + lint + test (full gate)
task example           # Run the full example suite

A pre-commit configuration is included (.pre-commit-config.yaml) for lint checks before commits.

Project Layout

crates/miniplan/         Core library
  src/
    ground/              PDDL grounding
    heuristic/           Heuristic implementations (h^FF, h^add, h^max, …)
    pddl_io.rs           PDDL file loading
    plan.rs              Plan representation and formatting
    search/              Planner implementations (BFS, A*, GBFS)
    task.rs              Grounded task representation
    util/                Utilities (bitsets, etc.)
bins/miniplan-cli/       CLI binary (produces `miniplan`)
examples/pddl/           Bundled PDDL problems
Taskfile.dist.yaml       Task definitions

Roadmap

  • Current: grounded STRIPS, BFS/A*/GBFS, h^FF
  • Planned: landmark heuristics, plan validation, more PDDL coverage (numerics, durative actions)

License

Licensed under the European Union Public Licence 1.2 (EUPL-1.2). See the SPDX entry or the official EU page for details.

Acknowledgements

  • pddl-rs — PDDL parser, forked and maintained for this project.
  • Jörg Hoffmann & Bernhard Nebel — The FF planning system and relaxed-plan heuristic (Hoffmann & Nebel, The FF Planning System: Fast Planning Generation, JAIR 2001).