如何强制Pulp与Pyomo在所有算术运算中仅使用float32精度
我明白你遇到的困扰了——Pulp和Pyomo默认都用float64(也就是Python里的原生float)来处理所有算术运算,这就导致当你需要严格按照float32精度来构建约束时,会出现模型不可行或者弹出警告的情况,就像你示例里展示的那样:用float32的目标值时求解失败,但换成float64的目标就能正常得到最优解。
这是因为哪怕你传入float32类型的数值,这两个库在内部运算时还是会自动把数值提升到float64,最终的约束计算逻辑和你预期的单精度结果不匹配。下面我给你分别针对Pulp和Pyomo给出实用的解决方法:
Pulp 处理方案
Pulp没有直接设置全局精度类型的开关,但我们可以通过显式转换所有输入值为float32,同时调整求解器的精度容忍度来适配单精度的误差范围,以此对齐float32的运算逻辑。
修改你的pulp_example函数如下:
import pulp import numpy as np def pulp_example(values, target_float): # 把输入值和目标值都转换为float32类型 float32_values = np.array(values, dtype=np.float32) float32_target = np.float32(target_float) variables = pulp.LpVariable.dicts("x", range(len(values)), 0, 1, pulp.LpBinary) problem = pulp.LpProblem("Problem", pulp.LpMinimize) problem += 0 problem += pulp.lpSum([variables[i] for i in range(len(values))]) == 2 # 构建求和表达式时使用转换后的float32数值 sum_exp = pulp.lpSum([variables[i] * float32_values[i] for i in range(len(values))]) # 约束条件使用float32目标值 problem += sum_exp == float32_target # 调整求解器的相对间隙容忍度,适配float32的精度误差 problem.solve(pulp.PULP_CBC_CMD(gapRel=1e-6)) return pulp.LpStatus[problem.status]
Pyomo 处理方案
Pyomo同样默认依赖float64,但我们可以在构建模型时强制使用float32数值,同时给求解器设置合适的可行性容忍度,让求解器接受单精度范围内的误差。
修改你的pyomo_example函数如下:
import pyomo.environ as pyo import numpy as np def pyomo_example(values, target_float): float32_values = np.array(values, dtype=np.float32) float32_target = np.float32(target_float) model = pyo.ConcreteModel() model.I = pyo.RangeSet(0, len(values) - 1) model.x = pyo.Var(model.I, within=pyo.Binary) model.selection_constraint = pyo.Constraint(expr=sum(model.x[i] for i in model.I) == 2) def sum_expr_rule(model): # 每一项运算都基于float32数值 return sum(model.x[i] * float32_values[i] for i in model.I) model.eq_expr = pyo.Expression(rule=sum_expr_rule) # 约束使用float32目标值 model.constraint = pyo.Constraint(expr=model.eq_expr == float32_target) model.objective = pyo.Objective(expr=0, sense=pyo.minimize) solver = pyo.SolverFactory('cbc') # 设置求解器的可行性容忍度,匹配float32的精度范围 solver.options['feasibilitytolerance'] = 1e-6 result = solver.solve(model, tee=False) return result.solver.status
测试验证
使用你原来的测试代码验证修改后的效果,现在无论是float32还是float64目标值,都应该能得到预期的求解结果:
input_values = [90.2743351459503174, 4.8144315292034] float32_target = 95.0887680053711 # float32算术结果 float64_target = 95.08876667515372 # float64算术结果 assert np.sum([input_values], dtype=np.float32) == float32_target assert np.sum([input_values], dtype=np.float64) == float64_target # 现在float32目标也能正常求解 pulp_res = pulp_example(input_values, float32_target) # 返回'Optimal' pyomo_res = pyomo_example(input_values, float32_target) # 返回'ok' # float64目标依然正常工作 pulp_res = pulp_example(input_values, float64_target) # 返回'Optimal' pyomo_res = pyomo_example(input_values, float64_target) # 返回'ok'
额外说明
需要注意的是,大多数求解器(比如CBC)内部本身还是用双精度计算,但通过我们的转换和容忍度调整,可以让模型的行为完全符合float32算术的预期。如果需要极端严格的单精度运算,可能需要寻找支持float32的专用求解器,但对于大多数场景来说,上面的方案已经足够实用。
备注:内容来源于stack exchange,提问作者FreeDom Sy

