基于采购量的成本最小化:Pyomo中添加供应商依赖型运费问题
To model the freight costs that depend on your total purchase from each supplier, we'll use binary variables and big-M constraints to capture the step-function logic. Here's how to modify your existing code:
Step 1: Add Binary Variables
We'll create binary variables for each supplier to indicate whether we're in the freight-paying region (1 if total cost ≤ threshold, 0 otherwise):
model.y_A = Var(within=Binary) # 1 if total from A ≤100, else 0 model.y_B = Var(within=Binary) # 1 if total from B ≤150, else 0
Step 2: Calculate Total Purchase Cost per Supplier
Define expressions to track the total cost spent at each supplier:
# Total cost from supplier A model.total_A = Expression(expr=sum(p[n, 'A'] * model.x[n, 'A'] for n in N)) # Total cost from supplier B model.total_B = Expression(expr=sum(p[n, 'B'] * model.x[n, 'B'] for n in N))
Step 3: Add Constraints to Link Binary Variables to Total Cost
We use "big-M" constraints to enforce that the binary variable correctly reflects whether we're above or below the freight threshold. Choose a large M (bigger than the maximum possible total cost from either supplier) and a small epsilon to handle continuous variables:
M = 1000 # Larger than max possible total from A (790) or B (710) epsilon = 0.001 # Small enough to avoid overlap between threshold regions # Supplier A constraints def constraint_A1(model): # If y_A=1, total_A ≤100; if y_A=0, total_A can be up to 100+M (no restriction) return model.total_A <= 100 + M * (1 - model.y_A) model.constraint_A1 = Constraint(rule=constraint_A1) def constraint_A2(model): # If y_A=0, total_A ≥100+epsilon (so >100); if y_A=1, this is automatically satisfied return model.total_A >= 100 + epsilon * (1 - model.y_A) model.constraint_A2 = Constraint(rule=constraint_A2) # Supplier B constraints def constraint_B1(model): return model.total_B <= 150 + M * (1 - model.y_B) model.constraint_B1 = Constraint(rule=constraint_B1) def constraint_B2(model): return model.total_B >= 150 + epsilon * (1 - model.y_B) model.constraint_B2 = Constraint(rule=constraint_B2)
Step 4: Update the Objective Function to Include Freight Costs
Modify your objective to add the freight costs based on the binary variables:
def obj_rule(model): purchase_cost = sum(p[n,m] * model.x[n,m] for n in N for m in M) freight_cost = 10 * model.y_A + 8 * model.y_B return purchase_cost + freight_cost model.obj = Objective(rule=obj_rule)
Full Modified Code
Putting it all together, your code will look like this:
from pyomo.environ import ConcreteModel, Var, Objective, Constraint, SolverFactory, Binary, Expression model = ConcreteModel(name="(MN_2)") # products N = ['prod1', 'prod2', 'prod3'] # suppliers M = ['A', 'B'] # price p = {('prod1', 'A'): 10, ('prod2', 'A'): 9, ('prod3', 'A'): 50, ('prod1', 'B'): 16, ('prod2', 'B'): 20, ('prod3', 'B'): 35} # user quantity constraint q_u = {('prod1', 'A'): 2, ('prod2', 'A'): 1, ('prod3', 'A'): 1, ('prod1', 'B'): 1, ('prod2', 'B'): 1, ('prod3', 'B'): 1} # seller quantity constraint q_s = {('prod1', 'A'): 20, ('prod2', 'A'): 10, ('prod3', 'A'): 10, ('prod1', 'B'): 10, ('prod2', 'B'): 10, ('prod3', 'B'): 10} # Quantity of product n bought from supplier m model.x = Var(N, M, bounds=(0,10)) # Binary variables for freight thresholds model.y_A = Var(within=Binary) model.y_B = Var(within=Binary) # Total cost expressions per supplier model.total_A = Expression(expr=sum(p[n, 'A'] * model.x[n, 'A'] for n in N)) model.total_B = Expression(expr=sum(p[n, 'B'] * model.x[n, 'B'] for n in N)) # Objective function including purchase and freight costs def obj_rule(model): purchase_cost = sum(p[n,m] * model.x[n,m] for n in N for m in M) freight_cost = 10 * model.y_A + 8 * model.y_B return purchase_cost + freight_cost model.obj = Objective(rule=obj_rule) # User minimum quantity constraints def user_quantity(model, n, m): return model.x[n,m] >= q_u[n,m] model.user_quantity = Constraint(N, M, rule=user_quantity) # Seller maximum quantity constraints def seller_quantity(model, n, m): return model.x[n,m] <= q_s[n,m] model.seller_quantity = Constraint(N, M, rule=seller_quantity) # Freight threshold constraints M = 1000 epsilon = 0.001 def constraint_A1(model): return model.total_A <= 100 + M * (1 - model.y_A) model.constraint_A1 = Constraint(rule=constraint_A1) def constraint_A2(model): return model.total_A >= 100 + epsilon * (1 - model.y_A) model.constraint_A2 = Constraint(rule=constraint_A2) def constraint_B1(model): return model.total_B <= 150 + M * (1 - model.y_B) model.constraint_B1 = Constraint(rule=constraint_B1) def constraint_B2(model): return model.total_B >= 150 + epsilon * (1 - model.y_B) model.constraint_B2 = Constraint(rule=constraint_B2) # Solve the model solver = SolverFactory('glpk') solver.solve(model) # Print results print("Purchase Quantities:") model.x.pprint() print("\nFreight Indicators (1 = pay freight, 0 = no freight):") print(f"y_A: {model.y_A.value}") print(f"y_B: {model.y_B.value}") print("\nTotal Purchase Cost:", sum(p[n,m]*model.x[n,m].value for n in N for m in M)) print("Total Freight Cost:", 10*model.y_A.value +8*model.y_B.value) print("Total Cost:", model.obj())
How It Works
- The binary variables
y_Aandy_Bact as switches: they're 1 when your total purchase from the supplier is below the threshold (so you pay freight), and 0 when you're above (no freight). - The big-M constraints ensure the binary variables correctly align with the total cost: if
y_Ais 1, your total from A can't exceed 100; ify_Ais 0, your total from A must be over 100. - GLPK (your chosen solver) handles mixed-integer linear programming, so it can optimize both the continuous purchase quantities and binary freight switches.
内容的提问来源于stack exchange,提问作者Malcolm

