Supply Chains
Please Log In for full access to the web site.
Note that this link will take you to an external site (https://shimmer.mit.edu) to authenticate, and then you will be redirected back to this page.
Recall the Warehouse Problem from week 4, where we maintained a warehouse's inventory based on a series of transactions. We will now construct a large program that will take in customer orders and determine which ones to fulfill based on profit and available inventory. We will write our program using a series of smaller functions because that will allow us to better to test each step, and the framework for this program can be downloaded HERE.
In this folder there is also a test.py
file. Running this file will run tests on all the functions. At the start, none of them will pass, but if for example you correctly complete the function compute_delivery_profit
, then the tests for that function, such as test_compute_delivery_profit_1
will pass. These are the same tests that will be run on your code once you submit it.
For example, if the input is orders_tiny.csv
, the returned value should be the list [((10, 7), "chains", 15), ((1.8, -9), "anvils", 32)]
.
After completing this function, you should be passing Tests 1-3.
Week 3 section on reading in csv files may be a Relevant Reading.
Fortunately we will provide functions that take in two location tuples and provide the delivery cost. For example if we wanted to know the truck delivery cost from point (1,0)
to point (5,6)
, we could call:
print(truck_delivery_cost((1,0), (5,6)))
We'll want to compute potentially a lot of delivery costs reusing the same warehouse location and delivery mode, but with different customer locations. One way is to write a function that takes in those three inputs:
compute_delivery_cost(warehouse_location, delivery_cost_function, customer1_location)
compute_delivery_cost(warehouse_location, delivery_cost_function, customer2_location)
but since some of the inputs aren't changing, we can instead create a function that returns a function as mentioned in the readings.
The goal is to fill in the function make_cost_computer
so that it takes in a warehouse location and a delivery mode and then returns a function. The returned function should take in a customer location and then return the delivery cost. In the end we should be able to use this function in this manner:
truck_compute_cost = make_cost_computer(warehouse_location, truck_cost_function)
truck_cost_function(customer1_location)
truck_cost_function(customer2_location)
As a concrete example, we could use the function as such:
def manhattan_cost(point1, point2):
(x1,y1),(x2,y2) = point1, point2
return abs(x1-x2) + abs(y1-y2)
truck_compute_cost = make_cost_computer((1,2), manhattan_cost)
print(truck_compute_cost((4,6))) # should print 7 as that is the manhattan distance from (1,2)
After completing these functions, you should be able to pass tests 4-7.
For example:
def manhattan_cost(point1, point2):
(x1,y1),(x2,y2) = point1, point2
return abs(x1-x2) + abs(y1-y2)
prices = {"anvils": 10}
truck_compute_profit = make_profit_computer((1,2), manhattan_cost, prices)
print(truck_compute_profit(((4,6), "anvils", 4)) # should print 33 as that is 4*10 sale price minus the manhattan cost of 7
You may assume that any product name that appears in an order will be in the prices dictionary.
After completing this function, you should be able to pass tests 8-10.
l = [9,3,18]
l.sort()
print(l) # [3, 9, 18] - now sorted in ascending order
By default it will modify the list to be in ascending order, but we can also provide a custom sorting function to sort
using the keyword arguement key
. The function should take in an element of the list, and return a value by which the element should be sorted. For example, if we want the list to be sorted in descending order, we could pass in a function to negate the value of the elements so they are sorted in reverse.
l = [9,3,18]
def negate(x):
return -x
l.sort(key = negate)
print(l) # [18, 9, 3] - now sorted in descending order
For the next part of the program, we want a function sort_orders
that can takes in a list of orders and a profit computing function returned by the make_profit_computer
and modifies the orders to be sorted from largest profit to lowest. The function should return None
. One way to do this is by making a helper function that takes in an order and returns a value by which to sort the orders (in this case the profit). Then we can use that function as the key
argument of the sort
function.
For example, if our profit computing function is defined to return just the amount of the order, then a call to sort_orders
should behave as follows:
orders = [((10, 7), "chains", 15), ((1.8, -9), "anvils", 32), ((3, 7.5), "chains", 13)]
profit_computer = lambda x: x[2]
result = sort_orders(orders, profit_computer)
'''
result should be None, but now orders should be modified to:
[((1.8, -9), "anvils", 32), ((10, 7), "chains", 15), ((3, 7.5), "chains", 13)]
which is sorted in order according to the amounts of each order, with the largest amount first.
After completing this function, you should be able to pass tests 11-14.
To determine the orders to fulfill, we will prioritize the most profitable orders and fulfill them as long as there is sufficient inventory stock. One approach is to first sort the orders by profit and then check in order if there is enough inventory. If there is, we should add the order to our output list and update the inventory to reflect the current available amount of that commodity.
Note, we should not fulfill any orders with a negative profit. Also, if an item is not in the inventory, then we should treat that situation as having 0 of that item.
def manhattan_cost(point1, point2):
(x1,y1),(x2,y2) = point1, point2
return abs(x1-x2) + abs(y1-y2)
prices = {"anvils": 1.2, "chains": 1.2}
inventory = {"anvils": 20, "chains": 15, "pipes":5}
answer = select_orders("orders_small.csv", inventory, prices, (1,2), manhattan_cost)
'''
answer should be [((1,3),"chains",10)] since the profit for that order is 10*1.2-1 = 11, while the order ((10,7),"chains",20) has profit 1.2*20 - 14 = 10, and there's not enough inventory for both. Finally, the order ((1.8,-9),"anvils",32) cannot be fulfilled due to inventory constraints.
'''
Next Exercise: Survey