Photo by Antoine Dautry on Unsplash
Why Optimization?
Optimization is the process of finding the best solution from a set of possible options, given certain constraints or limitations.
Broadly speaking, it’s about making the most of available resources to achieve a specific goal. This goal could range from maximizing profits and minimizing costs to improving efficiency.
Why Should You Pay Attention?
If you’ve ever encountered problems similar to the ones below, you may want to learn more about how to do optimization.
Use-Case Examples:
- Supply Chain Optimization
- Problem: Determine the most cost-effective way to distribute products from multiple suppliers to multiple consumers.
- Objective: Minimize transportation and storage costs.
- Production Planning
- Problem: Determine the optimal production levels for different products in a factory.
- Objective: Maximize profit while considering constraints such as raw material availability, labor, and machine time.
- Portfolio Optimization
- Problem: Allocate assets in an investment portfolio to maximize returns while minimizing risk.
- Objective: Maximize expected return for a given level of risk.
- Vehicle Routing
- Problem: Optimize the routes for a fleet of vehicles delivering goods to a set of locations.
- Objective: Minimize total distance or time while meeting delivery windows.
There are various other methods for solving optimization problems, most popularly through linear programming, integer programming, and convex optimization. This article will cover the basis of LP and IP utilizing python package PuLP.
Key terms:
- Linear Programming: It’s a way to find the best solution to a problem using math. You have a goal (like making the most money) and some rules you have to follow (like not going over budget).
- Decision Variables: These are the choices you can make to reach your goal. For example, how many of each product to make.
- Objective Function: This is your goal put into a math formula. It could be to make the most money, use the least time, etc.
- Constraints: These are the rules you have to follow, like not going over budget or making sure you have enough materials.
What is PuLP?
TL;DR PuLP is a free open source software written in Python for linear and integer programming.
It is used to describe optimization problems as mathematical models. PuLP can then call any of numerous external LP solvers (CBC, GLPK, CPLEX, Gurobi etc) to solve this model and then use python commands to manipulate and display the solution.
It stands for “Python Linear Programming” and is widely used by students, professionals, and researchers alike. PuLP offers a range of features and capabilities that make it an efficient tool for solving linear programming problems.
One of the key advantages of PuLP is its simplicity and ease of use. It provides a high-level modeling language that allows users to express their optimization problems in a concise and intuitive manner. With PuLP, users can define decision variables, objective functions, and constraints using familiar mathematical notation. This makes it easier for beginners to get started with linear programming and enables experienced practitioners to quickly model complex optimization problems.
SCM - Inventory Balance Optimization
Forecasting is never perfect. Therefore, there is need of re-balancing inventory in every retail or manufacturing organization. I will provide very basic example so someone can grasp some understanding.
Understanding the Transportation Problem
The transportation problem revolves around determining the most cost-effective distribution plan that meets all supply and demand constraints. Imagine we have a set of warehouses (suppliers) and a set of stores (consumers). Each warehouse has a certain supply capacity, each store has a demand that must be met, and there are costs associated with transporting goods from each warehouse to each store. The goal is to minimize the total transportation cost.
Installation: To solve this with PuLP, we first need to install the library if you haven’t
pip install pulp
Define constants: Now, let’s dive into a simple example. Suppose we have three warehouses or factories, A and B, C with inventories of 100, 300, and 150 units, respectively. We need to supply three stores, V, W, X, Y, and Z, with demands of 190, 40, 180, 160, and 60 units, respectively. The transportation costs per unit are given in a cost matrix.
# Creates a list of all the supply nodes
Warehouses = ["A", "B", "C"]
# Creates a dictionary for the number of units of supply for each supply node
supply = {"A": 100, "B": 500, "C": 150}
# Creates a list of all demand nodes
stores = ["V", "W", "X", "Y", "Z"]
# Creates a dictionary for the number of units of demand for each demand node
demand = {
"V": 190,
"W": 40,
"X": 180,
"Y": 160,
"Z": 60,
}
# Costs matrix as provided
costs = [
# V W X Y Z (Stores)
[2, 4, 2, 2, 5], # A (Warehouse)
[3, 2, 1, 3, 4], # B
[1, 5, 3, 2, 2], # C
]
Converts the cost matrix into a dictionary for easier manipulation in PuLP. The makeDict utility function is used to map the costs matrix to the corresponding warehouses and stores, setting a default value of 0 for any missing entries.
# Convert costs to a dictionary
costs = makeDict([Warehouses, stores], costs, 0)
Initializes the optimization problem in PuLP. “LpMinimize” indicates that the goal is to minimize the total transportation cost.
# Creates the 'prob' variable to contain the problem data
prob = LpProblem("Transportation Problem", LpMinimize)
Generates a list of tuples representing all possible routes for transportation, combining each warehouse with each store.
Routes = [(w, s) for w in Warehouses for s in stores]
Creates a dictionary of decision variables representing the quantity of products to be transported along each route. These variables are integer (LpInteger) and must be greater than or equal to 0.
vars = LpVariable.dicts("Route", (Warehouses, stores), 0, None, LpInteger)
Defines the objective function of the problem, which is to minimize the total transportation cost. This is achieved by summing up the product of the quantity transported along each route and its corresponding cost.
# The objective function is added to 'prob' first
prob += lpSum([vars[w][s] * costs[w][s] for (w, s) in Routes]), "Sum of Transporting Costs"
Adds supply constraints for each warehouse, ensuring that the total quantity transported from a warehouse does not exceed its supply capacity.
# Supply maximum constraints are added to 'prob' for each supply node
for w in Warehouses:
prob += lpSum([vars[w][s] for s in stores]) <= supply[w], f"Sum of Products out of Warehouse {w}"
Adds demand constraints for each store, ensuring that the total quantity transported to a store meets or exceeds its demand requirement.
# Demand minimum constraints are added to 'prob' for each demand node
for s in stores:
prob += lpSum([vars[w][s] for w in Warehouses]) >= demand[s], f"Sum of Products into Store {s}"
Solve the optimization problem using PuLP’s default solver.
prob.solve()
Prints the status of the solution (Optimal, Infeasible, Unbounded, etc.).
# The problem is solved using PuLP's choice of Solver
prob.solve()
Iterates through each decision variable (route) and prints its optimal value (quantity to be transported) if the problem was solved successfully.
for v in prob.variables():
print(v.name, "=", v.varValue)
Prints the total minimum cost of transportation calculated by the solver.
# The optimized objective function value is printed to the screen
print("Total Cost of Transportation = ", value(prob.objective))
Done!
In conclusion, leveraging PuLP in Python to solve the transportation optimization problem offers a powerful and flexible approach to navigating the complexities of supply chain logistics. Through the thoughtful structuring of cost matrices and the careful application of linear programming principles, businesses can significantly reduce their transportation costs while ensuring that supply and demand constraints are met.