Reputation: 67
I'm seeing rather surprising behavior from ctx-solver-simplify
, where the order of parameters to z3.And() seems to matter, using the latest version of Z3 from the master branch of https://git01.codeplex.com/z3 (89c1785b):
#!/usr/bin/python
import z3
a, b = z3.Bools('a b')
p = z3.Not(a)
q = z3.Or(a, b)
for e in z3.And(p, q), z3.And(q, p):
print e, '->', z3.Tactic('ctx-solver-simplify')(e)
produces:
And(Not(a), Or(a, b)) -> [[Not(a), Or(a, b)]]
And(Or(a, b), Not(a)) -> [[b, Not(a)]]
Is this a bug in Z3?
Upvotes: 2
Views: 821
Reputation: 21475
No, this is not a bug. The tactic ctx-solver-simplify
is very expensive and inherently asymmetric. That is, the order the sub-formulas are visited affect the final result. This tactic is implemented in the file src/smt/tactic/ctx_solver_simplify_tactic.cpp
. The code is quite readable. Note that, it uses a "SMT" solver (m_solver
), and makes several calls to m_solver.check()
while traversing the input formula. Each one of these calls can be quite expensive. For particular problem domains, we can implement a version of this tactic that is even more expensive, and avoids the asymmetry described in your question.
EDIT:
You may also consider the tactic ctx-simplify
, it is cheaper than ctx-solver-simplify
, but it is symmetric. The tactic ctx-simplify
will essentially applies rules such as:
A \/ F[A] ==> A \/ F[false]
x != c \/ F[x] ==> F[c]
Where F[A]
is a formula that may contain A
. It is cheaper than ctx-solver-simplify
because it does not invoke a SMT solver when it is traversing the formula. Here is an example using this tactic (also available online):
a, b = Bools('a b')
p = Not(a)
q = Or(a, b)
c = Bool('c')
t = Then('simplify', 'propagate-values', 'ctx-simplify')
for e in Or(c, And(p, q)), Or(c, And(q, p)):
print e, '->', t(e)
Regarding humand-readability, this was never a goal when implementing any tactic. Please, tell us if the tactic above is not good enough for your purposes.
Upvotes: 2
Reputation: 8359
I think it will be better to write a custom tactic because there are other tradeoffs when you essentially ask for canonicity. Z3 does not have any tactics that convert formulas into a canonical form. So if you want something that always produces the same answer for formulas that are ground equivalent you will have to write a new normalizer that ensures this.
The ctx_solver_simplify_tactic furthermore makes some tradeoffs when simplifying formulas: it avoids simplifying the same sub-formula multiple times. If it did, it could increase the size of the resulting formula significantly (exponentially).
Upvotes: 2