A national e-commerce company operates three distribution centers in the Metro region. Each center currently stores different quantities of standard and premium products as shown below:

| Distribution Center | Standard Products | Premium Products |
|---------------------|-------------------|------------------|
| Center 1            | 150               | 30               |
| Center 2            | 250               | 100              |
| Center 3            | 300               | 70               |

The company plans to open three retail fulfillment hubs that will serve customers with same-day delivery. Corporate policy requires that each fulfillment hub must maintain exactly 300 products in total inventory, and each hub must stock the same number of premium products to ensure consistent customer experience across all locations. The shipping distances (in kilometers) between distribution centers and fulfillment hubs are shown as follows:

| From / To           | Hub 1 | Hub 2 | Hub 3 |
|---------------------|-------|-------|-------|
| Center 1            | 0     | 12    | 18    |
| Center 2            | 12    | 0     | 15    |
| Center 3            | 18    | 15    | 0     |

What is the minimum total transportation distance required to move the products under the company's inventory requirements?

Problem type: ILP (Small: 18 variables, 21 constraints, 36 nonzeros)

Mathematical model:
Set:
1. DistributionCenter
The set of distribution centers, \( D = \{1, 2, ..., DistributionCenterNum\} \)
2. FulfillmentHub
The set of fulfillment hubs, \( H = \{1, 2, ..., FulfillmentHubNum\} \)
3. ProductType
The set of product types, \( P = \{1, 2, ..., ProductTypeNum\} \) 

Parameter:
1. DistributionCenterNum
3
2. FulfillmentHubNum
3
3. ProductTypeNum
2
4. HubInventoryRequirement
300
5. StoreStandardProducts
[150, 250, 300]
6. StorePremiumProducts
[30, 100, 70]
7. ShippingDistance
[
    [0, 12, 18],
    [12, 0, 15],
    [18, 15, 0]
]

Decision variable:
1. ShipAmount
Integer variable, \( ShipAmount_{d,h,p} \forall d \in D, h \in H, p \in P \), where \( ShipAmount_{d,h,p} \) is the amount of product type \( p \) shipped from distribution center \( d \) to fulfillment hub \( h \).

Objective:
1. Minimize the total transportation distance required to move the products.
min: \sum_{d \in D} \sum_{h \in H} \sum_{p \in P} (ShippingDistance[d][h] * ShipAmount[d][h][p])

Constraint:
1. Fulfillment hub inventory constraint.
HubInventoryRequirement = \sum_{d \in D} \sum_{p \in P} ShipAmount[d][h][p] \forall h \in H
2. Product type balance constraint.
ShipAmount[d][h][1] \leq StoreStandardProducts[d] \forall d \in D, h \in H


Type:
Integer, Linear, Linear
ILP

Gurobi implementation:
import gurobipy as gp
from gurobipy import GRB


def solve_product_distribution(
    HubInventoryRequirement=300,
    StoreStandardProducts=[150, 250, 300],
    StorePremiumProducts=[30, 100, 70],
    ShippingDistance=[[0, 12, 18], [12, 0, 15], [18, 15, 0]]):
    """
    Solves the product distribution optimization problem.
    """
    # Create a new model
    model = gp.Model("Product Distribution Optimization")

    # Parameters and Sets
    DistributionCenterNum = len(StoreStandardProducts)
    FulfillmentHubNum = len(ShippingDistance)
    ProductTypeNum = 2  # 0 for standard, 1 for premium

    DCs = range(DistributionCenterNum)
    Hubs = range(FulfillmentHubNum)
    Products = range(ProductTypeNum)

    # Decision Variables
    ShipAmount = model.addVars(DCs,
                               Hubs,
                               Products,
                               vtype=GRB.INTEGER,
                               name="ShipAmount")

    # Objective: Minimize total transportation distance
    model.setObjective(
        gp.quicksum(ShippingDistance[d][h] * ShipAmount[d, h, p]
                    for d in DCs for h in Hubs for p in Products),
        GRB.MINIMIZE)

    # Constraint 1: Fulfillment hub inventory constraint
    model.addConstrs(
        (gp.quicksum(ShipAmount[d, h, p] for d in DCs for p in Products) ==
         HubInventoryRequirement for h in Hubs),
        name="Hub_Inventory")

    # Constraint 2: Product type balance constraint (standard products)
    # This constraint limits the amount shipped on each individual path
    # based on the total stock at the distribution center.
    model.addConstrs(
        (ShipAmount[d, h, 0] <= StoreStandardProducts[d] for d in DCs
         for h in Hubs),
        name="Standard_Product_Limit")

    # Constraint 3: Product type balance constraint (premium products)
    model.addConstrs(
        (ShipAmount[d, h, 1] <= StorePremiumProducts[d] for d in DCs
         for h in Hubs),
        name="Premium_Product_Limit")

    # Optimize the model
    model.optimize()

    # Return results
    if model.status == GRB.OPTIMAL:
        return {"status": "optimal", "obj": model.ObjVal}
    else:
        return {"status": f"{model.status}"}


if __name__ == "__main__":
    result = solve_product_distribution()
    print(result)

Solution status: OPTIMAL
Optimal objective value: 1440