Install
$ agentstack add skill-kishorkukreja-awesome-supply-chain-hub-location-problem ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Hub Location Problem
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.
Initial Assessment
Before solving hub location problems, understand:
- Network Type
- Hub-and-spoke network? (consolidation through hubs)
- How many hubs to locate? (p-hub problem)
- Hub covering problem? (coverage requirements)
- Hub hierarchy? (multi-level hubs)
- Allocation Type
- Single allocation? (each node assigned to exactly one hub)
- Multiple allocation? (nodes can connect to multiple hubs)
- r-allocation? (each node connects to at most r hubs)
- Flow Characteristics
- Origin-destination (O-D) flow matrix?
- Demand between all pairs?
- Flow volumes/patterns?
- Directionality (symmetric or asymmetric)?
- Hub Characteristics
- Hub capacity constraints?
- Fixed costs to establish hubs?
- Hub processing/handling costs?
- Economies of scale at hubs?
- Discount factor (α) for inter-hub connections?
- Network Objectives
- Minimize total transportation cost?
- Minimize maximum travel time? (p-hub center)
- Minimize number of hubs?
- Balance hub loads?
Hub Location Problem Framework
Hub-and-Spoke Network Concept
Structure:
Origin → Access Hub → Hub-to-Hub → Egress Hub → Destination
Collection | Consolidation | Distribution
Leg | Leg | Leg
Key Features:
- Consolidation: Flows between O-D pairs routed through hubs
- Economies of Scale: Inter-hub transport cheaper (discount factor α)
- Hub Facilities: Special facilities with sorting/consolidation capability
- Reduced Connections: Not all nodes directly connected
Applications:
- Airline passenger networks
- Cargo/freight networks
- Postal/package delivery
- Telecommunications networks
- Supply chain distribution
- Public transportation
Problem Classification
1. p-Hub Median Problem (pHMP)
Description:
- Locate exactly p hubs
- Minimize total transportation cost
- Most common hub location variant
Variants:
- Single Allocation (SA-pHMP): Each non-hub node connected to exactly one hub
- Multiple Allocation (MA-pHMP): Non-hub nodes can connect to multiple hubs
Characteristics:
- Fixed number of hubs (p)
- Cost minimization
- Network efficiency focus
2. Hub Covering Problem
Description:
- Cover all O-D pairs within distance/time threshold
- Minimize number of hubs needed
Applications:
- Emergency service networks
- Time-sensitive delivery
- Service quality guarantees
3. p-Hub Center Problem
Description:
- Minimize maximum O-D distance/cost
- Equity-focused objective
- Ensure no O-D pair has excessive cost
Applications:
- Emergency response
- Equal service quality
- Fair network design
4. Hub Arc Location Problem
Description:
- Decide which inter-hub connections to establish
- Not all hubs necessarily connected
- Trade-off connectivity vs. cost
5. Hierarchical Hub Location
Description:
- Multiple levels of hubs (regional, national, international)
- Flows cascade through hierarchy
- Complex multi-tier networks
Mathematical Formulations
Single Allocation p-Hub Median Problem (SA-pHMP)
Sets:
- N = {1, ..., n}: Set of nodes (potential hubs and non-hubs)
Parameters:
- w_{ij}: Flow from node i to node j
- c_{ij}: Unit cost from node i to node j
- α: Inter-hub discount factor (typically 0.2 - 0.8)
- p: Number of hubs to locate
Decision Variables:
- y_k ∈ {0,1}: 1 if node k is a hub, 0 otherwise
- x_{ik} ∈ {0,1}: 1 if node i is allocated to hub k, 0 otherwise
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
Multiple Allocation p-Hub Median Problem (MA-pHMP)
Modified Variables:
- x_{ikm} ∈ [0,1]: Fraction of flow from i to j through hubs k and m
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
Exact Solution Methods
1. Single Allocation p-Hub Median (PuLP)
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 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}")
2. Multiple Allocation p-Hub Median
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] 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
Greedy Heuristics
1. Greedy Hub Selection
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 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'
}
Local Search Improvements
1. Hub Swap Local Search
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_
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [kishorkukreja](https://github.com/kishorkukreja)
- **Source:** [kishorkukreja/awesome-supply-chain](https://github.com/kishorkukreja/awesome-supply-chain)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.