Not quite an answer, but too long for comment.
calling the toString () method in Expr is very expensive for big restrictions.
I would suggest looking at the implementation of toString . There's one possible reason for this slow: dumb string concatenation instead of using StringBuilder . If so, then you might want to provide a faster implementation. However, a CSP solution whose printing takes too much time is useless anyway.
How to check if two restrictions in Z3 are very syntactically identical
I bet that using toString is not the way to go, since a && b and b && a equivalent, but their string representations are not.
You will probably need to normalize expressions using commutativity, associativity, and possibly other rules. To do this, you need some kind of arbitrary but equals consistent ordering in terms.
Of course, you do not want to check all pairs of equality constraints. Here, putting restrictions in buckets according to their hash code helps. I do not know if the provided hashCode applicable for this purpose.
Then I have to create my own equal () and hashCode () functions. But so far I still can’t find an effective way.
After normalization, this should be pretty simple. If you perform normalization and map each expression to a canonical representation (as String#intern does), you can use reference equality for equals . For hashCode you need a combining formula for each node tree, for example, you can calculate the hash code a && b as f(111 * ha + hb) , where ha and hb are hash codes a and b , respectively, and f(x) = x ^ (x>>15) or something like that.