面向新手的差分进化多约束优化:scipy differential_evolution变量递增约束实现
针对differential_evolution添加单调线性约束的落地方案
之前求解返回全inf的核心原因
differential_evolution从scipy 1.4版本开始才正式支持约束参数,完全不兼容minimize接口常用的字典格式约束(即{'type': 'ineq', 'fun': ...}这类写法)。直接传这类字典会被解析为无效约束,导致可行域被判定为空,最终返回全inf结果。- 第二个常见诱因是设置的变量边界
bounds和约束逻辑本身矛盾,比如变量边界的范围和单调要求没有交集,优化器找不到任何满足条件的解点。
适配你需求的线性约束构造方法
你要求的“每个变量严格大于前序变量”(即x[i] > x[i-1], i=1..20)属于典型的线性不等式约束,可以直接用scipy.optimize.LinearConstraint实现,不需要手动写惩罚项。
- 将单调要求转化为线性不等式:由于浮点优化的数值精度限制,不需要追求绝对严格的大于,可设置相邻变量的最小差值为1e-4,即约束为
x[i] - x[i-1] >= 1e-4 - 构造约束矩阵:21个变量对应20条相邻约束,约束矩阵为20行21列的稀疏矩阵:
- 第i行对应第i条相邻约束,仅在第i列位置为-1,第i+1列位置为1,其余元素为0
- 生成LinearConstraint实例:将约束矩阵、每个不等式的下限(1e-4)、上限(无穷大,不设置上界限制)传入即可。
完整可运行代码示例
import numpy as np from scipy.optimize import differential_evolution, LinearConstraint # 去掉手动惩罚项的简化目标函数,和你原逻辑完全一致 def obj_fun_cal(x, df_TZ_input, df_TZ_target): prodmx = df_TZ_input * x sum_vector = prodmx.sum(axis=1) MAE = np.mean(np.absolute(df_TZ_target - sum_vector)) return MAE # 构造单调递增约束 n_vars = 21 # 对应你优化的21个变量x0~x20 # 初始化约束矩阵 A = np.zeros((n_vars - 1, n_vars)) for i in range(n_vars - 1): A[i, i] = -1 A[i, i+1] = 1 # 约束规则:每个相邻变量差 >= 1e-4,无上限限制 mono_constraint = LinearConstraint(A, lb=1e-4, ub=np.inf) # 调用differential_evolution求解 # 注意替换为你实际的变量物理边界,避免和约束逻辑冲突 bounds = [(0, 100) for _ in range(n_vars)] result = differential_evolution( func=obj_fun_cal, bounds=bounds, args=(df_TZ_input, df_TZ_target), constraints=mono_constraint, seed=42, # 固定随机种子保证结果可复现 polish=True, # 优化最后阶段用梯度法细化结果 tol=1e-7 ) # 输出结果 print("最优MAE值:", result.fun) print("满足单调要求的最优变量:", result.x)
关键注意事项
- 对比你原来的手动惩罚方案:内置线性约束会在种群初始化、变异、交叉的全流程强制保持解在可行域内,不存在惩罚权重设置不当(太低压不住不可行解、太高降低拟合精度)的问题。
- 如果运行后仍然出现全inf结果,优先检查两点:一是scipy版本是否高于1.4,二是设置的
bounds范围是否能覆盖满足单调要求的解空间(比如不要给x0设比x1更高的上界)。 - 不需要对变量做转换(比如把x设成累加形式),线性约束会自动处理所有相邻变量的单调要求,不会改变你原来目标函数的计算逻辑。
内容的提问来源于stack exchange,提问作者Steve
相关产品推荐
相关产品推荐

