Minimizing Transportation Costs While Maintaining Service Levels
Supply chain optimization is one of the most impactful applications of operations research. In this post, we’ll tackle a classic but realistic problem: how to design a distribution network that minimizes transportation costs while guaranteeing customers receive their goods within acceptable timeframes.
The Problem Setup
Imagine a consumer goods company with:
- 3 factories (supply nodes) producing goods
- 5 warehouses (intermediate hubs)
- 8 customer zones (demand nodes)
We need to decide:
- How much to ship from each factory to each warehouse
- How much to ship from each warehouse to each customer zone
- Which warehouses to open (fixed costs apply)
Subject to:
- Each factory has a production capacity
- Each customer zone has a demand that must be fully met
- Each warehouse has a throughput capacity
- Each customer zone must be served by a warehouse within a maximum distance (service level constraint)
Mathematical Formulation
Let:
- $F$ = set of factories, $W$ = set of warehouses, $C$ = set of customer zones
- $x_{fw}$ = units shipped from factory $f$ to warehouse $w$
- $y_{wc}$ = units shipped from warehouse $w$ to customer zone $c$
- $z_w \in {0,1}$ = 1 if warehouse $w$ is open, 0 otherwise
Objective — minimize total cost:
$$\min \sum_{f \in F}\sum_{w \in W} c^{FW}{fw} x{fw} + \sum_{w \in W}\sum_{c \in C} c^{WC}{wc} y{wc} + \sum_{w \in W} f_w z_w$$
Subject to:
Supply capacity at each factory:
$$\sum_{w \in W} x_{fw} \leq S_f \quad \forall f \in F$$
Demand satisfaction at each customer zone:
$$\sum_{w \in W} y_{wc} = D_c \quad \forall c \in C$$
Flow balance at each warehouse:
$$\sum_{f \in F} x_{fw} = \sum_{c \in C} y_{wc} \quad \forall w \in W$$
Warehouse capacity:
$$\sum_{c \in C} y_{wc} \leq K_w \cdot z_w \quad \forall w \in W$$
Service level constraint — each customer must be reachable within $d_{max}$ km:
$$\sum_{w \in W: d_{wc} \leq d_{max}} y_{wc} \geq D_c \quad \forall c \in C$$
Non-negativity and binary:
$$x_{fw}, y_{wc} \geq 0, \quad z_w \in {0,1}$$
Full Python Source Code
1 | # ============================================================ |
Code Walkthrough
Section 1 — Network Data
We define 3 factories, 5 warehouses, and 8 customers placed on a 500×500 km grid. Geographic coordinates are realistic enough that distances (computed with scipy.spatial.distance.cdist) vary meaningfully across links. Transportation costs scale linearly with distance — a common real-world assumption — with a higher rate on the last-mile warehouse-to-customer leg ($1.20/unit/km vs $0.80/unit/km), reflecting the higher cost of smaller, more frequent deliveries.
Section 2 — MILP Formulation with PuLP
The core of the solution is a Mixed-Integer Linear Program (MILP). PuLP is used as the modeling layer on top of the open-source CBC solver (bundled with PuLP, no extra installation needed).
The binary variable $z_w$ is the key to making this a facility location problem rather than just a flow problem. The warehouse capacity constraint y[w][c] <= wh_cap[w] * z[w] is a big-M formulation: if $z_w = 0$, no flow is permitted through warehouse $w$; if $z_w = 1$, up to wh_cap[w] units can pass through.
The service level constraint is enforced cleanly: for each customer–warehouse pair where the distance exceeds d_max, the corresponding flow variable is forced to zero. This avoids ever violating the service radius, even if it would be cheaper.
Section 3 — Solving and Reporting
CBC is called with timeLimit=120 seconds — more than sufficient for this problem size (it typically solves in under 1 second). Results are extracted with value(), then decoded into human-readable factory→warehouse and warehouse→customer flow tables.
Section 4 — Visualizations
Figure 1 — Network Map: A 2D geographic map showing all nodes and optimal flows. Arrow thickness scales with flow volume. Dashed service radius circles show the d_max coverage area for each open warehouse.
Figure 2 — Cost Dashboard: Left panel: total cost split into three components (factory–WH transport, WH–customer transport, fixed warehouse opening costs). Right panel: per-unit last-mile cost for each customer, revealing which zones are expensive to serve.
Figure 3 — 3D Cost Surfaces: Two 3D surface plots show the unit cost landscape across all possible factory→warehouse and warehouse→customer links. Black dots mark which links are actually used in the optimal solution — letting you see instantly whether the optimizer chose the cheapest available routes.
Figure 4 — Service Level Heatmap: A color-coded matrix of all warehouse–customer distances. Dark overlaid cells are out of range (blocked by the service constraint). White text labels show the optimal flow on each used link, making it easy to verify that no out-of-range link carries any flow.
Key Takeaways
- Not all warehouses need to open. The optimizer balances fixed opening costs against the transportation savings from having more intermediate hubs. Opening too many warehouses increases fixed costs; too few drives up transport costs and risks violating service levels.
- Service level constraints genuinely bind. When
d_maxis tight, certain warehouse–customer combinations are forbidden, and the optimizer must find feasible routes that respect geography — sometimes at higher cost. - The 3D cost landscape immediately shows why some routes are never chosen: they sit on peaks in the surface, far from the valleys of cheap, nearby links.
- Last-mile costs dominate. The higher $/unit/km rate for WH→customer delivery means the system strongly prefers placing open warehouses close to high-demand customer zones.
=======================================================
Status : Optimal
Total Cost : $578,029.62
=======================================================
Open warehouses: ['W0', 'W2', 'W4']
Transportation cost : $533,029.62
Fixed (warehouse) : $45,000.00
Total : $578,029.62
Factory → Warehouse flows (units):
F0 → W0 : 800 units (dist=158 km)
F2 → W2 : 700 units (dist=178 km)
F2 → W4 : 270 units (dist=326 km)
Warehouse → Customer flows (units):
W0 → C0 : 200 units (dist=82 km)
W0 → C1 : 250 units (dist=82 km)
W0 → C2 : 180 units (dist=234 km)
W0 → C3 : 20 units (dist=234 km)
W0 → C5 : 150 units (dist=240 km)
W2 → C3 : 200 units (dist=104 km)
W2 → C4 : 230 units (dist=100 km)
W2 → C7 : 270 units (dist=136 km)
W4 → C4 : 70 units (dist=124 km)
W4 → C6 : 200 units (dist=45 km)



