In practice, most of these optimization tasks require a kind of iterative-to-maximum / minimum external driver from their SMT solver. Quantitative approaches are also possible that can take advantage of the specific capabilities of SMT solvers, but in practice, such constraints are too complex for the main solver. See this discussion in particular: How to optimize a piece of code in Z3? (PI_NON_NESTED_ARITH_WEIGHT)
Having said that, most high-level language bindings include the necessary mechanism to simplify your life. For example, if you use the Haskell SBV library for script z3, you can:
Prelude> import Data.SBV Prelude Data.SBV> maximize Quantified head 2 (\[x, y] -> x.==3 &&& y.>=1) Just [3,1] Prelude Data.SBV> maximize Quantified (head . tail) 2 (\[x, y] -> x.==3 &&& y.>=1) Nothing Prelude Data.SBV> minimize Quantified head 2 (\[x, y] -> x.==3 &&& y.>=1) Just [3,1] Prelude Data.SBV> minimize Quantified (head . tail) 2 (\[x, y] -> x.==3 &&& y.>=1) Just [3,1]
The first result of the state x = 3, y = 1 maximizes x relative to the predicate x == 3 && y> = 1. The second result says that there is no value that maximizes y relative to the same predicate. The third call says x = 3, y = 1 minimizes the predicate with respect to x. The fourth call says x = 3, y = 1 minimizes the predicate in y. (For more details see http://hackage.haskell.org/packages/archive/sbv/0.9.24/doc/html/Data-SBV.html#g:34 .)
You can also use the Iterative option (instead of Quantified) so that the library performs iterative optimization instead of using quantifiers. These two methods are not equivalent, since the latter can get stuck in local minima / maxima, but iterative approaches can solve problems when the quantitative version is too much to process the SMT solver.
source share