Constraints not working in Optimization using Scipy

1.8k views Asked by At

I am trying to solve a profit maximization problem. There are a few promotion schemes run by the company. I make an objective function for profit maximization subject to some constraints. I want to make a constraint which says the company cannot run more than 2 promotion schemes in parallel. However this condition is not working

Here is my code:

from scipy.optimize import minimize
from math import floor

#Objective function
def objective(x):
    return -((-14.12*x[0]+1*floor(x[1])+2*floor(x[2])+4*floor(x[3]))*x[0] \ #total revenue (qty*price)
            -(-14.12*x[0]+1*floor(x[1])+2*floor(x[2])+4*floor(x[3]))*10000*0.05\ #Gold price cost
            -(-14.12*x[0]+1*floor(x[1])+2*floor(x[2])+4*floor(x[3]))*x[0]*0.1\ #Rebate cost
            -(-14.12*x[0]+1*floor(x[1])+2*floor(x[2])+4*floor(x[3])/15000*100000)) 

#Points constraint

#Constraints
def PriceConstraint(x):
    return x[0]-3000

def GoldCoinConstraint(x):
    return floor(x[1])-1

def PointsConstraint(x):
    return floor(x[2])-1

def ProgressiveRebateConstraint(x):
    return floor(x[3])-1

def CombinationConstraint(x):
    return 2-floor(x[1])+floor(x[2])+floor(x[3])

#Initial guesses
n=5
x0=np.zeros(5)
x0[0]=1
x0[1]=2
x0[2]=2
x0[3]=1
x0[4]=3

# show initial objective
print('Initial Objective: ' + str(objective(x0)))

# optimize
b = (0.0,1.0)
pricebound = (1,3000)
bnds = (pricebound, b, b, b,b)
con1= {'type':'ineq','fun':PriceConstraint}
con2= {'type':'ineq','fun':GoldCoinConstraint}
con3= {'type':'ineq','fun':PointsConstraint}
con4= {'type':'ineq','fun':ProgressiveRebateConstraint}
con5= {'type':'ineq','fun':CombinationConstraint}

cons = ([con1, con2, con3, con4, con5])

solution = minimize(objective,x0,method='SLSQP',\
                    bounds=bnds,constraints=cons)

x = solution.x

# show final objective
print('Final Objective: ' + str(objective(x)))

# print solution
print('Solution')
print('x1 = ' + str(x[0]))
print('x2 = ' + str(x[1]))
print('x3 = ' + str(x[2]))
print('x4 = ' + str(x[3]))

As you can see I have put a constraint that the number of marketing schemes should not be more than 2 using the combinationconstrain function. Somehow it does not seems to be working? I am getting the output as 1 for x[1], x[2], and x[3].

Can someone help me why this is not working?

Also in a different line of thought is there something like shadow price in non linear optimization. I know it exists in linear programming, but not sure of non-linear?

1

There are 1 answers

1
Erwin Kalvelagen On BEST ANSWER
2-floor(x[1])+floor(x[2])+floor(x[3])

should probably read

2-(floor(x[1])+floor(x[2])+floor(x[3]))

That does not really help you. When you run it you may get something like:

     fun: -976376.097853337
     jac: array([567.265625,   0.      ,   0.      ,   0.      ,   0.      ])
 message: 'Positive directional derivative for linesearch'
    nfev: 32
     nit: 7
    njev: 3
  status: 8
 success: False
       x: array([300.9,   1. ,   1. ,   1. ,   1. ])

Your floor function makes the problem non-differentiable. SLSQP only likes problems that are smooth (i.e. differentiable).

I think your problem is actually a Mixed-Integer Quadratic Programming problem. So I would suggest to not solve this with a continuous NLP solver but rather with a MIQP solver. That way you don't need the floor functions. Unfortunately scipy does not contain an MIQP solver.


Q: is there something like shadow price in non linear optimization?.

A: yes, for continuous problems most NLP solvers can provide duals or shadow prices (scipy solvers don't do that afaik). Duals are not available for discrete problems.