# Problem with elastic constraints in PuLP

(This is my first question on Stack Exchange) I am working on a production allocation problem, whereby sales orders have to be allocated over three production plants. I am using PuLP in Python, and this works fine until I try to make the constraints "elastic". The elasticity is needed when the total of the sales orders is larger than the total capacity, and I have to increase the capacity of the plants, although with a "penalty". (Since producing in overtime increases the cost).

I got stuck in the lack of user-friendly documentation on this aspect of PuLP (sorry to say so) and encounter several difficulties. For applying "makeElasticSubProblem", I need to give the constraint a name. However, a) When I use the traditional syntax of prob += lpSum([alloc[s][l]for s in so]) <= cap_dict[l], 'Myname', and when I use Myname in the next line, Python returns a message that Myname is not defined. b) when I use the following syntax: Myname = LpConstraintlpSum([alloc[s][l]for s in so]), sense = 1, rhs = cap_dict[l]), the name is accepted, but the definition of the elasticity that follows for the name, seems to be "ignored" by the script.

Could someone give me a hint about what I am doing wrong ? Thanks!

Here follows a simplified version of my code (working with Python 3.8.2 64-bit):

from pulp import *

# define the locations and their production capacity
#---------------------------------------------------
location = ['locA', 'locB', 'locC']
capacity = [90, 60, 20]
cap_dict = dict(zip(location, capacity))

# define the sales orders (so) and their volumes (demand)
#--------------------------------------------------------
so = ['s1', 's2', 's3', 's4', 's5']
demand = [20,10,15,8,5]
order_dict = dict(zip(so, demand))

# define the problem
#-------------------
prob = LpProblem("Production_planning",LpMaximize)

# define the decision variables
#------------------------------
alloc = LpVariable.dicts("Alloc", (so, location), cat='Integer')

# set the objective function
#---------------------------
prob += lpSum(alloc[s][l] for s in so for l in location)

# define the constraints
#-----------------------
# 1) allocations should be positive
for s in so:
for l in location:
prob += (alloc[s][l] >= 0)

# 2) allocation limited by the capacity of the location
for l in location:
prob += lpSum([alloc[s][l] for s in so]) <= cap_dict[l]

# 3) Location A should receive > 50% of all the allocations
prob += (alloc[s]['locA'] >= lpSum(alloc[s][l] for l in location)/2)

# 4) allocation limited by the amount ordered
for s in so:
prob += lpSum([alloc[s][l] for l in location]) <= order_dict[s]

# solve the optimization problem
#-------------------------------
prob.solve()
print("Status :", LpStatus[prob.status])

for v in prob.variables():
if "Alloc_" in v.name:
if v.varValue > 0:
print(v.name, v.varValue)


After the following definition of the capacity constraints, the specification of the 20% upward elasticity of the constraint for location C is simply ignored (the program allocates to location C any large amount if I increase the sales orders, without any limit):

# capacity constraints:
#---------------------
# same definition for locations A and B

for l in ['locA', 'locB']:
prob += lpSum([alloc[s][l] for s in so]) <= cap_dict[l]

# different and specific definition for location C, where an elastic capacity increase needs to be introduced:

cap_locC = LpConstraint(lpSum([alloc[s][l]for s in so]), sense = 1, rhs = cap_dict['locC'])
elastic_cap_locC = cap_locC.makeElasticSubProblem(penalty = 100, proportionFreeBoundList = [0,0.2])