Skip to content

pablocopete/ascal

Repository files navigation

ASCAL — Anytime Sound and Complete Action Learning

PyPI version Python versions License: GPL v3+ Paper: KR 2024

ASCAL is an online algorithm for Action Model Learning (AML) with full observability. It leverages Version Space Learning to maintain a compact representation of all action models consistent with a set of positive and negative demonstrations. The output can be used to build action models guaranteed to be sound or complete with respect to the true underlying model.

Paper: https://proceedings.kr.org/2024/75/kr2024-0075-aineto-et-al.pdf


What is ASCAL?

Given a sequence of fully-observable demonstrations (pre_state, action, post_state), ASCAL maintains per-action version-space boundaries:

  • L_pre / U_pre — lower and upper bounds on the precondition hypothesis space
  • L_eff / U_eff — lower and upper bounds on the effect hypothesis space

Eight operators (RUP, RLP, ULP, UUP for preconditions; RLE, RUE, ULE, UUE for effects) keep these bounds consistent with every new demonstration.

From the learned boundaries you can extract:

Model Method Guarantee
Sound model learner.sound_model() Never permits a transition the true model forbids
Complete model (split) learner.upper_border_split() U-border with non-contradictory effects. Splits ambiguous polarities into maximal completions
Complete model (single) learner.upper_border_single() U-border with non-contradictory effects. Does not split hypothesis
Upper-border model learner.raw_upper_bound() Compact U-boundary (one operator per precondition hypothesis). It is not complete but it is fast for planning. Can be used instead of blind search as a first approach
Full version-space model learner.complete_model() All consistent hypotheses (can be large)

Installation

Requires Python 3.10+.

# From PyPI (recommended for users)
pip install ascal
# Notebook and evaluation workflow (adds numpy, matplotlib, jupyter)
pip install "ascal[notebook]"
# Optional planner backend: Fast Downward via Unified Planning plugin
pip install "ascal[planner]"

For contributors working from a clone:

# Editable install with development tools (pytest, build, twine, ruff)
pip install -e ".[dev]"
# Paper / cluster environment (matches gpuserver conda stack: UP + FD + numpy/scipy only)
pip install -r requirements-experiment-server.txt
pip install -e .
# Full notebook + plotting + experiments (large lockfile; spans Python 3.10–3.13)
# Prefer a fresh venv (avoid pip install into conda "base").
pip install -r requirements-repro-lock.txt
pip install -e .

Why two files: the gpuserver environment was minimal (no Jupyter). requirements-repro-lock.txt also pulls IPython, matplotlib, scipy 1.15, numpy 2.x, … and some of those pins were chosen on Python 3.11+. On Python 3.10, packages such as networkx>=3.5 and contourpy==1.3.3 are rejected by pip; the lock file is adjusted where possible (networkx==3.4.2, contourpy==1.3.2) so one file can still install on 3.10–3.13.

The locked file pins up-fast-downward==0.2.3, matching the conda environment used on the experiment server (unified-planning==1.3.0 is pinned there too). Newer FD plugin releases are available via pip install "ascal[planner]" without that lock file.

The notebook stack pins Pillow>=11 (e.g. 11.2.1 in the lock) so Python 3.13 installs get wheels; Pillow 10.1.0 does not build cleanly on 3.13. For a line-by-line match to an older Python 3.10 conda env, you can pin Pillow==10.1.0 there instead.

Python 3.10: contourpy is pinned to 1.3.2 in the lock (version 1.3.3 declares Requires-Python >=3.11).

See documentation/dependency-classification.md for the full classification of dependencies and documentation/dependency-validation-checklist.md for post-install smoke tests.


Quick start

from ascal import (
    Learner,
    generate_lifted_demonstrations_from_problem,
)

# --- Build demonstrations from a Unified Planning problem ---
# pos_demos, neg_demos = generate_lifted_demonstrations_from_problem(problem, plan, planner_name="pyperplan")

# --- Initialise learner from domain info ---
learner = Learner(all_fluents, all_actions, static_fluents)

# --- Feed demonstrations one at a time ---
for demo in demonstrations:
    ok = learner.update(demo)
    if not ok:
        print(f"Version space collapsed for {demo.action.name}")

# --- Or feed a batch ---
n_collapses = learner.update_batch(remaining_demos)

# --- Inspect convergence ---
print(learner.converged)           # True when L == U for all actions
print(learner.version_space_size)  # Per-action statistics dict
print(learner.demo_count)          # Total demonstrations processed

# --- Extract learned models as UP Problem objects ---
sound_problem  = learner.sound_model()        # Sound (L-boundary)
border_problem    = learner.raw_upper_bound()        # Compact U-boundary
upper_border_split = learner.upper_border_split()   # U-border split completions
complete_problem   = learner.complete_model()        # Full version space (may be large)

# --- Evaluate against labelled test data ---
f1_s, f1_c, p_s, r_s, p_c, r_c = learner.evaluate(test_pos, test_neg)

# Variant: representative hypothesis for complete model
f1_s, f1_c, p_s, r_s, p_c, r_c, status = learner.evaluate_repr(test_pos, test_neg)

# Variant: only fully-converged actions
f1_s, f1_c, p_s, r_s, p_c, r_c, status = learner.evaluate_gated(test_pos, test_neg)

Package structure

src/ascal/
    __init__.py     — Public API (Learner + all exported symbols)
    models.py       — Data classes: Literal, State, Action, Demonstration
    transitions.py  — Unified Planning bridge: plan execution, grounding,
                      lifting, demonstration generation
    algorithm.py    — ASCAL operators (RUP, RLP, ULP, UUP, RLE, RUE, ULE, UUE),
                      initialisation, iteration, and model generation
    evaluation.py   — F1, precision, recall; version-space size computation;
                      three evaluation strategies (detailed, representative, gated)
    learner.py      — Learner: stateful high-level wrapper
    logger.py       — Logging utilities (get_logger)

Key public symbols

from ascal import (
    # Core data classes
    Literal, State, Action, Demonstration,

    # High-level learner (recommended entry point)
    Learner,

    # Demonstration generation from Unified Planning
    generate_lifted_demonstrations_from_problem,
    generate_transitions_from_problem,
    lift_demonstrations,

    # Low-level ASCAL algorithm
    ASCAL_initialization,
    run_ASCAL_iteration,
    run_ASCAL,

    # Version-space operators
    RUP, RLP, ULP, UUP,   # preconditions
    RLE, RUE, ULE, UUE,   # effects

    # Model generation (used internally by Learner)
    generate_sound_action_model,
    generate_complete_border,
    generate_complete_border_consistent,
    generate_complete_border_consistent_split,

    # Evaluation
    evaluate_detailed,
    evaluate_representative,
    evaluate_convergence_gated,
    compute_version_space_size,
)

Repository structure

benchmarks/
    blocks/             — Blocks-world domain
    driverlog/          — Driverlog domain
    miconic/            — Miconic domain
    satellite/          — Satellite domain
    mockup/             — Small synthetic domain for quick sanity checks
    debug_pq/           — Debug / edge-case domain
    Each subdomain contains:
        domain_original.pddl   — Original IPC domain
        domain_extended.pddl   — Extended variant used in experiments
        problems/              — Problem instances (.pddl)

notebooks/
    ascal_validation.ipynb          — Mockup domain: ASCAL validation trace,
                                      L/U checks, evaluate_detailed, version-space size
    MultiProblemEvaluation.ipynb    — Multi-problem pipeline, 80/20 split,
                                      learning curves, convergence, ground-truth checks
    Evaluation Learner.ipynb        — Learner pipeline: update, snapshots, F1 metrics

tests/
    GroupA_StructuralInvariants.ipynb  — Structural invariants of version-space bounds
    GroupB_OperatorVerification.ipynb  — Operator correctness (RUP/RLP/…)
    GroupC_TheoreticalGuarantees.ipynb — Theoretical soundness/completeness guarantees
    GroupD_Monotonicity.ipynb          — Monotonicity properties
    GroupE_GroundTruthComparison.ipynb — Comparison against known ground truth
    _run_comparison.py                 — Script: compare evaluate vs evaluate_repr

Running tests and notebooks

# Run the comparison script from the repo root
python tests/_run_comparison.py

# Launch notebooks
jupyter lab

Ensure the package is installed (pip install -e .) so that import ascal resolves correctly.


Citation

If you use ASCAL in academic work, please cite the KR 2024 paper. Structured metadata is provided in CITATION.cff and GitHub will render a "Cite this repository" button from it.

@inproceedings{aineto:2024:VSLAM,
    title     = {{Action Model Learning with Guarantees}},
    author    = {Aineto, Diego and Scala, Enrico},
    booktitle = {{KR}},
    pages     = {801--811},
    year      = {2024},
    month     = {8},
    doi       = {10.24963/kr.2024/75},
    url       = {https://doi.org/10.24963/kr.2024/75},
  }

License

ASCAL is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License v3.0 or later (GPL-3.0-or-later) as published by the Free Software Foundation. See LICENSE for the full license text.

Copyright (c) 2026 Diego Aineto, Enrico Scala, Pablo Copete.

Third-party planner licensing

This project integrates with planners through the Unified Planning Framework (Apache-2.0). Some optional planner backends — for example Fast Downward via up-fast-downward — are distributed under separate licenses including GPL-family licenses. Those components are not part of ASCAL's own license and must be installed, used, and redistributed according to their respective terms.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors