运筹系列47:JuMP数学建模语言

本文参考:https://jump.dev/
首先,JuMP是一种AML(数学建模语言),而不是solver。可用的sovler列表如下:
在这里插入图片描述
JuMP是MOI的一个封装。

1. 快速开始

加载一个solver(这里使用GLPK),也可以先定义Model,再加载solver:
在这里插入图片描述
在这里插入图片描述

然后依次定义变量、目标函数、约束,调用optimize!(model)进行求解。
在这里插入图片描述

1.1 变量

使用base_name可以定义变量别名,也可以使用set_name方法,也可以使用 JuMP.variable_by_name。
在这里插入图片描述

has_lower_bound、lower_bound、lowerBoundRef、delete_lower_bound三者分别用于判断是否有lb,lb的值,lb的引用,删除lb。

使用下述方法定义变量数组:
在这里插入图片描述
如果使用表达式的话,会生成DenseAxisArrays:
在这里插入图片描述
还可以手动指定:
在这里插入图片描述
在这里插入图片描述

如果下标不构成矩形的话,会生成sparseAxisArrays:
在这里插入图片描述
在这里插入图片描述
一样可以手动指定:
在这里插入图片描述

两种定义0-1变量的方法:

julia> @variable(model, x, Bin)
julia> @variable(model, x, binary=true)

两种定义整型变量的方法

julia> @variable(model, x, Int)
julia> @variable(model, x, integer=true)

四种定义半正定变量的方法:

julia> @variable(model, x[1:2, 1:2], PSD)
julia> @variable(model, x[1:2, 1:2] in PSDCone())
julia> x = @variable(model, [1:2, 1:2], set = PSDCone())
julia> @variable(model, x[1:2, 1:2], Symmetric)

下面是定义初始值的几种方法:
在这里插入图片描述

最后,可以用@container宏来指定生成下标的方法:

julia> Containers.container((i, j) -> i + j, Containers.vectorized_product(Base.OneTo(3), Base.OneTo(3)))
3×3 Array{Int64,2}:
 2  3  4
 3  4  5
 4  5  6

julia> Containers.container((i, j) -> i + j, Containers.vectorized_product(1:3, 1:3))
2-dimensional DenseAxisArray{Int64,2,...} with index sets:
    Dimension 1, 1:3
    Dimension 2, 1:3
And data, a 3×3 Array{Int64,2}:
 2  3  4
 3  4  5
 4  5  6

julia> Containers.container((i, j) -> i + j, Containers.vectorized_product(2:3, Base.OneTo(3)))
2-dimensional DenseAxisArray{Int64,2,...} with index sets:
    Dimension 1, 2:3
    Dimension 2, Base.OneTo(3)
And data, a 2×3 Array{Int64,2}:
 3  4  5
 4  5  6

julia> Containers.container((i, j) -> i + j, Containers.nested(() -> 1:3, i -> i:3, condition = (i, j) -> isodd(i) || isodd(j)))
SparseAxisArray{Int64,2,Tuple{Int64,Int64}} with 5 entries:
  [1, 2]  =  3
  [2, 3]  =  5
  [3, 3]  =  6
  [1, 1]  =  2
  [1, 3]  =  4

1.2 表达式

表达式可以用于目标函数和约束

model = Model()
@variable(model, x)
@variable(model, y)
ex = @expression(model, 2x + y - 1)
@objective(model, Min, 2 * ex - 1)

下面是二次的例子
在这里插入图片描述
二次问题还可以用标准的QP方法进行定义:
在这里插入图片描述

也可以使用下面的方法进行创建:

model = Model()
@variable(model, x)
@variable(model, y)
ex = AffExpr(-1.0)
add_to_expression!(ex, 2.0, x)
add_to_expression!(ex, 1.0, y)

# output

2 x + y - 1

使用drop_zeros删除0变量
在这里插入图片描述
非线性的表达式只能用 @NLexpression进行构建,并只能用在@NLobjective和@NLconstraint之中。

1.3 目标函数

使用@objective,下面是两种定义方式
在这里插入图片描述
在这里插入图片描述

1.4 约束

使用@constraint宏
在这里插入图片描述
注意,定义完约束后,生成了一个JuMP变量和model索引,两者同名。修改JuMP变量不影响索引。
在这里插入图片描述
如果要批量创建约束,同样有三种方法:
Arrays方法如下:
在这里插入图片描述
DenseAxisArray方法如下:
在这里插入图片描述

SparseAxisArrays方法如下:
在这里插入图片描述
另外,也可以使用向量化的方法:
在这里插入图片描述
最后,也可以对单变量使用in约束:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
indicator constraints示例如下,当a为1时满足x+y<=1
在这里插入图片描述

使用set_normalized_rhs设置rhs,或者使用add_to_function_constant给表达式添加一定的数值。

使用set_normalized_coefficient设置系数,如下:
在这里插入图片描述
使用delete方法删除约束:
在这里插入图片描述
使用all_constraints获得约束
在这里插入图片描述
使用⟂定义互补约束,定义为:
在这里插入图片描述

布尔变量约束转换如下:
在这里插入图片描述
半整变量如下:
在这里插入图片描述
SOS1变量:最多一个不为0,如下
在这里插入图片描述
SOS2变量:最多两个不为0,如下
在这里插入图片描述

1.5 求解器

有两种指定参数的方法:
在这里插入图片描述

julia model = Model(Gurobi.Optimizer) 
setoptimizerattribute(model, "Presolve", 0) 
setoptimizerattribute(model, "OutputFlag", 1) 

下面是设置参数的示例:
在这里插入图片描述

使用write_to_file/read_from_file方法将模型读、写入文件。
调用TerminationStatusCode获得求解器的终止状态:
在这里插入图片描述
在这里插入图片描述
调用ResultStatusCode获得解的状态:
在这里插入图片描述
如果解的状态不是MOI.NO_SOLUTION,那么最优解可以通过value方法获得。下面是一个推荐的流程:
在这里插入图片描述

2. 回调函数

回调函数在混合整数规划中经常使用到,比如:分支定界法中修改braching decision,添加user-cut等等,仅限于CPLEX, GLPK, Gurobi。

2.1 Lasy constraint

下面是一个简单的例子,在x<=2不满足时,添加x<=2的约束。
回调函数传入的参数用 callback_value方法获取值
其中MOI.submit(optimizer::AbstractOptimizer,sub::AbstractSubmittable,values)在这里指将约束条件提交到model的LazyConstraint中。

model = Model(GLPK.Optimizer)
@variable(model, x <= 10, Int)
@objective(model, Max, x)

function my_callback_function(cb_data)
    x_val = callback_value(cb_data, x)
    if x_val > 2 + 1e-6
        con = @build_constraint(x <= 2)
        MOI.submit(model, MOI.LazyConstraint(cb_data), con)
    end
end

MOI.set(model, MOI.LazyConstraintCallback(), my_callback_function)

下面看一个实战案例,使用lazy constraint求解tsp问题。
首先是定义tsp问题的松弛模型和添加lazy constraint的subtour条件:

function build_tsp_model(d, n)
    model = Model(GLPK.Optimizer)
    @variable(model, x[1:n, 1:n], Bin, Symmetric)
    @objective(model, Min, sum(d .* x) / 2)
    @constraint(model, [i in 1:n], sum(x[i, :]) == 2)
    @constraint(model, [i in 1:n], x[i, i] == 0)
    return model
end

function subtour(edges::Vector{Tuple{Int,Int}}, n)
    ......
    return shortest_subtour
end

接下来我们定义subtour_elimination_callback方法,一旦违反subtour约束则进行调用:

lazy_model = build_tsp_model(d, n)
function subtour_elimination_callback(cb_data)
    cycle = subtour(callback_value.(cb_data, lazy_model[:x]))
    if !(1 < length(cycle) < n)
        return  # Only add a constraint if there is a cycle
    end
    S = [(i, j) for (i, j) in Iterators.product(cycle, cycle) if i < j]
    con = @build_constraint(
        sum(lazy_model[:x][i, j] for (i, j) in S) <= length(cycle) - 1,
    )
    MOI.submit(lazy_model, MOI.LazyConstraint(cb_data), con)
    return
end
MOI.set(lazy_model, MOI.LazyConstraintCallback(), subtour_elimination_callback)
optimize!(lazy_model)
objective_value(lazy_model)

2.2 User cut

User cut不改变整数解空间,它是用来剔除非整数解的。
操作方法和上面如出一辙:

model = Model(GLPK.Optimizer)
@variable(model, x <= 10.5, Int)
@objective(model, Max, x)
function my_callback_function(cb_data)
    x_val = callback_value(cb_data, x)
    con = @build_constraint(x <= floor(x_val))
    MOI.submit(model, MOI.UserCut(cb_data), con)
end
MOI.set(model, MOI.UserCutCallback(), my_callback_function)

2.3 Heuristic solutions

可以使用启发式算法添加cut,使用方法也类似。其中第三项是JuMP变量,第四项是每个变量的值。

model = Model(GLPK.Optimizer)
@variable(model, x <= 10.5, Int)
@objective(model, Max, x)
function my_callback_function(cb_data)
    x_val = callback_value(cb_data, x)
    status = MOI.submit(
        model, MOI.HeuristicSolution(cb_data), [x], [floor(Int, x_val)]
    )
    println("I submitted a heuristic solution, and the status was: ", status)
end
MOI.set(model, MOI.HeuristicCallback(), my_callback_function)
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值