Code to use Z3 as a constraint solver inside SWI Prolog, for a CLP(CC) implementation. Currently supports a subset of Z3's capabilities, including propositional logic, equality, arithmetic, bit-vectors, and uninterpreted function symbols.
With the high-level API in z3.pl
,
Z3 asserts are incremental and backtrackable, and the Z3 solver context is pushed and popped automatically.
Alternatively, stateful-repl
accumulates state from one query to the next.
Tomas Uribe, t****.u****@gmail.com
Github: https://github.com/turibe/swi-prolog-z3.
Tested on MacOS Sonoma, with Z3 version 4.12.6.0 and SWI-Prolog version 9.1.21 for arm64-darwin.
-
Install swi-prolog. This can be done via brew, macports, or download. See https://www.swi-prolog.org/.
After this, you should have
swipl
andswipl-ld
executables. -
Install and build Z3, including
libz3.dylib
. This can also be done via brew, macports, or download. See https://github.com/Z3Prover/z3. -
Add a symbolic link to the
libz3.dylib
file in this directory (or copy it over). -
Compile the C code for the Prolog-Z3 interface, using the
swipl-ld
tool.
(Adapt the -I
and -L
paths accordingly.)
swipl-ld -I/<path-to-z3>/z3/src/api/ -L. -o z3_swi_foreign -shared z3_swi_foreign.c -lz3
This creates a z3_swi_foreign.so
binary that is loaded into SWI Prolog when use_module(z3_swi_foreign)
is executed.
- Start
swipl
, import thez3.pl
orstateful_repl.pl
module (e.g.use_module(z3)
), and you're done!
swipl
?- use_module(z3).
Installing Z3 package
Using Z3 version Z3 4.12.5.0
true.
You can now issue Z3 queries, for example:
?- z3_push(a:int > b:int), z3_push(b:int > a:int).
false.
?- z3_push(a:int > b:int), z3_push(b:int > c:int), z3_is_implied(a > c).
true.
?- z3_push(a:int=f(b) and b = 1 and f(a) > 3), z3_push(f(f(a)) = 5), z3_model(M).
M1 = model{constants:[a-2, b-1], functions:[f/1-else-2, f(4)-5, f(2)-4]}.
- If you prefer, run
use_module(stateful_repl)
and add assertions interactively:
?- add(a:int > 1).
?- add(a > b).
?- model(M).
M = model{constants:[a=2, b=0], functions:[]}.
Unit tests can be run from the PL prompt with ?- run_tests.
, or running
swipl -g run_tests -t halt z3.pl stateful_repl.pl z3_swi_foreign.pl
The z3.pl module offers a high-level, user-friendly API, with:
-
Type inference, to minimize the number of declarations needed.
-
Prolog goals that push assertions onto the Z3 solver, that are automatically popped when Prolog backtracks.
The type inference at this level also uses a backtrackable type map, so both types and assertions start afresh from one query to the next. (If you don't want this, see stateful_repl.pl.)
The basic operations are:
z3_push
: Typechecks and pushes a formula, as in
z3_push(f(a:int) = b and b = 3, Status). %% Status = l_true
Status will be one of {l_true, l_false, l_undef, l_type_error}
.
One can also use z3_push/1
, which fails if status is l_false
or l_type_error
.
z3_is_consistent(+Formula)
: Tests whetherFormula
is consistent with what has been pushed so far.
?- z3_push(a > b:int), z3_is_consistent(a = 3 and b = 2). %% succeeds
?- z3_push(a > b:int), z3_is_consistent(a < c and c < b). %% fails
z3_is_implied(+Formula)
: Tests whetherFormula
is implied by what has been pushed so far.
?- z3_push(a > b:int and b > c), z3_is_implied(a > c). %% succeeds
z3_model(-Model)
: Gets a model, if the assertions pushed so far are found to be satisfiable.
?- z3_push(a: int > b and b = f(a) and a > f(b) and f(c:int) > a), z3_model(M).
M = model{constants:[a-0, b- -1, c-5], functions:[f/1-else- -1, f(5)-1]}.
- z3_eval(+Formula, -Result) : Evaluates
Formula
over a model for the assertions pushed so far, if there is one.
Terms with Prolog variables can be asserted as well. Prolog attributes are used to associate each variable with a fresh Z3 constant, and new equalities will be pushed upon unification. For example:
z3_push(X > b), z3_push(b > c), member(X, [a,b,c,d])
will only succeed for X = a
and X = d
.
Using z3.pl
, all assertions and type declarations (except for enums) are reset from one query to the next.
The stateful_repl module offers an alternative to z3.pl
, where assertions and declarations are accumulated,
similarly to the Python integration or the Z3 prompt.
The main queries / commands are:
- add(+Formula)
- asserted(-FormulaList)
- reset
- is_implied(+Formula)
- is_consistent(+Formula)
- model(-Model)
- declarations(-Declarations)
The lower-level z3_swi_foreign.pl
module has no Prolog globals.
It is used by both z3.pl
and stateful_repl.pl
.
The z3_swi_foreign.c
file has the C code that glues things together, to be compiled with swipl-ld
tool.
(See Installation).
type_inference.pl
has basic capabilities for inferring types for constants and functions in Prolog Z3 expressions, saving the work of having to declare everything beforehand (functions, in particular).
type_inference_global_backtrackable.pl
uses this to implement a global backtrackable type inference map.
Finite-domain enumerated types are "sticky" in Z3, and are declared with
z3_declare_enum(+enum_name, +values_list)
For example, z3_declare_enum(fruit, [apple, banana, pear])
.
The associated declarations can be listed with z3_enum_declarations
.
To clear them, we must reset the entire Z3 context,
with z3_reset_context
(low-level), z3_reset
(for z3.pl
), or reset
(for stateful_repl.pl
).
For convenience, we expand isoneof(a,v1,...,vn)
to or(a=v1, ... a=vn)
.
alldifferent
is an alias for Z3's distinct
.
- We also include a generic Prolog implementation of Junker's QuickXplain algorithms,
for explanation (finding minimal unsatisfiable subsets) and relaxation (maximal satisfiable subsets).
See https://cdn.aaai.org/AAAI/2004/AAAI04-027.pdf.
This code can be used on stand-alone basis, plugging in any monotonic
check
orassert
predicate. For this Z3 integration, we usez3_push
. See quickexplain.pl.
From swipl, run
?- doc_server(8080).
and navigate to http://localhost:8080/pldoc/ to see docs.
- The current version handles the following Z3 capabilities: {int,real,bool} types, propositional logic, equality, arithmetic, bit vectors, and uninterpreted function symbols. Other Z3 features such as arrays, sets and quantifiers are future work.