0

Let's say you have a system of pure expressions, like,

(bi0, bi1, bi2, ai0, ai1, ai2) := inputs
b0 := bi0 && bi1
a1 := b0 ? ai0 : cbrt(ai0)
a2 := bi2 ? a1 : ai1
output := a2 > ai2

# prove:
output == True

Can an automated theorem prover be programmed, not just to find some inputs for which output is true, but to find all possible inputs for which it is true?

alias
  • 28,120
  • 2
  • 23
  • 40
alexchandel
  • 532
  • 6
  • 15

1 Answers1

0

SMT solvers are quite good at solving these sorts of constraints and there are many to choose from. See https://smtlib.cs.uiowa.edu for an overview.

One implementation is Microsoft's z3, which can easily handle queries like the one you posed: https://github.com/Z3Prover/z3

SMT solvers can be programmed in the so called SMTLib syntax (see first link above), or they provide APIs in many languages for you to program them in; including C/C++/Java/Python/Haskell etc. Furthermore, most theorem provers these days (including Isabelle/HOL) come with tactics that use these provers behind the scenes; so many integrations are possible.

As I mentioned, z3 can be programmed in various languages, a good paper to review is https://theory.stanford.edu/~nikolaj/programmingz3.html, which uses Python to do so.

A Solution using Z3 in Haskell

For your particular problem, here's how it'd be solved using z3 in Haskell, using the SBV layer (http://leventerkok.github.io/sbv/):

import Data.SBV

problem :: IO AllSatResult
problem = allSatWith z3{allSatPrintAlong = True} $ do
            [bi0, bi1, bi2] <- sBools ["bi0", "bi1", "bi2"]
            [ai0, ai1, ai2] <- sReals ["ai0", "ai1", "ai2"]

            cbrt_ai0 <- sReal "cbrt_ai0"
            let cube x = x * x * x
            constrain $ cube cbrt_ai0 .== ai0

            let b0 = bi0 .&& bi1
                a1 = ite b0  ai0 cbrt_ai0
                a2 = ite bi2 a1  ai1

            pure $ a2 .> ai2

When I run this, I get:

*Main> problem
Solution #1:
  bi0      = False :: Bool
  bi1      = False :: Bool
  bi2      = False :: Bool
  ai0      = 0.125 :: Real
  ai1      =  -0.5 :: Real
  ai2      =  -2.0 :: Real
  cbrt_ai0 =   0.5 :: Real
Solution #2:
  bi0      = True :: Bool
  bi1      = True :: Bool
  bi2      = True :: Bool
  ai0      =  0.0 :: Real
  ai1      =  0.0 :: Real
  ai2      = -1.0 :: Real
  cbrt_ai0 =  0.0 :: Real
Solution #3:
  bi0      =  True :: Bool
  bi1      = False :: Bool
  bi2      =  True :: Bool
  ai0      =   0.0 :: Real
  ai1      =   0.0 :: Real
  ai2      =  -1.0 :: Real
  cbrt_ai0 =   0.0 :: Real

...[many more lines deleted...]

I interrupted the output since it seems there's a lot of assignments (possibly infinite by looking at the structure of your problem).

A solution using Z3 in Python

Another popular choice is to use Python to program the same. The code is a bit more verbose than Haskell in this case, there's some boiler-plate to get all-solutions and other Python gotchas, but otherwise follow a similar path:

from z3 import *

s = Solver()

bi0, bi1, bi2 = Bools('bi0 bi1 bi2')
ai0, ai1, ai2 = Reals('ai0 ai1 ai2')

cbrt_ai0 = Real('cbrt_ai0')

def cube(x):
    return x*x*x

s.add(cube(cbrt_ai0) == ai0)

b0 = And(bi0, bi1)
a1 = If(b0, ai0, cbrt_ai0)
a2 = If(bi2, a1, ai1)

s.add(a2  > ai2)

def all_smt(s, initial_terms):
    def block_term(s, m, t):
        s.add(t != m.eval(t, model_completion=True))
    def fix_term(s, m, t):
        s.add(t == m.eval(t, model_completion=True))
    def all_smt_rec(terms):
        if sat == s.check():
           m = s.model()
           yield m
           for i in range(len(terms)):
               s.push()
               block_term(s, m, terms[i])
               for j in range(i):
                   fix_term(s, m, terms[j])
               yield from all_smt_rec(terms[i:])
               s.pop()...
    yield from all_smt_rec(list(initial_terms))

for model in all_smt(s, [ai0, ai1, ai2, bi0, bi1, bi2]):
    print(model)

Again, when run this continuously spits out solutions.

alias
  • 28,120
  • 2
  • 23
  • 40