Scipy.optimize violating/not respecting constraint - python

The problem definition is as follows:
Objective function: maximize Z = 45x1 + 20x2
Constraint 1 (material): 20x1 + 5x2 ≤ 9500
Constraint 2 (time): 0.04x1 + 0.12x2 ≤ 40
Constraint 3 (storage): x1 + x2 ≤ 550
Positivity: x1, x2 ≥ 0
This is done as follow in Python:
# import libraries
import numpy as np
import scipy.optimize as so
# Initialization
bnds = ((0, 550), (0, 550))
initial = np.asarray([400, 150])
# objective function
def maxZ(x_in, sign=1.0):
x1, x2 = x_in[0:2]
return sign * (45 * x1 + 20 * x2)
# constraints
def Cmaterial(x_in):
x1, x2 = x_in[0:2]
return 20 * x1 + 5 * x2 - 9500
def Ctime(x_in):
x1, x2 = x_in[0:2]
return 0.04 * x1 + 0.12 * x2 - 40
def Cstorage(x_in):
x1, x2 = x_in[0:2]
return x1 + x2 - 550
# constraint terms
con1 = {'type':'ineq', 'fun':Cmaterial}
con2 = {'type':'ineq', 'fun':Ctime}
con3 = {'type':'ineq', 'fun':Cstorage}
cons = [con1, con2, con3]
# Optimization
out = so.minimize(maxZ, initial, method='SLSQP', bounds=bnds, constraints=cons, args=(1.0,))
print(f"Optimum solution occurs at {out.x}, where Z = {out.fun}")
print(f"Material: {Cmaterial(out.x) + 9500}")
print(f"Production time: {Ctime(out.x) + 40}")
print(f"Storage: {Cstorage(out.x) + 550}")
The outcome is:
Optimum solution occurs at [427.27272727 190.90909091], where Z = 23045.45454545614
Material: 9500.00000000076
Production time: 40.00000000000009
Storage: 618.1818181818464
I have verified through graphical method and Excel verification that the expected result should be x1 = 450, x2 = 100.
The result from Scipy.optimize is x1 = 427.27, x2 = 190.91.
My question: the storage constraint of x1 + x2 ≤ 550 is clearly violated since the result is 618.18. What could be the reason for this?

First, you need to transform your maximization problem into a minimization problem, i.e. the sign argument inside maxZ should be -1.0, not 1.0. Note also that scipy.optimize.minimize expects inequality constraints with g(x) >= 0, not g(x) <= 0. Hence, you have to transform your constraints accordingly:
con1 = {'type':'ineq', 'fun': lambda x: -1.0*Cmaterial(x)}
con2 = {'type':'ineq', 'fun': lambda x: -1.0*Ctime(x)}
con3 = {'type':'ineq', 'fun': lambda x: -1.0*Cstorage(x)}
cons = [con1, con2, con3]
out = so.minimize(maxZ, initial, method='SLSQP', bounds=bnds, constraints=cons, args=(-1.0,))
yields your expected solution. Last but not least, this is a linear optimization problem (LP) and thus, should be solved with scipy.optimize.linprog. However, this requires that you formulate the problem in the standard LP form .

Related

Common tangent using python

I am trying to find a common tangent to two curves using python but I am not able to solve it.
The equations to the two curves are complicated that involve logarithms.
Is there a way in python to compute the x coordinates of a tangent that is common to both the curves in general. If I have 2 curves f(x) and g(x), I want to find the x-coordinates x1 and x2 on a common tangent where x1 lies on f(x) and x2 on g(x). I am trying f'(x1) = g'(x2) and f'(x1) = f(x1) - f(x2) / (x1 - x2) to get x1 and x2 but I am not able to get values using nonlinsolve as the equations are too complicated.
I want to just find x-coordinates of the common tangent
Can anyone suggest a better way?
import numpy as np
import sympy
from sympy import *
from matplotlib import pyplot as plt
x = symbols('x')
a, b, c, d, e, f = -99322.50019502985, -86864.87072433547, -96876.05627516498, -89703.35055202093, -3390.863799999999, -20942.518
def func(x):
y1_1 = a - a*x + b*x
y1_2 = c - c*x + d*x
c1 = (1 - x) ** (1 - x)
c2 = (x ** x)
y2 = 12471 * (sympy.log((c1*c2)))
y3 = 2*f*x**3 - x**2*(e + 3*f) + x*(e + f)
eqn1 = y1_1 + y2 + y3
eqn2 = y1_2 + y2 + y3
return eqn1, eqn2
val = np.linspace(0, 1)
f1 = sympy.lambdify(x, func(x)[0])(val)
f2 = sympy.lambdify(x, func(x)[1])(val)
plt.plot(val, f1)
plt.plot(val, f2)
plt.show()
I am trying this
x1, x2 = sympy.symbols('x1 x2')
fun1 = func(x1)[0]
fun2 = func(x2)[0]
diff1 = diff(fun1,x1)
diff2 = diff(fun2,x2)
eq1 = diff1 - diff2
eq2 = diff1 - ((fun1 - fun2) / (x1 - x2))
sol = nonlinsolve([eq1, eq2], [x1, x2])
the first thing that needs to be done is to reduce the formulas
for example the first formula is actually this:
formula = x*(1 - x)*(17551.6542 - 41885.036*x) + x*(1 - x)*(41885.036*x - 24333.3818) + 12457.6294706944*x + log((x/(1 - x))**(12000*x)*(1 - x)**12000) - 99322.5001950298
formula = (x-x^2)*(17551.6542 - 41885.036*x) + (x-x^2)*(41885.036*x - 24333.3818) + 12457.6294706944*x + log((x/(1 - x))**(12000*x)*(1 - x)**12000) - 99322.5001950298
# constants
a = 41885.036
b = 17551.6542
c = 24333.3818
d = 12457.6294706944
e = 99322.5001950298
f = 12000
formula = (x-x^2)*(b - a*x) + (x-x^2)*(a*x - c) + d*x + log((x/(1 - x))**(f*x)*(1 - x)**f) - e
formula = (ax^3 -bx^2 + bx - ax^2) + (x-x^2)*(a*x - c) + d*x + log((x/(1 - x))**(f*x)*(1 - x)**f) - e
formula = ax^3 -bx^2 + bx - ax^2 -ax^3 + ax^2 + cx^2 -cx + d*x + log((x/(1 - x))**(f*x)*(1 - x)**f) - e
# collect x terms by power (note how the x^3 tern drops out, so its easier).
formula = (c-b)*x^2 + (b-c+d)*x + log((x/(1 - x))**(f*x)*(1 - x)**f) - e
which is much cleaner and is a quadratic with a log term.
i expect that you can do some work on the log term too, but this is an excercise for the original poster.
likewise the second formula can be reduced in the same way, which is again an excercise for the original poster.
From this, both equations need to be differentiated with respect to x to find the tangent. Then set both formulas to be equal to each other (for a common tangent).
This would completely solve the question.
I actually wonder if this is a python question at all or actually a pure maths question.....
The important point to note is that, since the derivatives are monotonic, for any value of derivative of fun1, there is a solution for fun2. This can be easily seen if you plot both derivatives.
Thus, we want a function that, given an x1, returns an x2 that matches it. I'll use numerical solution because the system is too cumbersome for numerical solution.
import scipy.optimize
def find_equal_value(f1, f2, x, x1):
goal = f1.subs(x, x1)
to_solve = sympy.lambdify(x, (f2 - goal)**2) # Quadratic functions tend to be better behaved, and the result is the same
sol = scipy.optimize.fmin(func=to_solve, x0=x1, ftol=1e-8, disp=False) # The value for f1 is a good starting guess
return sol[0]
I used fmin as the solver above because it worked and I knew how to use it by heart. Maybe root_scalar can give better results.
Using the function above, let's get some pairs (x1, x2) where the derivatives are equal:
df1 = sympy.diff(func(x)[0])
df2 = sympy.diff(func(x)[1])
x1 = 0.25236537 # Close to the zero derivative
x2 = find_equal_value(df1, df2, x, x1)
print(f'Derivative of f1 in x1: {df1.subs(x, x1)}')
print(f'Derivative of f2 in x2: {df2.subs(x, x2)}')
print(f'Error: {df1.subs(x, x1) - df2.subs(x, x2)}')
This results is:
Derivative of f1 in x1: 0.0000768765858083498
Derivative of f2 in x2: 0.0000681969431752805
Error: 0.00000867964263306931
If you want a x2 for several x1s (beware that in some cases the solver hits a value where the logs are invalid. Always check your result for validity):
x1s = np.linspace(0.2, 0.8, 50)
x2s = [find_equal_value(df1, df2, x, x1) for x1 in x1s]
plt.plot(x1s, x2s); plt.grid(); plt.show()

How Can I Solve a Non-Linear Programming Problem In Python?

I have very simple model as mentioned bellow:
Assume that:
CW = [1.004455981050443, 0.9937806249035503, 0.9963341786199054, 1.000775606323324, 1.0006315883554697]
# Suppose That these are Percent of return of each bond
I want to solve this optimization and identify value of bi .
How can I code this problem in Python?
Update:
Suppose that I want to solve a optimization problem in my portfolio.
My final goal is to identify weights of investment(amounts between 0 and 1) in every bond that exist in the portfolio.
My bonds in portfolio :
Bond 1
Bond 2
Bond 3
Bond 4
Bond 5
so my portfolio contains 5 bonds.
I put the model(objective function and subjective in
I wrote this code for solve this Optimization like this:
from scipy.optimize import minimize
import math as mt
CW = [0.9862898856860483, 0.9944441063388774, 0.9934069612349462, 0.9952892270523128, 0.9951914282293151] # Suppose That these are Percent of return of each bond
def Objective(x):
x1 = x[0]
x2 = x[1]
x3 = x[2]
x4 = x[3]
x5 = x[4]
fin = (- (mt.log10(CW[0]* x1))) + (- (mt.log10(CW[1]* x2))) + (- (mt.log10(CW[2]* x3))) + (- (mt.log10(CW[3]* x4))) + (- (mt.log10(CW[4]* x5)))
return fin
def Equality_Constraint(x):
x1 = x[0]
x2 = x[1]
x3 = x[2]
x4 = x[3]
x5 = x[4]
return x1 + x2 + x3 + x4 + x5 - 1
bounds_x1 = (0, 1)
bounds_x2 = (0, 1)
bounds_x3 = (0, 1)
bounds_x4 = (0, 1)
bounds_x5 = (0, 1)
Bounds = [bounds_x1, bounds_x2, bounds_x3, bounds_x4, bounds_x5]
Constraint1 = {'type' : 'eq', 'fun': Equality_Constraint}
Constraint = [Constraint1]
x0 = [0.2, 0.2, 0.2, 0.2, 0.2] #This is an initial Value
Result = minimize(Objective, x0, method='SLSQP' , bounds=Bounds, constraints=Constraint)
print(Result)
Output
fun: 3.510281934983529
jac: array([-2.17147237, -2.17147237, -2.17147237, -2.17147237, -2.17147234])
message: 'Optimization terminated successfully'
nfev: 6
nit: 1
njev: 1
status: 0
success: True
x: array([0.2, 0.2, 0.2, 0.2, 0.2])
Here is the problem! cause it says I should devote 20% of my total money for bound1, 20% for bound2 and so on. but its not true! the percent of investment(Weights) should be different! cause my return of bond4 = 0.9952892270523128 that this is best return between returns of each bond that mentioned as CW List !
This result of solver says all weights should be 0.2 and this is optimal solution! but this doesn't seems to be correct! they should be different weights!
Now can any one help me to find out where is the problem? or how can I achieve correct weights through solve optimization problem?

Python scipy fsolve to solve a large number of nonlinear equations

I want to solve a system of 6 nonlinear equations using Python. I found that I can use scipy's fsolve pretty easily to solve a system of 3 nonlinear equations. However, when I expand this to a larger system, I find that the solution does not solve the system of equations. Is there something I can correct that will allow for the solution of 6 nonlinear equations?
import numpy as np
from scipy.optimize import fsolve
def system(z):
#arbitrary system of 3 nonlinear equations
x1 = z[0]
x2 = z[1]
x3 = z[2]
F = np.empty((3))
F[0] = 20* x1 + x2**2
F[1] = x2 - x1
F[2] = x3 + 5 - x1*x2
return F
def system2(z):
#arbitrary system of 6 nonlinear equations
x1 = z[0]
x2 = z[1]
x3 = z[2]
x4 = z[3]
x5 = z[4]
x6 = z[5]
F = np.empty((6))
F[0] = 20* x1 + x2**2
F[1] = x2 - x1
F[2] = x3 + 5 - x1*x2
F[3] = x3 + x2
F[4] = x5 + x4**2
F[5] = x6**2 + x1 - 20
return F
uInitial = np.array([1,1,1])
u = fsolve(system,uInitial)
print('Solution: ',u)
print('Solution check: ',system(u),'\n') #yields zeros as expected
vInitial = np.array([1,1,1,1,1,1])
v = fsolve(system2,vInitial)
print('Solution: ',v)
print('Solution check: ',system2(v)) #unexpectedly does not yield zeros. Equations not solved correctly.
When applying the given solution back into the system of equations, I should expect to receive zeros (or nearly zero). This would confirm that the computed solution solves the given set of equations. I tried checking with this method for both the system of 3 equations and the system of 6 equations, but only the system of 3 equations is solved correctly with this check. What can I do to solve the system of 6 nonlinear equations?
Your system is inconsistent and your initial guess is off. Try adding fourth equation to the first system of three equations:
F[0] = 20 * x1 + x2**2 # "first" equation
F[1] = x2 - x1 # "second" (=> x1 == x2)
F[2] = x3 + 5 - x1*x2 # "third"
F[3] = x3 + x2 # "fourth" equation (=> x3 == -x2)
First, let's solve first three equations. From the second equation it follows that x1 is equal to x2. Therefore the first equation can be re-written as:
F[0] = 20 * x1 + x1**2
which leads to x1 = -20 (and x2 = -20). Using this in the third equation leads to x3 = 395. Try to modify initial conditions for the first system to uInitial = np.array([-30, -30, 1]) - you should get the correct answer.
Now, let's solve all four equations. The third equation, using the fact that x2 == x1, can be re-written as:
F[2] = x3 + 5 - x1**2
From the fourth equation it follows that x3 == -x2 (and so x3 == -x1 as well). Therefore, this equation can be rewritten as x3 + 5 - x3**2 == 0 => x3 = 0.5 +(-) sqrt(21)/2 which is different from 395 that we got above using first three equations.
This shows that you have an inconsistent system of equations which has no solution.

Minimize function subject to constraint

I am trying to solve a constrained optimization problem using
cipy.optimize.minimize but so far had no success.
Specifically I want to minimize the objective function over y1 and y2:
f(y1,y2)=(x1(y1,y2)-x1)^2+(x2(y1,y2)-x2)^2
Subject to the constraint:
y1*y2>0
The goal is to find the values of y1 and y2 for different pairs of x1 and x2.
This is what i have so far
def f(x1,x2):
k=(x1(y1,y2)-x1)^2+(x2(y1,y2)-x2)^2
return k
But i am not sure how to set up the function holding the aforementioned constraint:
def constraint(x):
....
Once i have the constraint is the following syntax correct?
optimize.minimize(f, np.array([0, 0]), method="SLSQP",
constraints={"fun": constraint, "type": "ineq"})
I am new in Python so any help would be appreciated.
For constraints. From the docs:
Equality constraint means that the constraint function result is to be zero whereas inequality means that it is to be non-negative. Note that COBYLA only supports inequality constraints.
Therefore, your constraint is simply a function that must be non-negative. In your case:
def constraint(y):
return y[0] * y[1]
Note that the function must input a vector. e.g.:
def f(x):
x1, x2 = x
return x1**2 + x2**2
EDIT Using a function that tries to fit calculated vs. observed data.
def calculated_x(y):
""" example """
y1, y2 = y
x1 = 0.5 + 0.2 * y1 + 0.3 * y2
x2 = 0.4 + 0.1 * y1 + 0.3 * y2
def f(y, x1, x2):
x1_calc, x2_calc = calculated_x(y)
return (x1- x1_calc)**2 + (x2 - x2_calc)**2
m = minimize(f, [0,0], args=(3,2), constraints=({'fun': lambda y: y[0] * y[1], 'type': 'ineq'},))
print(m)
>> array([3, 1.999999])
You can also build a function based on your minimization (example above):
def minimize_y(x1, x2):
# note that x1 and x2 become arguments
m = minimize(f, [0,0], args=(x1,x2), constraints=({'fun': lambda y: y[0] * y[1], 'type': 'ineq'},)
return m.x

optimize.minimize in Python not finding the solution

I am trying to maximize a function using optimize.minimize, but for some reason is not giving me the correct answer.
import numpy as np
import pandas as pd
import math
from scipy.optimize import minimize
import matplotlib.pyplot as plt
eps = 0.001
def safe_log(x):
if x > eps:
return math.log(x)
logeps = np.log(eps)
a = 1 / (3 * eps * (3 * logeps * eps)**2)
b = eps * (1 - 3 * logeps)
return a * (x - b)**3
def funct(x):
return -(( (1 - 0.45192566) * safe_log( 1 - x )) + ( 0.45192566 * safe_log( 1 + ((5.5 - 1)*x) )))
cons = ({'type': 'ineq', 'fun': lambda x: x-1})
bnds = ((0, None),)
res = minimize(funct, (0), constraints=cons, bounds=bnds)
print("res", res.x)
print("mes", res.message)
x = []
y = []
for i in range(0, 100):
x.append(i/100)
y.append(funct(i/100))
pass
plt.plot(x,y)
plt.show()
The calculated answer is 1.
If I plot the function there is a clear min as it can be seen in the picture
I have been stuck with this for a couple of days now and I cannot find answer to this.
You've constrained your x to be at least 1:
cons = ({'type': 'ineq', 'fun': lambda x: x-1})
SciPy has successfully minimized your objective function subject to your bounds and constraints. Subject to those bounds and constraints, the minimum occurs at x=1.

Categories

Resources