I am trying to use Z3 to reason about substrings, and have come across some non-intuitive behavior. Z3 returns 'sat' when asked to determine if 'xy' appears within 'xy', but it returns 'unknown' when asked if 'x' is in 'x', or 'x' is in 'xy'.
I've commented the following code to illustrate this behavior:
(set-logic AUFLIA)
(declare-sort Char 0)
;characters to build strings are _x_ and _y_
(declare-fun _x_ () Char)
(declare-fun _y_ () Char)
(assert (distinct _x_ _y_))
;string literals
(declare-fun findMeX () (Array Int Char))
(declare-fun findMeXY () (Array Int Char))
(declare-fun x () (Array Int Char))
(declare-fun xy () (Array Int Char))
(declare-fun length ( (Array Int Char) ) Int )
;set findMeX = 'x'
(assert (= (select findMeX 0) _x_))
(assert (= (length findMeX) 1))
;set findMeXY = 'xy'
(assert (= (select findMeXY 0) _x_))
(assert (= (select findMeXY 1) _y_))
(assert (= (length findMeXY) 2))
;set x = 'x'
(assert (= (select x 0) _x_))
(assert (= (length x) 1))
;set xy = 'xy'
(assert (= (select xy 0) _x_))
(assert (= (select xy 1) _y_))
(assert (= (length xy) 2))
Now that the problem is set up, we try to find the substrings:
;search for findMeX='x' in x='x'
(push 1)
(assert
(exists
((offset Int))
(and
(<= offset (- (length x) (length findMeX)))
(>= offset 0)
(forall
((index Int))
(=>
(and
(< index (length findMeX))
(>= index 0))
(=
(select x (+ index offset))
(select findMeX index)))))))
(check-sat) ;'sat' expected, 'unknown' returned
(pop 1)
If we instead search for findMeXY
in xy
, the solver returns 'sat', as expected. It would seem that since the solver can handle this query for 'xy', it should be able to handle it for 'x'. Further, if searching for findMeX='x'
in 'xy='xy'
, it returns 'unknown'.
Can anyone suggest an explanation, or perhaps an alternate model for expressing this problem within a SMT solver?