skills/hub-location-problem/SKILL.md
When the user wants to solve hub location problems, design hub-and-spoke networks, optimize hub placement for logistics networks. Also use when the user mentions "hub location," "hub-and-spoke," "p-hub median," "hub covering," "single allocation hub," "multiple allocation hub," "airline hub network," "postal hub network," or "consolidation center location." For general facility location, see facility-location-problem. For distribution centers, see distribution-center-network.
npx skillsauth add kishorkukreja/awesome-supply-chain hub-location-problemInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are an expert in hub location problems and hub-and-spoke network design. Your goal is to help design efficient hub-and-spoke networks where flows between origin-destination pairs are consolidated through hub facilities, minimizing total transportation and routing costs.
Before solving hub location problems, understand:
Network Type
Allocation Type
Flow Characteristics
Hub Characteristics
Network Objectives
Structure:
Origin → Access Hub → Hub-to-Hub → Egress Hub → Destination
Collection | Consolidation | Distribution
Leg | Leg | Leg
Key Features:
Applications:
Description:
Variants:
Characteristics:
Description:
Applications:
Description:
Applications:
Description:
Description:
Sets:
Parameters:
Decision Variables:
Cost Structure:
For flow from i to j allocated to hubs k and m:
- Collection: c_{ik} (origin to hub)
- Transfer: α × c_{km} (hub to hub, discounted)
- Distribution: c_{mj} (hub to destination)
Total: c_{ik} + α × c_{km} + c_{mj}
Objective Function:
Minimize: Σ_i Σ_j Σ_k Σ_m w_{ij} × (c_{ik} + α×c_{km} + c_{mj}) × x_{ik} × x_{jm}
Constraints:
1. Each node allocated to exactly one hub:
Σ_k x_{ik} = 1, ∀i ∈ N
2. Exactly p hubs located:
Σ_k y_k = p
3. Allocation only to hubs:
x_{ik} ≤ y_k, ∀i ∈ N, ∀k ∈ N
4. Hubs allocated to themselves:
x_{kk} = y_k, ∀k ∈ N
5. Binary variables:
y_k ∈ {0,1}, ∀k ∈ N
x_{ik} ∈ {0,1}, ∀i,k ∈ N
Complexity: NP-hard
Modified Variables:
Objective:
Minimize: Σ_i Σ_j Σ_k Σ_m w_{ij} × (c_{ik} + α×c_{km} + c_{mj}) × x_{ikm}
Key Constraints:
1. Flow conservation:
Σ_k Σ_m x_{ikm} = 1, ∀i,j ∈ N
2. Exactly p hubs:
Σ_k y_k = p
3. Flow only through hubs:
x_{ikm} ≤ y_k, ∀i,j,k,m
x_{ikm} ≤ y_m, ∀i,j,k,m
from pulp import *
import numpy as np
def solve_single_allocation_phub(flows, costs, p, alpha=0.75):
"""
Solve Single Allocation p-Hub Median Problem
Args:
flows: n x n matrix of flows between O-D pairs
costs: n x n matrix of unit transportation costs
p: number of hubs to locate
alpha: inter-hub discount factor (0 < α < 1)
Returns:
optimal hub location and allocation solution
"""
n = len(flows)
# Create problem
prob = LpProblem("Single_Allocation_pHub_Median", LpMinimize)
# Decision variables
# y[k] = 1 if node k is a hub
y = LpVariable.dicts("hub", range(n), cat='Binary')
# x[i,k] = 1 if node i is allocated to hub k
x = LpVariable.dicts("allocation",
[(i, k) for i in range(n) for k in range(n)],
cat='Binary')
# Objective: Minimize total weighted transportation cost
# Cost for flow from i to j through hubs k and m:
# c_ik + alpha*c_km + c_mj
objective = []
for i in range(n):
for j in range(n):
if i == j or flows[i][j] == 0:
continue
for k in range(n):
for m in range(n):
cost_ij_via_km = (costs[i][k] +
alpha * costs[k][m] +
costs[m][j])
objective.append(
flows[i][j] * cost_ij_via_km * x[i,k] * x[j,m]
)
prob += lpSum(objective), "Total_Transportation_Cost"
# Constraints
# 1. Each node allocated to exactly one hub
for i in range(n):
prob += (
lpSum([x[i,k] for k in range(n)]) == 1,
f"Allocation_{i}"
)
# 2. Exactly p hubs located
prob += (
lpSum([y[k] for k in range(n)]) == p,
"p_Hubs"
)
# 3. Can only allocate to hubs
for i in range(n):
for k in range(n):
prob += (
x[i,k] <= y[k],
f"Allocation_to_Hub_{i}_{k}"
)
# 4. Hubs must be allocated to themselves
for k in range(n):
prob += (
x[k,k] == y[k],
f"Hub_Self_Allocation_{k}"
)
# Solve
import time
start_time = time.time()
prob.solve(PULP_CBC_CMD(msg=1, timeLimit=600))
solve_time = time.time() - start_time
# Extract solution
if LpStatus[prob.status] in ['Optimal', 'Feasible']:
hubs = [k for k in range(n) if y[k].varValue > 0.5]
allocations = {}
for i in range(n):
for k in range(n):
if x[i,k].varValue > 0.5:
allocations[i] = k
break
# Calculate flows through each hub
hub_inflow = {k: 0 for k in hubs}
hub_outflow = {k: 0 for k in hubs}
for i in range(n):
for j in range(n):
if i != j and flows[i][j] > 0:
hub_i = allocations[i]
hub_j = allocations[j]
hub_outflow[hub_i] += flows[i][j]
hub_inflow[hub_j] += flows[i][j]
return {
'status': LpStatus[prob.status],
'total_cost': value(prob.objective),
'hubs': hubs,
'num_hubs': len(hubs),
'allocations': allocations,
'hub_inflow': hub_inflow,
'hub_outflow': hub_outflow,
'solve_time': solve_time,
'alpha': alpha
}
else:
return {
'status': LpStatus[prob.status],
'solve_time': solve_time
}
# Example usage
if __name__ == "__main__":
np.random.seed(42)
# 10-node network
n = 10
# Generate random coordinates
coords = np.random.rand(n, 2) * 100
# Calculate Euclidean distance matrix
costs = np.zeros((n, n))
for i in range(n):
for j in range(n):
costs[i][j] = np.linalg.norm(coords[i] - coords[j])
# Generate flow matrix (random demands)
flows = np.random.uniform(10, 100, (n, n))
np.fill_diagonal(flows, 0) # No self-flow
# Parameters
p = 3 # Number of hubs
alpha = 0.75 # Inter-hub discount factor
print(f"{'='*70}")
print(f"SINGLE ALLOCATION p-HUB MEDIAN PROBLEM")
print(f"{'='*70}")
print(f"Nodes: {n}")
print(f"Hubs to locate: {p}")
print(f"Inter-hub discount factor: {alpha}")
print(f"Total O-D flow: {flows.sum():.2f}")
result = solve_single_allocation_phub(flows, costs, p, alpha)
print(f"\n{'='*70}")
print(f"SOLUTION")
print(f"{'='*70}")
print(f"Status: {result['status']}")
print(f"Total Cost: {result['total_cost']:,.2f}")
print(f"Hubs Located: {result['hubs']}")
print(f"Solve Time: {result['solve_time']:.2f} seconds")
print(f"\nNode Allocations:")
for node, hub in result['allocations'].items():
hub_indicator = " (HUB)" if node in result['hubs'] else ""
print(f" Node {node} → Hub {hub}{hub_indicator}")
print(f"\nHub Traffic:")
for hub in result['hubs']:
print(f" Hub {hub}:")
print(f" Inflow: {result['hub_inflow'][hub]:.2f}")
print(f" Outflow: {result['hub_outflow'][hub]:.2f}")
print(f" Total: {result['hub_inflow'][hub] + result['hub_outflow'][hub]:.2f}")
def solve_multiple_allocation_phub(flows, costs, p, alpha=0.75):
"""
Solve Multiple Allocation p-Hub Median Problem
Allows each node to send/receive flows through multiple hubs
Args:
flows: n x n flow matrix
costs: n x n cost matrix
p: number of hubs
alpha: inter-hub discount factor
Returns:
optimal solution
"""
n = len(flows)
prob = LpProblem("Multiple_Allocation_pHub_Median", LpMinimize)
# Decision variables
y = LpVariable.dicts("hub", range(n), cat='Binary')
# x[i,j,k,m] = fraction of flow from i to j through hubs k and m
x = {}
for i in range(n):
for j in range(n):
if i != j and flows[i][j] > 0:
for k in range(n):
for m in range(n):
x[i,j,k,m] = LpVariable(f"flow_{i}_{j}_{k}_{m}",
lowBound=0, upBound=1,
cat='Continuous')
# Objective: Minimize total cost
objective = []
for i in range(n):
for j in range(n):
if i != j and flows[i][j] > 0:
for k in range(n):
for m in range(n):
cost_via_km = (costs[i][k] +
alpha * costs[k][m] +
costs[m][j])
objective.append(
flows[i][j] * cost_via_km * x[i,j,k,m]
)
prob += lpSum(objective), "Total_Cost"
# Constraints
# 1. Flow conservation: all flow from i to j routed through some hub pair
for i in range(n):
for j in range(n):
if i != j and flows[i][j] > 0:
prob += (
lpSum([x[i,j,k,m] for k in range(n) for m in range(n)]) == 1,
f"Flow_{i}_{j}"
)
# 2. Exactly p hubs
prob += (
lpSum([y[k] for k in range(n)]) == p,
"p_Hubs"
)
# 3. Flow only through open hubs
for i in range(n):
for j in range(n):
if i != j and flows[i][j] > 0:
for k in range(n):
for m in range(n):
prob += (
x[i,j,k,m] <= y[k],
f"Hub_k_{i}_{j}_{k}_{m}"
)
prob += (
x[i,j,k,m] <= y[m],
f"Hub_m_{i}_{j}_{k}_{m}"
)
# Solve
start_time = time.time()
prob.solve(PULP_CBC_CMD(msg=1, timeLimit=600))
solve_time = time.time() - start_time
if LpStatus[prob.status] in ['Optimal', 'Feasible']:
hubs = [k for k in range(n) if y[k].varValue > 0.5]
# Extract flow routing
flow_routing = {}
for i in range(n):
for j in range(n):
if i != j and flows[i][j] > 0:
flow_routing[i,j] = []
for k in range(n):
for m in range(n):
if x[i,j,k,m].varValue > 0.01:
flow_routing[i,j].append({
'via_hubs': (k, m),
'fraction': x[i,j,k,m].varValue,
'flow': flows[i][j] * x[i,j,k,m].varValue
})
return {
'status': LpStatus[prob.status],
'total_cost': value(prob.objective),
'hubs': hubs,
'flow_routing': flow_routing,
'solve_time': solve_time,
'alpha': alpha
}
else:
return {'status': LpStatus[prob.status]}
# Example usage
result_ma = solve_multiple_allocation_phub(flows, costs, p=3, alpha=0.75)
print(f"\n{'='*70}")
print(f"MULTIPLE ALLOCATION p-HUB MEDIAN SOLUTION")
print(f"{'='*70}")
print(f"Status: {result_ma['status']}")
print(f"Total Cost: {result_ma['total_cost']:,.2f}")
print(f"Hubs: {result_ma['hubs']}")
print(f"\nSample Flow Routing (first 5 O-D pairs):")
count = 0
for (i, j), routing in result_ma['flow_routing'].items():
if count >= 5:
break
print(f" Flow {i}→{j} (total={flows[i][j]:.2f}):")
for route in routing:
via_k, via_m = route['via_hubs']
print(f" via hubs ({via_k}, {via_m}): "
f"{route['fraction']*100:.1f}% ({route['flow']:.2f} units)")
count += 1
def greedy_hub_selection(flows, costs, p, alpha=0.75):
"""
Greedy heuristic for p-Hub Median
Iteratively select hub that gives maximum cost reduction
Args:
flows: flow matrix
costs: cost matrix
p: number of hubs
alpha: inter-hub discount
Returns:
heuristic solution
"""
n = len(flows)
hubs = []
allocations = {}
def calculate_cost(current_hubs):
"""Calculate total cost for given hub set"""
if not current_hubs:
return float('inf')
# Allocate each node to nearest hub
node_allocations = {}
for i in range(n):
nearest_hub = min(current_hubs, key=lambda k: costs[i][k])
node_allocations[i] = nearest_hub
# Calculate total cost
total_cost = 0
for i in range(n):
for j in range(n):
if i != j and flows[i][j] > 0:
hub_i = node_allocations[i]
hub_j = node_allocations[j]
cost = (costs[i][hub_i] +
alpha * costs[hub_i][hub_j] +
costs[hub_j][j])
total_cost += flows[i][j] * cost
return total_cost, node_allocations
# Greedily select p hubs
for iteration in range(p):
best_hub = None
best_cost = float('inf')
best_allocations = None
# Try adding each remaining node as a hub
for k in range(n):
if k in hubs:
continue
test_hubs = hubs + [k]
cost, allocations_test = calculate_cost(test_hubs)
if cost < best_cost:
best_cost = cost
best_hub = k
best_allocations = allocations_test
if best_hub is not None:
hubs.append(best_hub)
allocations = best_allocations
return {
'hubs': hubs,
'allocations': allocations,
'total_cost': best_cost,
'method': 'Greedy Hub Selection'
}
def concentration_heuristic(flows, costs, p, alpha=0.75):
"""
Concentration-based heuristic for hub location
Select nodes with highest total flow (originating + terminating)
Args:
flows: flow matrix
costs: cost matrix
p: number of hubs
alpha: inter-hub discount
Returns:
heuristic solution
"""
n = len(flows)
# Calculate total flow for each node
node_flows = []
for i in range(n):
total_flow = flows[i, :].sum() + flows[:, i].sum()
node_flows.append((total_flow, i))
# Sort by flow (descending)
node_flows.sort(reverse=True)
# Select top p nodes as hubs
hubs = [node for _, node in node_flows[:p]]
# Allocate each node to nearest hub
allocations = {}
for i in range(n):
nearest_hub = min(hubs, key=lambda k: costs[i][k])
allocations[i] = nearest_hub
# Calculate total cost
total_cost = 0
for i in range(n):
for j in range(n):
if i != j and flows[i][j] > 0:
hub_i = allocations[i]
hub_j = allocations[j]
cost = (costs[i][hub_i] +
alpha * costs[hub_i][hub_j] +
costs[hub_j][j])
total_cost += flows[i][j] * cost
return {
'hubs': hubs,
'allocations': allocations,
'total_cost': total_cost,
'method': 'Concentration Heuristic'
}
def hub_swap_local_search(flows, costs, initial_hubs, alpha=0.75,
max_iterations=100):
"""
Local search with hub swap moves
Swap a hub with a non-hub if it improves objective
Args:
flows: flow matrix
costs: cost matrix
initial_hubs: initial hub locations
alpha: inter-hub discount
max_iterations: maximum iterations
Returns:
improved solution
"""
n = len(flows)
current_hubs = set(initial_hubs)
non_hubs = set(range(n)) - current_hubs
def evaluate_solution(hub_set):
"""Evaluate cost for given hub configuration"""
# Allocate nodes
allocations = {}
for i in range(n):
allocations[i] = min(hub_set, key=lambda k: costs[i][k])
# Calculate cost
total_cost = 0
for i in range(n):
for j in range(n):
if i != j and flows[i][j] > 0:
hub_i = allocations[i]
hub_j = allocations[j]
cost = (costs[i][hub_i] +
alpha * costs[hub_i][hub_j] +
costs[hub_j][j])
total_cost += flows[i][j] * cost
return total_cost, allocations
current_cost, current_allocations = evaluate_solution(current_hubs)
best_hubs = current_hubs.copy()
best_cost = current_cost
best_allocations = current_allocations
for iteration in range(max_iterations):
improved = False
# Try all possible swaps
for hub_out in list(current_hubs):
for hub_in in non_hubs:
# Test swap
test_hubs = (current_hubs - {hub_out}) | {hub_in}
test_cost, test_allocations = evaluate_solution(test_hubs)
if test_cost < best_cost - 1e-6:
best_cost = test_cost
best_hubs = test_hubs
best_allocations = test_allocations
improved = True
break
if improved:
break
if not improved:
break
# Update current solution
current_hubs = best_hubs.copy()
non_hubs = set(range(n)) - current_hubs
return {
'hubs': list(best_hubs),
'allocations': best_allocations,
'total_cost': best_cost,
'method': 'Hub Swap Local Search'
}
import random
import math
def simulated_annealing_hub(flows, costs, p, alpha=0.75,
initial_temp=1000, cooling_rate=0.95,
max_iterations=5000):
"""
Simulated Annealing for p-Hub Median
Args:
flows: flow matrix
costs: cost matrix
p: number of hubs
alpha: inter-hub discount
initial_temp: starting temperature
cooling_rate: cooling factor
max_iterations: max iterations
Returns:
best solution found
"""
n = len(flows)
def evaluate(hub_set):
"""Calculate cost for hub configuration"""
allocations = {}
for i in range(n):
allocations[i] = min(hub_set, key=lambda k: costs[i][k])
total_cost = 0
for i in range(n):
for j in range(n):
if i != j and flows[i][j] > 0:
hub_i = allocations[i]
hub_j = allocations[j]
cost = (costs[i][hub_i] +
alpha * costs[hub_i][hub_j] +
costs[hub_j][j])
total_cost += flows[i][j] * cost
return total_cost, allocations
# Initial solution: random hubs
current_hubs = set(random.sample(range(n), p))
current_cost, current_allocations = evaluate(current_hubs)
best_hubs = current_hubs.copy()
best_cost = current_cost
best_allocations = current_allocations
temperature = initial_temp
for iteration in range(max_iterations):
# Generate neighbor: swap one hub
non_hubs = list(set(range(n)) - current_hubs)
hub_out = random.choice(list(current_hubs))
hub_in = random.choice(non_hubs)
neighbor_hubs = (current_hubs - {hub_out}) | {hub_in}
neighbor_cost, neighbor_allocations = evaluate(neighbor_hubs)
delta = neighbor_cost - current_cost
# Accept or reject
if delta < 0 or random.random() < math.exp(-delta / temperature):
current_hubs = neighbor_hubs
current_cost = neighbor_cost
current_allocations = neighbor_allocations
if current_cost < best_cost:
best_hubs = current_hubs.copy()
best_cost = current_cost
best_allocations = current_allocations
# Cool down
temperature *= cooling_rate
if temperature < 0.1:
break
return {
'hubs': list(best_hubs),
'allocations': best_allocations,
'total_cost': best_cost,
'method': 'Simulated Annealing'
}
class HubLocationSolver:
"""
Comprehensive Hub Location Problem Solver
Supports single/multiple allocation, various solution methods
"""
def __init__(self):
self.flows = None
self.costs = None
self.coords = None
self.n = None
def load_problem(self, flows, costs, coords=None):
"""
Load problem data
Args:
flows: n x n flow matrix
costs: n x n cost matrix
coords: optional node coordinates for visualization
"""
self.flows = np.array(flows)
self.costs = np.array(costs)
self.coords = np.array(coords) if coords is not None else None
self.n = len(flows)
print(f"Loaded hub location problem:")
print(f" Nodes: {self.n}")
print(f" Total O-D flow: {self.flows.sum():.2f}")
print(f" Average cost: {self.costs.mean():.2f}")
def solve_exact_sa(self, p, alpha=0.75, time_limit=600):
"""Solve single allocation with exact MIP"""
print(f"\nSolving Single Allocation p-Hub Median (exact)...")
return solve_single_allocation_phub(self.flows, self.costs, p, alpha)
def solve_exact_ma(self, p, alpha=0.75, time_limit=600):
"""Solve multiple allocation with exact MIP"""
print(f"\nSolving Multiple Allocation p-Hub Median (exact)...")
return solve_multiple_allocation_phub(self.flows, self.costs, p, alpha)
def solve_heuristic(self, method, p, alpha=0.75):
"""
Solve with heuristic method
Args:
method: 'greedy', 'concentration', 'sa' (simulated annealing)
p: number of hubs
alpha: inter-hub discount
Returns:
heuristic solution
"""
print(f"\nSolving with {method} heuristic...")
if method == 'greedy':
return greedy_hub_selection(self.flows, self.costs, p, alpha)
elif method == 'concentration':
return concentration_heuristic(self.flows, self.costs, p, alpha)
elif method == 'sa':
return simulated_annealing_hub(self.flows, self.costs, p, alpha)
else:
raise ValueError(f"Unknown method: {method}")
def compare_methods(self, p, alpha=0.75,
methods=['greedy', 'concentration', 'sa', 'exact_sa']):
"""
Compare multiple solution methods
Args:
p: number of hubs
alpha: inter-hub discount
methods: list of methods to compare
Returns:
comparison dataframe
"""
import pandas as pd
import time
results = []
for method in methods:
start_time = time.time()
try:
if method == 'exact_sa':
solution = self.solve_exact_sa(p, alpha)
elif method == 'exact_ma':
solution = self.solve_exact_ma(p, alpha)
else:
solution = self.solve_heuristic(method, p, alpha)
solve_time = time.time() - start_time
results.append({
'Method': method,
'Total Cost': solution['total_cost'],
'Hubs': str(solution['hubs']),
'Time (s)': f"{solve_time:.3f}"
})
except Exception as e:
print(f" Error with {method}: {e}")
df = pd.DataFrame(results)
# Calculate gap from best
if len(df) > 0:
best_cost = df['Total Cost'].min()
df['Gap %'] = ((df['Total Cost'] - best_cost) / best_cost * 100).round(2)
return df
def visualize_hub_network(self, solution, title="Hub Network"):
"""
Visualize hub-and-spoke network
Args:
solution: solution dictionary with hubs and allocations
title: plot title
"""
import matplotlib.pyplot as plt
if self.coords is None:
print("No coordinates available for visualization")
return
plt.figure(figsize=(12, 8))
hubs = solution['hubs']
allocations = solution['allocations']
# Plot non-hub nodes (small blue circles)
for i in range(self.n):
if i not in hubs:
plt.plot(self.coords[i, 0], self.coords[i, 1],
'o', color='lightblue', markersize=8)
# Plot connections (spoke lines)
for node, hub in allocations.items():
if node not in hubs:
plt.plot([self.coords[node, 0], self.coords[hub, 0]],
[self.coords[node, 1], self.coords[hub, 1]],
'k-', alpha=0.2, linewidth=0.5)
# Plot inter-hub connections (thick red lines)
for i, hub_i in enumerate(hubs):
for hub_j in hubs[i+1:]:
plt.plot([self.coords[hub_i, 0], self.coords[hub_j, 0]],
[self.coords[hub_i, 1], self.coords[hub_j, 1]],
'r-', alpha=0.6, linewidth=2)
# Plot hub nodes (large red squares)
for hub in hubs:
plt.plot(self.coords[hub, 0], self.coords[hub, 1],
's', color='red', markersize=15, label='Hub' if hub == hubs[0] else '')
plt.xlabel('X Coordinate')
plt.ylabel('Y Coordinate')
plt.title(title)
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# Complete example
if __name__ == "__main__":
print("="*70)
print("HUB LOCATION PROBLEM - COMPREHENSIVE EXAMPLE")
print("="*70)
# Generate problem data
np.random.seed(42)
n = 15
# Node coordinates
coords = np.random.rand(n, 2) * 100
# Cost matrix (Euclidean distances)
costs = np.zeros((n, n))
for i in range(n):
for j in range(n):
costs[i][j] = np.linalg.norm(coords[i] - coords[j])
# Flow matrix (random demands)
flows = np.random.uniform(10, 100, (n, n))
np.fill_diagonal(flows, 0)
# Parameters
p = 3 # Number of hubs
alpha = 0.75 # Inter-hub discount (25% discount on hub-to-hub travel)
# Create solver
solver = HubLocationSolver()
solver.load_problem(flows, costs, coords)
# Compare methods
print(f"\n{'='*70}")
print(f"COMPARING SOLUTION METHODS (p={p}, α={alpha})")
print(f"{'='*70}")
comparison = solver.compare_methods(p, alpha,
methods=['greedy', 'concentration', 'sa'])
print("\n" + comparison.to_string(index=False))
# Solve with best method and visualize
print(f"\n{'='*70}")
print(f"DETAILED SOLUTION")
print(f"{'='*70}")
solution = solver.solve_heuristic('greedy', p, alpha)
print(f"\nHubs Located: {solution['hubs']}")
print(f"Total Cost: {solution['total_cost']:,.2f}")
print(f"\nNode Allocations:")
for node, hub in solution['allocations'].items():
hub_indicator = " (HUB)" if node in solution['hubs'] else ""
distance = costs[node][hub]
print(f" Node {node} → Hub {hub}{hub_indicator} (distance={distance:.2f})")
# Visualize
solver.visualize_hub_network(solution,
title=f"Hub Network: {solution['method']} (p={p}, α={alpha})")
Optimization:
Network Analysis:
Visualization:
Problem:
Solutions:
Problem:
Solutions:
Problem:
Solutions:
Problem:
Solutions:
Problem Instance:
Optimal Solution:
| Metric | Value | |--------|-------| | Total Cost | $1,247,385 | | Solution Method | MIP Optimal | | Hubs Located | {5, 12, 18, 23} | | Solution Time | 45.3 seconds | | Status | Optimal |
Hub Details:
| Hub ID | Location | Nodes Allocated | Total Inflow | Total Outflow | |--------|----------|-----------------|--------------|---------------| | 5 | (34.5, 67.2) | 7 | 3,240 | 3,180 | | 12 | (78.3, 23.8) | 6 | 2,890 | 2,950 | | 18 | (12.7, 89.1) | 5 | 2,450 | 2,520 | | 23 | (56.4, 45.3) | 7 | 3,870 | 3,800 |
Cost Breakdown:
Network Statistics:
documentation
When the user wants to optimize yard operations, manage trailer parking, or improve dock door utilization. Also use when the user mentions "yard management," "trailer tracking," "yard jockey," "drop trailer program," "trailer pool," "dock scheduling," or "gate management." For cross-dock operations, see cross-docking. For warehouse design, see warehouse-design.
tools
When the user wants to optimize workforce scheduling, create shift plans, or balance labor demand. Also use when the user mentions "staff scheduling," "labor planning," "shift optimization," "crew scheduling," "roster optimization," or "employee scheduling." For task assignment, see task-assignment-problem. For wave planning labor, see wave-planning-optimization.
testing
When the user wants to optimize pick wave planning, schedule warehouse operations, or improve order fulfillment efficiency. Also use when the user mentions "wave management," "batch picking," "pick wave scheduling," "order release optimization," "wave design," or "pick wave strategy." For order batching, see order-batching-optimization. For workforce scheduling, see workforce-scheduling.
testing
When the user wants to optimize warehouse slot assignments, improve pick efficiency, or design warehouse layouts. Also use when the user mentions "slotting optimization," "slot assignment," "ABC slotting," "pick path optimization," "storage location assignment," "warehouse layout optimization," or "forward pick locations." For picker routing, see picker-routing-optimization. For warehouse design, see warehouse-design.