0

I was trying to solve certain set of constraints using z3 in python. My code:

import math
from z3 import *

### declaration
n_co2 = []
c_co2 = []
alpha = []
beta = []
m_dot_air = []
n_pir = []
pir_sensor = []

for i  in range(2):
    c_co2.append(Real('c_co2_'+str(i)))
    n_pir.append(Real('n_pir_'+str(i)))

n_co2.append(Real('n_co2_'+str(0)))
alpha.append(Real('alpha_'+str(0)))
beta.append(Real('beta_'+str(0)))
m_dot_air.append(Real('m_dot_air_'+str(0)))   
pir_sensor.append(Real('pir_sensor_'+str(0)))

s = Solver()

s.add(n_co2[0]>0)
s.add(c_co2[0]>0)
s.add(c_co2[1]>=0.95*c_co2[0])
s.add(c_co2[1]<=1.05*c_co2[0])
s.add(n_co2[0]>=0.95*n_pir[1])
s.add(n_co2[0]<=1.05*n_pir[1])
s.add(c_co2[1]>0)
s.add(alpha[0]<=-1)
s.add(beta[0]>0)
s.add(m_dot_air[0]>0)
s.add(alpha[0]==-1*(1+ m_dot_air[0] + (m_dot_air[0]**2)/2.0 + (m_dot_air[0]**3)/6.0 ))
s.add(beta[0]== (1-alpha[0])/m_dot_air[0])
s.add(n_co2[0]== (c_co2[1]-alpha[0]*c_co2[0])/(beta[0]*19.6)-(m_dot_air[0]*339)/19.6)
s.add(n_pir[1]>=0)
s.add(pir_sensor[0]>=-1)
s.add(pir_sensor[0]<=1)
s.add(Not(pir_sensor[0]==0))
s.add(n_pir[1]==(n_pir[0]+pir_sensor[0]))

#### testing
s.add(pir_sensor[0]==1)
s.add(n_pir[1]==1)
s.add(n_co2[0]==1)

print(s.check())
print(s.reason_unknown())
print(s.model())

The output of the code:

sat
[c_co2_0 = 355,
 c_co2_1 = 1841/5,
 m_dot_air_0 = 1,
 n_co2_0 = 1,
 n_pir_1 = 1,
 pir_sensor_0 = 1,
 n_pir_0 = 0,
 beta_0 = 11/3,
 alpha_0 = -8/3,
 /0 = [(19723/15, 1078/15) -> 1793/98,
       (11/3, 1) -> 11/3,
       else -> 0]]

What is the significance "/0 = ..." part of the output model.

But when I change the type of n_pir from Real to Int, z3 cannot solve it. Although we saw that we have an Int solution for n_pir. Reason of unknown:

smt tactic failed to show goal to be sat/unsat (incomplete (theory arithmetic))

How this problem can be solved? Could anyone please provide reasoning about this problem?

Nur Imtiazul Haque
  • 383
  • 1
  • 2
  • 10

1 Answers1

0

For the "/0" part: It's an internally generated constraint from converting real to int solutions. You can totally ignore that. In fact, you shouldn't really even look at the value of that, it's an artifact of the z3py bindings and should probably be hidden from the user.

For your question regarding why you cannot make 'Real' to 'Int'. That's because you have a non-linear set of equations (where you multiply or divide two variables), and non-linear integer arithmetic is undecidable in general. (Whereas non-linear real arithmetic is decidable.) So, when you use 'Int', solver simply uses some heuristics, and in this case fails and says unknown. This is totally expected. Read this answer for more details: How does Z3 handle non-linear integer arithmetic?

Z3 does come with an NRA solver, you can give that a try. Declare your solver as:

s = SolverFor("NRA")

But again you're at the mercy of the heuristics and you may or may not get a solution. Also, watch out for z3py bindings coercing constants to when you mix and match arithmetic like that. A good way is to write:

print s.sexpr()

before you call s.check() and take a look at the output and convince yourself that the translation has been done correctly. For details on that, see this question: Python and Z3: integers and floating, how to manage them in the correct way?

alias
  • 28,120
  • 2
  • 23
  • 40