Pyomo非线性优化中实现目标变量为指定倍数的问题求助
已知销售份额与空间份额的关系:
$S=B^b$
$\log(S)=b*\log(B)$ # 这是目标函数#
其中:
- S=销售份额,即“品类销售额/部门销售额”
- b=空间隔间份额,即“品类空间隔间数/部门空间隔间数”
目标函数实现代码如下:
model.objective = pyomo.Objective(rule= sum( exp(log( model.space[c] / model.sapce_sum[c] ) * model.sales_coeff[c][0] + model.sales_intercept[c] ) * model.sales_sum[c] for c in model.dept_cat_nbr ), sense=pyomo.maximize)
通过回归模型得到系数用于销售预测,根据空间隔间数计算销售额。其中:
- space[c]=品类级空间隔间数
- model.space_sum[c]=部门级空间隔间数
- (model.space[c]/model.sapce_sum[c])=空间隔间份额,用于调用预计算的系数
- model.dept_cat_nbr为类似'90_1400'的索引集合
- model.space_bay为待优化变量,用于定义各品类的空间分配以优化需求
- model.multiple为参数,取值为0.5或1,用于控制品类是否占用半格或整格货架
期望输出:
- 对于半格品类(如'90_1400'),model.space_bay需为2.5、3这类0.5的倍数,而非2.7
- 对于整格品类(如'95_1200'),model.space_bay需为3、4这类整数,而非3.5、4.7
方法1:取模约束
尝试通过以下约束实现倍数要求:
def abide_space_multiples(model,i): return model.space_bay[i]% model.multiple[i]==0 model.space_multiple_cons = pyomo.Constraint(model.dept_cat_nbr,rule = abide_space_multiples)
其中model.multiple[i]示例:{'95_1200':0.5, '95_4200':0.5,...}
结论:失败,Pyomo不支持%运算符
方法2:整数变量转换
定义整数变量model.space_bay_integer,通过约束使目标变量model.space_bay为其与model.multiple[i]的乘积:
# Define Bays only in integer terms model.space_bay_integer=pyomo.Var(model.dept_cat_nbr, domain=pyomo.Integers) ## Second Variable(Target Variable) # Lower and Upper Bound model.bay_lb=pyomo.Param(model.dept_cat_nbr, initialize={df_sum_wt.loc[i,'dept_catg']: df_sum_wt.loc[i,'min_bay'] for i in range(len(df_sum_wt))}) model.bay_ub=pyomo.Param(model.dept_cat_nbr, initialize={df_sum_wt.loc[i,'dept_catg']: df_sum_wt.loc[i,'max_bay'] for i in range(len(df_sum_wt))}) model.space_bay = pyomo.Var(model.dept_cat_nbr, domain = pyomo.NonNegativeReals, bounds = {i: (model.bay_lb[i],model.bay_ub[i]) for i in model.dept_cat_nbr}) # create space constraint def abide_space_multiples(model,i): return model.space_bay[i]==model.space_bay_integer[i]*model.multiple[i] model.space_multiple_cons = pyomo.Constraint(model.dept_cat_nbr,rule = abide_space_multiples)
结论:失败,使用IPOPT等非线性求解器时,model.space_bay_integer未保持整数域,返回分数值(如'92_1802':4.4099...),导致model.space_bay也不是指定倍数。而线性求解器(如GLPK、CPLEX)不支持含log和exp的非线性目标函数。
要解决非线性优化场景下的整数/半整数约束问题,核心是使用支持混合整数非线性规划(MINLP)的求解器,同时优化变量定义与目标函数:
1. 更换为MINLP求解器
替换IPOPT为支持非线性目标+整数变量约束的求解器,比如:
- Bonmin:开源MINLP求解器,适合处理此类问题
- Couenne:开源全局优化求解器,适配非凸MINLP场景
- Gurobi/CPLEX:商业求解器,部分版本支持非线性MINLP(需确认版本特性)
以Bonmin为例,Pyomo中调用方式:
from pyomo.environ import SolverFactory solver = SolverFactory('bonmin') result = solver.solve(model, tee=True)
2. 优化变量约束定义
保留整数变量转换思路,可拆分变量类型减少浮点数误差:
# 定义整数变量 model.space_bay_int = pyomo.Var(model.dept_cat_nbr, domain=pyomo.Integers) # 约束逻辑:整格品类直接等于整数变量,半格品类为整数变量*0.5 def space_bay_rule(model, i): if model.multiple[i] == 1: return model.space_bay[i] == model.space_bay_int[i] else: return model.space_bay[i] == model.space_bay_int[i] * 0.5 model.space_bay_constraint = pyomo.Constraint(model.dept_cat_nbr, rule=space_bay_rule)
这种拆分更清晰,降低求解器处理浮点数倍数的复杂度。
3. 简化目标函数运算
原目标函数中exp(log(x))可等价替换为x,减少非线性运算层级,提升求解稳定性:
model.objective = pyomo.Objective( rule=sum( (model.space[c] / model.space_sum[c]) ** model.sales_coeff[c][0] * exp(model.sales_intercept[c]) * model.sales_sum[c] for c in model.dept_cat_nbr ), sense=pyomo.maximize )
注:由$\log(S) = b*\log(B) + intercept$推导得$S = B^b * \exp(intercept)$,此等价转换能减少嵌套运算,降低求解难度。
内容的提问来源于stack exchange,提问作者stat

