Skip to main content

A Python ORM interface for building and solving clingo ASP programs

Project description

ASPAlchemy

ASPAlchemy is a Python library for building clingo ASP (Answer Set Programming) programs with a clean, object-oriented interface.

Term Hierarchy

The library is built around a rich hierarchy of term types that represent different ASP constructs:

  • Term (abstract base class)
    • ComparableTerm (abstract: usable in comparisons; provides ==, !=, <, <=, >, >=)
      • Value (abstract, for basic values — also a BasicTerm)
        • Variable (e.g., X, Y)
        • ConstantBase (abstract)
          • Number (numeric constants, e.g., 42)
          • String (string literals, e.g., "hello")
          • DefinedConstant (#const-defined constants, e.g., max_size)
          • Supremum / Infimum (#sup/#inf, the ordering's end markers; SUP and INF are the singletons)
      • Expression (arithmetic expressions, e.g., X+Y*2)
      • AggregateBase (abstract marker so core can recognize aggregates)
        • Aggregate (abstract)
          • Count, Sum, SumPlus, Min, Max
    • BasicTerm (abstract, can be direct predicate arguments: Value, Predicate, Pool)
      • Predicate (e.g., person(john, 42))
      • Pool (abstract)
        • RangePool (e.g., 1..5)
        • ExplicitPool (e.g., (1;3;5))
    • Negatable (abstract mixin: ~ builds not term on atoms; on plain comparisons it builds the complement — see Not)
      • Comparison (comparisons, e.g., X < Y)
      • DefaultNegation (default negation, e.g., not p(X))
    • ConditionalLiteral (e.g., p(X) : q(X))
    • Choice (e.g., { p(X) : q(X) })

Core Concepts

Values

Values represent basic elements in an ASP program. Plain Python literals coerce automatically wherever terms are expected — an int becomes an ASP number, a str becomes a quoted ASP string. Variables you construct by hand, and bare atoms (the n in direction(n)) are zero-arity predicates:

from aspalchemy import Predicate, Variable

X = Variable("X")  # an ASP variable
n = Predicate.define("n", [], show=False)()  # a bare atom: n, distinct from the string "n"

Predicates

Declare predicates as classes; fields are statically checked, so typos and missing arguments are type errors, and instances autocomplete:

from aspalchemy import Predicate, PredicateField

class Person(Predicate):
    name: PredicateField
    age: PredicateField

john = Person(name="john", age=30)
mary = Person(name="mary", age=25)

The ASP name defaults to the class name, snake-cased (HasSymbol becomes has_symbol); class kwargs override it and set namespacing and visibility (class Pipe(Predicate, name="pipe_seg", show=False)). When the schema is only known at runtime, build the same thing dynamically:

Edge = Predicate.define("edge", ["a", "b"])

For fully typed fields, annotate them as Field[int], Field[str], or Field[SomePredicate]. Writes accept rule terms (Variables, Expressions) as well as ground values, and are validated per field; reads are plain Python values, statically typed — so solution atoms come back as real ints and strs:

from aspalchemy import ASPProgram, Field, Predicate

class Score(Predicate):
    player: Field[str]
    points: Field[int]

program = ASPProgram()
program.fact(Score(player="ada", points=3), Score(player="ben", points=5))
model = program.solve().first()  # raises UnsatisfiableError if there is no model
total = sum(score.points for score in model.atoms(Score))  # plain ints
assert total == 8

Rules

Build rules with clear syntax:

from aspalchemy import ANY, ASPProgram, Variable

program = ASPProgram()
X, Y = Variable("X"), Variable("Y")
Adult = Predicate.define("adult", ["name"])

# Facts
program.fact(john)
program.fact(mary)

# Rules
program.when(Person(name=X, age=Y), Y >= 18).derive(Adult(name=X))

# Constraints
program.forbid(Person(name=ANY, age=Y), Y < 0)

Comparisons

Compare terms:

X < Y  # Creates a comparison X < Y

Aggregates

Use aggregates for advanced computations:

from aspalchemy import ANY, Count, Variable

X = Variable("X")
count = Count(X, Person(name=X, age=ANY)) > 5

Solving

Solve your ASP program:

# Generate ASP code
asp_code = program.render()
print(asp_code)

# Solve
result = program.solve()  # a lazy stream: consume the models you want
for model in result:
    print("Solution found:")
    for adult in model.atoms(Adult):
        print(f"  {adult}")
print(f"Satisfiable: {result.satisfiable}")

Diagnostics point at your code

Every statement remembers the Python line that authored it. A when() left unclosed reports where it was opened; grounding diagnostics from clingo carry a "generated by file:line" note back to your source; program.render(annotate=True) appends a trailing % file:line comment to each rule — without changing line numbers, so the annotated text doubles as a map from raw-clingo error lines back to your source; and when a grounding is slow or huge, program.ground().analyze_grounding() reports ground-atom counts per signature, largest first, each joined back to the lines that derive it. Frameworks built on aspalchemy call register_skip_package() (whole package, or per-module dotted prefixes) so locations point at their caller's code; mark a single helper with @attribute_to_caller to attribute its statements to its call sites. Switch capture off with ASPProgram(source_locations=False) when generating rules in bulk.

Requirements

  • Python 3.14+
  • clingo 5.8+

License

MIT License

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

aspalchemy-1.0.0.tar.gz (111.1 kB view details)

Uploaded Source

Built Distribution

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

aspalchemy-1.0.0-py3-none-any.whl (123.0 kB view details)

Uploaded Python 3

File details

Details for the file aspalchemy-1.0.0.tar.gz.

File metadata

  • Download URL: aspalchemy-1.0.0.tar.gz
  • Upload date:
  • Size: 111.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for aspalchemy-1.0.0.tar.gz
Algorithm Hash digest
SHA256 6469efc6a51e9bb14017b4bc4f76237bfb46126e99a67214d083b93762736cb8
MD5 5bea4bd9d7e959b677db95e82f1554f8
BLAKE2b-256 68730537b40c51b4cdeed3ce657011a90baf01a464044683cac0a1f863443439

See more details on using hashes here.

File details

Details for the file aspalchemy-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: aspalchemy-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 123.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for aspalchemy-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c8615954a9ec3ffb4c9cbdfafc4575d5a0313855a519d96e4bb6cc3c6fc264be
MD5 5ce482bbdf3fc2fb0fac5df341dcf44d
BLAKE2b-256 de38fe67050b975f2e1244f9016fa158258d58fc1348ba009d26af62093eff2b

See more details on using hashes here.

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