End-to-End Optimization Workflows
The instance attributes exposed by s2mflow integrate seamlessly into algebraic modeling languages and solvers. By utilizing unified, tuple-keyed dictionary structures, you can map the generated multicommodity data directly to solver variables and constraints.
We have provided complete, runnable workflows in the examples/ directory of the repository. Below are the primary integration workflows.
1. Data Generation & Serialization Pipeline
Before executing optimization routines, you must generate the multicommodity benchmark data. This workflow demonstrates how to load a standard DIMACS single-commodity network, apply a generation strategy (such as the heterogeneous Spread method with added capacity/cost noise), and serialize the output to disk for reproducible experimentation.
import s2mflow
# 1. Load a baseline single-commodity network (DIMACS .min format)
network = s2mflow.load_min_instance("data/input.min")
# 2. Lift into the multicommodity space
# Using the 'Spread' strategy with randomized noise bounds
mc_data = s2mflow.generate_multi_commodity_data(
instance=network,
num_commodities=4,
is_uniform=False, # Activates stochastic Spread partitioning
randomize_caps=True,
cap_a=0.6,
cap_b=1.0,
randomize_costs=True,
cost_a=0.5,
cost_b=2.0,
seed=42,
)
# 3. Serialize to disk for reproducible benchmarking
output_filename = "data/spread_full.mcfmin"
s2mflow.save_multi_commodity_instance(output_filename, network, mc_data)
print(f"Successfully generated and saved to {output_filename}")
# 4. Verify serialization by loading the instance back into memory
loaded_mc_data = s2mflow.load_multi_commodity_instance(output_filename)
print(f"Loaded {loaded_mc_data.num_commodities} commodities from disk.")
2. File-Based Parsing with Pyomo & HiGHS (Open-Source)
This workflow loads a serialized .mcfmin file from disk, builds a mathematical program using the Pyomo algebraic modeling language, and solves it using the open-source HiGHS solver.
import pyomo.environ as pyo
import s2mflow
# 1. Load multi-commodity benchmark instance
mc = s2mflow.load_multi_commodity_instance("data/spread_full.mcfmin")
# 2. Initialize Model
model = pyo.ConcreteModel("MCMCF")
# 3. Decision Variables: 0 <= x_e^k <= u_e^k
def commodity_edge_bounds(m, k, u, v):
upper_bound = mc.commodity_capacities[(u, v)][k]
return (0.0, float(upper_bound))
model.flow = pyo.Var(
mc.commodity_edges,
domain=pyo.NonNegativeReals,
bounds=commodity_edge_bounds,
name="x"
)
# 4. Objective: Minimize total routing cost
model.obj = pyo.Objective(
expr=sum(
model.flow[k, u, v] * mc.commodity_weights[(u, v)][k]
for (k, u, v) in mc.commodity_edges
),
sense=pyo.minimize
)
# 5. Shared Mutual Capacity Constraints
model.shared_caps = pyo.ConstraintList()
for i, (u, v) in enumerate(mc.edges):
model.shared_caps.add(
sum(model.flow[k, u, v] for k in range(mc.num_commodities)) <= mc.capacities[i]
)
# 6. Flow Conservation Constraints
model.flow_balance = pyo.ConstraintList()
incoming, outgoing = s2mflow.get_adjacency_mapping(mc.nodes, mc.edges)
for k in range(mc.num_commodities):
for node in mc.nodes:
in_flow = sum(model.flow[k, u, node] for u in incoming.get(node, []))
out_flow = sum(model.flow[k, node, v] for v in outgoing.get(node, []))
demand = mc.commodity_supply_demand_data[node][k] if node in mc.commodity_supply_demand_data else 0.0
model.flow_balance.add(out_flow - in_flow == demand)
# 7. Solve
solver = pyo.SolverFactory("highs")
results = solver.solve(model, tee=True)
print(f"[+] Optimal Objective Value: {pyo.value(model.obj)}")
3. In-Memory Generation with Gurobi (Commercial Solver)
For researchers solving large-scale programs, s2mflow interfaces smoothly with the gurobipy API for the commercial solver Gurobi. This example bypasses file serialization entirely, generating the multicommodity partitions dynamically in-memory and passing them directly to the solver matrix.
(Note: Executing this script for large instances requires a valid Gurobi license).
import gurobipy as grb
import s2mflow
# 1. Load baseline and generate data in-memory
net = s2mflow.load_min_instance("data/input.min")
mc_data = s2mflow.generate_multi_commodity_data(net, num_commodities=3, is_uniform=False, seed=512)
# 2. Initialize Gurobi Model
model = grb.Model("MCMCF")
# 3. Decision Variables
upper_bounds = [mc_data.commodity_capacities[(u, v)][k] for (k, u, v) in mc_data.commodity_edges]
flow = model.addVars(
mc_data.commodity_edges,
lb=0.0,
ub=upper_bounds,
vtype=grb.GRB.CONTINUOUS,
name="x"
)
# 4. Objective Function
model.setObjective(
grb.quicksum(flow[k, u, v] * mc_data.commodity_weights[(u, v)][k] for (k, u, v) in mc_data.commodity_edges),
sense=grb.GRB.MINIMIZE
)
# 5. Shared Mutual Capacity Constraints
model.addConstrs(
(
grb.quicksum(flow[k, u, v] for k in range(mc_data.num_commodities)) <= net.capacities[i]
for i, (u, v) in enumerate(net.arcs)
), name="Shared_Cap"
)
# 6. Flow Conservation Constraints
incoming, outgoing = s2mflow.get_adjacency_mapping(net.nodes, net.arcs)
for k in range(mc_data.num_commodities):
for node in net.nodes:
in_flow = grb.quicksum(flow[k, u, node] for u in incoming.get(node, []))
out_flow = grb.quicksum(flow[k, node, v] for v in outgoing.get(node, []))
demand = mc_data.supply_partition[node][k] if node in mc_data.supply_partition else 0.0
model.addConstr(
out_flow - in_flow == demand,
name=f"balance_{k}_{node}"
)
# 7. Solve
model.optimize()
if model.Status == grb.GRB.OPTIMAL:
print(f"[+] Optimal Objective Value: {model.ObjVal}")