How do I evaluate this equation in z3 for python - python

I'm trying to evaluate a simple absolute value inequality like this using z3.
x = Int("x")
y = Int("y")
def abs(x):
return If(x >= 0,x,-x)
solve(abs( x / 1000 - y / 1000 ) < .01, y==1000)
The output is no solution every time. I know this is mathematically possible, I just can't figure out how z3 does stuff like this.

This is a common gotcha in z3py bindings. Constants are "promoted" to fit into the right type, following the usual Python methodology. But more often than not, it ends up doing the wrong conversion, and you end up with a very confusing situation.
Since your variables x and y are Int values, the comparison against .01 forces that constant to be 0 to fit the types, and that's definitely not what you wanted to say. The general advice is simply not to mix-and-match arithmetic like this: Cast this as a problem over real-values, not integers. (In general SMTLib doesn't allow mixing-and-matching types in numbers, though z3py does. I think that's misguided, but that's a different discussion.)
To address your issue, the simplest thing to do would be to wrap 0.01 into a real-constant, making the z3py bindings interpret it correctly. So, you'll have:
from z3 import *
x = Int("x")
y = Int("y")
def abs(x):
return If(x >= 0,x,-x)
solve(abs( x / 1000 - y / 1000 ) < RealVal(.01), y==1000)
Note the use of RealVal. This returns:
[x = 1000, y = 1000]
I guess this is what you are after.
But I'd, in general, recommend against using conversions like this. Instead, be very explicit yourself, and cast this as a problem, for instance, over Real values. Note that your division / 1000 is also interpreted in this equation as an integer division, i.e., one that produces an integer result. So, I'm guessing this isn't really what you want either. But I hope this gets you started on the right path.

Int('a') < 0.01 is turned (rightly or wrongly) into Int('a') < 0 and clearly the absolute value can never be smaller than 0.
I believe you want Int('a') <= 0 here.
Examples:
solve(Int('a') < 0.01, Int('a') > -1)
no solution
solve(Int('a') <= 0.01, Int('a') > -1)
[a = 0]
Int('a') < 0.01
a < 0

Related

Z3: Find if variable is multiple of some other number

I would like to create a constraint that makes sure that a Real value is quantized to some tick value.
TICK = 0.5
x = Real('x')
solve(x % TICK == 0)
Unfortunately, this does not work with Real numbers (it works with Int and FP).
Another solution that I thought of was to create a set of valid numbers and check whether the number is part of the set, however the set would need to be really big.
Is there any other solution?
As Christoph mentioned, computing the modulus of a real-number isn't really meaningful. But your question is still valid: You're asking if x is an integer multiple of TICK. You can do this as follows:
from z3 import *
TICK = 0.5
x = Real('x')
k = Int('k')
solve(x == 1, ToReal(k) * TICK == x)
solve(x == 1.2, ToReal(k) * TICK == x)
This prints:
[k = 2, x = 1]
no solution
Note that unless x is a constant, this'll lead to a mixed integer-real arithmetic, and it might give rise to non-linear constraints. This can make it hard for the solver to answer your query, i.e., it can return unknown or take too long to respond. It all depends on what other constraints you have on x.

Add constraint in GurobiPy using conditional decision variable

I am new to optimisation and have a fairly basic query.
I have a model with two decision variables x and y that vary in time. I'd like to add a conditional constraint on y at time t depending upon x[t-1], such that I've implemented the following code:
for t in model.timesteps:
if t>1:
if model.x[t-1] <= 1:
model.addConstr(model.y[t] >= 100)
elif model.x[t-1] <= 0.5:
model.addConstr(model.y[t] >= 50)
elif model.x[t-1] <= 0.3:
model.addConstr(model.y[t] >= 20)
However, the above code produces the error:
File "tempconstr.pxi", line 44, in gurobipy.TempConstr.bool
gurobipy.GurobiError: Constraint has no bool value (are you trying "lb <= expr <= ub"?)
Having done a little reading on previous related queries on this page, I believe I might need to use a binary indicator variable in order to implement the above. However, I'm not certain as to whether this would solve the above issue.
Could anyone point me in the right direction here please?
Many thanks in advance!
First, I assume your order of operations is incorrect; you intended that the right-hand side is 20 for 0 ≤ x[t-1] ≤ 0.3, 50 for 0.3 < x[t-1] ≤ 0.5 and 100 for 0.5 < x[t-1] ≤ 1.0.
The bigger issue is that you were mixing Python programming with MIP modeling. What you need is to convert that logic into a MIP model. There are several ways to do this. One is to use a piecewise linear constraint to represent the right-side values of the y[t] constraints. However, I prefer to model this explicitly. There are a few similar options; here is one I think is easy to understand: add binary variables z[0], z[1] and z[2] to represent the 3 ranges of x[t-1]; this gives the following code:
for t in model.timesteps:
if t>1:
z = model.addVars(3, vtype='B', name="z_%s" % str(t))
model.addConstr(x[t-1] <= z.prod([0.3, 0.5, 1.0]))
model.addConstr(y[t] >= z.prod([20, 50, 100]))
model.addConstr(z.sum() == 1)

When I try to solve a equation python only uses integers

I am working on a new project, it is like a math site. I am trying to create a program that will solve equations.
It is working normally with simple equations for example x + 10 = 12, however when I try to do equations with exponents like x**2 + 3 = 5 it doesn't give me anything. I believe that this python code doesn't work with decimals.
Code in below
import math
def solve():
x = -1000
while x < 1001:
if x**2 + 1 == 4:
print("x = " + str(x))
x += 1
solve()
I expect the output to be 1.73205080757 and -1.73205080757.
However I get nothing (Because it couldn't find an answer).
You're expecting an answer that's between 1 & 2. You're starting at -1000 and incrementing by 1. So you'll go from -1000 to 0 to 1 to 2 to 3....skipping over your expected answer altogether.
You should be using something like: https://en.wikipedia.org/wiki/Newton%27s_method
(With floats i.e x=1.0)
Looking at your code, your minimal step is 1 (x was increased by x += 1), hence x can be only integer. There is no such integer can full-fill your condition x**2 + 1 == 4
this will only check integer values from x = - 1000 to x = 1000, ie it will ask is -1000 the answer? No, is -999 the answer? No etc, and never try 1.7 or 1.73 or 1.73...! The answer is a float not an integer, so the method as written can't possibly get it. You would need somehow to iterate closer and closer answers. This is a question of mathematical algorithm design I think, you can first look up math formulae how to approximate quadratic solutions (probably some 17th century mathematician did the formula!), then try convert this formula into Python. If you don't know about float, int, "duck typing" in Python difference, try googling this also may help you.
The code doesn't give nor will give the solution you expect because of two reasons:
The while loop increments x by 1 at each step, so there is no way x can be a float number. It will always be an integer.
The solution you expect for this case has infinite decimals, so even if x were a float, it could never be the desired value in order to solve the equation.
As a remark/suggestion, if you are trying to solve an equation in python, why don't you just create a function that give the result to an equation of the form: x^2 +a = b. The following code should be an example:
import numpy as np
def solve_prueba(a,b):
"""
The function solves a function of the form x^2 + a = b
"""
x = np.sqrt((b-a))
return x
This way is much more time effective rather than create a while loop has to pass over all the numbers with their infinite decimals to give the solution to a given equation.
Good luck!
It is not necessary to start at -1000 and go up to 1000 since you are looking for a value from 1 to 2. In your code, you increment with 1, which means that x will never be a decimal value. You could however increment with a very small number (e.g.) x += 0.0000000000001.
If you use this small increment, you should probably use math.isclose() instead of ==, because small float values tend to have some precision error. Like in your expected answer where you expect the outcome to be 1.73205080757 altough 1.73205080757**2 + 1 == 4.000000000003889 and not 4.
Using math.isclose((x**2 + 3), 4, rel_tol=1e-9) like this, it checks if the calculation from x**2 + 3 is somewhere close to 4 with a tolerance of 1e-9, which would output the values that you expect from this equation.
So for your code:
import math
def solve():
x = -1000
while x < 1001:
if math.isclose((x**2 + 1), 4, rel_rol=1e-9):
print("x = " + str(x))
x += 0.0000000000001
solve()

Numerical accuracy loss in Python

I wish to calculate the standard error of a series of numbers. Suppose the numbers are x[i] where i = 1 ... N. To do this
I set
averageX = 0.0
averageXSquared = 0.0
I then loop over all i=1,...N and for each I calculate
averageX += x[i]
averageXSquared += x[i]**2
I then divide by N
averageX = averageXC / N
averageXSquared = averageXSquared/N
I then take the square root of the difference
stdX = math.sqrt(averageXSquared - averageX * averageX)
The argument here is sure to always be >=0.
However if I set all x[i] = 0.07 (for example) then I get a math domain error as the argument of the root function is negative. There seems to be some loss of precision.
The argument is of the order of 10e-15.
This does not look encouraging. I now have to check myself to see if the result is negative before taking the root.
Or have I done something wrong.
This is not a python problem, but a problem with finite precision in general. If you set all numbers to the same value, the standard error is mathematically 0, but not for a computer. The correct way to handle this, is to set very small values <0 to 0.
x = [0.7, 0.7, 0.7]
average = sum(x) / len(x)
sqav = sum(y**2 for y in x) / len(x)
stderr = math.sqrt(max(sqav - average**2, 0))
The correct way, of course is never subtract large numbers. Have another pass, which guarantees non-negativity (you need to do some algebra to realize that the result is mathematically the same):
y = [ v - average for v in x ]
dev = sum(v*v for v in y) / len(x)
stderr = math.sqrt(dev)

Why is sin(180) not zero when using python and numpy?

Does anyone know why the below doesn't equal 0?
import numpy as np
np.sin(np.radians(180))
or:
np.sin(np.pi)
When I enter it into python it gives me 1.22e-16.
The number π cannot be represented exactly as a floating-point number. So, np.radians(180) doesn't give you π, it gives you 3.1415926535897931.
And sin(3.1415926535897931) is in fact something like 1.22e-16.
So, how do you deal with this?
You have to work out, or at least guess at, appropriate absolute and/or relative error bounds, and then instead of x == y, you write:
abs(y - x) < abs_bounds and abs(y-x) < rel_bounds * y
(This also means that you have to organize your computation so that the relative error is larger relative to y than to x. In your case, because y is the constant 0, that's trivial—just do it backward.)
Numpy provides a function that does this for you across a whole array, allclose:
np.allclose(x, y, rel_bounds, abs_bounds)
(This actually checks abs(y - x) < abs_ bounds + rel_bounds * y), but that's almost always sufficient, and you can easily reorganize your code when it's not.)
In your case:
np.allclose(0, np.sin(np.radians(180)), rel_bounds, abs_bounds)
So, how do you know what the right bounds are? There's no way to teach you enough error analysis in an SO answer. Propagation of uncertainty at Wikipedia gives a high-level overview. If you really have no clue, you can use the defaults, which are 1e-5 relative and 1e-8 absolute.
One solution is to switch to sympy when calculating sin's and cos's, then to switch back to numpy using sp.N(...) function:
>>> # Numpy not exactly zero
>>> import numpy as np
>>> value = np.cos(np.pi/2)
6.123233995736766e-17
# Sympy workaround
>>> import sympy as sp
>>> def scos(x): return sp.N(sp.cos(x))
>>> def ssin(x): return sp.N(sp.sin(x))
>>> value = scos(sp.pi/2)
0
just remember to use sp.pi instead of sp.np when using scos and ssin functions.
Faced same problem,
import numpy as np
print(np.cos(math.radians(90)))
>> 6.123233995736766e-17
and tried this,
print(np.around(np.cos(math.radians(90)), decimals=5))
>> 0
Worked in my case. I set decimal 5 not lose too many information. As you can think of round function get rid of after 5 digit values.
Try this... it zeros anything below a given tiny-ness value...
import numpy as np
def zero_tiny(x, threshold):
if (x.dtype == complex):
x_real = x.real
x_imag = x.imag
if (np.abs(x_real) < threshold): x_real = 0
if (np.abs(x_imag) < threshold): x_imag = 0
return x_real + 1j*x_imag
else:
return x if (np.abs(x) > threshold) else 0
value = np.cos(np.pi/2)
print(value)
value = zero_tiny(value, 10e-10)
print(value)
value = np.exp(-1j*np.pi/2)
print(value)
value = zero_tiny(value, 10e-10)
print(value)
Python uses the normal taylor expansion theory it solve its trig functions and since this expansion theory has infinite terms, its results doesn't reach exact but it only approximates.
For e.g
sin(x) = x - x³/3! + x⁵/5! - ...
=> Sin(180) = 180 - ... Never 0 bout approaches 0.
That is my own reason by prove.
Simple.
np.sin(np.pi).astype(int)
np.sin(np.pi/2).astype(int)
np.sin(3 * np.pi / 2).astype(int)
np.sin(2 * np.pi).astype(int)
returns
0
1
0
-1

Categories