Assortment Optimization: Implementation of Python PuLP and IBM Ilog Cplex

Şükrü İmre, PhD
7 min readMay 20, 2021

The paper is to aim to solve assortment optimization problem of a retail company. To solve the problem, a mathematical model is set and coded platforms which are Python Pulp and IBM Ilog Cplex are used. The paper has benifitted from the rich contents of “PuLP ile Doğrusal Optimizasyon 1” which is written by Orkun Berk in Python PuLP solution and “IBM CPLEX MSSQL Database Connection” which is written by Sabri Suyunu in IBM Ilog Cplex solution. The assortment optimization problem was solved with obtained information.

Problem Definition:

There are 600 stores of the retail firm X. The products which don’t sell at the season in the stores are returned to warehouse of the firm. For instance, when the summer season is reached, the sweaters in stores are returned since they are products of winter season. Such products (sweater, jacket etc.) are put into mixed boxes by store operation team and are returned to warehouses during the season changes. The products returned from all stores are kept in warehouse and a new type of assortment is decided to ship to stores next year according to returned amounts of each size of products.

Assortment is a whole in which different sizes of the product are combined in different amounts. For example; 4 green sweater in one of the sizes XS, S, M, L and XL is an assortment.

X firm want to make assortment from return products of sweater and jacket under below constraints:

  • the number of sizes in assortment should be minimum 4 and maximum 6,
  • amount of a size should be “1” in assortment,
  • two option should not be in same assortment (one assortment should not contain sweater and jacket; it should include only sweaters or jacket)

The return amount of sweater and jacket in warehouse are shown below.

X firm aims to make assortments by minimizing remaining stock amount in warehouse under above constraints for each option.

Mathematical Model:

To solve the assortment problem under constraints that is determined by X firm, the mixed-integer programming is modeled. The sets, parameters and decision variables beloging to model are shown below.

There are two options (sweater and jacket) in this problem and it is indicated with the index i. There are six sizes starting from XS to XXL and it is indicated with the index j. The assortment id is indicated with the index k.

Ti is the total stock quantity of the option i. Sij is the option i’s the stock quantity of size j. M4 is the minimum quantity to be included in the assortment. M6 is the maximum quantity to be included in the assortment. The coefficient M represents a sufficiently large number to allow products to be put in the assortment as much as possible. The total stock amount of sweater and jacket is respectively 289 and 225. M4, M6 and M values are respectively 4, 6 and 10. Sij values are clearly seen above in table. For example; The stock amount of sweater’s size S is 26.

Xijk is the binary decision variable that indicates whether size j of option i is assigned assortment k. If size S of sweater is assigned assortment k, Xijk takes 1 value. Another binary decision variable is Yik that is whether option i assigns assortment k. For example; if sweater is assigned assortment 7, Yik takes 1 value.

The aim of firm X is to make maximum number assortment packages and obtain minimum stock of each option. Objective function are shown below in order to reach this aim.

The constraints of mathematical model are shown below.

Constraint 1 (minimum amount) and Constraint 2 (maximum amount) provide minimum 4 and maximum 6 items to be in the assortment. Stock constraint means that the total stock amount of each option’s size is not exceeded. For example, this constraint ensures that the size L of sweater cannot be assigned more than 20. Constraint 4 ensures that an assortment has only one option. Total stock constraint means that the stock amount of each option does not exceed. Finally, Xijk and Yik are binary decision variables and they take only 0 or 1.

Solution with Pyton PuLP:

The paper benefit from Python PuLP to solve mathematical model. Firstly, PuLP library imports and sets and parameters of the model is defined.

# PuLP library imports. 
from pulp import *
#sets
option=['Sweater','Jacket'] # i
size=['XS','S','M','L','XL','XXL'] # j
assortment_id=list(range(1,150)) # k
#parameters
# Sij values are defined as dictionary.
stock_dict={}
stock_dict[('Sweater','XS')]=89
stock_dict[('Sweater','S')]=26
stock_dict[('Sweater','M')]=48
stock_dict[('Sweater','L')]=20
stock_dict[('Sweater','XL')]=32
stock_dict[('Sweater','XXL')]=74
stock_dict[('Jacket','XS')]=23
stock_dict[('Jacket','S')]=54
stock_dict[('Jacket','M')]=21
stock_dict[('Jacket','L')]=87
stock_dict[('Jacket','XL')]=35
stock_dict[('Jacket','XXL')]=5
# Ti values are defined as dictionary.
total_stock={}
total_stock['Sweater']=289
total_stock['Jacket']=225
# M,M4 ve M6 values are defined.
M4=4
M6=6
M=10

Then, decision variables and objective function are defined. Decision variables are defined as binary. The type of objective function is minimization and it is defined as below.

# descision variables are defined.
x = LpVariable.dicts("", [(i,j,k) for i in option for j in size for k in assortment_id], cat='Binary')
y = LpVariable.dicts("", [(i,k) for i in option for k in assortment_id], cat='Binary')
# the type of the objective function is defined.
model=LpProblem("Min_", LpMinimize)
# the objective function is defined.
model += lpSum([y[(i,k)] for i in option for k in assortment_id])-M*lpSum([x[(i,j,k)] for i in option for j in size for k in assortment_id])

The constraints are added to the model.

# minimum and maksimum amount
for i in option:
for k in assortment_id:
model+=lpSum([x[(i,j,k)] for j in size])>=M4*y[(i,k)]
model+=lpSum([x[(i,j,k)] for j in size])<=M6*y[(i,k)]
# stock
for i in option:
for j in size:
model += lpSum([x[(i,j,k)] for k in assortment_id])<=stock_dict[(i,j)]
# only one option in assortment
for k in assortment_id:
for j in size:
model += lpSum([x[(i,j,k)] for i in option])<=1

# total stock
for i in option:
model+=lpSum([x[(i,j,k)] for j in size for k in assortment_id])<=total_stock[i]

The model is solved.

# model solution
model.solve()
print("Solved Model : {}".format(LpStatus[model.status]))
Solved Model : Optimal

The values of decision variables are imported to excel with the help of below code.

# the results are imported dataframe. 
import pandas as pd
import openpyxl
var_name= []
var_value=[]
for v in model.variables():
if v.varValue>0:
var_name.append(v.name)
var_value.append(v.varValue)
df_result=pd.DataFrame(zip(var_name,var_value), columns=['Variable','Value'])
df_result.head()
# the results are imported excel.
df_result.to_excel("output.xlsx")

Solution with IBM Ilog Cplex:

The mathematical model is also coded on IBM Ilog Cplex. There are two different code blocks in Ilog. First block is “mod” that the mathematical model is coded . The other one is “dat” that data belonging to model is read.

Firstly, the “dat” code block is shared. The data reads from excel file that is names as Assortment_Opt with help to below code block.

SheetConnection sheet("Assortment_Opt.xlsx");OptionSizeStock from SheetRead(sheet,"stock!A2:C13");
Assortment_Id from SheetRead(sheet,"asortment!A2:B201");
TotalStock from SheetRead(sheet,"toplam_stock!A2:B201");

The parameters and tuples are shown in “mod” code block.

// parameters
int M6=6;
int M4=4;
int M=10;
//tuples
// the tuple of option-size-stock
tuple OSS {
string option;
string size;
int stock;
} {OSS} OptionSizeStock=...;
// the tuple of option-total stock
tuple TS {
string option;
int total_stock;
} {TS} TotalStock=...;
// the tuple of option-assortment_id
tuple Assortment_Id {
string option;
int assortment_id;
} {Assortment_Id} Assortment_Id=...;
// the tuple of X decision variable
tuple Variable {
string option;
string size;
int asorti_id;
} {Variable} VariableTuple= {<k.option, k.size, l.assortment_id>| k in OptionSizeStock, l in Assortment_Id : k.option==l.option};

The decision variables and the objective function are defined.

// decision variables
dvar boolean x[VariableTuple];
dvar boolean y[Assortment_Id];
// objective function
minimize sum(k in Assortment_Id) y[k]-M*sum(i in VariableTuple)x[i];

The constraints are defined.

subject to {
maximum_and_minimum_amount: forall (k in Assortment_Id)
{
sum (i in VariableTuple: i.option==k.option && i.assortment_id==k.assortment_id) x[i]>=M4*y[k];
sum (i in VariableTuple: i.option==k.option && i.assortment_id==k.assortment_id) x[i]<=M6*y[k];
}
stock: forall (s in OptionSizeStock)
{
sum(i in VariableTuple : i.option==s.option && i.size==s.size) x[i]<=s.stock;
}
total_stock: forall (t in TotalStock)
{
sum(i in VariableTuple: i.option==t.option) x[i]<=t.total_stock;
}
only_one_option_in_assortment: forall (l in VariableTuple)
{
sum(i in VariableTuple: i.size==l.size && i.assortment_id==l.assortment_id) x[i]<=1;
}
}

Finally, optimum result is found in each coding platform that the number of assortments is 105. The number of assortments of jacket and sweater is respectively 42 and 63. The total amount of options left in stock is 94.

The optimization models that are related to plenty of sectors can solve with help of Python PuLP and IBM Ilog Cplex. The paper shows that how specific problem solves with two code platforms.

I would like to thanks specifically to Orkun Berk and Sabri Suyunu to inspire to me with valuable papers.

Şükrü İMRE

--

--

Şükrü İmre, PhD

// Service Analytics Manager at Yapı Kredi Bank // Guest Lecturer at MEF University // Author at Harvard Business Review Türkiye // Author at Medium