1

I'm trying to use Z3 to solve equations involving unknown projection functions, to find a valid interpretation of the functions that satisfy the equation. So for example for the equation: snd . f = g . fst a valid interpretation would be f = \(x,y) -> (y,x) and g = id. I know that Z3 isn't higher order so I've been trying to encode the problem in first order form. So for example for f = g.fst I use:

(declare-datatypes (T) ((Tree (leaf (value T)) (node (children TreeList)))
                        (TreeList nil (cons (head Tree) (tail TreeList)))))
(define-fun fst ((x (Tree Int))) (Tree Int) (head (children x)))
(define-fun snd ((x (Tree Int))) (Tree Int) (head (tail (children x))))
(declare-fun f ((Tree Int)) (Tree Int))
(declare-fun g ((Tree Int)) (Tree Int))
(assert (forall ((x (Tree Int))) (= (f x) (g (fst x)))))
(check-sat)
(get-model)

Which sort of works returning:

(define-fun g ((x!1 (Tree Int))) (Tree Int)
  (leaf 0))
(define-fun f ((x!1 (Tree Int))) (Tree Int)
  (g (head (children x!1))))

However for snd . f = g . fst (I've simplified trees to pairs to try and help):

(declare-datatypes (T) ((Pair (leaf (value T)) (pair (fst Pair) (snd Pair)))))
(declare-fun f ((Pair Int)) (Pair Int))
(declare-fun g ((Pair Int)) (Pair Int))
(assert (forall ((x (Pair Int))) (= (snd (f x)) (g (fst x)))))

I get unknown.

I've also tried to encode a similar problem without the ADT just using booleans or ints as parameters, but then the model just assigns constant values to the functions. I've also tried to define a simple ADT for function constants, the identity function, and pairwise and sequential composition, and then define an "equals" function that can simplify expressions like f.id = f, but this either involves a recursive function like:

(declare-datatypes () (
  (Fun id 
       (fun (funnum Int))
       (seq (after Fun) (before Fun))
       (pair (fst Fun) (snd Fun)) )))
(define-fun eq ((x Fun) (y Fun)) Bool (or
    (= x y)
      (eq x (seq y id)) ; id neutral for seq
      (eq x (seq id y))
      (eq y (seq x id))
      (eq y (seq id x))))
(declare-const f Fun)
(declare-const g Fun)
(assert (eq f (seq id g)))
(check-sat)
(get-model)

Which is obviously invalid. Or if I use an uninterpretted function, it makes "eq" a constant function i.e.

(declare-fun eq (Fun Fun) Bool)
(assert (forall ((x Fun) (y Fun))  ; semantic equality
    (= (eq x y) (or
      (= x y) ; syntactic eq
      (eq x (seq y id)) ; id neutral for seq
      (eq x (seq id y))
      (eq y (seq x id))
      (eq y (seq id x))
    ))
))
=>
(define-fun eq ((x!1 Fun) (x!2 Fun)) Bool
    true)

And similarly with equations involving functions with type Int -> Int, this returns constant functions for f and g:

(declare-fun f (Int) Int)
(declare-fun g (Int) Int)
(assert (forall ((x Int)) (= (+ (f x) 1) (+ (g x) 2)) ))

and adding these times out:

(assert (forall ((x Int) (y Int)) (=> 
  (not (= x y)) 
  (not (= (g x) (g y))))))
(assert (forall ((x Int) (y Int)) (=> 
  (not (= x y)) 
  (not (= (f x) (f y))))))

Any ideas how I can get this sort of thing to work?

Many thanks!

Taj
  • 21
  • 4

1 Answers1

1

Z3 searches for essentially finite models so isn't well suited for solving functional equations directly. The main trick for finding models of these kinds is to strengthen the formulas by providing a finite set of alternative interpretations of functions that can be composed. For example, you can allow f(x) to be either identity, permutation, or repeat x or y, or return a constant value in one of the fields, This may be composed with functions that perform simple arithmetical operations. You will have to bound the number of compositions you are willing to admit. You assert a similar set of templates for g. So far this has worked best for bit-vectors. The search space for such interpretations can easily get overwhelming. I tried your example with algebraic data-types and templates. Z3 is not able to find an interpretation in this case, at least not by stating the problem directly as a template search.

Nikolaj Bjorner
  • 8,229
  • 14
  • 15