scipy.minimize with SLSQP not respecting constraints - python

I'm attempting to minimize the function f(x) = x[0] * x[1] over a system of inequality constraints using scipy.minimize and the solver is returning values which do not respect all of the constraints. For example:
import numpy as np
from scipy.optimize import minimize
from scipy.optimize import fmin_slsqp
# small constant for enforcing strict inequalities
c = 0.000001
# Objective Function to Optimize
def objective(x):
return x[0] * x[1]
# Constraints
def con1(x):
return (x[0]*(1/(7**x[2]))) + 6*x[1] - c
def con2(x):
return (x[0]*(1/(2**x[2]))) + x[1] - c
def con3(x):
return (x[0]*(1/(3**x[2]))) + 2*x[1] - c
def con4(x):
return (x[0]*(1/(4**x[2]))) + 3*x[1] - c
def con5(x):
return (x[0]*(1/(7**x[2]))) - (x[0]*(1/(1**x[2]))) + 7*x[1] - c
def con6(x):
return (x[0]*(1/(2**x[2]))) - (x[0]*(1/(1**x[2]))) + 2*x[1] - c
def con7(x):
return (x[0]*(1/(3**x[2]))) - (x[0]*(1/(1**x[2]))) + 3*x[1] - c
def con8(x):
return -(x[0]*(1/(1**x[2]))) + 4*x[1] - c
def con9(x):
return (x[0]*(1/(1**x[2]))) + 2*x[1] - c
def con10(x):
return (x[0]*(1/(7**x[2]))) + 4*x[1] - c
def con11(x):
return (x[0]*(1/(2**x[2]))) - x[1] - c
def con12(x):
return (x[0]*(1/(4**x[2]))) + x[1] - c
def con13(x):
return x[0] - 1
def con14(x):
return x[1] - 1
def con15(x):
return x[2] - c
# Initial Guesses
x0 = [1,1,1]
# Constraint Objects
constr1 = {'type':'ineq', 'fun':con1}
constr2 = {'type':'ineq', 'fun':con2}
constr3 = {'type':'ineq', 'fun':con3}
constr4 = {'type':'ineq', 'fun':con4}
constr5 = {'type':'ineq', 'fun':con5}
constr6 = {'type':'ineq', 'fun':con6}
constr7 = {'type':'ineq', 'fun':con7}
constr8 = {'type':'ineq', 'fun':con8}
constr9 = {'type':'ineq', 'fun':con9}
constr10 = {'type':'ineq', 'fun':con10}
constr11 = {'type':'ineq', 'fun':con11}
constr12 = {'type':'ineq', 'fun':con12}
constr13 = {'type':'ineq', 'fun':con13}
constr14 = {'type':'ineq', 'fun':con14}
constr15 = {'type':'ineq', 'fun':con15}
cons = [constr1,constr2,constr3,constr4,constr5,constr6,constr7,constr8,constr9,constr10,constr11,constr12,constr13,constr14,constr15]
solution = minimize(objective,x0,method='SLSQP',constraints=cons)
for con in cons:
print(str(con) + str(con['fun'](solution.x)))
Looping over the constraints with the solution values shows that some of the constraints are evaluating as negative even though the constraints are of the form >= 0.
Is this due to some error on my part in the specifications? An issue with numerical precision? Or is this an issue with SLSQP? (see, for example: https://github.com/scipy/scipy/issues/7618).
If this isn't an issue with my specifications, I would also accept suggestions for formulations of this constraint solving problem in other frameworks (preferably in Python).

Related

Parametric constrained optimization loop over parameters?

Say that I want to solve a parametric constrained optimization, method. Is there any method I can use to avoid looping over the parameters?
I would use a code (adapted to my problem) such as (credit):
import numpy as np
from scipy.optimize import minimize
import random
import pandas as pd
pd.options.display.float_format = '{:.5f}'.format
def uncovered(x):
## note x[0]--> x ; x[1]--> y
first_prod = x[0]**2-2*x[0]-4*x[1]+1
second_prod_first = x[0]**3*(8*a**2*(b-1)*b-4*a*(b-2)-3)
second_prod_second = x[0]**2*(-8*a**2*(b-1)*b-8*a+5)
second_prod_third = x[0]*(4*x[1]*(4*a*(b-1)+1)+4*a*b - 1) -4*x[1] - 1
denom = 8*(x[0]-1)*(x[0]*(2*a*(b-1)+1)-1)**2
if (x[0]-1==0):
final_func=0
else:
final_func = first_prod*(second_prod_first+second_prod_second+second_prod_third)/denom
return -final_func
def constraint_strict(x):
return x[1]-0.5*a*x[0]*(x[0]-1)*(1-b)-0.0001
def constraint_nonstrict(x):
return 0.25*(1-2*x[0]+x[0]**2) - x[1]
def constraint_1(x):
return 1-x[0]
def constraint_2(x):
return x[0]
def run_uncovered_market_maximization(alpha,xi):
a=alpha
b=xi
# initial guesses
n = 2
x0 = np.zeros(n)
x0[0] = 1
x0[1] = 1.0
success = False
# show initial objective
print("We are maximizing uncovered welfare:")
print('Initial Objective: ' + str(uncovered(x0)))
while success == False:
# optimize
bnds = ((0,1),(-np.inf,np.inf))
con1 = {'type': 'ineq', 'fun': constraint_strict}
con2 = {'type': 'ineq', 'fun': constraint_nonstrict}
cons = ([con1,con2])
solution = minimize(uncovered,x0,method='SLSQP', bounds=bnds,constraints=cons,options={'disp': False})
x = solution.x
success = solution.success
if success == False:
x0[0] = x0[0]-random.uniform(0, 1)
x0[1] = 5.0-random.uniform(0, 1)
indcons=(x[1]+a*(1-x[0])*x[0]*(1-b)/2)/((((1-x[0])**2)/4)+a*(1-x[0])*(1-b)*x[0]/2)
# show final objective
print("Success:"+str(solution.success))
print('Final Objective: ' + str(uncovered(x)))
print('First Constraint: ' + str(constraint_strict(x)))
print('Second Constraint: ' + str(constraint_nonstrict(x)))
# print solution
print('Solution')
print('d = ' + str(x[0]))
print('p = ' + str(x[1]))
print('market demand:'+ str(1-indcons))
return (solution.success, str(x[0]),str(x[1]),str(uncovered(x)),indcons)
I am unsure on how to handle a and b given that the maximization should not be over these and they only represent parameters. I want to have a pair (x,y) for any acceptable value of (a,b). How to set the boundaries? and how to insert a and b if not by looping over them?
For now I do:
optlist =[]
for b in np.linspace(start=0, stop=1, num=2000):
for a in np.linspace(start=0, stop=1, num=2000):
print('-------------Optimization for a:{} and b:{}-------------------'.format(a,b))
print('-------------Uncovered market')
(usuccess, du, pu,ufinal,uindcons) = run_uncovered_market_maximization(a,b)

SciPy Optimise minimise error - challenge to solve

How do I solve this error?
TypeError: NumPy boolean subtract, the `-` operator, is not supported, use the bitwise_xor, the `^` operator, or the logical_xor function instead.
I have programmed an optimizing program that must minimize the cost of a wall design. The wall is based on 3 parameters, x, k and m. There are constraints to the sizes of x, k and m as shown. Another constraint is that z (or deflection) must be kept under 100mm. The equation for deflection changes based on a certain t (or time) at which the blast wall is experiencing the blast. If t is below a certain time value which is calculated dependent on, x, k and m the equation is as shown. If t is above the same certain time value, the equation for z changes.
Here is the programming... Please help many thanks :)
import numpy as np
from numpy import linspace
from math import cos
from math import sin
from scipy.optimize import minimize
#Function for minimising
def calcCost(c):
k = c[0]
m = c[1]
x = c[2]
Cost = (900 + 825*k**2 - 1725) + (10*m - 200) + ((2400*x**2)/4)
return Cost
#Objective function
def objective(c):
return calcCost(c)
#Defining Variables
def calck(c):
k = c[0]
k=k
k.resize(12,)
return k
def calcm(c):
m = c[1]
m=m
m.resize(12,)
return m
def calcx(c):
x = c[2]
x=x
x.resize(12,)
return x
def calcz(c):
k = c[0]
x = c[1]
m = c[2]
l = linspace(0,140,141)
for t in l:
if t <= ((20 - 0.12*x**2 + 4.2*x)/1000):
deflection = ((((1000+9*x**2-183*x)*1000)/k)*(1-cos(t*((k/m)**0.5))) + (((1000+9*x**2-183*x)*1000)/k*((20 - 0.12*x**2 + 4.2*x)/1000))*((sin(t*((k/m)**0.5))/((k/m)**0.5))-t))*1000
else:
deflection = ((((1000+9*x**2-183*x)*1000)/(k*((k/m)**0.5)*((20 - 0.12*x**2 + 4.2*x)/1000)))*(sin(((k/m)**0.5)*t))-(sin(((k/m)**0.5)*(t-((20 - 0.12*x**2 + 4.2*x)/1000))))-(((1000+9*x**2-183*x)*1000)/k)*cos(((k/m)**0.5)*t))*1000
deflection.resize(12,)
return deflection
#Constraint functions
def kconstraint1(c):
k = c[0]
return k-(1*10**6) >= 0
def kconstraint2(c):
k = c[0]
return k-(7*10**6) <= 0
def mconstraint1(c):
m = c[0]
return m-200 >= 0
def mconstraint2(c):
m = c[0]
return m-1200 <= 0
def xconstraint1(c):
x = c[0]
return x >= 0
def xconstraint2(c):
x = c[0]
return x <= 10
def zconstraint1(c):
k = c[0]
x = c[1]
m = c[2]
l = linspace(0,140,141)
for t in l:
if t <= ((20 - 0.12*x**2 + 4.2*x)/1000):
deflection = ((((1000+9*x**2-183*x)*1000)/k)*(1-cos(t*((k/m)**0.5))) + (((1000+9*x**2-183*x)*1000)/k*((20 - 0.12*x**2 + 4.2*x)/1000))*((sin(t*((k/m)**0.5))/((k/m)**0.5))-t))*1000
else:
deflection = ((((1000+9*x**2-183*x)*1000)/(k*((k/m)**0.5)*((20 - 0.12*x**2 + 4.2*x)/1000)))*(sin(((k/m)**0.5)*t))-(sin(((k/m)**0.5)*(t-((20 - 0.12*x**2 + 4.2*x)/1000))))-(((1000+9*x**2-183*x)*1000)/k)*cos(((k/m)**0.5)*t))*1000
return deflection <= 99.99999999
b = (0.5,1)
be = (0.5,10)
bb = (0.1,2.0)
bnds = (b,be,bb,bb)
con1 = ({'type':'ineq','fun':kconstraint1})
con2 = ({'type':'ineq','fun':kconstraint2})
con3 = ({'type':'ineq','fun':mconstraint1})
con4 = ({'type':'ineq','fun':mconstraint2})
con5 = ({'type':'ineq','fun':xconstraint1})
con6 = ({'type':'ineq','fun':xconstraint2})
con7 = ({'type':'ineq','fun':zconstraint1})
cons = [con1,con2,con3,con4,con5,con6,con7]
xGUESS = 5
kGUESS = 3*10**6
mGUESS = 700
zGUESS = 90
x0 = np.array([xGUESS,kGUESS,mGUESS,zGUESS])
sol = minimize(objective,x0,method='SLSQP',bounds=bnds,constraints=cons,options={'disp':True})
xOpt = sol.x
CostOPT = sol.fun
kOPT = calck(xOpt)
xOPT = calcx(xOpt)
mOPT = calcm(xOpt)
zOPT = calcz(xOpt)
print(str(CostOPT))
print(str(calcx))
print(str(calcm))
print(str(calck))
print(str(calcz))

How can I use numba's jit in my program which contains only Numpy arrays?

My program evaluates error in solving a linear differential equation. It uses only numpy arrays. When I try to use numba's jit decorator for the functions I define, I just get errors. Can you please help me use it properly?
My code:
import numpy as np
from numba import jit
def rk4(t_prev, x_prev, derivs, dt):
k1 = dt * derivs(t_prev, x_prev)
k2 = dt * derivs(t_prev + 1/2*dt, x_prev + 1/2*k1)
k3 = dt * derivs(t_prev + 1/2*dt, x_prev + 1/2*k2)
k4 = dt * derivs(t_prev + dt, x_prev + k3)
x_next = x_prev + 1/6*k1 + 1/3*k2 + 1/3*k3 + 1/6*k4
return x_next
global k, x_0, v_0, t_0, t_f
k = 1
x_0 = 0
v_0 = np.sqrt(k)
t_0 = 0
t_f = 10
dtList = np.logspace(0, -5, 1000)
def derivs(t, X):
deriv = np.zeros([2])
deriv[0] = X[1]
deriv[1] = -k * X[0]
return deriv
def err(dt):
tList = np.arange(t_0, t_f + dt, dt)
N = tList.shape[0]
XList = np.zeros([N,2])
XList[0][0], XList[0][1] = x_0, v_0
for i in range(N-1):
XList[i+1] = rk4(tList[i], XList[i], derivs, dt)
error = np.abs(XList[-1][0] - np.sin(10))
return error
print(err(.001))
The following works for me:
import numpy as np
from numba import jit
#jit(nopython=True)
def rk4(t_prev, x_prev, derivs, dt):
k1 = dt * derivs(t_prev, x_prev)
k2 = dt * derivs(t_prev + 1/2*dt, x_prev + 1/2*k1)
k3 = dt * derivs(t_prev + 1/2*dt, x_prev + 1/2*k2)
k4 = dt * derivs(t_prev + dt, x_prev + k3)
x_next = x_prev + 1/6*k1 + 1/3*k2 + 1/3*k3 + 1/6*k4
return x_next
global k, x_0, v_0, t_0, t_f
k = 1
x_0 = 0
v_0 = np.sqrt(k)
t_0 = 0
t_f = 10
dtList = np.logspace(0, -5, 1000)
#jit(nopython=True)
def derivs(t, X):
deriv = np.zeros(2)
deriv[0] = X[1]
deriv[1] = -k * X[0]
return deriv
#jit(nopython=True)
def err(dt):
tList = np.arange(t_0, t_f + dt, dt)
N = tList.shape[0]
XList = np.zeros((N,2))
XList[0][0], XList[0][1] = x_0, v_0
for i in range(N-1):
XList[i+1] = rk4(tList[i], XList[i], derivs, dt)
error = np.abs(XList[-1][0] - np.sin(10))
return error
print(err(.001))
Note, the only two changes I made to your code was to replace the calls to np.zeros that passed in lists to either a tuple in the 2d case, or just the bare integer in the 1d case. See the following issue for an explanation of why this is:
https://github.com/numba/numba/issues/3993

Scipy minimize constraint in pandas df

I am trying to run the minimization process found in this publication. The equation is seen on page 6 of the document 16 of the pdf.
I have a dataframe that looks like the below
df = pd.DataFrame({'h_t':[7.06398,6.29948,5.04570,6.20774,4.80106],
'p_atm':[101057.772801,101324.416001,101857.702401,101724.380801,101991.024001],
'q_p':[5.768132,3.825600,2.772215,5.830429,2.619304],
'q_s':[2.684433,3.403679,2.384275,1.008078,2.387106],
'tdg_f':[117.678100,110.131579,108.376963,103.669725,113.594771],
'tdg_tw':[121.052635,119.710907,114.921463,112.156868,115.444900],
'temp_water':[11.92,19.43,16.87,7.45,11.83]})
I have a constraint that says the below function must be positive where b1 and b3 are the coefficients I am optimizing.
def q_ge(q_p,q_s,b1,b3):
return min(q_p,(b1*q_s+b3))
I wrote my constraint below, but I am not sure if it is right.
def constraint_q_ge(x):
b1,b2,b3=x
power_flow = df.apply(lambda x:q_ge(x['q_p'],x['q_s'],b1,b3), axis = 1)
const = power_flow<0
return -const.sum()
Is this correct? I run the function on all rows and check if any are less than 0 and sum this. The negative of that sum should be greater than or equal to 0. If there is even a single value less than 0 this constraint is not met.
EDIT:
Below is the full problem.
from scipy.constants import g as gravity
from sklearn.metrics import mean_squared_error
from math import sqrt
from scipy.optimize import minimize
import warnings
try:
from numpy import any as _any
except ImportError:
def _any(arg):
if arg is True:
return True
if arg is False:
return False
return any(arg)
def water_density(T=None, T0=None, units=None, a=None,
just_return_a=False, warn=True):
if units is None:
K = 1
m = 1
kg = 1
else:
K = units.Kelvin
m = units.meter
kg = units.kilogram
if T is None:
T = 298.15*K
m3 = m**3
if a is None:
a = (-3.983035*K, # C
301.797*K, # C
522528.9*K*K, # C**2
69.34881*K, # C
999.974950*kg/m3)
if just_return_a:
return a
if T0 is None:
T0 = 273.15*K
t = T - T0
if warn and (_any(t < 0*K) or _any(t > 40*K)):
warnings.warn("Temperature is outside range (0-40 degC)")
return a[4]*(1-((t + a[0])**2*(t + a[1]))/(a[2]*(t + a[3])))
def celsius_to_kelvin(t_celsius):
return t_celsius+273.15
def tailwater(h_t, temp_water, p_atm):
t_water_kelvin = celsius_to_kelvin(temp_water)
rho = water_density(t_water_kelvin)
g = gravity
return (1+(rho*g*h_t)/(2*p_atm))
def tailwater_tdg(q_s,q_p,x, h_t,temp_water,p_atm,tdg_f):
b1,b2,b3=x
A = ((q_s+b1*q_s+b3)/(q_s+q_p))
B = tailwater(h_t, temp_water, p_atm)
C = ((q_p-b1*q_s-b3)/(q_s+q_p))
return 100*A*B*b2+tdg_f*C
def q_ge(q_p,q_s,b1,b3):
return min(q_p,(b1*q_s+b3))
def rmse(y, y_hat):
return sqrt(mean_squared_error(y,y_hat))
def objective(x):
y_hat = df.apply(lambda r:tailwater_tdg(q_s=r['q_s'],q_p=r['q_p'],x=x, h_t=r['h_t'],temp_water=r['temp_water'],p_atm=r['p_atm'],tdg_f=r['tdg_f']), axis = 1)
y = df['tdg_tw']
return rmse(y, y_hat)
#constraints and bounds for optimization model. See reference for more information
def constraint_q_ge(x):
b1,b2,b3=x
power_flow = df.apply(lambda x:q_ge(x['q_p'],x['q_s'],b1,b3), axis = 1)
const = power_flow<0
return -const.sum()
constraints = [{'type':'ineq', 'fun':constraint_q_ge}]
bounds = [(-500,10000),(.00001,10000),(-500,10000)]
x0=[1,1,1]
sol = minimize(objective, x0, method = 'SLSQP',constraints = constraints, bounds = bounds,options={'disp':True, 'maxiter':100})

Issues with scipy minimize

I'm receiving the following error from scipy minimize.
RuntimeWarning: overflow encountered in double_scalars
inner = x[k]*((x[0]**int(cAnde[ses][sub][strats][0]))*(1.0-x[0])**int(cAnde[ses][sub][strats][1]))
Traceback (most recent call last):
File "MLEestimation.py", line 39, in ,module.
constraints=MLEfunction.MLEconstrain(x), method = 'SLSQP', options = {'disp': True})
File "C:\ProgramData\Anaconda2\lib\site-packages\scipy\optimize\_minimize.py", line 458, in minimize
constraints, callback=callback, **options)
File "C:\ProgramData\Anaconda2\lib\site-packages\scipy\optimize\slsqp.py", line 370 in _minimize_slsqp
raise ValueError("Objective function must return a scalar")
ValueError: Objective function must return a scalar
But, when I check if the output of the objective function is a scalar using np.isscalar, I get that it is, so I'm uncertain of what I'm doing wrong. Below is all the code for reference.
import math
import pickle
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from scipy.optimize import minimize
import MLEfunction
x = [0.9,0.2,0.1,0.2,0.1,0.2,0.2]
def MLE(x, cAnde):
est = 0.0
for ses in cAnde:
for sub in cAnde[ses]:
k = 0
for strats in cAnde[ses][sub]:
k += 1
inner = x[k] * x[0] ** int(cAnde[ses][sub][strats][0]) \
* (1.0 - x[0]) ** int(cAnde[ses][sub][strats][1])
est += math.log(inner)
est = -1.0 * est
return est
def MLE_deriv(x, cAnde):
der = np.zeros_like(x)
d0 = 0
for ses in cAnde:
for sub in cAnde[ses]:
for strat in cAnde[ses][sub]:
inner = float(cAnde[ses][sub][strat][0]) / x[0]
inner += float(cAnde[ses][sub][strat][1]) / (1 - x[0])
d0 += inner
der[0] = -1.0 * d0
for k in range(len(x) - 1):
d = 0
for ses in cAnde:
for sub in cAnde[ses]:
d += 1.0 / x[k + 1]
der[k + 1] = -1.0 * d
return der
def MLEconstraint(x):
cons = []
jacs = []
for j in range(len(x)):
if j == 0:
jacs.append(0.0)
else:
jacs.append(1.0)
sum1 = {'type': 'eq', 'fun': lambda x: np.array([sum(x[1:]) - 1]),
'jac': lambda x: np.array(jacs)}
cons.append(sum1)
for k in range(len(x)):
jacu = []
jacd = []
for l in range(len(x)):
if l == k:
jacu.append(-1.0)
jacd.append(1.0)
else:
jacu.append(0.0)
jacd.append(0.0)
up = {'type': 'ineq', 'fun': lambda x: np.array([1 - x[k]]),
'jac': lambda x: np.array(jacu)}
low = {'type': 'ineq', 'fun': lambda x: np.array([x[k]]),
'jac': lambda x: np.array(jacd)}
cons.append(up)
cons.append(low)
return tuple(cons)
res = minimize(
MLEfunction.MLE,
x,
args=CEs['48,0.75'],
jac=MLEfunction.MLE_deriv,
constraints=MLEfunction.MLEconstraint(x),
method='SLSQP',
options={'disp': True},
)
print res.x
Note: the minimization is being done in a different .py file than the functions are defined, and I am importing the original file defining the functions

Categories

Resources