I am trying to work with sequences (modeling SQL conditions, and need it to work with IN clauses of the form "US" in ImportCountries to see if two conditions can be True at the same time):
s = Solver()
test = Const('test', SeqSort(StringSort()))
expr1 = Contains(test, Unit(StringVal("US")))
expr2 = Not(Contains(test, Unit(StringVal("AE"))))
s.add(And(expr1 == True, expr2 == True))
print(s.check()) # Gives unknown.
Is this a limitation of Z3 or a limitation of my understanding or both?
trying the same with a Int sequence works as expected:
s = Solver()
test = Const('test', SeqSort(IntSort()))
expr1 = Contains(test, Unit(IntVal(1)))
expr2 = Not(Contains(test, Unit(IntVal(2))))
s.add(And(expr1 == True, expr2 == True))
print(s.check())
print(s.model())
You're not doing anything wrong. Looks like z3 is probably missing some internal "heuristics" to decide the string case. Note that when you mix strings and sequences the logic becomes semi-decidable: so this is a case where some internal rule failed to apply. But if the elements are integers, they handle it just fine.
As a point of reference, I tried your problem with CVC5 and it went through just fine. (i.e., CVC5 was able to answer your query in the affirmative.) Here's the SMTLib equivalent of your problem:
With z3 I get:
With CVC5 I get:
You should report this at https://github.com/Z3Prover/z3/issues. While this isn't strictly speaking a bug, I'm sure the developers would love to know about it. (Don't forget to mention that CVC5 solves it just fine!) In the mean time, perhaps you can switch to using CVC5 instead.
Using multiple solvers on the same source
SMTLib is the lingua-franca of SMT solvers, i.e., all SMT solvers understand this format. (https://smtlib.cs.uiowa.edu). Unfortunately, it isn't suitable for end-user programming, this is why most people use higher-level bindings from different languages. Your example itself was in Python using the z3 bindings. There are also bindings from many other languages, and some of these aim to be solver-agnostic. i.e., you express your problem in the host-language, and tell the user to solve it using a given solver, and the infrastructure takes care of all the communication and resolves discrepancies (as best as they can!). When you are dealing with these sorts of problems, you might want to pick a system that allows for easy switching of solvers.
One such library is Haskell's SBV (https://hackage.haskell.org/package/sbv). As an example, here's how you'd code your problem in SBV in a solver agnostic way:
And now you can "run" it with the supported solvers of your choosing:
This produces:
Allowing you to use these tools in a more productive manner.