Applied Optimization with Julia
University of Hamburg
Question: What are current trends in e-commerce?
After this lecture, you will be able to:
Question: What is a split order?

Question: Why might they occur?
Question: What are the consequences?
Question: What are possible mitigations?

Question: What could be our objective?
We aim to improve the SKU1-warehouse allocation to minimize the number of split parcels resulting from SKUs being stored in different warehouses.
Question: What could be the sets here?
Question: What are possible parameters?
Question: What could the transactional data look like?
| \(t_{m,i}\) | A | B | C | D |
|---|---|---|---|---|
| 1 | 1 | 1 | 1 | 0 |
| 2 | 1 | 1 | 1 | 0 |
| 3 | 1 | 1 | 0 | 0 |
| 4 | 1 | 0 | 0 | 1 |
| 5 | 1 | 0 | 0 | 1 |
| 6 | 1 | 0 | 0 | 1 |
| 7 | 1 | 0 | 0 | 1 |
| 8 | 0 | 0 | 1 | 1 |
Question: What is your opinion on the assumption?
Question: What could be our decision variable/s?
We have the following sets:
Question: Does anyone have an idea what this could mean?
Variables explode with the instance size
10,000 SKUs, 1,000,000 orders and 2 warehouses already lead to \(2 \times 10^{10}\) binary variables \(Y_{m,i,k}\)!
Any idea what
could be done?
Different view on the problem
Focus on the warehouses and the coappearance of SKUs! Discard the exact information about the customer orders.

Question: What could be the objective?
Maximize the coappearance of products that are often part of the same customer orders.
Question: What do the principal diagonal values tell us?
Question: What could be the sets?
No customer order information is needed!
We can focus on the SKUs and the warehouses, making the problem much smaller!
Question: What are possible parameters?
Transactional Data replaced
Instead of the transactional data, we just use the coappearance matrix in our model!
We have the following sets:
Our objective is to:
Maximize the coappearance of products that are often part of the same customer orders. In more mathematical terms: Maximize the sum of all unique pair-wise values \(q_{i,j}\) of all SKUs stored in the same warehouse.
Question: What could be our decision variable/s?
Only one variable per SKU and warehouse!
As we don’t need the customer order information, we only need to make a decision for each SKU and warehouse pair!
Question: How could we formulate the variable in Julia?
2-dimensional DenseAxisArray{JuMP.VariableRef,2,...} with index sets:
Dimension 1, ["A", "B", "C", "D"]
Dimension 2, ["Hamburg", "Berlin"]
And data, a 4×2 Matrix{JuMP.VariableRef}:
X[A,Hamburg] X[A,Berlin]
X[B,Hamburg] X[B,Berlin]
X[C,Hamburg] X[C,Berlin]
X[D,Hamburg] X[D,Berlin]
We need the following:
Our objective is to:
Maximize the sum of all unique pair-wise values \(q_{i,j}\) of all SKUs stored in the same warehouse. Note, that this is a quadratic objective function!
Question: What could the objective function look like?
\[\text{Maximize} \quad \sum_{i=2}^{|\mathcal{I}|} \sum_{j=1}^{i-1} \sum_{k \in \mathcal{K}} X_{i,k}\times X_{j,k} \times q_{i,j}\]
This is a quadratic objective function!
The quadratic terms are \(X_{i,k}\times X_{j,k}\). This objective function is based on the Quadratic Multiple Knapsack Problem (QMKP), formulated by Hiley and Julstrom (2006).
Question: How could we formulate this in Julia?
3 X[B,Hamburg]*X[A,Hamburg] + 3 X[B,Berlin]*X[A,Berlin] + 2 X[C,Hamburg]*X[A,Hamburg] + 2 X[C,Berlin]*X[A,Berlin] + 2 X[C,Hamburg]*X[B,Hamburg] + 2 X[C,Berlin]*X[B,Berlin] + 4 X[D,Hamburg]*X[A,Hamburg] + 4 X[D,Berlin]*X[A,Berlin] + X[D,Hamburg]*X[C,Hamburg] + X[D,Berlin]*X[C,Berlin]

Question: What constraints?
The goal of this constraint is to:
Ensure that each SKU is allocated at least once.
We need the following variable:
Question: What could the constraint look like?
\[\sum_{k \in \mathcal{K}} X_{i,k} \geq 1 \quad \forall i \in \mathcal{I}\]
Remember, this is the variable:
Question: How could we change the constraint to ensure that each SKU is allocated only once?
Question: How could we add the constraint in Julia?
1-dimensional DenseAxisArray{JuMP.ConstraintRef{JuMP.Model, MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.GreaterThan{Float64}}, JuMP.ScalarShape},1,...} with index sets:
Dimension 1, ["A", "B", "C", "D"]
And data, a 4-element Vector{JuMP.ConstraintRef{JuMP.Model, MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.GreaterThan{Float64}}, JuMP.ScalarShape}}:
single_allocation[A] : X[A,Hamburg] + X[A,Berlin] ≥ 1
single_allocation[B] : X[B,Hamburg] + X[B,Berlin] ≥ 1
single_allocation[C] : X[C,Hamburg] + X[C,Berlin] ≥ 1
single_allocation[D] : X[D,Hamburg] + X[D,Berlin] ≥ 1
The goal of these constraints is to:
Ensure that the capacity of each warehouse is not exceeded.
We need the following variables and parameters:
Question: What could the second constraint be?
\[\sum_{i \in \mathcal{I}} X_{i,k} \leq c_k \quad \forall k \in \mathcal{K}\]
And that’s basically it!
Question: How could we add the second constraint in Julia?
1-dimensional DenseAxisArray{JuMP.ConstraintRef{JuMP.Model, MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.LessThan{Float64}}, JuMP.ScalarShape},1,...} with index sets:
Dimension 1, ["Hamburg", "Berlin"]
And data, a 2-element Vector{JuMP.ConstraintRef{JuMP.Model, MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.LessThan{Float64}}, JuMP.ScalarShape}}:
capacity[Hamburg] : X[A,Hamburg] + X[B,Hamburg] + X[C,Hamburg] + X[D,Hamburg] ≤ 2
capacity[Berlin] : X[A,Berlin] + X[B,Berlin] + X[C,Berlin] + X[D,Berlin] ≤ 2
\[\text{Maximize} \quad \sum_{i=2}^{|\mathcal{I}|} \sum_{j=1}^{i-1} \sum_{k \in \mathcal{K}} X_{i,k}\times X_{j,k} \times q_{i,j}\]
subject to:
\[ \begin{align*} & \sum_{k \in \mathcal{K}} X_{i,k} \geq 1 && \forall i \in \mathcal{I}\\ & \sum_{i \in \mathcal{I}} X_{i,k} \leq c_{k} && \forall k \in \mathcal{K}\\ & X_{i,k} \in \{0,1\} && \forall i \in \mathcal{I}, k \in \mathcal{K} \end{align*} \]
The optimal objective value is: 6.0
The optimal solution is: 2-dimensional DenseAxisArray{Float64,2,...} with index sets:
Dimension 1, ["A", "B", "C", "D"]
Dimension 2, ["Hamburg", "Berlin"]
And data, a 4×2 Matrix{Float64}:
1.0 0.0
0.0 1.0
0.0 1.0
1.0 0.0
Question: What does this value tell us?
The objective value is 6
It sums the coappearances of SKUs sharing a warehouse: \(q_{A,D} + q_{B,C} = 4 + 2 = 6\). It does not directly count the avoided split orders!
Commercial Solvers
Commercial solvers are faster and more robust than open source solvers but also more expensive. During your studies, you can use most of them for free though! Nonetheless, we will only use open source solvers in this course.
Mixed-Integer Quadratic Program (MIQP)
Our model is a MIQP, as only the objective is quadratic. It is not a MIQCP, which would also allow quadratic constraints. MIQCP solvers, e.g. SCIP, subsume MIQPs and can thus solve our model.
| SKUs | SCIP | HiGHS | Gurobi |
|---|---|---|---|
| 100 | 118s | X | ~400s |
| 1,000 | 1,011s (18%) | X | ~200s (2%) |
| 10,000 | X | X | X |
Questions: On model assumptions
Duplicates can inflate the objective
With slack capacity, the model may store one SKU in several warehouses. A shared pair then counts once per warehouse, raising the objective without avoiding any further splits.
Allocation changes:
Workload imbalance:
Can this be
applied?
CHI-Heuristic
Detect dependencies between products and allocate them accordingly, as products within orders can have dependencies and products are bought with different frequencies!
Question: When are two SKUs independent?
If purchasing one doesn’t affect purchasing the other:
\[P(A \cap B) = P(A) \times P(B)\]
Example: If \(A\) appears in 10% of orders and \(B\) in 20%:
Use chi-square tests to detect if SKUs are dependent:
Two-Phase Approach
Phase 1: Build allocation using dependencies
Phase 2: Refine with local search
Result: Works on large instances, 50,000 SKUs in 10 minutes!
Question: What challenges might arise in practice?
In the following case study (Vlćek and Voigt 2024), we:
In the case study, SKUs were grouped by category-brand combinations to reduce the problem size while maintaining allocation quality. New SKUs inherit the allocation of their cluster!
Problem characteristics:
Heuristics compared1:
Retailer’s status quo: 6.95% of all orders were split
Theoretical reduction of these splits:
And that’s it for today’s lecture!
We now have covered the Quadratic Multiple Knapsack Problem and are ready to start solving some tasks in the upcoming tutorial.
Questions?
References for this lecture:
For more interesting literature to learn more about Julia, take a look at the literature list of this course.
Lecture VI - Minimizing Split Orders | Dr. Tobias Vlćek | Home