z3smtthree-valued-logic

A three-valued boolean logic with Z3 solver


I have been trying to define a three-valued propositional logic and reason about it using Z3 SMT Solver. To be more precise, I have define a sort Boolean with three values: TRUE, FALSE and NULL with some assertions.

(declare-sort Boolean 0)       ;I declare a sort Boolean
(declare-const TRUE Boolean)   ;I define three constants
(declare-const TRUE Boolean)
(declare-const FALSE Boolean)
(declare-const NULL Boolean)

(assert (distinct TRUE FALSE)) ;I define the meaning of these constants
(assert (distinct TRUE NULL))
(assert (distinct FALSE NULL))

(assert (forall ((b Boolean))
    (or (= b TRUE)
        (= b FALSE)
        (= b NULL))))

Next, let's say I am defining an the semantics of >= operator of integer under this logic, assuming that an integer can be NULL.

(declare-const nullInt Int)
(declare-fun >=_B (Int Int) Boolean)

(assert (forall ((i1 Int) (i2 Int))
    (= (= TRUE (>=_B i1 i2))
       (and (distinct i1 nullInt)
            (distinct i2 nullInt)
            (>= i1 i2)))))

(assert (forall ((i1 Int) (i2 Int))
    (= (= FALSE (>=_B i1 i2))
       (and (distinct i1 nullInt)
            (distinct i2 nullInt)
            (not (>= i1 i2))))))

(assert (forall ((i Int))
    (= NULL (>=_B i nullInt))))

(assert (forall ((i Int))
    (= NULL (>=_B nullInt i))))

Finally, with the above definitions, I use the >=_B function in my assertions but keep getting unexpected UNSAT or unknown. I would like to know what makes the theory falls into the undecidable area. Is it because of the Boolean sort I made? or Is it because of the assertions in which I quantify over the infinite set of Int?


Solution

  • I think you're complicating the modeling by using quantifiers and uninterpreted sorts. Simply make your boolean an enumeration and define your predicate accordingly:

    (declare-datatype Boolean ((TRUE) (FALSE) (NULL)))
    
    (declare-const nullInt Int)
    
    (define-fun >=_B ((i1 Int) (i2 Int)) Boolean
        (ite (or (= i1 nullInt) (= i2 nullInt)) NULL (ite (>= i1 i2) TRUE FALSE)))
    
    (check-sat)
    (get-model)
    

    This produces:

    sat
    (
      (define-fun nullInt () Int
        0)
    )
    

    Arbitrarily picking nullInt as 0. Now you can build your other operations on top of this and model whatever aspects of your 3-valued logic you want.

    Two notes:

    (declare-datatype NullableInt ((NullInt) (RegInt (getRegInt Int))))
    

    and then define all your operations on this type. Of course, this is more hairy, since you have to lift all your arithmetic operations (i.e., +, -, * etc.) as well.

    Final note: while SMTLib is lingua-franca for SMT solvers, it's not the most human readable/writable. If you're experimenting, I'd recommend using a higher-level interface, such as one from Python/Haskell etc., that can get rid of most of the noise.