You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Gurobipy.abs_()遇TypeError:LinExpr与GenExprAbs无法执行+=运算

问题解决:Gurobipy中绝对值函数的线性化处理

错误原因

你遇到的TypeError: unsupported operand type(s) for +=: 'gurobipy.LinExpr' and 'GenExprAbs',本质是因为Gurobi不支持将gp.abs_()生成的非线性表达式(GenExprAbs类型)直接与线性表达式(LinExpr类型)相加。整数规划中所有非线性部分(绝对值、除法等)必须通过引入辅助变量+添加线性约束的方式转化为线性形式。

此外你的目标函数还存在两处逻辑/形式问题:

  1. 人口偏差项的逻辑错误:原代码是对每个选区下的单个precinct人口与目标值的差取绝对值求和,正确逻辑应为每个选区的总人口与目标值的差的绝对值之和。
  2. 比例偏差项包含除法:l[i]x[i,j]/sum(x[i,j])是分式,属于非线性表达式,必须线性化处理。

分步解决方案

1. 修正基础变量定义

首先注意:precinct的人口pp应为已知常量,而非决策变量(重分区中人口是固定数据,不是需要优化的变量):

# 替换原pp的变量定义为输入常量
pp = {i: 1980 for i in set_I}  # 示例:每个precinct人口1980,5个precinct对应总V=9900

2. 线性化人口偏差的绝对值

为每个选区j的总人口与目标值的偏差引入非负辅助变量,通过约束将绝对值转化为线性形式:

# 定义每个选区的总人口
pop_j = {(j): gp.quicksum(pp[i] * x[i,j] for i in set_I) for j in set_J}

# 引入辅助变量存储人口偏差的绝对值
y_pop = {(j): opt_model.addVar(vtype=gp.GRB.CONTINUOUS, lb=0, name=f"y_pop_{j}") for j in set_J}

# 添加线性约束替代绝对值
for j in set_J:
    opt_model.addConstr(pop_j[j] - targ <= y_pop[j], name=f"pop_pos_{j}")
    opt_model.addConstr(-(pop_j[j] - targ) <= y_pop[j], name=f"pop_neg_{j}")

3. 线性化比例偏差的绝对值

针对abs(选区lean比例 - 整体lean比例)的分式结构,先引入比例变量,再通过辅助变量线性化绝对值:

# 定义每个选区的lean数量、precinct总数
r_j = {(j): gp.quicksum(l[i] * x[i,j] for i in set_I) for j in set_J}
s_j = {(j): gp.quicksum(x[i,j] for i in set_I) for j in set_J}

# 整体lean比例(线性表达式,n为precinct总数常量)
actual_proportion = gp.quicksum(l[i] for i in set_I) / n

# 引入比例变量z_j表示选区j的lean比例
z_j = {(j): opt_model.addVar(vtype=gp.GRB.CONTINUOUS, lb=0, ub=1, name=f"z_lean_{j}") for j in set_J}
# 引入辅助变量存储比例偏差的绝对值
y_lean = {(j): opt_model.addVar(vtype=gp.GRB.CONTINUOUS, lb=0, name=f"y_lean_{j}") for j in set_J}

# 添加线性约束:lean数量 = 比例 * 选区precinct总数
for j in set_J:
    opt_model.addConstr(r_j[j] == z_j[j] * s_j[j], name=f"lean_ratio_{j}")
    # 线性化绝对值约束
    opt_model.addConstr(z_j[j] - actual_proportion <= y_lean[j], name=f"lean_pos_{j}")
    opt_model.addConstr(-(z_j[j] - actual_proportion) <= y_lean[j], name=f"lean_neg_{j}")

4. 构建线性目标函数

用辅助变量的和替代原非线性目标:

obj = gp.quicksum(y_pop[j] for j in set_J) + gp.quicksum(y_lean[j] for j in set_J)
opt_model.setObjective(obj, gp.GRB.MINIMIZE)

5. 补充必要约束

不要忘记添加precinct的分配约束(每个precinct必须分配到恰好一个选区):

for i in set_I:
    opt_model.addConstr(gp.quicksum(x[i,j] for j in set_J) == 1, name=f"assign_{i}")

完整可运行代码示例

import gurobipy as gp
from gurobipy import GRB
import math

# 示例集合定义
set_I = {0,1,2,3,4}  # 5个precinct
set_J = {0,1}        # 2个选区
n = len(set_I)
m = len(set_J)

V = 9900
targ = math.floor(V // m)

# 固定precinct人口数据
pp = {i: 1980 for i in set_I}

opt_model = gp.Model("Redistricting")

# 决策变量定义
l = {(i): opt_model.addVar(vtype=GRB.BINARY, name=f"l_{i}") for i in set_I}
x = {(i,j): opt_model.addVar(vtype=GRB.BINARY, name=f"x_{i}_{j}") for i in set_I for j in set_J}

# 基础分配约束
for i in set_I:
    opt_model.addConstr(gp.quicksum(x[i,j] for j in set_J) == 1, name=f"assign_{i}")

# 定义选区统计量
pop_j = {(j): gp.quicksum(pp[i] * x[i,j] for i in set_I) for j in set_J}
r_j = {(j): gp.quicksum(l[i] * x[i,j] for i in set_I) for j in set_J}
s_j = {(j): gp.quicksum(x[i,j] for i in set_I) for j in set_J}

actual_proportion = gp.quicksum(l[i] for i in set_I) / n

# 线性化人口偏差绝对值
y_pop = {(j): opt_model.addVar(vtype=GRB.CONTINUOUS, lb=0, name=f"y_pop_{j}") for j in set_J}
for j in set_J:
    opt_model.addConstr(pop_j[j] - targ <= y_pop[j], name=f"pop_pos_{j}")
    opt_model.addConstr(-(pop_j[j] - targ) <= y_pop[j], name=f"pop_neg_{j}")

# 线性化比例偏差绝对值
z_j = {(j): opt_model.addVar(vtype=GRB.CONTINUOUS, lb=0, ub=1, name=f"z_lean_{j}") for j in set_J}
y_lean = {(j): opt_model.addVar(vtype=GRB.CONTINUOUS, lb=0, name=f"y_lean_{j}") for j in set_J}
for j in set_J:
    opt_model.addConstr(r_j[j] == z_j[j] * s_j[j], name=f"lean_ratio_{j}")
    opt_model.addConstr(z_j[j] - actual_proportion <= y_lean[j], name=f"lean_pos_{j}")
    opt_model.addConstr(-(z_j[j] - actual_proportion) <= y_lean[j], name=f"lean_neg_{j}")

# 目标函数
obj = gp.quicksum(y_pop[j] for j in set_J) + gp.quicksum(y_lean[j] for j in set_J)
opt_model.setObjective(obj, GRB.MINIMIZE)

# 求解
opt_model.optimize()

内容的提问来源于stack exchange,提问作者Lyxcoder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 01:12:54