I am trying to do an Optimization problem with PuLP but I am having an issue with writing my objective function.
I've simplified my real-life example to a simpler one using cereal. So let's say I have a list of products and a number of Aisles I can put them into (for this example 2). Each product has a number we normally sell each week (ex: we sell 20 boxes of Fruit Loops and 6 boxes of Cheerios each week). Each item also needs a certain amount of shelves (ex: Frosted Flakes needs 1 shelf, but Corn Flakes needs 2).
Product | Sales | Shelves | Assigned Aisle |
---|---|---|---|
Fruit Loops | 20 | 2 | |
Frosted Flakes | 15 | 1 | |
Cocoa Pebbles | 8 | 1 | |
Fruitty Pebbles | 9 | 1 | |
Corn Flakes | 12 | 2 | |
Cheerios | 6 | 1 |
Each aisle only has 4 shelves. So in theory I could put Fruit Loops and Corn Flakes together in one Aisle (2 shelves + 2 shelves). If I put those in an Aisle the weekly sales from that Aisle would be 20 + 12 = 32. If I put the other 4 items (1 shelf + 1 + 1 + 1) in an Aisle then that Aisles sales would be 15 + 8 + 9 + 6 = 38. The average sales in an Aisle should be 35. The goal of my optimization problem is for each Aisle to be as close to that Average number as possible. I want to minimize the total absolute difference between each Aisles Total Weekly Sales and the Average number. In this example my deviation would be ABS(38-35) + ABS(32-35) = 6. And this is the number I want to minimize.
I cannot figure out the way to write that so PuLP accepts my objective. I can't find an example online with that level of complexity where it's comparing each value to an average and taking the cumulative absolute deviation. And when I write out code that technically would calculate that PuLP doesn't seem to accept it.
Here's some example code:
products = ['Fruit Loops', 'Frosted Flakes', 'Cocoa Pebbles', 'Fruitty Pebbles', 'Corn Flakes', 'Cheerios']
sales = {'Fruit Loops': 20, 'Frosted Flakes': 15, 'Cocoa Pebbles': 8, 'Fruitty Pebbles': 9, 'Corn Flakes': 12, 'Cheerios': 6}
shelves = {'Fruit Loops': 2, 'Frosted Flakes': 1, 'Cocoa Pebbles': 1, 'Fruitty Pebbles': 1, 'Corn Flakes': 2, 'Cheerios': 1}
from pulp import *
problem = LpProblem('AisleOptimization', LpMinimize)
# For this simplified example there are only 2 aisles
Num_of_Aisles = 2
# Each Aisle has 4 shelves and can't have more than 4 shelves filled
Max_Shelves_Aisle = 4
# The Optimizer can change the Aisle each Product is assigned to try and solve the problem
AislePick = LpVariable.dicts('AislePick', products, lowBound = 0, upBound = (Num_of_Aisles - 1), cat='Integer')
# My attempt at the Minimization Formula
# First Calculate what the average sales would be if split perfectly between aisles
avgsales = sum(sales.values()) / Num_of_Aisles
# Loop through and calculate total sales in each aisle and then subtract from the average
problem += lpSum([sum(v for _, v in value) - avgsales for _, value in itertools.groupby(sorted([(aisle, sales[product]) for product, aisle in AislePick.items()]), lambda x: x[0])]), 'Hits Diff'
# Restriction so each Aisle can only have 4 shelves
for aisle in range(Num_of_Aisles):
problem += lpSum([shelves[prod] for prod, ais in AislePick.items() if ais == aisle]) <= Max_Shelves_Aisle, f"Sum_of_Slots_in_Aislel{aisle}"
problem.solve()
The result I get is -3
If I run LpStatus[problem.status]
I get Undefined
. I assume my is that my objective function is too complex, but I'm not sure.
Any help is appreciated.