Background:
This is a fairly simple script that is looking to achieve the following:
- For a list of four Items, each has a Demand
- For each of those items, there are four Vendors who have differing prices and quantities for each of those four items and fixed shipping costs
- Shipping is only added once per checkout, regardless of the number of items ordered from the Vendor (although shipping will not be charged if nothing is ordered from that Vendor)
I've gotten so far as to returning the minimal cost and breakdown of what to order from where without shipping.
I'm currently stuck on how to working in the SUM(VendorVar[x]{0:1} * ShippingData[x])
portion, as I essentially need a way to switch the binary value to ON/1
if the quantity of items I'm ordering from a Seller is > 0
from pulp import *
items = ["Item1", "Item2", "Item3", "Item4"]
vendors = ["Vendor1", "Vendor2", "Vendor3", "Vendor4"]
# List containing lists for each Vendor and their Item costs for Item1, Item2, Item3, Item4 respectively:
costData = [[1.00,5.00,10.00,0.15],
[1.50,2.50,5.00,0.25],
[0.50,1.00,15.00,0.50],
[1.75,10.00,2.00,0.10]]
# List containing lists for each Vendor and their Supply for Item1, Item2, Item3, Item4 respectively:
supplyData = [[0,2,4,1],
[4,0,1,4],
[1,1,1,1],
[8,8,8,8]]
# Created nested dictionaries per Item per Vendor for Costs: {Item1: {Vendor1:Cost, Vendor2:Cost...}}
vendoritemcosts = makeDict([items,vendors],costData)
# Created nested dictionaries per Item per Vendor for Supply: {Item1: {Vendor1:Supply, Vendor2:Supply...}}
vendoritemsupply = makeDict([items,vendors],supplyData)
# Shipping costs per Vendor:
shippingData = {"Vendor1":0.99,
"Vendor2":1.99,
"Vendor3":0.00,
"Vendor4":2.99}
# Number of items desired:
demand = {"Item1":4,
"Item2":4,
"Item3":4,
"Item4":8}
# Number of items to purchase for each Vendor/Item combination:
vendoritemvar = LpVariable.dicts("item",(items,vendors),0,None,LpInteger)
# Binary flag that (hopefully) will determine if a Vendor is included in the final optimized formula or not:
vendorvar = LpVariable.dicts("vendor",vendors,0,1,LpBinary)
prob = LpProblem("cart",LpMinimize)
# Objective Function: Take the sum of quantity ordered of each unique Vendor+Item combination multiplied by its price
# For every Vendor included in the list, multiple {0:1} to their shipping costs, with 1 being used if they have any items in the first portion of the function above
prob += lpSum([vendoritemvar[a][b] * vendoritemcosts[a][b] for a in vendoritemvar for b in vendoritemvar[a]]) \
+ lpSum(vendorvar[c] * shippingData[c] for c in vendorvar)
for a in vendoritemvar:
# Sum total of each item must equal Demand
prob += lpSum(vendoritemvar[a]) == demand[a]
# Currently testing minimum checkout values which will be a future addition that isn't a fixed value:
prob += lpSum(vendoritemvar[a][b] * vendoritemcosts[a][b] for b in vendoritemvar[a]) >= 2.00
for b in vendoritemvar[a]:
# Non-negativity constraint
prob += vendoritemvar[a][b] >= 0
# Can't exceed available supply
prob += vendoritemvar[a][b] <= vendoritemsupply[a][b]
prob.solve()
print("Status: %s" % LpStatus[prob.status])
for v in prob.variables():
print("%s = %s" % (v.name,v.varValue))
print("Total cart = %s" % value(prob.objective))