Python Z3 API Query : Can we get a partial model using the z3 python API when solver returns unkown status

366 Views Asked by At

I am pretty sure it has something to do with the python API. Is there a way to get back a partial model from z3 even when the status is unknown?

I am trying to get a model out of z3 even when the status returns unknown result. It fails with raising an exception for model not available. Any suggestions what can be done?

I converted the assertions to smt-lib format using sexpr() method from the z3 Solver interface and it returns a partial model even when the status is unknown. I have attached example below.

# -*- coding: utf-8 -*-
​
from z3 import *
​
x, y, z = Reals('x y z')
m, n, l = Reals('m n l')
u, v = Ints('u v')
​
S = SolverFor("NRA")
​
S.add(x >= 0)
S.add(y >= 30, z <= 50)
S.add(m >= 5, n >= 5)
S.add(m * x + n * y + l > 300)
​
S.add(ForAll([u, v], Implies(m * u + n * v + l > 300, u + v + z <= 50)))
​
print(S.check())
print(S.sexpr())

In SMMT-LIB Format

(set-option :print-success true) 
(set-option :produce-unsat-cores true) ; enable generation of unsat cores
(set-option :produce-models true) ; enable model generation
(set-option :produce-proofs true) ; enable proof generation
(declare-fun x () Real)
(declare-fun y () Real)
(declare-fun z () Real)
(declare-fun m () Real)
(declare-fun n () Real)
(declare-fun l () Real)
(assert (>= x 0.0))
(assert (>= y 30.0))
(assert (<= z 50.0))
(assert (>= m 5.0))
(assert (>= n 5.0))
(assert (not (<= (+ (* m x) (* n y) l) 300.0)))
(assert (forall ((u Int) (v Int))
  (let ((a!1 (<= (+ (* m (to_real u)) (* n (to_real v)) l) 300.0)))
    (or (<= (+ (to_real u) (to_real v) z) 50.0) a!1))))
(check-sat)
(get-model)

I am running this file like this from the command line (terminal)

$ z3 example.py

Output :

success
success
success
success
success
success
success
success
success
success
success
success
success
success
success
success
success
unknown
(model 
  (define-fun z () Real
    20.0)
  (define-fun y () Real
    30.0)
  (define-fun l () Real
    145.0)
  (define-fun x () Real
    0.0)
  (define-fun n () Real
    5.0)
  (define-fun m () Real
    5.0)
)

Any suggestions on how to get this model via the python interface directly?

The exception which z3 raises after model() call on unknown status.

unknown
Traceback (most recent call last):

  File "C:\Python38\Lib\site-packages\z3\z3.py", line 6696, in model
    return ModelRef(Z3_solver_get_model(self.ctx.ref(), self.solver), self.ctx)

  File "C:\Python38\Lib\site-packages\z3\z3core.py", line 3759, in Z3_solver_get_model
    _elems.Check(a0)

  File "C:\Python38\Lib\site-packages\z3\z3core.py", line 1385, in Check
    raise self.Exception(self.get_error_message(ctx, err))

Z3Exception: b'there is no current model'


During handling of the above exception, another exception occurred:

Traceback (most recent call last):

  File "C:\Users\lahir\Documents\GitHub\codersguild\Research\tangram-solve\z3_tryouts.py", line 19, in <module>
    print(S.model())

  File "C:\Python38\Lib\site-packages\z3\z3.py", line 6698, in model
    raise Z3Exception("model is not available")

Z3Exception: model is not available

Thanks

2

There are 2 best solutions below

2
On BEST ANSWER

You cannot rely on the model if the solver returned unknown. That is, the model you get is not "partial" by any means: It may or may not satisfy some of the constraints, but otherwise, there's not much you can do with it. It's at best a representation of the internal state of the solver. But in general, it is not guaranteed to be a representation of anything.

Also, when I run your SMTLib script with z3, I get:

unknown
(error "line 21 column 10: model is not available")

and I've z3 built from their github master about a week ago. I gather you've an old version, I'd strongly recommend you upgrade.

For reference, when you get unknown as an answer, the only valid thing to do is to ask the solver why the result is unknown. This is typically done with code like:

r = S.check()
if r == sat:
    print(S.model())
elif r == unknown:
    print("Unknown, reason: %s" % S.reason_unknown())
else:
    print("Solver said: %s" % r)

For your Python program, I get:

smt tactic failed to show goal to be sat/unsat (incomplete quantifiers)

and that's really the only information you have at this point: Any "extraction" of model values would be simply wrong if the solver state is unknown.

(A related question is also about "interrupting" the computation during a call to z3's optimizing solver. The "model" you get if you interrupt the solver will not be a "best" so far by any means, it may or may not satisfy the existing constraints. Long story short, unless solver reports sat, do not count on the model you get: z3 is doing the right thing here and telling you there's no viable model available.)

1
On

I haven't yet found an alternate other than the one I mentioned above.