官方文档
目录
官方文档:GRBModel.AddGenConstrXxx() - Gurobi Optimization
数学规划的约束类型
基本约束(fundamental constraints):
通用约束(general constraints):
1. GUROBI求解器有针对这类约束的函数,直接调用这类函数即可
2. 自己对这类约束进行数学形式转化
官方文档
数学规划的约束类型
数学规划的约束可以分为线性约束和非线性约束两大类,对于线性约束可以很好地处理,而对于非线性约束,则需要使用一些特殊的方法来转化为线性约束才能被处理。
基本约束(fundamental constraints):
上下界约束(variable bound constraints)、线性约束(linear constraints)、二次约束(quadratic constraints)、整数约束(intergrality constraints)、SOS约束等,这些类型的约束是可以被GUROBI的底层算法直接处理的。
补充一下什么是SOS约束:SOS - Gurobi Optimization
简言之:type1型的SOS约束就是一组变量中最多只有一个变量可以取非0值;type2型的SOS约束就是一组有序变量中最多只有两个变量可以取非0值,如果有两个非零变量的话,这两个变量必须是位置连续的。
通用约束(general constraints):
在实际建模的时候,约束类型经常不局限于以上几种基本类型,比如最大最小约束、绝对值约束、函数约束(指数函数、对数函数、幂函数等等),这种约束我们称为genaral constraints
在解决实际问题时,如果遇到了这类约束,我们可以使用以下两种方法来解决:
1. GUROBI求解器有针对这类约束的函数,直接调用这类函数即可
那么这些函数背后的底层逻辑又是如何处理genaral constraints 的呢?
其实,这类求解器也不能直接处理这类约束,只是在预处理阶段(presolve)阶段,求解器会将这类约束转化为由fundamental constraints组成的简单约束,通过直接处理fundamental constraints来间接处理genaral constarints。
那么这种转化是等价的吗?(好问题)
大多数情况下,这种转化在数学上是等价的;但有时候,无法等价转化,只能近似;这取决于general constarints的具体形式。后面再解释一下。
那么具体如何实现呢?
(1)一种是直接使用GUROBI的专有函数Model.addGenConstrXxx(),如下:
(2)一种是使用Model.addConstr或者Model.addConstrs(搭配genaral constraints辅助函数和loaded operates一起),如下:
两种方法的实现代码如下:(x和y是决策变量,其他的均为需要输入的常量)
addGenConstrMax:
Model.addGenConstrMax() - Gurobi Optimization
# y = max(x1, x3, x4, 2.0)
# addGenConstrXXX
addGenConstrMax ( resvar, vars, constant=None, name="" )
model.addGenConstrMax(y, [x1, x3, x4], 2.0, "maxconstr")
# overloaded forms
model.addConstr(y == max_([x1, x3, x4], constant = 2.0), name = "maxconstr")
model.addConstr(y == max_(x1, x3, x4, constant = 2.0), name = "maxconstr")
addGenConstrMin:
Model.addGenConstrMin() - Gurobi Optimization
# y = min(x1, x3, x4, 2.0)
model.addGenConstrMin(y, [x1, x3, x4], 2.0, name="minconstr")
model.addConstr(y == min_([x1, x3, x4], constant = 2.0), name = "minconstr")
model.addConstr(y== min_(x1, x3, x4, constant = 2.0), name ="minconstr")
addGenConstrAbs:
Model.addGenConstrAbs() - Gurobi Optimization
# y = |x1|
model.addGenConstrAbs(y, x1, name = "absconstr")
model.addConstr(y == abs_(x1), name = "absconstr")
addGenConstrAnd:
Model.addGenConstrAnd() - Gurobi Optimization
# y = and(x1, x3, x4)
model.addGenConstrAnd(y, [x1, x3, x4], name = "addconstr")
model.addConstr(y == and_([x1, x3, x4]), name ="addconstr")
model.addConstr(y == and_(x1, x3, x4), name = "addconstr")
addGenConstrOr:
Model.addGenConstrOr() - Gurobi Optimization
# y = or(x1, x3, x4)
model.addGenConstrOr(y, [x1, x3, x4], name = "orconstr")
model.addConstr(y == or_([x1, x3, x4]), name = "orconstr")
model.addConstr(y == or_(x1, x3, x4), name = "orconstr")
addGenConstrNorm:
Model.addGenConstrNorm() - Gurobi Optimization
# y = 2-norm(x1, x3, x4)
model.addGenConstrNorm(y, [x1, x3, x4], 2, "normconstr") # 0\1\2范数
addGenConstrIndicator:
Model.addGenConstrIndicator() - Gurobi Optimization
# y = 1 ——》 x1 + 2x3 + x4 =1
model.addGenConstrIndicator(y, True, x1 + 2*x3 + x4, GRB.EQUAL, 1.0)
# 或者
model.addGenConstrIndicator(y, True, x1 + 2*x3 + x4 == 1.0)
model.Constr((x == 1) >> (x1 + 2*x3 + x4 == 1.0))
addGenConstrPwl:
分段线性函数线性化
Model.addGenConstrPWL() - Gurobi Optimization
model.addGenConstrPWL(x, y, [0, 1, 2], [1.5, 0, 3])
# 分段点:(0,1.5)、(1,0)、(2,3)
addGenConstrPoly:
Model.addGenConstrPoly() - Gurobi Optimization
# y = 2 x^3 + 1.5 x^2 + 1
model.addGenConstrPoly(x, y, [2, 1.5, 0, 1])
addGenConstrExp:
Model.addGenConstrExp() - Gurobi Optimization
# y = exp(x)
nodel.addGenConstrExp(x, y)
addGenConstrExpA:
Model.addGenConstrExpA() - Gurobi Optimization
# y = 3^x
model.addGenConstrExpA(x, y, 3.0)
addGenConstrLog:
Model.addGenConstrLog() - Gurobi Optimization
# y = ln(x)
model.addGenConstrLog(x, y)
addGenConstrLogA:
Model.addGenConstrLogA() - Gurobi Optimization
# y = log2(x)
model.addGenConstrLogA(x, y, 2)
addGenConstrLogistic:
Model.addGenConstrLogistic() - Gurobi Optimization
model.addGenConstrLogistic(x, y)
addGenConstrPow:
Model.addGenConstrPow() - Gurobi Optimization
# y = x^3.5
model.addGenConstrPow(x, y, 3.5)
addGenConstrSin:
model.addGenConstrSin(x, y)
addGenConstrCos:
Model.addGenConstrSin() - Gurobi Optimization
addGenConstrTan:
从上面我们可以发现: general constraints可以分为两类
simple constraints: max、min、abs 、and 、or、norm、indicatior、precewise-linear,ect
这类简单约束可以通过引入隐式辅助变量、线性约束、SOS约束来处理(线性化),例如:
function constraints:
这类约束更加复杂,无法像simple constraints那样来处理,目前最有效的方式是通过线性分段函数来近似逼近这些非线性函数,而这种近似线性化又有静态处理和动态处理两种方式,可以用户自行设置:
当然啦,这些处理都是求解器自己在背后处理的!
还有一个问题,既然是近似,那么就有可能存在偏差,GUROBI提供了一系列参数供用户设置根据自己的模型来尽可能的控制这个偏差;并且,这种近似所造成的violation依然是在feasiblity tolerance内的,也就是说这种近似依然可以保证满足原始约束的。
2. 自己对这类约束进行数学形式转化
对于min、max、abs 、and 、or、indicator等,通过引入辅助变量和大M等,在数学形式都是可以将其转化为线性表达式的(无论是在目标函数中还是在约束条件中),所以可以自己在建模时直接将这类约束建模为线性约束(可以看成是做了求解器在背后做的事情),当然这种方法需要掌握一定的建模技巧,后面有需要会专门来总结一下。但是,如果只是为了解决实际问题,直接使用GUROBI提供的函数当然是更佳的选择。
留坑再次,下次总结一下线性化建模的技巧!