如何调整Pyomo模型实现按需JIT采购(含最小采购/库存限制)
Pyomo实现JIT采购的约束修正方案
问题背景
使用Pyomo计算准时制(JIT)采购时,现有模型可正常运行,但存在以下问题:
- 原模型强制每月采购,导致库存持续积压(最终月末库存达16),不符合按需采购的JIT核心要求
- 尝试用Python的
if函数添加"按需采购"逻辑时,触发错误:Cannot convert non-constant Pyomo expression (%s) to bool
需要满足的核心约束:
- 若当月采购,采购量不得低于5
- 每月月末库存不得低于3
原模型代码
model=ConcreteModel(name='Inventory-MIN') demand=[4,3,2,1,2,3,4] months=len(demand) holding_cost=1 purchase_cost=2 model.purchase_qty=Var(range(months), domain=NonNegativeIntegers) # Not NonNegativeReals # ending quantity of inventory per month model.end_inventory=Var(range(months), domain=NonNegativeIntegers) model.cost=Objective(expr=sum( model.purchase_qty[month]* # Purchase quantity purchase_cost+ # Purchase price model.end_inventory[month]* # previous inventory holding_cost # Holding cost for month in range(1,months,1)), sense=minimize) # start at 1, stop at months, iterate by 1 model.constraints=ConstraintList() # empty list of constraints model.constraints.add(model.end_inventory[0] == 0) for month in range(1,months,1): next_end_inventory = model.end_inventory[month-1]+model.purchase_qty[month]-demand[month] model.constraints.add(model.end_inventory[month] == next_end_inventory) # here is where we define a minimum inventory level model.constraints.add(model.end_inventory[month]>=3) # must buy in bulk model.constraints.add(model.purchase_qty[month]>=5) # none of the months have ZERO ORDER !??!?!?!? # Solve the model SolverFactory('glpk',executable='/usr/bin/glpsol').solve(model).write()
原模型求解结果
# How much we purchase every month purchase_qty : Size=7, Index=purchase_qty_index Key : Lower : Value : Upper : Fixed : Stale : Domain 0 : 0 : None : None : False : True : NonNegativeIntegers 1 : 0 : 6.0 : None : False : False : NonNegativeIntegers 2 : 0 : 5.0 : None : False : False : NonNegativeIntegers 3 : 0 : 5.0 : None : False : False : NonNegativeIntegers 4 : 0 : 5.0 : None : False : False : NonNegativeIntegers 5 : 0 : 5.0 : None : False : False : NonNegativeIntegers 6 : 0 : 5.0 : None : False : False : NonNegativeIntegers # # Inventory at end of month end_inventory : Size=7, Index=end_inventory_index Key : Lower : Value : Upper : Fixed : Stale : Domain 0 : 0 : 0.0 : None : False : False : NonNegativeIntegers 1 : 0 : 3.0 : None : False : False : NonNegativeIntegers 2 : 0 : 6.0 : None : False : False : NonNegativeIntegers 3 : 0 : 10.0 : None : False : False : NonNegativeIntegers 4 : 0 : 13.0 : None : False : False : NonNegativeIntegers 5 : 0 : 15.0 : None : False : False : NonNegativeIntegers 6 : 0 : 16.0 : None : False : False : NonNegativeIntegers
解决方案
问题根源
原模型直接添加purchase_qty[month]>=5,强制每月必须采购至少5个单位,导致库存持续累积。而Pyomo不支持直接用Python的if语句处理变量表达式(变量在求解前是未知的,无法转为布尔值),需使用Big-M方法实现"要么不采购,要么采购量≥5"的逻辑约束。
修改步骤与完整代码
from pyomo.environ import ConcreteModel, Var, Objective, ConstraintList, NonNegativeIntegers, Binary, SolverFactory model=ConcreteModel(name='Inventory-JIT') demand=[4,3,2,1,2,3,4] months=len(demand) holding_cost=1 purchase_cost=2 # 定义足够大的M值,确保覆盖最大可能采购量(总需求+安全库存) M = sum(demand) + 10 # 采购量变量 model.purchase_qty=Var(range(months), domain=NonNegativeIntegers) # 月末库存变量 model.end_inventory=Var(range(months), domain=NonNegativeIntegers) # 采购标记变量:1表示当月采购,0表示不采购 model.buy = Var(range(months), domain=Binary) # 目标函数:最小化采购成本+库存持有成本 model.cost=Objective(expr=sum( model.purchase_qty[month] * purchase_cost + model.end_inventory[month] * holding_cost for month in range(1, months)), sense=minimize) model.constraints=ConstraintList() # 初始库存为0 model.constraints.add(model.end_inventory[0] == 0) for month in range(1, months): # 库存平衡约束:上月库存+当月采购-当月需求=当月库存 model.constraints.add(model.end_inventory[month] == model.end_inventory[month-1] + model.purchase_qty[month] - demand[month]) # 最小库存约束 model.constraints.add(model.end_inventory[month] >= 3) # 采购逻辑约束:要么不采购(purchase_qty=0),要么采购量≥5 # 1. 若不采购(buy=0),则purchase_qty必须为0 model.constraints.add(model.purchase_qty[month] <= M * model.buy[month]) # 2. 若采购(buy=1),则purchase_qty≥5 model.constraints.add(model.purchase_qty[month] >= 5 * model.buy[month]) # 求解模型 SolverFactory('glpk', executable='/usr/bin/glpsol').solve(model).write() # 打印结果 print("采购量:") for month in range(months): print(f"第{month+1}月: {model.purchase_qty[month].value}") print("\n月末库存:") for month in range(months): print(f"第{month+1}月: {model.end_inventory[month].value}")
方案说明
- 二进制变量
buy:标记当月是否采购,实现按需采购的逻辑开关 - Big-M约束:通过足够大的M值,确保当
buy=0时采购量强制为0,buy=1时采购量不低于5 - 保留核心约束:维持库存平衡和最小库存要求,同时避免强制每月采购,贴合JIT按需采购的目标
内容的提问来源于stack exchange,提问作者Michael Emerson
相关产品推荐
相关产品推荐

