optimize.minimize in Python not finding the solution - python

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.

Related

Scipy.optimize violating/not respecting constraint

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 .

Solving system of linear equations with different orders of magnitude

I am trying to solve a system of two non-linear equations to obtain the value of one of the unknowns. The system of equations is:
p_sis=K*e^((-T/3)/(R_tot * C_tot))
p_dia=K*e^(-T/(R_tot * C_tot))
Data: p_sis = 22931.447 ; p_dia = 11865.691 ; T = 1.15384 ; R_tot = 9785025352
I have solved the system of equations through Wolfram Alpha and by hand and the results are the same:
K = 31865
C_tot = 1.18974*10^-10
I only need the value of C_tot. I would like to solve this system of equations automatically through Python. However, after having applied several possible methods, none of them have provided the correct answer (even when changing the initial guess values and using values very close to the actual solution).
Here are the different sets of code I have used:
METHOD #1: through scipy.optimize.fsolve
import numpy as np
from scipy.optimize import fsolve #
p_sis = 22931.447
p_dia = 11865.691
T = 1.15384
R_tot = 9785025352
def myFunction(z):
x = z[0] # = K
y = z[1] # = C_tot
F = np.empty((2))
F[0] = p_sis-x * np.exp((-T/3)/(R_tot * y))
F[1] = p_dia-x * np.exp((-T)/(R_tot * y))
return F
zGuess = np.array([30000,0.0000001])
z = fsolve(myFunction,zGuess)
print(z)
RESULT: [3.18787051e+04 4.14184981e-08].
METHOD #2: through simpy
from sympy import *
import numpy as np
import mpmath
p_sis = 22931.447
p_dia = 11865.691
T = 1.15384
R_tot = 9785025352
mpmath.mp.dps=40
x = Symbol('x') # = K
y = Symbol('y') # = C_tot
aaa= nsolve([p_sis - x * 2.71828**(((-T / 3) / (R_tot * y))), p_dia - x * 2.71828**(((-T) / (R_tot * y)))], [x, y], [30000, 0.000000001])
print(aaa)
RESULT: Could not find root within the given tolerance.
METHOD #3: Least Squares Method with Levenberg-Marquardt (scipy.optimize.root)
import numpy as np
import sympy
import pandas as pd
from scipy.optimize import fsolve, root
def func(x):
return [p_sis - x[0] * np.exp(((-T / 3) / (R_tot * x[1]))), p_dia - x[0] * np.exp(((-T) / (R_tot * x[1])))]
def jac(x):
return np.array([[-np.exp(-T/(3*R_tot*x[1])),
-(T*x[0]*np.exp(-T/(3*R_tot*x[1])))/(3*R_tot*x[1]**2)],
[-np.exp(-T/(R_tot*x[1])),
-(T*x[0]*np.exp(-T/(R_tot*x[1])))/(R_tot*x[1]**2)]])
initialGuess = np.array([30000, 1e-10])
results = root(func, initialGuess, method='lm', jac=jac, tol=(1e-90), options={'maxiter': 5000000})
print(results)
RESULT: [2.89750393e+07, 8.07125772e-10].
METHOD #4: Newton's Method
from sympy import *
import numpy as np
p_sis = 22931.447
p_dia = 11865.691
T = 1.15384
R_tot = 9785025352
def Newton_system(F, J, x, eps):
from numpy import pi, exp
F_value = F(x)
F_norm = np.linalg.norm(F_value, ord=2) # l2 norm of vector
iteration_counter = 0
while abs(F_norm) > eps and iteration_counter < 100:
delta = np.linalg.solve(J(x), -F_value)
x = x + delta
F_value = F(x)
F_norm = np.linalg.norm(F_value, ord=2)
iteration_counter += 1
# Here, either a solution is found, or too many iterations
if abs(F_norm) > eps:
iteration_counter = -1
return x, iteration_counter
def test_Newton_system1():
from numpy import pi, exp
def F(x):
return np.array(
[p_sis - x[0] * exp(((-T / 3) / (R_tot * x[1]))),
p_dia - x[0] * exp(((-T) / (R_tot * x[1])))])
def J(x):
return np.array(
[[-exp(-T/(3*R_tot*x[1])), -(T*x[0]*exp(((-T / 3) / (R_tot * x[1]))))/(3*R_tot*x[1]**2)],
[-exp(-T/(R_tot*x[1])), -(T*x[0]*exp(((-T) / (R_tot * x[1]))))/(R_tot*x[1]**2)]])
expected = np.array([10000, 0.000001])
tol = 1e-16
x, n = Newton_system(F, J, x=np.array([0.02, 0.2]), eps=0.00000001)
print(n, x)
error_norm = np.linalg.norm(expected - x, ord=2)
assert error_norm < tol, 'norm of error =%g' % error_norm
print( 'norm of error =%g' % error_norm)
aaaaa=test_Newton_system1()
print(aaaaa)
RESULT: Singular matrix (did not reach a solution).
METHOD #5: Eq from sympy
from sympy import symbols, Eq, solve
import numpy as np
p_sis = 22931.447
p_dia = 11865.691
T = 1.15384
R_tot = 9785025352
x, y = symbols("x y") # x = K ; y = C_tot ; e = approx 2.71828
equation_1 = Eq((p_sis - x * 2.71828**((-T/3)/(R_tot * y))), 0)
equation_2 = Eq((p_dia - x * 2.71828**((-T)/(R_tot * y))), 0)
print("Equation 1:", equation_1)
print("Equation 2:", equation_2)
solution = solve((equation_1, equation_2), (x, y))
print("Solution:", solution)
RESULT: computes forever and does not return a solution.
I have even conjoined the two equations into one and tried to solve it to obtain only C_tot:
p_sis*e^((T/3)/(R_tot * C_tot)) = p_dia*e^((T)/(R_tot * C_tot))
The code looks like this:
from math import exp
import scipy.optimize
p_sis = 22931.447
p_dia = 11865.691
T = 1.15384
R_tot = 9785025352
def fun(y): # y = C_tot
z = p_dia*exp((T)/(R_tot * y)) - p_sis*exp((T/3)/(R_tot * y))
return z
z = scipy.optimize.fsolve(fun,10e-6, maxfev=1000)
print (z)
RESULT: The iteration is not making good progress, as measured by the improvement from the last ten iterations (solution equal to initial guess of 10e-6).
I suspect that the fact that K and C_tot have very different magnitudes, the program has trouble converging to the solution. Increasing the number of decimal places didn't really impact the performance.
How could I solve this issue? The fact that all the used methods did not reach the final solution makes me wonder if I made any mistakes in the definition of the problem.
Thank you in advance for all the help.

Sympy gives a residual value when trying to solve the logistics equation

If I try solving the logistics differential equation in Sympy I get a residual value (10^(-13)) which prevents sympy from getting the correct values for the initial coditions. If I run this code:
import numpy as np
import sympy as sp
M = 10000
a = 0.03
x = sp.symbols("x")
# x, a, M = sp.symbols("x a M")
f = sp.Function('f')
fl = sp.Derivative(f(x),x)
sol = sp.dsolve(fl - a*(1 - f(x)/M)*f(x), f(x));sol
I get:
Eq(f(x), (9.09494701772928e-13*exp(0.03*C1 - 0.03*x) - 10000.0)/(exp(0.03*C1 - 0.03*x) - 1))
How can one get rid of these residuals in the solution?
Either don't use Float (use a = Rational(3, 100)) or if you know you want those 1e-13 magnitude numbers to be 0 then you can replace them with 0:
>>> eq
Eq(f(x), (9.09494701772928e-13*exp(0.03*C1 - 0.03*x) - 10000.0
... )/(exp(0.03*C1 - 0.03*x) - 1))
>>> eq.replace(lambda x: x.is_Float and abs(x) < 1e-12, lambda x: 0)
Eq(f(x), -10000.0/(exp(0.03*C1 - 0.03*x) - 1))

Minimize system of nonlinear equation (integral on exponent)

General:
I am using maximum entropy to find distribution for on positive integers vectors, I can estimate the mean and variance, and have three equation I am trying to find a and b,
The equations:
integral(exp(a*x^2+bx+c) from (0 , infinity))-1
integral(xexp(ax^2+bx+c)from (0 , infinity))- mean
integral(x^2*exp(a*x^2+bx+c) from (0 , infinity))- mean^2 - var
(integrals between [0,∞))
The problem:
I am trying to use numerical solver and I used fsolve of sympy
But I guess I am missing some knowledge.
My code:
import numpy as np
import sympy as sym
from scipy.optimize import *
def myFunction(x,*data):
y = sym.symbols('y')
m,v=data
F = [0]*3
x[0] = - abs(x[0])
print(x)
F[0] = (sym.integrate(sym.exp(x[0] * y ** 2 + x[1] * y + x[2]), (y, 0,sym.oo)) -1).evalf()
F[1] = (sym.integrate(y*sym.exp(x[0] * y ** 2 + x[1] * y + x[2]), (y, 0,sym.oo))-m).evalf()
F[2] = (sym.integrate((y**2)*sym.exp(x[0] * y ** 2 + x[1] * y + x[2]), (y,0,sym.oo)) -v-m).evalf()
print(F)
return F
data = (10,3.5) # mean and var for example
xGuess = [1, 1, 1]
z = fsolve(myFunction,xGuess,args = data)
print(z)
my result are not that accurate, is there a better way to solve it?
integral(exp(a*x^2+bx+c))-1 = 5.67659292676884
integral(xexp(ax^2+bx+c))- mean = −1.32123173796713
integral(x^2*exp(a*x^2+bx+c))- mean^2 - var = −2.20825624606312
Thanks
I have rewritten the problem replacing sympy with numpy and lambdas (inline functions).
Also note that in your problem statement you subtract the third equation with $mean^2$, but in your code you only subtract $mean$.
import numpy as np
from scipy.optimize import minimize
from scipy.integrate import quad
def myFunction(x,data):
m,v=data
F = np.zeros(3) # use numpy array
# use scipy.integrade.quad for integration of lambda functions
# quad output is (result, error), so we just select the result value at the end
F[0] = quad(lambda y: np.exp(x[0] * y ** 2 + x[1] * y + x[2]), 0, np.inf)[0] -1
F[1] = quad(lambda y: y*np.exp(x[0] * y ** 2 + x[1] * y + x[2]), 0, np.inf)[0] -m
F[2] = quad(lambda y: (y**2)*np.exp(x[0] * y ** 2 + x[1] * y + x[2]), 0, np.inf)[0] -v-m**2
# minimize the squared error
return np.sum(F**2)
data = (10,3.5) # mean and var for example
xGuess = [-1, 1, 1]
z = minimize(lambda x: myFunction(x, data), x0=xGuess,
bounds=((None, 0), (None, None), (None, None))) # use bounds for negative first coefficient
print(z)
# x: array([-0.99899311, 2.18819689, 1.85313181])
Does this seem more reasonable?

Finding the minimum of a function on a closed interval with Python

Updated: How do I find the minimum of a function on a closed interval [0,3.5] in Python? So far I found the max and min but am unsure how to filter out the minimum from here.
import sympy as sp
x = sp.symbols('x')
f = (x**3 / 3) - (2 * x**2) + (3 * x) + 1
fprime = f.diff(x)
all_solutions = [(xx, f.subs(x, xx)) for xx in sp.solve(fprime, x)]
print (all_solutions)
Since this PR you should be able to do the following:
from sympy.calculus.util import *
f = (x**3 / 3) - (2 * x**2) - 3 * x + 1
ivl = Interval(0,3)
print(minimum(f, x, ivl))
print(maximum(f, x, ivl))
print(stationary_points(f, x, ivl))
Perhaps something like this
from sympy import solveset, symbols, Interval, Min
x = symbols('x')
lower_bound = 0
upper_bound = 3.5
function = (x**3/3) - (2*x**2) - 3*x + 1
zeros = solveset(function, x, domain=Interval(lower_bound, upper_bound))
assert zeros.is_FiniteSet # If there are infinite solutions the next line will hang.
ans = Min(function.subs(x, lower_bound), function.subs(x, upper_bound), *[function.subs(x, i) for i in zeros])
Here's a possible solution using sympy:
import sympy as sp
x = sp.Symbol('x', real=True)
f = (x**3 / 3) - (2 * x**2) - 3 * x + 1
#f = 3 * x**4 - 4 * x**3 - 12 * x**2 + 3
fprime = f.diff(x)
all_solutions = [(xx, f.subs(x, xx)) for xx in sp.solve(fprime, x)]
interval = [0, 3.5]
interval_solutions = filter(
lambda x: x[0] >= interval[0] and x[0] <= interval[1], all_solutions)
print(all_solutions)
print(interval_solutions)
all_solutions is giving you all points where the first derivative is zero, interval_solutions is constraining those solutions to a closed interval. This should give you some good clues to find minimums and maximums :-)
The f.subs commands show two ways of displaying the value of the given function at x=3.5, the first as a rational approximation, the second as the exact fraction.

Categories

Resources