Skip to content

Clausal Prolog

Prolog in Python for Neurosymbolic AI

GitHub · GitLab · PyPI

Neural networks are good at perception, language and pattern-matching, but are slow, resource-hungry, unreliable and need to be constrained. Symbolic AI is good at rules, reasoning and guarantees but can't deal with the real world. Neurosymbolic AI combines the two, giving you the best of both worlds:

  • Capable: neural networks do what hand-written rules can't. They read images, understand and generate language, and learn patterns from data.
  • Explainable: conclusions come from rules you can read and a derivation you can trace, not only from weights.
  • Reliable: hard constraints such as regulations, safety rules and business policy hold exactly, every time, rather than approximately.
  • Data-efficient: knowledge you can state as a rule doesn't have to be learned from examples.
  • Correctable: change a rule and the behaviour changes, with no retraining.

Each side already has a leading platform. Python is the de facto home of neural networks: PyTorch, JAX and the rest of the deep-learning ecosystem are built for it. Prolog is the leading language of symbolic AI. It has more than 50 years of maturity behind it, and it is still chosen for demanding systems, IBM Watson's question analysis among them, for its robustness and expressive power. Neurosymbolic AI needs the two working together.

Clausal Prolog is a Prolog implemented in Python that aims for ISO Prolog conformity. It is not a wrapper around an external engine: it runs in the Python process, on the same objects and the same garbage collector, so Prolog and Python call each other freely. A neural model can call a logic program, and that logic can call back into the model, with no serialisation in between.

Start here

The rest of this page is a quick tour. Command line and Examples are good next stops after the tutorial.

Running Prolog

Install the package from PyPI. It also installs the clausal command:

pip install clausal

Write ordinary Prolog in a .clausal file. Tests live next to the code:

% fibonacci.clausal
:- module(fibonacci, [fib/2]).
:- use_module(library(clpz)).

:- table(fib/2).
fib(0, 0).
fib(1, 1).
fib(N, F) :-
    N #> 1,
    N1 #= N - 1, N2 #= N - 2,
    fib(N1, F1), fib(N2, F2),
    F #= F1 + F2.

test("fib(10) = 55") :- fib(10, 55).

:- end_module(fibonacci).

Run it with the clausal command. -g runs a goal and prints its answers, the way a Prolog toplevel does; --test runs the module's tests:

$ clausal fibonacci.clausal -g "fib(10, F)"
F = 55.
$ clausal --test fibonacci.clausal
1 tests: 1 passed, 0 failed [PASSED]

A program that defines main/0 runs it when given just the file: clausal program.clausal. See Command line.

Existing ISO Prolog lives in .pl files; see Importing Prolog.

Running from Python

Import the module like any Python module and ask it questions. In a .seam file, a goal after -- is a query, and its variables become Python variables:

# report.seam
-import_from(fibonacci, [fib])

for F in --fib(10, F):
    print(F)  # 55
python -c "import clausal, report"

Answers are plain Python values: an atom is a str, a compound term is a tuple such as ('point', 1, 2). See Python Integration.

Three source surfaces

Extension Surface Use it for
.clausal Clausal Prolog: ISO syntax, cut-free Your logic programs
.pl ISO Prolog (experimental) Existing Prolog code
.seam The seam: Python syntax The boundary with Python: ++expr, hosted Python, adapters

Many language pages in these docs still show their examples in seam syntax. Each one says so, and the semantics carry over to Clausal Prolog. Clausal Prolog gives the spelling differences and the rules the surface enforces.


Why Clausal Prolog?

  • Heading for ISO Prolog — ISO syntax, ISO builtin names and ISO error terms; where ISO is silent, Clausal Prolog follows Scryer Prolog. See Operators and Public API.
  • Pure by design — no cut and no committed choice: dif/2, reified if-then-else, constraints and tabling keep programs monotonic. See Purity.
  • Deep integration — terms are Python tuples and atoms are Python strings, backtracking uses Python generators, and Python libraries are reached through library(...) facades and .seam modules.
  • Asyncio, not a home-grown scheduler — queries run on Python's own mature asyncio event loop rather than a scheduler of our own. A proof can wait on a model, a database, a person or an event stream while other queries run, and timeouts, cancellation and racing come straight from asyncio. Backtracking still works across waits (experimental).
  • Full-featured — tabling, CLP(ℤ), DCGs, EDCGs, modules, term expansion, goal expansion, reified if-then-else.
  • Fast — C extension for unification/trails, first-argument indexing, groundness-keyed dispatch, tail recursion optimization, bytecode caching.

Interactive example — Sudoku in IPython

The IPython integration uses seam syntax, since it runs inside Python.

Start IPython with the integration enabled:

CLAUSAL_IPYTHON=True ipython

Then solve a Sudoku puzzle interactively:

In [1]: from clausal.examples.sudoku import *

In [2]: *(ROWS is [
   ...:   [1, _, _, 8, _, 4, _, _, _],
   ...:   [_, 2, _, _, _, _, 4, 5, 6],
   ...:   [_, _, 3, 2, _, 5, _, _, _],
   ...:   [_, _, _, 4, _, _, 8, _, 5],
   ...:   [7, 8, 9, _, 5, _, _, _, _],
   ...:   [_, _, _, _, _, 6, 2, _, 3],
   ...:   [8, _, 1, _, _, _, 7, _, _],
   ...:   [_, _, _, 1, 2, 3, _, 8, _],
   ...:   [2, _, 5, _, _, _, _, _, 9],
   ...: ], solve(ROWS))
Out[2]: ROWS is [
  [1, 5, 6, 8, 9, 4, 3, 2, 7],
  [9, 2, 8, 7, 3, 1, 4, 5, 6],
  [4, 7, 3, 2, 6, 5, 9, 1, 8],
  [3, 6, 2, 4, 1, 7, 8, 9, 5],
  [7, 8, 9, 3, 5, 2, 6, 4, 1],
  [5, 1, 4, 9, 8, 6, 2, 7, 3],
  [8, 3, 1, 5, 4, 9, 7, 6, 2],
  [6, 9, 7, 1, 2, 3, 5, 8, 4],
  [2, 4, 5, 6, 7, 8, 1, 3, 9]
]
# No more solutions.

Uppercase names (ROWS) are automatically allocated as logic variables. The *(...) form is the IPython query syntax — see IPython / Jupyter REPL for the full feature set.


What's inside

Section What you'll find
Foundations
Thinking Relationally The most important idea: predicates as relations, not functions
Purity and Monotonicity Why pure code has better properties and how to write it
Start Here
For Python Programmers Bridge from functions and loops to relations and search
For Prolog Programmers Syntax mapping, what's the same, what's different
For AI Agents Why LLMs should generate logic programs
For Decision Makers The business case: explainability, reliability, rules-as-code
Getting Started
Syntax The trailing-comma convention, escape operators, logic variables, clause syntax
Style & Formatting One goal per line, clause separation, clausal-fmt
Clausal Prolog The .clausal surface: ISO syntax, cut-free, its rules and how it differs from the seam
Predicates How to define predicates
Builtins Complete index of built-in predicates
Dicts & Sets DictTerm, SetTerm, __unify__ protocol
Constraints dif/2, CLP(ℤ) integer constraints, and CLP(ℝ) real-domain constraints
CLP(ℝ) Interval arithmetic, non-linear propagation, and bisection labeling over the reals
CLP(Q) Exact rational constraints via Gaussian elimination and the revised simplex method
Tabling SLG resolution and well-founded semantics
Lambdas Goal closures for higher-order logic programming
If-Then-Else Reified branching (no cut, no committed choice)
Import System Module loading, module directives, qualified calls
Importing Prolog Import .pl files directly — on-the-fly translation and caching (experimental in 1.0)
Architecture Layer stack, execution model, why not a WAM
Python Integration Querying with --goal, the ++() escape, solve(), converters
Asyncio Queries on Python's asyncio loop: waiting on models, data, people and streams mid-search
Operators What each operator means bare (Python) and quoted (Scryer/ISO)
Arithmetic Evaluation, exact rationals, the evaluable functors
Public API What the 1.0 semantic-versioning promise covers
Reflection Reify .seam source as matchable terms — linters and matchers written as logic programs
IPython / Jupyter REPL Interactive queries, *(goals) syntax, solution browsing
Standard Library Modules
Physical Units n(Unit) sugar, dimensional arithmetic, AttVar constraints
Currency Exact-decimal money as units base dimensions; rounding/display; precision checks
Regex Pattern matching, group extraction, auto-binding
Symbolic Math SymPy integration — calculus, algebra, number theory
YAML YAML parsing and generation
Date/Time Date, time, and datetime predicates
Logging Structured logging predicates
UUID UUID generation and inspection
Graphs Graph traversal, pathfinding, connectivity, MST
Random Random number generation, selection, seeding
JSON JSON parsing, generation, DictTerm integration
CSV CSV parsing, generation, DictTerm records
OS Environment variables, working directory, process info, platform
Files File/directory existence, listing, metadata, CRUD, path manipulation
Process Shell commands, subprocess execution, sleep
SQLite SQLite database predicates
spaCy NLP NLP pipeline — tokenisation, NER, POS, similarity
scipy.special Special mathematical functions — gamma, Bessel, elliptic, hypergeometric, orthogonal polynomials
scipy.linalg Linear algebra — solvers, decompositions, matrix functions, factorisations
scipy.optimize Optimisation — minimisation, root finding, curve fitting, linear programming
scipy.integrate Numerical integration — quadrature, ODE solvers, sampled-data methods
scipy.interpolate Interpolation — splines, PCHIP, Akima, regular grids, radial basis functions
scipy.stats statistics — descriptive stats, hypothesis tests, distributions
scipy.fft Discrete Fourier transforms — FFT, inverse FFT, helper functions
scipy.ndimage N-dimensional image processing — filters, morphology, transforms, measurements
scipy.spatial Spatial algorithms — distance functions, KD-tree, ConvexHull, Delaunay, Rotation
Crypto Cryptographic hashing, HMAC signing, PBKDF2 key derivation
HTTP & URL HTTP requests (GET, POST, JSON), URL encoding and parsing
TCP TCP client/server sockets — connect, listen, send, receive
Testing Writing test predicates, running the test suite
DCGs Definite Clause Grammars for parsing
Exceptions throw/catch, structured error terms
Coroutining freeze/2, when/2, setup_call_cleanup/3, call_nth/2, count_all/2
CLP(B) Boolean constraint programming
Z3 SMT Solver Multi-theory constraints via Z3 — integers, reals, booleans, bitvectors, arrays, strings, optimization, unsat cores
Meta-Interpreter Specialization Partial deduction — specialize MIs to remove interpretation overhead
Prolog Translation Bidirectional seam ↔ Prolog translation
Trealla Prolog Embedding In-process Trealla Prolog engine via ctypes — fast, lightweight, instant startup
Scryer Prolog Embedding In-process Scryer Prolog engine via PyO3 — lazy queries, tabling support
Examples Example programs: Fibonacci, N-Queens, Sudoku, meta-interpreters
Scientific Computing
scikit-learn Machine learning: estimators, pipelines, cross-validation
scipy.cluster Hierarchical clustering, k-means, vector quantisation
scipy.constants CODATA physical constants, SI prefixes
scipy.differentiate Numerical differentiation: Derivative, Jacobian, Hessian
scipy.signal Signal processing: filter design, filtering, spectral analysis
scipy.sparse Sparse matrices and sparse linear algebra
Infrastructure
Compiler Compilation pipeline: head patterns, body goals, trampoline, TRO
Jupyter Notebooks Notebook integration with HTML rendering
Free Threading Free-threaded Python (PEP 703) support, C extension safety
Parallel Predicates Writing thread-safe predicates
Parallel Queries Running parallel queries from Python