There are four variables
(S1, S2, S3, S4)
with the constraint
(S1+S2+S3+S4=100).
There are four given constants (C1, C2, C3, C4). I want to maximize the value of (S1/C1 + S2/C2 + S3/C3 + S4/C4). Here is my code in python:
#!/usr/bin/env python3
import numpy as np
from scipy.optimize import minimize
S0 = [25, 25, 25, 25]
C = [89415,8991,10944,15164]
def objective(S, C):
total = 0
for index in range(4):
total = total + S[index]/C[index]
return -total
def constraint(S):
return (100 - S[0] - S[1] - S[2] - S[3])
b = (0.0, 100.0)
boundaries = (b,b,b,b)
con = ({'type':'eq', 'fun':constraint})
solution = minimize(objective,S0,args=(C),method='SLSQP',bounds=boundaries,constraints=con)
print (solution)
My code is simply returning the initial guess for S as the final result
fun: -0.0069931517268763755
jac: array([-1.11838453e-05, -1.11222384e-04, -9.13742697e-05, -6.59456709e-05])
message: 'Optimization terminated successfully.'
nfev: 6
nit: 1
njev: 1
status: 0
success: True
x: array([25., 25., 25., 25.])
Where am I going wrong?
It looks like the differences in the output values of your functions are within the default tolerance for the optimizer to stop optimizing your function between iterations. Setting your tolerance to a smaller value like 1e-12 helps with this:
solution = minimize(objective,S0,args=(C),method='SLSQP',bounds=boundaries,constraints=con, tol=1e-12)
Result:
fun: -0.01112223334445557
jac: array([ -1.11837871e-05, -1.11222267e-04, -9.13742697e-05,
-6.59456709e-05])
message: 'Optimization terminated successfully.'
nfev: 192
nit: 32
njev: 32
status: 0
success: True
x: array([ 0.00000000e+00, 1.00000000e+02, 3.01980663e-14,
0.00000000e+00])
which is approximately equal to the absolute maximum solution [0,100,0,0].
Related
Friends - Can someone help me formulate a LP problem using scipy in python as below, sorry for this naive ask, I am not able to get started at all with this. I could do this in excel, but finding it difficult in python (am new to this library and couldn't solve)
I would be very thankful if someone could help me out please:
This is the data:
This is problem formulated
import pulp as p
import numpy as np
arr = np.array([[0.1167, 2.40, 6.95], [0.1327, 3.44, 15.1], [0.1901, 3.76, 12.7]])
arr = arr.transpose()
# create a problem
Lp_prob = p.LpProblem('Problem', p.LpMinimize)
# create variables
x1 = p.LpVariable("x1", lowBound=0, upBound=np.inf)
x2 = p.LpVariable("x2", lowBound=0, upBound=np.inf)
x3 = p.LpVariable("x3", lowBound=0, upBound=np.inf)
# define problem
Lp_prob += 6.95 * x1 + 15.1 * x2 + 12.7 * x3
# define constraints
Lp_prob += x1 * 0.1167 + x2 * .1327 + x3 * 0.1901 >= 1.95
Lp_prob += x1 * 2.4 + x2 * 3.44 + x3 * 3.76 >= 0
Lp_prob += x1 >= x2
Lp_prob += x1 >= 0
Lp_prob += x2 >= 0
Lp_prob += x3 >= 0
# see the problem created
print(Lp_prob)
status = Lp_prob.solve()
PulpSolverError: Pulp: Error while executing C:\Users\FinanceProfessional\.conda\envs\spyder-env\Lib\site-packages\pulp\apis\..\solverdir\cbc\win\64\cbc.exe
Using scipy
from scipy.optimize import linprog
arr = np.array([[0.1167, 2.40, 6.95], [0.1327, 3.44, 15.1], [0.1901, 3.76, 12.7]])
arr = arr.transpose()
c = arr[-1]
A = [arr[0], arr[1], [1,1,0]]
b = [0.09, 0, 0]
x0_bounds = (0, None)
x1_bounds = (0, None)
x2_bounds = (0, None)
result = linprog(c, A_ub=A, b_ub=b, bounds=[x0_bounds, x1_bounds, x2_bounds], method='revised simplex')
print(result)
con: array([], dtype=float64)
fun: 0.0
message: 'Optimization terminated successfully.'
nit: 0
slack: array([0.09, 0. , 0. ])
status: 0
success: True
x: array([0., 0., 0.])
from scipy.optimize import minimize
a1, a2, a3 = 1167,1327,1907
b1,b2,b3 = 24000, 34400, 36000
c1,c2,c3 = 69500,15100,12700
x = [10000,10000,10000]
res = minimize(
lambda x: c1*x[0]+c2*x[1]+c3*x[2], #what we want to minimize
x,
constraints = (
{'type':'eq','fun': lambda x: x[0]*a1-x[1]*a2}, #1st subject
{'type':'ineq','fun': lambda x: a1*x[0]+a2*x[1]+a3*x[2]-7}, #2st subject
{'type':'ineq','fun': lambda x: b1*x[0]+b2*x[1]+b3*x[2]-0}, #3st subject
{'type':'eq','fun': lambda x: x[0]%5+x[1]%5+x[2]%5-0}, # x1 x2 x3 are multiple of 5
),
bounds = ((0,None),(0,None),(0,None)),
method='SLSQP',options={'disp': True,'maxiter' : 10000})
print(res)
here the output :
> Optimization terminated successfully (Exit mode 0)
> Current function value: 381000000.00006175
> Iterations: 2
> Function evaluations: 9
> Gradient evaluations: 2
> fun: 381000000.00006175
> jac: array([69500., 15100., 12700.]) message: 'Optimization terminated successfully'
> nfev: 9
> nit: 2
> njev: 2 status: 0 success: True
> x: array([ 0., 0., 30000.])
I had to multiplied all value by 10000 to avoid mode 8 as explained here
I hope this is what you needed. However you should try Or-Tools, a CP library powerful and easier than scipy.
edit: answer to comment
here is a link to a google collab as the original poster cannot run this code on his side.
I am trying to solve an LP problem with two variables with two constraints where one is inequality and the other one is equality constraint in Scipy.
To convert the inequality in the constraint I have added another variable in it called A.
Min(z) = 80x + 60y
Constraints:
0.2x + 0.32y <= 0.25
x + y = 1
x, y <= 0
I have changed the inequality constraints by the following equations by adding an extra variable A
0.2x + 0.32y + A = 0.25
Min(z) = 80x + 60y + 0A
X+ Y + 0A = 1
from scipy.optimize import linprog
import numpy as np
z = np.array([80, 60, 0])
C = np.array([
[0.2, 0.32, 1],
[1, 1, 0]
])
b = np.array([0.25, 1])
x1 = (0, None)
x2 = (0, None)
sol = linprog(-z, A_eq = C, b_eq = b, bounds = (x1, x2), method='simplex')
However, I am getting an error message
Invalid input for linprog with method = 'simplex'. Length of bounds
is inconsistent with the length of c
How can I fix this?
The problem is that you do not provide bounds for A. If you e.g. run
linprog(-z, A_eq = C, b_eq = b, bounds = (x1, x2, (0, None)), method='simplex')
you will obtain:
con: array([0., 0.])
fun: -80.0
message: 'Optimization terminated successfully.'
nit: 3
slack: array([], dtype=float64)
status: 0
success: True
x: array([1. , 0. , 0.05])
As you can see, the constraints are met:
0.2 * 1 + 0.32 * 0.0 + 0.05 = 0.25 # (0.2x + 0.32y + A = 0.25)
and also
1 + 0 + 0 = 1 # (X + Y + 0A = 1)
I am able to solve the following minimization problem with scipy linprog with the matrix A_ub:
A_ub = [[ 1 10 0 3]
[ 6 2 3 6]
[ 3 5 4 2]
[ 4 9 2 2]]
and
b_ub = [1,1,1,1]
and the minimization problem is c = [-1,-1,-1,-1] (i.e negative of norm 1).
Calling the linprog from scipy gives the following result (as expected):
scipy.optimize.linprog(c, A_ub=A_ub, b_ub=b_ub)
con: array([], dtype=float64)
fun: -0.2777777777777778
message: 'Optimization terminated successfully.'
nit: 7
slack: array([0.83333333, 0. , 0. , 0.44444444])
status: 0
success: True
x: array([0. , 0. , 0.22222222, 0.05555556])
However, I also need to find the solution for the dual of the problem.
From my understanding of the minimax theorem, the above problem is equivalent to:
scipy.optimize.linprog(-b_ub, A_ub=A_ub.T, b_ub=c)
However, running such command would result in errors:
con: array([], dtype=float64)
fun: 0.0
message: "Phase 1 of the simplex method failed to find a feasible solution. The pseudo-objective function evaluates to 4.0e+00 which exceeds the required tolerance of 1e-12 for a solution to be considered 'close enough' to zero to be a basic solution. Consider increasing the tolerance to be greater than 4.0e+00. If this tolerance is unacceptably large the problem may be infeasible."
nit: 0
slack: array([-1., -1., -1., -1.])
status: 2
success: False
x: array([0., 0., 0., 0.])
If I increase the tolerance to a large value (10) then it does terminate with a solution but I don't think it is correct as the function value is not the same as the primal value.
I really appreciate any help and hint regarding this problem and how to find the solution to the dual.
best,
Hieu.
I made a mistake in calling linprog,
the dual of the problem should be :
minimizing b_ub
s.t
-A_transpose *x <= c
Thus, the linprog call would work if I use:
linprog(b_ub, -A_transpose, c)
I have the following code:
def constraint(params):
if abs(params[0] - 15) < 2 and abs(params[1] + 10) < 2:
return -1
else:
return 0
def f(params):
x, z = params
if abs(x - 15) < 2 and abs(z + 10) < 2:
return -9999999
return (x - 15) ** 2 + (z + 10) ** 2 * numpy.sqrt(numpy.abs(numpy.sin(x)))
# Last: 15.00024144, -9.99939634
result = optimize.minimize(f, (-15, -15),
bounds=((-15.01, 15.01,), (-15.01, 15.01,),),
method="SLSQP",
options={'maxiter': 1024 * 1024},
jac=False,
constraints={
'type': 'ineq',
'fun': constraint,
})
print(result)
print(f(result.x))
And it gives the following result:
fun: -9999999.0
jac: array([0., 0.])
message: 'Optimization terminated successfully.'
nfev: 12
nit: 7
njev: 3
status: 0
success: True
x: array([ 15.01 , -11.60831378])
-9999999
The given values [ 15.01, -11.60831378] should be dropped by the constraint (and they were: if I add more verbose logging, I see that constraint function returns -1, but scipy ignores it. Why?
I'm pretty far from data science and maths, so I'm sorry for stupid mistakes if they are there.
To help the algorithm find the right direction, you need to separate your constraints:
def f(params):
print(params)
x, z = params
if abs(x - 15) < 2 and abs(z + 10) < 2:
return -9999999
return (x - 15) ** 2 + (z + 10) ** 2 * numpy.sqrt(numpy.abs(numpy.sin(x)))
# Last: 15.00024144, -9.99939634
result = optimize.minimize(f, (-15, -15),
bounds=((-15.01, 15.01,), (-15.01, 15.01,),),
method="SLSQP",
options={'disp':True, 'maxiter': 1024 * 1024},
jac=False,
constraints=({
'type': 'ineq',
'fun': lambda params : abs(params[0] - 15) -2,
},
{
'type': 'ineq',
'fun': lambda params : abs(params[1] + 10) -2,
},)
)
print(result)
print(f(result.x))
Gives:
Optimization terminated successfully. (Exit mode 0)
Current function value: 6.5928117149596535
Iterations: 6
Function evaluations: 24
Gradient evaluations: 6
fun: 6.5928117149596535
jac: array([-1.2001152, 2.5928117])
message: 'Optimization terminated successfully.'
nfev: 24
nit: 6
njev: 6
status: 0
success: True
x: array([13., -8.])
[13. -8.]
6.5928117149596535
Bingo!
I'm trying to minimize a dot product of 2 vectors but it doesn't work and I have no idea why. Can someone please help me?
I have a matrix c of this form:
c = [[c11, c12, c13, c14, c15],
[c21, c22, c23, c24, c25]]
I want to get a matrix p of this form:
p = [[p11, p12, p13, p14, p15],
[p21, p22, p23, p24, p25]]
I want to maximize this value :
c11*p11 + c12*p12 +c13*p13 + c14*p14 + c15*p15 + c21*p21 + c22*p22 +c23*p23 + c24*p24 + c25*p25
To get that I convert the c and p to 1-D vector and do the dot product so that my function to maximize is:
f(p) = c.dot(p)
The constraints are:
c11 + c12 + c13 + c14 + c15 = 1
c21 + c22 + c23 + c24 + c25 = 1
every element in p must be between 0.01 and 0.99.
I have tried scipy.optimize.linprog and it works:
from scipy.optimize import linprog
c = np.array([0. , 0. , 0. , 0. , 0. , 0. , 20094.21019108, 4624.08079143, 6625.51724138, 3834.81081081])
A_eq = np.array([[1,1,1,1,1,0,0,0,0,0],
[0,0,0,0,0,1,1,1,1,1]])
b_eq = np.array([1, 1])
res = linprog(-c, A_eq=A_eq, b_eq=b_eq, bounds=(0.01, 0.99))
res
Out[561]:
fun: -19441.285871873002
message: 'Optimization terminated successfully.'
nit: 13
slack: array([0.03, 0.98, 0.98, 0.98, 0.98, 0.98, 0.03, 0.98, 0.98, 0.98, 0. ,
0. , 0.95, 0. , 0. , 0. , 0. , 0. , 0. , 0. ])
status: 0
success: True
x: array([0.96, 0.01, 0.01, 0.01, 0.01, 0.01, 0.96, 0.01, 0.01, 0.0
But I'm trying to use scipy.optimize.minimize with SLSQP instead and that's where I get this 'Singular matrix C in LSQ subproblem' . Here is what I've done:
from scipy.optimize import minimize
def build_objective(ck, sign = -1.00):
"""
Builds the objective fuction for matrix ck
"""
# Here I turn my c matrix to a 1-D matrix
ck = np.concatenate(ck)
def objective(P):
return sign*(ck.dot(P))
return objective
def build_constraint_rows(ck):
"""
Builds the constraint functions that specify that the sum of the proportions for
each bin equals 1
"""
ncol = ck.shape[1]
nrow = ck.shape[0]
constrain_dict = []
for i in range(nrow):
vector = np.zeros((nrow,ncol))
vector[i, :] = 1
vector = np.concatenate(vector)
def row_constrain(P):
return 1 - vector.dot(P)
constrain_dict.append({'type': 'eq', 'fun': row_constrain})
return constrain_dict
# Matrix: Notice that this is not in vector form yet
c = np.array([[0. , 0. , 0. , 0., 0.],
[0. , 20094.21019108, 4624.08079143, 6625.51724138, 3834.81081081]])
# I need some initial p matrix for the function 'minimize'. I look for the value of the row that is the highest and assign it a proportion p of 0.96 and the rest 0.01 so the sum in 1 per row
P_initial = np.ones(c.shape)*0.01
nrow = test.shape[0]
for i in range(nrow):
index= np.where(c[i,] == np.max(c[i,]))[0]
if index.shape[0] > 1:
index = int(np.random.choice(index, size = 1))
else:
index = int(index)
P_initial[i,index] = 0.96
# I turn the P_initial to vector form
P_initial = np.concatenate(P_initial)
# These are the bounds of each p value
b = (0.01,0.99)
bnds = (b,)*c.size
# I then use my previous functions
objective_fun = build_objective(c)
cons = build_constraint_rows(c)
res = minimize(objective_fun,P_initial,method='SLSQP',\
bounds=bnds,constraints=cons)
This is my final result:
res
Out[546]:
fun: -19434.501741138763
jac: array([0. , 0.,0. , 0. ,0. , 0., -20094.21020508, -4624.08056641, -6625.51708984, -3834.81079102])
message: 'Singular matrix C in LSQ subproblem'
nfev: 24
nit: 2
njev: 2
status: 6
success: False
x: array([0.96 , 0.01 , 0.01 , 0.01 , 0.01 ,
0.01020202, 0.95962502, 0.01006926, 0.01001178, 0.01009192])
Please help me understand what I'm doing wrong.
Thank you in advanced,
Karol