Manuel Jacob
Manuel Jacob

Reputation: 2014

SMT: check uniqueness and totality of function

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

Answers (1)

alias
alias

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

Related Questions