LogiTech’s main distribution center is organized into two key areas: a packaging area located at coordinates (0, 0) and a loading dock located at coordinates (800, 600) (distances are in meters). The company must determine where to locate a sorting facility and a storage facility to minimize the daily cost of moving goods through the center. All movement within the distribution center is restricted to either east–west or north–south directions.  

The number of daily trips between the areas are as follows:  

| From/To          | Packaging Area | Loading Dock | Sorting Facility | Storage Facility |  
|-------------------|----------------|--------------|-------------------|------------------|  
| Packaging Area    | -              | 20           | 15                | 10               |  
| Loading Dock      | 20             | -            | 25                | 30               |  
| Sorting Facility  | 15             | 25           | -                 | 40               |  
| Storage Facility  | 10             | 30           | 40                | -                |  

The cost of moving goods is $0.50 per meter per trip. The sorting facility and storage facility must be located within the boundaries of the distribution center, which spans from (0, 0) to (1000, 1000). And any two areas among the four areas are at least 100 units apart.

How should LogiTech locate the sorting and storage facilities to minimize daily transportation costs?

Problem type: LP (Toy: 5 variables, 3 constraints, 5 nonzeros)

Mathematical model:
Set:
1. Area
The set of areas, \( A = \{1, 2, 3, 4\} \)  # 1: Packaging Area, 2: Loading Dock, 3: Sorting Facility, 4: Storage Facility

Parameter:
1. PackagingAreaCoordinates
(0, 0)  # coordinates of the Packaging Area
2. LoadingDockCoordinates
(800, 600)  # coordinates of the Loading Dock
3. DistanceBetweenPackagingAndLoading
1400
4. DailyTrips
[[0, 20, 15, 10], [20, 0, 25, 30], [15, 25, 0, 40], [10, 30, 40, 0]]  # daily trips between areas
5. CostPerMeterPerTrip
0.50  # cost of moving goods per meter per trip
6. DistributionCenterBoundaries
(0, 0, 800, 600)  # boundaries of the distribution center (x_min, y_min, x_max, y_max)
7. MinimalDistance
100  # minimal distance between two areas in meters

Decision variable:
1. DistanceBetweenSortingAndStorage
Continuous variable, \( DistanceBetweenSortingAndStorage \), the distance between the Sorting Facility and Storage Facility in meters.
2. DistanceBetweenSortingAndPackaging
Continuous variable, \( DistanceBetweenSortingAndPackaging \), the distance between the Sorting Facility and Packaging Area in meters.
3. DistanceBetweenSortingAndLoading
Continuous variable, \( DistanceBetweenSortingAndLoading \), the distance between the Sorting Facility and Loading Dock in meters.
4. DistanceBetweenStorageAndPackaging
Continuous variable, \( DistanceBetweenStorageAndPackaging \), the distance between the Storage Facility and Packaging Area in meters.
5. DistanceBetweenStorageAndLoading
Continuous variable, \( DistanceBetweenStorageAndLoading \), the distance between the Storage Facility and Loading Dock in meters.

Objective:
1. Minimize the total daily transportation cost.
min: [(DailyTrips[1][2]+DailyTrips[2][1])*DistanceBetweenPackagingAndLoading + 
       (DailyTrips[1][3]+DailyTrips[3][1])*DistanceBetweenSortingAndPackaging + 
       (DailyTrips[1][4]+DailyTrips[4][1])*DistanceBetweenStorageAndPackaging + 
       (DailyTrips[2][3]+DailyTrips[3][2])*DistanceBetweenSortingAndLoading + 
       (DailyTrips[2][4]+DailyTrips[4][2])*DistanceBetweenStorageAndLoading + 
       (DailyTrips[3][4]+DailyTrips[4][3])*DistanceBetweenSortingAndStorage] * CostPerMeterPerTrip

Constraint:
1. DistanceRelationships
DistanceBetweenSortingAndLoading + DistanceBetweenSortingAndPackaging == DistanceBetweenPackagingAndLoading
DistanceBetweenStorageAndPackaging + DistanceBetweenStorageAndLoading == DistanceBetweenPackagingAndLoading
DistanceBetweenSortingAndStorage <= DistanceBetweenPackagingAndLoading
2. MinimalDistanceConstraint
DistanceBetweenSortingAndStorage >= MinimalDistance
DistanceBetweenSortingAndLoading >= MinimalDistance
DistanceBetweenSortingAndPackaging >= MinimalDistance
DistanceBetweenStorageAndLoading >= MinimalDistance
DistanceBetweenStorageAndPackaging >= MinimalDistance

Type:
Continuous, Linear, Linear
LP

Gurobi implementation:
import gurobipy as gp
from gurobipy import GRB


def solve_logistics_optimization(
    distance_packaging_loading=1400.0,
    daily_trips=[
        [0, 20, 15, 10],
        [20, 0, 25, 30],
        [15, 25, 0, 40],
        [10, 30, 40, 0]
    ],
    cost_per_meter_per_trip=0.50,
    minimal_distance=100.0
):
    """
    Solves the logistics optimization problem.
    """
    try:
        # --- Create a new model ---
        model = gp.Model("LogisticsOptimization")

        # --- Decision Variables ---
        d_S_St = model.addVar(lb=minimal_distance, name="DistanceBetweenSortingAndStorage")
        d_S_Pk = model.addVar(lb=minimal_distance, name="DistanceBetweenSortingAndPackaging")
        d_S_L = model.addVar(lb=minimal_distance, name="DistanceBetweenSortingAndLoading")
        d_St_Pk = model.addVar(lb=minimal_distance, name="DistanceBetweenStorageAndPackaging")
        d_St_L = model.addVar(lb=minimal_distance, name="DistanceBetweenStorageAndLoading")

        # --- Objective Function ---
        cost_Pk_L = (daily_trips[0][1] + daily_trips[1][0]) * distance_packaging_loading
        cost_Pk_S = (daily_trips[0][2] + daily_trips[2][0]) * d_S_Pk
        cost_Pk_St = (daily_trips[0][3] + daily_trips[3][0]) * d_St_Pk
        cost_L_S = (daily_trips[1][2] + daily_trips[2][1]) * d_S_L
        cost_L_St = (daily_trips[1][3] + daily_trips[3][1]) * d_St_L
        cost_S_St = (daily_trips[2][3] + daily_trips[3][2]) * d_S_St
        total_transport_cost_before_factor = cost_Pk_L + cost_Pk_S + cost_Pk_St + cost_L_S + cost_L_St + cost_S_St
        model.setObjective(total_transport_cost_before_factor * cost_per_meter_per_trip, GRB.MINIMIZE)

        # --- Constraints ---
        model.addConstr(d_S_L + d_S_Pk == distance_packaging_loading, "Collinearity_Sorting")
        model.addConstr(d_St_Pk + d_St_L == distance_packaging_loading, "Collinearity_Storage")
        model.addConstr(d_S_St <= distance_packaging_loading, "MaxDist_SortingStorage")

        # --- Optimize model ---
        model.optimize()

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

    except gp.GurobiError as e:
        return {"status": f"Gurobi error: {e}"}
    except Exception as e:
        return {"status": f"An unexpected error occurred: {e}"}


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


Solution status: OPTIMAL
Optimal objective value: 70000