Skip to main content

ASCAL: Anytime Sound and Complete Action Learning (online action-model learning with version-space boundaries).

Project description

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]"
# Exact reproducibility environment for paper/benchmark reruns
pip install -r requirements-repro-lock.txt
pip install -e .

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ascal-0.1.0.tar.gz (45.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ascal-0.1.0-py3-none-any.whl (47.3 kB view details)

Uploaded Python 3

File details

Details for the file ascal-0.1.0.tar.gz.

File metadata

  • Download URL: ascal-0.1.0.tar.gz
  • Upload date:
  • Size: 45.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ascal-0.1.0.tar.gz
Algorithm Hash digest
SHA256 bbe7fe681a69c5eb518e34fac4a32eaedb56659173391535a9e3fe0996f70c6f
MD5 23068b0f49ece4da612ff28b23108367
BLAKE2b-256 8455f1cb453ee65d6749419eb2b3c4101bf9510c6d0091bbed5f16dbba95f25f

See more details on using hashes here.

Provenance

The following attestation bundles were made for ascal-0.1.0.tar.gz:

Publisher: publish.yml on pablocopete/ascal

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ascal-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: ascal-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 47.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ascal-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9406546ef5f630233288cfcd2f2308db604586f6bdc9a94c8e6a0d1047f7133c
MD5 7c199b740ff457b489e9bf7a47d3f9da
BLAKE2b-256 f21388d516e80d75f131ad774771828b7f6f42acce5d856072c4b0465f25991e

See more details on using hashes here.

Provenance

The following attestation bundles were made for ascal-0.1.0-py3-none-any.whl:

Publisher: publish.yml on pablocopete/ascal

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page