Reputation: 2014
Given is a function and a description of its behavior:
add: (ℤ ∪ {Error, None})² → (ℤ ∪ {Error, None})
For x ∈ (ℤ ∪ {Error, None}):
add(x, None) = add(None, x) = None
For x ∈ (ℤ ∪ {Error}):
add(x, Error) = add(Error, x) = Error
For x, y ∈ ℤ:
add(x, y) = x + y
How can I transform this description to SMT (I'm using Z3) and check whether the description defines a total function?
To give you an idea what I want to achieve: in the end I want to generate Python code implementing this function with the minimum number of branches. For example:
def add(x, y):
if x is None or y is None:
return None
if x is Error or y is Error:
return Error
return x + y
This is what I tried so far:
(declare-datatypes () ((ABC (int (val Int)) error none)))
(declare-fun f (ABC ABC) ABC)
(assert (forall ((x ABC)) (=> (or (is-int x) (= x error) (= x none)) (= (f none x) none))))
(assert (forall ((x ABC)) (=> (or (is-int x) (= x error) (= x none)) (= (f x none) none))))
(assert (forall ((x ABC)) (=> (or (is-int x) (= x error)) (= (f error x) error))))
(assert (forall ((x ABC)) (=> (or (is-int x) (= x error)) (= (f x error) error))))
(assert (forall ((x ABC) (y ABC)) (=> (and (is-int x) (is-int y)) (= (f x y) (int (+ (val x) (val y)))))))
(check-sat)
(get-model)
This timeouts.
EDIT: In Simplify function interpretation in model I described a simpler axiomatization of f where int
is a single case, resulting in finitely many cases. Can I somehow tell Z3 that the concrete val
in int
doesn't matter and there are effectively finitely many cases?
Upvotes: 1
Views: 297
Reputation: 30525
To check uniqueness: Your axiomatization is good; you just need to "replicate" it for two functions then ask for two values such that they differ:
(declare-datatypes () ((ABC (int (val Int)) error none)))
(declare-fun f (ABC ABC) ABC)
(assert (forall ((x ABC)) (=> (or (is-int x) (= x error) (= x none)) (= (f none x) none))))
(assert (forall ((x ABC)) (=> (or (is-int x) (= x error) (= x none)) (= (f x none) none))))
(assert (forall ((x ABC)) (=> (or (is-int x) (= x error)) (= (f error x) error))))
(assert (forall ((x ABC)) (=> (or (is-int x) (= x error)) (= (f x error) error))))
(assert (forall ((x ABC) (y ABC)) (=> (and (is-int x) (is-int y)) (= (f x y) (int (+ (val x) (val y)))))))
(declare-fun g (ABC ABC) ABC)
(assert (forall ((x ABC)) (=> (or (is-int x) (= x error) (= x none)) (= (g none x) none))))
(assert (forall ((x ABC)) (=> (or (is-int x) (= x error) (= x none)) (= (g x none) none))))
(assert (forall ((x ABC)) (=> (or (is-int x) (= x error)) (= (g error x) error))))
(assert (forall ((x ABC)) (=> (or (is-int x) (= x error)) (= (g x error) error))))
(assert (forall ((x ABC) (y ABC)) (=> (and (is-int x) (is-int y)) (= (g x y) (int (+ (val x) (val y)))))))
; check that there is no {x,y} such that f and g differ on:
(declare-const x ABC)
(declare-const y ABC)
(assert (not (= (f x y) (g x y))))
(check-sat)
(get-model)
Z3 quickly returns unsat for this; meaning f
and g
cannot differ. Now, you can try commenting out some of the asserts for f
and/or g
and ask for a model. Depending on which asserts you comment out, Z3 might come up with a model for f
and g
such that they have different behavior; OR it can still time-out, trying to construct a model to do so. In the presence of quantifiers, you can't really expect anything better from an SMT solver.
Upvotes: 1