Binary variable in GEKKO optimization problem - python

I have an optimization problem that has a condition such that if the variable value is greater than 0, a certain constraint applies.
I tried to use m.if3 to generate a binary value that assumes the value of 1 if the variable value is greater than 0, but that did not work because the m.if3 condition applies for greater than or equal to 0, and I need it to be strictly greater than 0. Here is an example of the code I had with if3 but it didn't work.
x = m.Var(lb=0)
a = m.if3(x,0,1)
m.Equation(b - c == a*f ) #b,c,f are calculated values
I also attempted to use a typical if statement condition, but that did not work with the gekko variable (example below).
if x>0:
m.Equation(b - c == f )
Is there another way for me to be able to apply this condition (strictly greater than zero)?

You can flip the variable's sign and flip the conditions to get your desired behaviour:
In a = m.if3(x,0,1) a = 0 if x < 0, and a = 1 if x >= 0.
Instead you can do a = m.if3(-x,1,0). Here a = 1 if -x < 0 (equivalent to x > 0).
Using an if statement as in the second part of your question will not work because x does not have a value. It will only have a value after the optimization. And that will be accessed with something like x.value, not directly like this, afaik.

The if3(x,0,1) is correct for the desired application but the condition that is turned off or on should have a*() on both sides of the equation. Alternatively, move all of the expression to one side so that it is:
m.Equation(a*(b-c-f)==0)
so that 0==0 when a=0. Here is a complete script that demonstrates the solution.
from gekko import GEKKO
import numpy as np
m = GEKKO()
b = 0; c=1
f = m.Var()
x = m.Param(-1)
# binary variable (a=0 if x<0, a=1 otherwise)
a = m.if3(x,0,1)
# turn off condition when a==0
m.Equation(a*(b-c-f)==0)
# initialize with IPOPT (NLP solver)
m.options.SOLVER=3
m.solve(disp=False)
# Find integer solution with APOPT (MINLP solver)
m.options.SOLVER=1
m.solve(disp=True)
print(a.value[0])
This gives a solution with a=0 and the equation deactivated. Change x = m.Param(-1) to x = m.Param(1) to observe a solution with a=1.

Related

How to use Raise a value error when calculating derivatives using numpy

``I need to Write a function which calculates the following math equation and round your answer to 2 decimal places: z = π*ex2/4y.
There are the following contstraints: The input variables x and y are single values (that is, not a list/array). If a division by zero occurs, raise a ValueError. Output should be rounded to 2 decimal places. (hint) You can calculate ex using the NumPy function np.exp(x).
I have done the following but still fail the value error tests (ValueError
Inputs: [0.5, 0]
def test_question_3_ValueError(test_input)):
def custom_function(x, y):
# your code here
a = (np.pi*np.exp(x**2))
b = 4*y
z = a / b
if b < 0:
raise ValueError("Div by zero")
return round(z, 2)
First thing, I believe that the division by 0, will only happen if y = 0, as b = 4*y, and b can be only 0 if y = 0, thus, you should change the if statement for b == 0.
Anoter thing, that if statement should be before calculating the z value, because you want to raise the Error before any computations has been started, and thus stopping everything that follows afterwards.

Minimize function with scipy

I'm trying to do one task, but I just can't figure it out.
This is my function:
1/(x**1/n) + 1/(y**1/n) + 1/(z**1/n) - 1
I want that sum to be as close to 1 as possible.
And these are my input variables (x,y,z):
test = np.array([1.42, 5.29, 7.75])
So n is the only decision variable.
To summarize:
I have a situation like this right now:
1/(1.42**1/1) + 1/(5.29**1/1) + 1/(7.75**1/1) = 1.02229
And I want to get the following:
1/(1.42^(1/0.972782944446024)) + 1/(5.29^(1/0.972782944446024)) + 1/(7.75^(1/0.972782944446024)) = 0.999625
So far I have roughly nothing, and any help is welcome.
import numpy as np
from scipy.optimize import minimize
def objectiv(xyz):
x = xyz[0]
y = xyz[1]
z = xyz[2]
n = 1
return 1/(x**(1/n)) + 1/(y**(1/n)) + 1/(z**(1/n))
test = np.array([1.42, 5.29, 7.75])
print(objectiv(test))
OUTPUT: 1.0222935270013889
How to properly define a constraint?
def conconstraint(xyz):
x = xyz[0]
y = xyz[1]
z = xyz[2]
n = 1
return 1/(x**(1/n)) + 1/(y**(1/n)) + 1/(z**(1/n)) - 1
And it is not at all clear to me how and what to do with n?
EDIT
I managed to do the following:
def objective(n,*args):
x = odds[0]
y = odds[1]
z = odds[2]
return abs((1/(x**(1/n)) + 1/(y**(1/n)) + 1/(z**(1/n))) - 1)
odds = [1.42,5.29,7.75]
solve = minimize(objective,1.0,args=(odds))
And my output:
fun: -0.9999999931706812
x: array([0.01864994])
And really when put in the formula:
(1/(1.42^(1/0.01864994)) + 1/(5.29^(1/0.01864994)) + 1/(7.75^(1/0.01864994))) -1 = -0.999999993171
Unfortunately I need a positive 1 and I have no idea what to change.
We want to find n that gets our result for a fixed x, y, and z as close as possible to 1. minimize tries to get the lowest possible value for something, without negative bound; -3 is better than -2, and so on.
So what we actually want is called least-squares optimization. Similar idea, though. This documentation is a bit hard to understand, so I'll try to clarify:
All these optimization functions have a common design where you pass in a callable that takes at least one parameter, the one you want to optimize for (in your case, n). Then you can have it take more parameters, whose values will be fixed according to what you pass in.
In your case, you want to be able to solve the optimization problem for different values of x, y and z. So you make your callback accept n, x, y, and z, and pass the x, y, and z values to use when you call scipy.optimize.least_squares. You pass these using the args keyword argument (notice that it is not *args). We can also supply an initial guess of 1 for the n value, which the algorithm will refine.
The rest is customization that is not relevant for our purposes.
So, first let us make the callback:
def objective(n, x, y, z):
return 1/(x**(1/n)) + 1/(y**(1/n)) + 1/(z**(1/n))
Now our call looks like:
best_n = least_squares(objective, 1.0, args=np.array([1.42, 5.29, 7.75]))
(You can call minimize the same way, and it will instead look for an n value to make the objective function return as low a value as possible. If I am thinking clearly: the guess for n should trend towards zero, making the denominators increase without bound, making the sum of the reciprocals go towards zero; negative values are not possible. However, it will stop when it gets close to zero, according to the default values for ftol, xtol and gtol. To understand this part properly is beyond the scope of this answer; please try on math.stackexchange.com.)

Initial Guess/Warm start in CVXPY: give a hint of the solution

In this bit of code:
import cvxpy as cvx
# Examples: linear programming
# Create two scalar optimization variables.
x = cvx.Variable()
y = cvx.Variable()
# Create 4 constraints.
constraints = [x >= 0,
y >= 0,
x + y >= 1,
2*x + y >= 1]
# Form objective.
obj = cvx.Minimize(x+y)
# Form and solve problem.
prob = cvx.Problem(obj, constraints)
prob.solve(warm_start= True) # Returns the optimal value.
print ("status:", prob.status)
print ("optimal value", prob.value)
print ("optimal var", x.value, y.value)
I'm looking for a way to choose the warm start value myself (for example: x = 1/2 and y = 1/2), not the previous solver result.
Is there any way to give the solver this input? And if not, is there a non-commercial alternative to cvxpy?
To the 2021 readers: today is impossible (in cvxpy) to give a hand to the solver with an initial guess. Warm start right now only works when you solve the same problem with different parameter values, initializing with the previous solution (see https://github.com/cvxpy/cvxpy/issues/1355).
You can manually assign the values using x.value = 1/2, and then passing the warm_start=True parameter in the available solvers. Keep in mind not all solvers allow this, one that does is for example SCS.
More info available on: https://www.cvxpy.org/tutorial/advanced/index.html

how to write a algorithm given the condition using python

Approximate the value of n for the formula (1-1/n)**n for which the difference between the value of n in the formula and 1/e is less than 0.0001.
How can we do using while and for loop in python .
I tried using while with the following code
from math import exp
value = 1/exp(1) # e being the exponential
n = 1;
while check < 0.0001:
n=n+1
formula = (1-1/n)^n
check = value - formula
if check <0.0001:
print(n)
but since check is not defined before while the program doesn't run.
Is there any better solution?
Define check at the beginning, and replace ^ with **, as the latter one is the correct way to write power in python
import math
value = 1/math.exp(1) # e being the exponential
n = 1
check=1
while check > 0.0001:
n=n+1
formula = (1-1/n)**n
check = value - formula
print(n)
By the way, ^ is the bitwise xor operator in python. You can look here for further description:
http://python-reference.readthedocs.io/en/latest/docs/operators/bitwise_XOR.html

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