Skip to main content

clausal

A Prolog-style logic programming DSL embedded in Python. write relational logic programs in .clausal source files that load via Python's standard import system, with full constraint solving, tabling, DCGs, and a rich standard library.

Features

  • .clausal source files — import logic modules with Python's standard import system
  • Prolog-style predicates — Horn clauses, unification, backtracking search
  • Constraint solving — CLP(FD) for integers, CLP(B) for booleans, dif/2 disequality
  • SLG tabling — memoised subgoal calls for termination on cyclic structures
  • DCGs — Definite Clause Grammars with >> syntax and Phrase/2,3
  • Well-Founded Semantics — negation-as-failure for tabled predicates
  • Module system-import_from/2, -import_module/1, qualified calls
  • Meta-predicatesfindall/3, bagof/3, setof/3, forall/2, Call/N
  • Higher-ordermaplist/2,3, foldl/4, include/3, exclude/3
  • Python interop++expr escape, lambda goal closures, f-string support in terms
  • SciPy integration — wrappers for scipy.special, scipy.linalg, scipy.optimize, scipy.interpolate, scipy.signal
  • Regexmatch/2,3, search/2,3, replace/4, split/3 with auto-binding goal expansion
  • Term expansion — macro system for source-level term rewriting
  • C extensions — fast logic variables, trail-based backtracking, trampoline (no WAM)

Installation

pip install clausal

Requires Python ≥ 3.13 and a C compiler (used automatically by pip when building from source).

Quick start

.clausal source files

Facts use a trailing comma. Rules use <- with a parenthesised, comma-separated body. Predicate names are lowercase (snake_case by convention); variables are ALL_CAPS.

# family.clausal
parent(tom, bob),
parent(tom, liz),
parent(bob, ann),
parent(bob, pat),

grandparent(X, Z) <- (parent(X, Y), parent(Y, Z))
import family   # .clausal files load via the import hook

from clausal import call, deref, Var

GRANDCHILD = Var()
results = [deref(GRANDCHILD) for _ in call("Grandparent", "tom", GRANDCHILD, module=family)]
# → ['ann', 'pat']

Arithmetic

# fib.clausal
Fib(0, 0),
Fib(1, 1),
Fib(N, RESULT) <- (
    N > 1,
    N1 := N - 1,
    N2 := N - 2,
    Fib(N1, A),
    Fib(N2, B),
    RESULT := A + B
)

Tabling (memoisation for cyclic graphs)

-table(Path/2)

Edge(1, 2),
Edge(2, 3),
Edge(3, 1),

Path(X, Y) <- Edge(X, Y)
Path(X, Y) <- (Edge(X, Z), Path(Z, Y))

DCGs

Sentence >> (NounPhrase, VerbPhrase)
NounPhrase >> (["the", "dog"] or ["the", "cat"])
VerbPhrase >> (["runs"] or ["barks"])
from clausal import once
result = once(call("Phrase", "Sentence", ["the", "cat", "runs"], module=grammar))

CLP(FD) — constraint logic programming over integers

Sendmoney(S, E, N, D, M, O, R, Y) <- (
    in_domain([S, E, N, D, M, O, R, Y], 0, 9),
    all_different([S, E, N, D, M, O, R, Y]),
    S != 0,
    M != 0,
    label([S, E, N, D, M, O, R, Y]),
    SEND  := S * 1000 + E * 100 + N * 10 + D,
    MORE  := M * 1000 + O * 100 + R * 10 + E,
    MONEY := M * 10000 + O * 1000 + N * 100 + E * 10 + Y,
    SEND + MORE == MONEY
)

Meta-predicates

Squares(Ns, Squares) <- (
    findall(
        Sq,
        (in_(X, Ns), Sq := X * X),
        Squares
    )
)

Testing

.clausal files can include inline tests as Test/1 clauses:

Test("fib(5) = 5") <- Fib(5, 5)
Test("tom's grandchildren") <- (
    Grandparent(tom, ann),
    Grandparent(tom, pat)
)

Standalone runner:

python -m clausal.testing clausal/examples/           # all .clausal files
python -m clausal.testing clausal/examples/hanoi.clausal  # single file
python -m clausal.testing -v clausal/examples/        # verbose

Via pytest (.clausal tests collected automatically):

python -m pytest tests/ clausal/examples/ -q

Logic variables and backtracking

The C extension clausal.logic.variables provides Prolog-style logic variables and trail-based backtracking without a Warren Abstract Machine.

from clausal.logic.variables import Var, Trail, unify, is_var

trail = Trail()
X = Var()
Y = Var()

unify(X, 42, trail)
assert X.value == 42

mark = trail.mark()
unify(Y, "temporary", trail)
trail.undo(mark)
assert is_var(Y)   # Y is unbound again

Constraints

from clausal.logic.variables import Var, Trail, unify
from clausal.logic.constraints import dif

trail = Trail()
X, Y = Var(), Var()

dif(X, Y, trail)      # post: X ≠ Y
unify(X, 1, trail)    # ok — still satisfiable
unify(Y, 2, trail)    # ok — satisfied (1 ≠ 2)

Requirements

  • Python ≥ 3.13
  • greenlet
  • pyyaml ≥ 6.0
  • C compiler (for building from source)

License

MIT

Download files

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

Source Distribution

clausal-0.4.0.tar.gz (1.2 MB view details)

Uploaded Source

File details

Details for the file clausal-0.4.0.tar.gz.

File metadata

  • Download URL: clausal-0.4.0.tar.gz
  • Upload date:
  • Size: 1.2 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for clausal-0.4.0.tar.gz
Algorithm Hash digest
SHA256 1ee0e49ba0741d0b452f6b3ca1e3e15323f2feac3973a15128ecf12d64bde31e
MD5 990db72bc678985e663fd0a5106acc2f
BLAKE2b-256 3df1c8f2d145ed08787fbe2c65658fcc253d9c29b228d10dfd4121e1e00b1bab

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.4.0 This release

1 file

0.3.1

1 file

0.3.0

1 file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page