I am writing a code which maximizes the value for my objective function given a set of constraints. It has four variables labeled x1 to x4, with two equality constraints and two inequality constraints. Solving with Linprog gives me a proper result. But using pulp method is only giving me zero as results.
from pulp import LpMaximize, LpProblem, LpStatus, lpSum, LpVariable
import numpy as np
# Create the model
model = LpProblem(name="optimize", sense=LpMaximize)
# Initialize the decision variables
x1 = LpVariable(name="x1", lowBound= 0, upBound = None, cat='Continuous')
x2 = LpVariable(name="x2", lowBound= 0, upBound = 5, cat='Continuous')
x3 = LpVariable(name="x3", lowBound=None, upBound = 0.5, cat='Continuous')
x4 = LpVariable(name="x4", lowBound=-3, upBound = None, cat='Continuous')
#Objective function of the model
obj_func = (29 * x1 + 45 * x2)
model += obj_func
# Add the constraints to the model
model += (x1 - x2 - 3 * x3 <= 5, "Constraint_1")
model += (2 * x1 - 3 * x2 -7 * x3 + 3 * x4 >= 10, "Constraint_2")
model += (2 * x1 + 8 * x2 + x3 == 60, "Constraint_3")
model += (4 * x1 + 4 * x2 + x4 == 60, "Constraint_4")
model
# Solve the problem
status = model.solve()
LpStatus[model.status]
model.variables()
for var in model.variables():
print(f"{var.name}: {var.value()}")
I can see that the LpStatus[model.status] is saying that the solutions are Undefined.
Same set of equations gives me a solution in LinProg as
[ 6.60059411, 3.9736669 , -0.52664072, 1.09008012]
Your solution does not satisfy the 2nd constraint. Check:
2x6.60059411 - 3x3.9736669 - 7x(-0.52664072) + 3x1.09008012 = 8.2369 < 10
Related
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 .
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.
I would like to solve in Python the following Mixed-Integer Quadratic Programming in Python. Nevertheless, I'm not familiar with the optimization
toolboxes of Python.
Can someone provide an example of code with the vectors X1, X2, X3, X4 given as below ?
X1 = np.array([3,10,20,10])
X2 = np.array([5,1,3,4])
X3 = np.array([2,3,1,4])
X4 = np.array([10,0,1,2])
The MIQP is written as :
I tried to solve it with CVXPY but i encoutered problem with the boolean
variable x = cp.Variable(1, boolean=True):
import numpy
import numpy as np
import cvxpy as cp
X1 = np.array([3,10,20,10])
X2 = np.array([5,1,3,4])
X3 = np.array([2,3,1,4])
X4 = np.array([10,0,1,2])
M = 100
x = cp.Variable(1, boolean=True)
Y1 = cp.Parameter(4)
Y2 = cp.Parameter(4)
a = cp.Parameter(1)
b = cp.Parameter(1)
c = cp.Parameter(1)
d = cp.Parameter(1)
delta = cp.Variable(1)
constraints = [Y1 <= X1 - a,
Y1 <= X2 - b,
Y1 >= X1 - a - M*delta,
Y1 >= X2 - b - M*(1-delta),
Y2 <= X3 - c,
Y2 <= X4 - d,
Y2 >= X3 - c - M*delta,
Y2 >= X4 - d - M*(1-delta),
0 <= a, a <= 10,
0 <= b, b <= 5,
0 <= c, c <= 5,
0 <= d, d <= 10,
delta == x]
obj = cp.Minimize(cp.sum_squares(Y1-Y2))
prob = cp.Problem(obj, constraints)
print(prob.solve())
In cvxpy, parameter is something you have a value to set to it. In your problem, basically all symbols other than the X1 to X4 are variables. So do a global replace of cp.Parameter to cp.Variable will work.
Then, I found the result to be
$ python3 cvxtest.py
69.99998471073722
Gekko with the APOPT solver can handle MIQP problems in addition to more general Nonlinear Mixed Integer Programming (MINLP). The solution is:
---------------------------------------------------
Solver : APOPT (v1.0)
Solution time : 2.610000000277068E-002 sec
Objective : 70.0000000000000
Successful solution
---------------------------------------------------
x: 1.0
obj: 70.0
Here is the Python script:
import numpy as np
from gekko import GEKKO
m = GEKKO()
X1 = m.Param([3,10,20,10])
X2 = m.Param([5,1,3,4])
X3 = m.Param([2,3,1,4])
X4 = m.Param([10,0,1,2])
M = 100
p = m.Array(m.FV,4,lb=0,ub=10); a,b,c,d=p
b.upper = 5; c.upper = 5
for pi in p:
pi.STATUS=1
x = m.FV(lb=0,ub=1,integer=True); x.STATUS=1
Y1,Y2 = m.Array(m.Var,2)
delta = m.FV(); delta.STATUS=1
m.Equations([Y1 <= X1 - a,
Y1 <= X2 - b,
Y1 >= X1 - a - M*delta,
Y1 >= X2 - b - M*(1-delta),
Y2 <= X3 - c,
Y2 <= X4 - d,
Y2 >= X3 - c - M*delta,
Y2 >= X4 - d - M*(1-delta),
delta == x])
m.Minimize((Y1-Y2)**2)
m.options.IMODE=2
m.options.SOLVER=1
m.solve()
print('x: ', x.value[0])
print('obj: ', m.options.OBJFCNVAL)
I'm trying to optimize the following problem in python using Gurobi and the answer comes out as a decimal. How do I get the output to solve for optimal integers?
from gurobipy import *
def main():
pass
if __name__ == '__main__':
main()
try:
#Create a new model
m = Model("Investment");
#Create variables
x1 = m.addVar(vtype=GRB.CONTINUOUS, name="x1")
x2 = m.addVar(vtype=GRB.CONTINUOUS, name="x2")
x3 = m.addVar(vtype=GRB.CONTINUOUS, name="x3")
x4 = m.addVar(vtype=GRB.CONTINUOUS, name="x4")
x5 = m.addVar(vtype=GRB.CONTINUOUS, name="x5")
#Intigrate new variables
m.update()
#Set Objective
m.setObjective(160*x1 + 160*x2 + 160*x3 + 75*x4 + 75*x5, GRB.MINIMIZE)
m.addConstr( x1 + x2 + x3 >= 3, "c0")
m.addConstr( x1 >= 1, "c1")
m.addConstr( x2 >= 0, "c2")
m.addConstr( x3 >= 1, "c3")
m.addConstr( x4 >= 0, "c4")
m.addConstr( x5 >= 0, "c5")
m.addConstr(40*x1 + 40*x2 + 40*x3 + 25*x4 + 25*x5 >= 365,"c6")
m.optimize()
for v in m.getVars():
print v.varName, v.x
print "Obj:", m.objVal
except GurobiError:
print "Error reported"
Use .addVar(vtype=GRB.INTEGER, ...).
See http://www.gurobi.com/documentation/5.6/reference-manual/py_model_addvar
vtype = GRB.INTEGER
For binary vtype = GRB.BINARY, total 5 variables types
The following is as much I could boil it down.
I'm trying to solve a system of equations with 18 equations and 18 variables. For the moment, I hold 4 of these variables fixed. Originally, I got weird results. So I simplified the problem so far such that the first 9 and the last 9 equations are separate and identical. Moreover, the problem is exactly identified: There should be one unique solution.
The solution vector contains 14 elements (18 minus the 4 fixed variables). Since these are ordered properly, the first 7 solution variables should be identical to the last 7 solution variables. However, they are not.
I checked identity of equations by putting in an identical vector x[:7] = x[7:] and checking that res[:9] == res[9:] were all true.
Following is the output that I get:
Optimization terminated successfully. (Exit mode 0)
Current function value: 0.125393271845
Iterations: 18
Function evaluations: 297
Gradient evaluations: 18
Out[223]:
J W w v U u Y
0 0.663134 0.237578 0.251245 10.00126 0.165647 0.093939 0.906657
1 0.022635 0.825547 1.000000 10.00340 0.512898 0.089790 0.909918
Where I have stacked the first 7 variables into the first row and the next 7 variables into the second row. Clearly, these are not identical.
Code for reproduction follows
import numpy as np
# parameters
class Parameters(object):
r = 1.03
sBar = 0.1
sB = 0.1
c = 0.1
z = 0.001
H = 1
epsilon = 1
beta = 0.1
def q(theta):
if theta <= 0:
return 999
return float(1)/theta
def f(theta):
if theta < 1:
return 0
return 1 - float(1)/theta
# sum_all allows to see residual as vector, not summed
def twoSectorFake(x, Param, sum_all=True):
JBar, WBar, wBar, vBar, UBar, uBar, YBar = x[:7]
JB, WB, wB, vB, UB, uB, YB = x[7:]
VBar = 0
VB = 0
pB = 1
pBar = 1
#theta = float(vB + vBar)/u
thetaBar = float(vBar)/uBar
thetaB = float(vB)/uB
res = np.empty(18,)
res[0] = Param.r*JBar - (pBar - wBar - Param.sBar*(JBar - VBar) )
res[1] = Param.r * VBar - ( -Param.c + q(thetaBar) * (JBar - VBar) )
res[2] = Param.r * WBar - (wBar - Param.sBar * (WBar - UBar) )
res[3] = Param.r * UBar - (Param.z + f(thetaBar) * (WBar - UBar) )
res[4] = Param.sBar * YBar - vBar * q(thetaBar)
res[5] = Param.sBar * YBar - uBar * f(thetaBar)
res[6] = JBar - (1 - Param.beta) * (JBar + WBar - UBar)
res[7] = Param.H - YBar - uBar
res[8] = thetaBar * uBar - vBar
res[9] = Param.r*JB - (pB - wB - Param.sB*(JB - VB))
res[10] = Param.r*VB - ( -Param.c + q(thetaB) * (JB - VB))
res[11] = Param.r * WB - (wB - Param.sB * (WB - UB))
res[12] = Param.r * UB - (Param.z + f(thetaB) * (WB - UB))
res[13] = Param.sB * YB - vB*q(thetaB)
res[14] = Param.sB * YB - uB * f(thetaB)
res[15] = JB - (1 - Param.beta) * (JB + WB - UB)
res[16] = Param.H - YB - uB
res[17] = thetaB * uB - vB
idx = abs(res > 10000)
# don't square too big numbers, they may become INF and the problem won't solve
res[idx] = abs(res[idx])
res[~idx] = res[~idx]**2
if (sum_all==False):
return res
return sum(res)
Param = Parameters()
x2 = np.empty(0,)
boundaries2 = []
# JBar
x2 = np.append(x2, 1)
boundaries2.append([0, 100])
# WBar
x2 = np.append(x2, 1)
boundaries2.append([0, 100])
# wBar
x2 = np.append(x2, 0.5)
boundaries2.append([0.01, 100])
# vBar
x2 = np.append(x2, 10)
boundaries2.append([0.01, 100000])
# UBar
x2 = np.append(x2, float(Param.z)/(Param.r-1)+1)
boundaries2.append([float(Param.z)/(Param.r-1) - 0.1, 100])
# uBar
x2 = np.append(x2, 0.5*Param.H)
boundaries2.append([0.0000001, Param.H])
# YBar
x2 = np.append(x2, 0.5*Param.H)
boundaries2.append([0.0001, Param.H])
# JB
x2 = np.append(x2, 1)
boundaries2.append([0, 100])
# WB
x2 = np.append(x2, 1)
boundaries2.append([0, 100])
# wB
x2 = np.append(x2, 0.5)
boundaries2.append([1, 100])
# vB
x2 = np.append(x2, 10)
boundaries2.append([0.01, 100])
# UB
x2 = np.append(x2, float(Param.z)/(Param.r-1)+1)
boundaries2.append([float(Param.z)/(Param.r-1) - 0.1, 100])
# uB
x2 = np.append(x2, 0.5*Param.H)
boundaries2.append([0.0000001, Param.H])
# YB
x2 = np.append(x2, 0.5*Param.H)
boundaries2.append([0.0001, Param.H])
result = optimize.fmin_slsqp(func=twoSectorFake, x0=x2, bounds=boundaries2, args=(Param,), iter=200)
res1 = result[:7]
res2 = result[7:]
df = pd.DataFrame(np.reshape(res1, (1,7)), columns=['J', 'W', 'w', 'v', 'U', 'u','Y'])
df.loc[1] = np.reshape(res2, (1,7))
print df
EDIT:
your variable boundaries2 isn't symmetric, i.e. boundaries2[7:]!=boundaries2[:7] .
Try writing
boundaries2 = boundaries2[7:]*2
just before your call to fmin_slsqp and you get a symmetric local minimum. I leave the previous general comments on your setup below, because they apply in any case.
First of all, are you sure that there exists only one solution to your problem? if not, I wouldn't expect necessarily the scipy numerical routine to return the solution you have in mind. It can converge to any other non-symmetric solution.
In second instance, you are not solving the system of equations. If you evaluate twoSectorFake(result,Param) at the end of your code, you get 0.15. You may be better off with other root solvers, see the root finding section.
This means that you're then looking at a local minimum of your target function, i.e. not a zero. Again, there's no reason why the numerical routine should necessarily calculate a symmetric local minimum.