Save and reload z3py solver constraints - z3

Can I save the constraints I created for a z3 solver and later reload them to continue looking for more solutions?
I have learned there is the SMT-LIB2 format for such things and that z3 and z3py have an API for saving and loading in that format. Unfortunately I cannot make it work.
Here's my example program which pointlessly saves and reloads:
import z3
filename = 'z3test.smt'
# Make a solver with some arbitrary useless constraint
solver = z3.Solver()
solver.add(True)
# Save to file
smt2 = solver.sexpr()
with open(filename, mode='w', encoding='ascii') as f: # overwrite
f.write(smt2)
f.close()
# Load from file
solver.reset()
solver.from_file(filename)
It fails with:
Exception has occurred: ctypes.ArgumentError
argument 3: <class 'TypeError'>: wrong type
File "C:\Users\Marian Aldenhövel\Desktop\FridgeIQ\z3\z3-4.8.4.d6df51951f4c-x64-win\bin\python\z3\z3core.py", line 3449, in Z3_solver_from_file
_elems.f(a0, a1, _to_ascii(a2))
File "C:\Users\Marian Aldenhövel\Desktop\FridgeIQ\z3\z3-4.8.4.d6df51951f4c-x64-win\bin\python\z3\z3.py", line 6670, in from_file
_handle_parse_error(e, self.ctx)
File "C:\Users\Marian Aldenhövel\Desktop\FridgeIQ\src\z3test.py", line 17, in <module>
solver.from_file(filename)
Is this a problem with my understanding or my code? Can it be done like this? Are sexpr() and from_file() the right pair of API calls?
I am using z3 and z3py 4.8.4 from https://github.com/z3prover/z3/releases on Windows 10 64bit.
More detail if required:
I am playing with z3 in Python to find solutions for a large disection-puzzle.
To find all solutions I am calling solver.check(). When it returns a sat verdict I interpret the model as image of my puzzle solution. I then add a blocking clause ruling out that specific solution and call solver.check() again.
This works fine and I am delighted.
The runtime to find all solutions will be on the order of many days or until I get bored. I am concerned that my machine will not be running continuously for that long. It may crash, run out of power, or be rebooted for other reasons.
I can easily recreate the initial constraints which is the whole point of the program. But the blocking clauses are a runtime product and a function of how far we have gotten.
I thought I could save the state of the solver and if at runtime I find such a file restart by loading that with the blocking clauses intact and go on finding more solutions instead of having to start over.
Thank you for taking your time reading and thinking.
Marian

With z3 4.4.1 and z3 4.8.5, I would dump (and reload) the constraints in smt2 format as follows:
import z3
filename = "z3test.smt2"
x1 = z3.Real("x1")
x2 = z3.Real("x2")
solver = z3.Solver()
solver.add(x1 != x2)
#
# STORE
#
with open(filename, mode='w') as f:
f.write(solver.to_smt2())
#
# RELOAD
#
solver.reset()
constraints = z3.parse_smt2_file(filename, sorts={}, decls={})
solver.add(constraints)
print(solver)
output:
~$ python t.py
[And(x1 != x2, True)]
file z3test.smt2:
(set-info :status unknown)
(declare-fun x2 () Real)
(declare-fun x1 () Real)
(assert
(and (distinct x1 x2) true))
(check-sat)
I have no idea whether the API changed in the version you are using. Feedback is welcome.

Related

Floor and Ceiling Function implementation in Z3

I have tried to implement Floor and Ceiling Function as defined in the following link
https://math.stackexchange.com/questions/3619044/floor-or-ceiling-function-encoding-in-first-order-logic/3619320#3619320
But Z3 query returning counterexample.
Floor Function
_X=Real('_X')
_Y=Int('_Y')
_W=Int('_W')
_n=Int('_n')
_Floor=Function('_Floor',RealSort(),IntSort())
..
_s.add(_X>=0)
_s.add(_Y>=0)
_s.add(Implies(_Floor(_X)==_Y,And(Or(_Y==_X,_Y<_X),ForAll(_W,Implies(And(_W>=0,_W<_X),And(_W ==_Y,_W<_Y))))))
_s.add(Implies(And(Or(_Y==_X,_Y<_X),ForAll(_W,Implies(And(_W>=0,_W<_X),And(_W==_Y,_W<_Y))),_Floor(_X)==_Y))
_s.add(Not(_Floor(0.5)==0))
Expected Result - Unsat
Actual Result - Sat
Ceiling Function
_X=Real('_X')
_Y=Int('_Y')
_W=Int('_W')
_Ceiling=Function('_Ceiling',RealSort(),IntSort())
..
..
_s.add(_X>=0)
_s.add(_Y>=0)
_s.add(Implies(_Ceiling(_X)==_Y,And(Or(_Y==_X,_Y<_X),ForAll(_W,Implies(And(_W>=0,_W<_X),And(_W ==_Y,_Y<_W))))))
_s.add(Implies(And(Or(_Y==_X,_Y<_X),ForAll(_W,Implies(And(_W>=0,_W<_X),And(_W==_Y,_Y<_W)))),_Ceiling(_X)==_Y))
_s.add(Not(_Ceilng(0.5)==1))
Expected Result - Unsat
Actual Result - Sat
[Your encoding doesn't load to z3, it gives a syntax error even after eliminating the '..', as your call to Implies needs an extra argument. But I'll ignore all that.]
The short answer is, you can't really do this sort of thing in an SMT-Solver. If you could, then you can solve arbitrary Diophantine equations. Simply cast it in terms of Reals, solve it (there is a decision procedure for Reals), and then add the extra constraint that the result is an integer by saying Floor(solution) = solution. So, by this argument, you can see that modeling such functions will be beyond the capabilities of an SMT solver.
See this answer for details: Get fractional part of real in QF_UFNRA
Having said that, this does not mean you cannot code this up in Z3. It just means that it will be more or less useless. Here's how I would go about it:
from z3 import *
s = Solver()
Floor = Function('Floor',RealSort(),IntSort())
r = Real('R')
f = Int('f')
s.add(ForAll([r, f], Implies(And(f <= r, r < f+1), Floor(r) == f)))
Now, if I do this:
s.add(Not(Floor(0.5) == 0))
print(s.check())
you'll get unsat, which is correct. If you do this instead:
s.add(Not(Floor(0.5) == 1))
print(s.check())
you'll see that z3 simply loops forever. To make this usefull, you'd want the following to work as well:
test = Real('test')
s.add(test == 2.4)
result = Int('result')
s.add(Floor(test) == result)
print(s.check())
but again, you'll see that z3 simply loops forever.
So, bottom line: Yes, you can model such constructs, and z3 will correctly answer the simplest of queries. But with anything interesting, it'll simply loop forever. (Essentially whenever you'd expect sat and most of the unsat scenarios unless they can be constant-folded away, I'd expect z3 to simply loop.) And there's a very good reason for that, as I mentioned: Such theories are just not decidable and fall well out of the range of what an SMT solver can do.
If you are interested in modeling such functions, your best bet is to use a more traditional theorem prover, like Isabelle, Coq, ACL2, HOL, HOL-Light, amongst others. They are much more suited for working on these sorts of problems. And also, give a read to Get fractional part of real in QF_UFNRA as it goes into some of the other details of how you can go about modeling such functions using non-linear real arithmetic.

Problem with function "from_file()" at z3py

Let's assume that we have the following files:
func.smt
(declare-datatypes (T) ((AVL leafA (nodeA (val T) (alt Int) (izq AVL) (der AVL)))))
espec.smt
(declare-const t (AVL Int))
And the following code:
from z3 import *
s = Solver()
s.from_file("func.smt")
s.from_file("espec.smt")
When instruction "s.from_file("espec.smt")" is executed, z3 throws the next exception:
z3.z3types.Z3Exception: b'(error "line 1 column 18: unknown sort \'AVL\'")\n'
It seems that the Solver "s" doesn't save the information of datatypes (and functions too). That is why he throws the exception (I guess).
The following code is a way to solve it:
s = Solver()
file = open("func.smt", "r")
func = file.read()
file.close()
file = open("espec.smt", "r")
espec = file.read()
file.close()
s.from_string(func + espec)
But this way it's not efficient.
Is there another more efficient way to solve this and make z3 save datatypes and functions for future asserts and declarations?
Edit:
In my real case for example, the file "func.smt" has a total of 54 declarations between functions and datatypes (some quite complex). The "spec.smt" file has few declarations and asserts. And finally I have a file in which there are many asserts which I have to read little by little and generate a model.
That is, imagine that I have 600 lines of asserts, and I read from 10 to 10, so every 10 lines I generate a model. That is, if we assume that those asserts that I have read are stored in a variable called "aux", every time I want to get a new model, I will have to do "s.from_string (func + spec + aux)" 60 times in total, and I don't know if that could be made more efficient.
Separately reading the files and loading to solver is your best bet, as you found out.
Unless efficiency is of paramount importance, I would not worry about trying to optimize this any further. For any reasonable problem, your solver time will dominate any time spent in loading the assertions from a few (or a few thousand!) files. Do you have a use case that really requires this part to be significantly faster?

Avoiding truncation of large model in Z3

For a large model, the function model() used through the Z3 Python API truncates the output (at some point the model is continued with "...").
Is there a way to avoid this?
If I remember correctly, this is a Python "feature". Instead of str(...) try using repr(...), which should produce a string that can be read back (if needed) by the interpreter. You can of course iterate over the model elements separately, so as to make all the strings needing to be output smaller. For instance, along these lines:
s = Solver()
# Add constraints...
print(s.check())
m = s.model()
for k in m:
print('%s=%s' % (k, m[k]))
I have the function from below which tries to save into fileName the answer given by str(self.solver.check()) and the model given by str(self.solver.model())
def createSMT2LIBFileSolution(self, fileName):
with open(fileName, 'w+') as foo:
foo.write(str(self.solver.check()))
foo.write(str(self.solver.model()))
foo.close()
The output of the problem is:
sat[C5_VM1 = 0,
... //these "..." are added by me
VM6Type = 6,
ProcProv11 = 18,
VM2Type = 5,
...]
The "...]" appear at line 130 in all the files which are truncated. I don't know if is a Python or Z3 thing. If the model can be written on less than 130 lines everything is fine.

z3: conversion of expressions with transcendental functions from z3py to smt-lib2

As per my knowledge, since z3 doesn't recognize transcendental functions its throwing me an error while conversion using following code.
def convertor(f, status="unknown", name="benchmark", logic=""):
v = (Ast * 0)()
if isinstance(f, Solver):
a = f.assertions()
if len(a) == 0:
f = BoolVal(True)
else:
f = And(*a)
return Z3_benchmark_to_smtlib_string(f.ctx_ref(), name, logic, status, "", 0, v, f.as_ast())
pi, EI, kA , kB, N = Reals('pi EI kA kB N')
s= Solver()
s.add(pi == 3.1415926525)
s.add(EI == 175.2481)
s.add(kA>= 0)
s.add(kA<= 100)
s.add(kB>= 0)
s.add(kB<= 100)
s.add(N>= 100)
s.add(N<= 200)
please change the path of the input file "beamfinv3.bch", which can be found at: link
continue_read=False
input_file = open('/home/mani/downloads/new_z3/beamfinv3.bch', 'r')
for line in input_file:
if line.strip()=="Constraints":
continue_read=True
continue
if line.strip()=="end":
continue_read=False
if continue_read==True:
parts = line.split(';')
if (parts[0]!="end"):
#print parts[0]
s.add(eval(parts[0]))
input_file.close()
file=open('cyber.smt2','w')
result=convertor(s, logic="None")
file.write (result)
error:
File "<string>", line 1, in <module>
NameError: name 'sin' is not defined
Any way out? or help?
Thanks.
The core of this problem is that eval tries to execute a Python script, i.e., all functions that occur within parts[0] must have a corresponding Python function of the same name, which is not the case for the trigonometric functions (the are neither in the Python API nor the C API, the former being based on the latter). For now you could try to add those functions yourself, perhaps with an implementation based on parse_smt2_string, or perhaps by replacing the Python strings with SMT2 strings altogether.
Z3 can represent expressions containing trigonometric functions, but it will refuse to do so when the logic is set to something; see arith_decl_plugin. I don't know Python well enough, but it might have to be None instead of "".
While Z3 can represent these expressions, it's probably not very good at solving them. See comments on the limitations in Can Z3 handle sinusoidal and exponential functions, Z3 supports for nonlinear arithmetics, and Z3 Performance with Non-Linear Arithmetic.

Z3 ast_to_string() API with Subtraction

The problem is pretty simple.
I assert following statement in Z3 using the C API interface.
(assert(>= (xA 1) (- (yB 0) period))))
Now Sometimes, I need to check what kind of assertions have been fed and the result in the SatSolver. I do this, by generating a text file by using ast_to_string() API. This API returns me above statement as -
(assert(>= (xA 1) (+ (yB 0) (* -1 period))))
When I feed this file to the Sat Solver it complains me with the error -
(error "ERROR: line 150 column 56: could not locate id -1.")
So then, I have to manually fix all -1 in the code and run the sat solver.
Is there any other way where I can avoid this?
Remember to set:
Z3_set_ast_print_mode(ctx,Z3_PRINT_SMTLIB2_COMPLIANT);
before using ast_to_string() in order that that output formulas comply with SMTLIB 2.0 format.

Resources