JuMP - 在整数节点上同时具有 Heuristic 和 LazyConstraints 回调
JuMP - Having both Heuristic and LazyConstraints Callbacks on integer nodes
对不起,这次我没能制作出MWE。我试过了,但不行。
在 JuMP Julia 中,我想知道是否可以在整数节点 上同时获得 Heuristic 和 LazyConstraints 回调 。在以下 NOT MWE 代码中:
function call_back_benders_two_opt(cb_data)
status = callback_node_status(cb_data, m)
if status != MOI.CALLBACK_NODE_STATUS_FRACTIONAL
@show status
end
if status == MOI.CALLBACK_NODE_STATUS_INTEGER
x_cb = zeros(Bool, n, n)
for i in 1:n
for j in i+1:n
x_cb[i,j] = Bool(round(callback_value(cb_data, x[i, j])))
end
end
if sum(x_cb) > 0
ŷ = [Bool(round(callback_value(cb_data, y[i,i]))) for i in 1:n]
ring_edges = create_ring_edges_lazy(cb_data, x, n)
is_valid = validate_solution_lazy(ring_edges, ŷ, n)
if is_valid
x′, y_sp, sp_cost = compute_sp_res(x_cb, ŷ, V, n, tildeV, r, s)
hubs, master_cost = compute_master_res(x_cb, ŷ, V, n, tildeV, o, r)
x_opt = run_two_opt_wiki(x_cb, x′, hubs, sp_cost+master_cost, pars, n, r, rp, tildeV)[1]
for i in V
status = MOI.submit(
m, MOI.HeuristicSolution(cb_data), [x[i,j] for j in i+1:n], [x_opt[i,j] for j in i+1:n]
)
end
println("I submitted a heuristic solution, and the status was: ", status)
end
end
end
MOI.set(m, MOI.HeuristicCallback(), call_back_benders_two_opt)
MOI.set(m, MOI.LazyConstraintCallback(), call_back_benders)
我的问题是 Heuristic Callback
仅在小数节点上调用。事实上,@show status
行从不打印,这意味着 status
始终是 MOI.CALLBACK_NODE_STATUS_FRACTIONAL
,而我想在 Integer 节点上调用我的 HeuristicCallback。请注意,在整数节点上,我确实成功添加了 Lazy Constraints
。所以我担心的是在整数节点上同时调用启发式回调和惰性约束。有什么我想念的吗?
请注意我用的是both Heuristic Callbacks and Lazy Constraints from this Julia Doc page.
编辑:感谢@mattmilten 的回答,我正在编辑我的post。然后我尝试将 Heuristic Callbacks 放在 Lazy Constraints Callback 中,因为在 JuMP 中不能有两个回调。
function call_back_benders(cb_data)
ŷ = [Bool(round(callback_value(cb_data, y[i,i]))) for i in 1:n]
start_time_sp = time()
for i in 1:n
for j in i+1:n
x̂[i,j] = Bool(round(callback_value(cb_data, x[i, j])))
end
end
ring_edges = create_ring_edges_lazy(cb_data, x, n)
is_valid = validate_solution_lazy(ring_edges, ŷ, n)
if !is_valid
nsubtour_cons = create_subtour_constraint_lazy(m, cb_data, x, y, n, ring_edges, nsubtour_cons)
else
λ0 = callback_value(cb_data, λ)
start_time_sp = time()
if pars.sp_solve == "poly"
λ_val, α, γ, β, δ = sp_optimize_poly(ŷ, x̂, V, tildeV, rp, s, pars.sp_solve)
else
λ_val, α, γ, β, δ = sp_optimize_ilp_dual(ŷ, x̂, V, tildeV, rp, s, pars.log_level, gurobi_env)
end
sp_time += time() - start_time_sp
if λ0 < λ_val
RHS = sum([2(1-y[i,i])α[i] for i in V]) +
sum([(x[mima(β[j][1],j)] + x[mima(β[j][2],j)] - 1)rp[mima(β[j][1],β[j][2])]
for j in tildeV if length(β[j]) > 0]) -
sum([y[j,j]γ[i,j] for i in V, j in V if i != j])
pars.log_level > 1 && @info "Optimality cut found"
nopt_cons += 1
con = @build_constraint(λ ≥ RHS)
MOI.submit(m, MOI.LazyConstraint(cb_data), con)
################ 2-opt
status = callback_node_status(cb_data, m)
if status != MOI.CALLBACK_NODE_STATUS_FRACTIONAL
@show status
end
if status == MOI.CALLBACK_NODE_STATUS_INTEGER
if sum(x̂) > 0
x′, y_sp, sp_cost = compute_sp_res(x̂, ŷ, V, n, tildeV, r, s)
hubs, master_cost = compute_master_res(x̂, ŷ, V, n, tildeV, o, r)
x_opt = run_two_opt_wiki(x̂, x′, hubs, sp_cost+master_cost, pars, n, r, rp, tildeV)[1]
for i in V
status = MOI.submit(
m, MOI.HeuristicSolution(cb_data), [x[i,j] for j in i+1:n], [x_opt[i,j] for j in i+1:n]
)
end
println("I submitted a heuristic solution, and the status was: ", status)
end
end
end
end
MOI.set(m, MOI.LazyConstraintCallback(), call_back_benders)
但是,Julia 告诉我这是一个无效的回调用法:
ERROR: InvalidCallbackUsage: Cannot submit MathOptInterface.HeuristicSolution{Gurobi.CallbackData}(Gurobi.CallbackData( sense : minimize
number of variables = 14952
number of linear constraints = 103
number of quadratic constraints = 0
number of sos constraints = 0
number of non-zero coeffs = 14952
number of non-zero qp objective terms = 0
number of non-zero qp constraint terms = 0
, Ptr{Nothing} @0x000055755c429d40, 4)) inside a
MathOptInterface.LazyConstraintCallback().
这是否意味着我不能使用 JuMP 的独立于求解器的回调?
在Gurobi中,只能将一个回调函数传递给求解器。在此函数内,您可以通过 where
参数将它们分开,从而为不同的场景定义多个操作,如 Python example:
所示
def mycallback(model, where):
if where == GRB.Callback.PRESOLVE:
# Presolve callback
cdels = model.cbGet(GRB.Callback.PRE_COLDEL)
rdels = model.cbGet(GRB.Callback.PRE_ROWDEL)
if cdels or rdels:
print(f"{cdel} columns and {rdel} rows are removed")
elif where == GRB.Callback.SIMPLEX:
# Simplex callback
itcnt = model.cbGet(GRB.Callback.SPX_ITRCNT)
print(f"{itcnt} simplex iterations")
elif where == GRB.Callback.MIP:
# General MIP callback
pass
elif where == GRB.Callback.MIPSOL:
# MIP solution callback
pass
elif where == GRB.Callback.MIPNODE:
# MIP node callback
pass
这应该也可以通过 JuMP 实现,但您需要自己定义回调并将它们组合成一个函数。 JuMP 文档明确警告只能使用 set
函数定义一个回调:
You can only set each callback once. Calling set twice will over-write the earlier callback. In addition, if you use a solver-independent callback, you cannot set a solver-dependent callback.
对不起,这次我没能制作出MWE。我试过了,但不行。
在 JuMP Julia 中,我想知道是否可以在整数节点 上同时获得 Heuristic 和 LazyConstraints 回调 。在以下 NOT MWE 代码中:
function call_back_benders_two_opt(cb_data)
status = callback_node_status(cb_data, m)
if status != MOI.CALLBACK_NODE_STATUS_FRACTIONAL
@show status
end
if status == MOI.CALLBACK_NODE_STATUS_INTEGER
x_cb = zeros(Bool, n, n)
for i in 1:n
for j in i+1:n
x_cb[i,j] = Bool(round(callback_value(cb_data, x[i, j])))
end
end
if sum(x_cb) > 0
ŷ = [Bool(round(callback_value(cb_data, y[i,i]))) for i in 1:n]
ring_edges = create_ring_edges_lazy(cb_data, x, n)
is_valid = validate_solution_lazy(ring_edges, ŷ, n)
if is_valid
x′, y_sp, sp_cost = compute_sp_res(x_cb, ŷ, V, n, tildeV, r, s)
hubs, master_cost = compute_master_res(x_cb, ŷ, V, n, tildeV, o, r)
x_opt = run_two_opt_wiki(x_cb, x′, hubs, sp_cost+master_cost, pars, n, r, rp, tildeV)[1]
for i in V
status = MOI.submit(
m, MOI.HeuristicSolution(cb_data), [x[i,j] for j in i+1:n], [x_opt[i,j] for j in i+1:n]
)
end
println("I submitted a heuristic solution, and the status was: ", status)
end
end
end
MOI.set(m, MOI.HeuristicCallback(), call_back_benders_two_opt)
MOI.set(m, MOI.LazyConstraintCallback(), call_back_benders)
我的问题是 Heuristic Callback
仅在小数节点上调用。事实上,@show status
行从不打印,这意味着 status
始终是 MOI.CALLBACK_NODE_STATUS_FRACTIONAL
,而我想在 Integer 节点上调用我的 HeuristicCallback。请注意,在整数节点上,我确实成功添加了 Lazy Constraints
。所以我担心的是在整数节点上同时调用启发式回调和惰性约束。有什么我想念的吗?
请注意我用的是both Heuristic Callbacks and Lazy Constraints from this Julia Doc page.
编辑:感谢@mattmilten 的回答,我正在编辑我的post。然后我尝试将 Heuristic Callbacks 放在 Lazy Constraints Callback 中,因为在 JuMP 中不能有两个回调。
function call_back_benders(cb_data)
ŷ = [Bool(round(callback_value(cb_data, y[i,i]))) for i in 1:n]
start_time_sp = time()
for i in 1:n
for j in i+1:n
x̂[i,j] = Bool(round(callback_value(cb_data, x[i, j])))
end
end
ring_edges = create_ring_edges_lazy(cb_data, x, n)
is_valid = validate_solution_lazy(ring_edges, ŷ, n)
if !is_valid
nsubtour_cons = create_subtour_constraint_lazy(m, cb_data, x, y, n, ring_edges, nsubtour_cons)
else
λ0 = callback_value(cb_data, λ)
start_time_sp = time()
if pars.sp_solve == "poly"
λ_val, α, γ, β, δ = sp_optimize_poly(ŷ, x̂, V, tildeV, rp, s, pars.sp_solve)
else
λ_val, α, γ, β, δ = sp_optimize_ilp_dual(ŷ, x̂, V, tildeV, rp, s, pars.log_level, gurobi_env)
end
sp_time += time() - start_time_sp
if λ0 < λ_val
RHS = sum([2(1-y[i,i])α[i] for i in V]) +
sum([(x[mima(β[j][1],j)] + x[mima(β[j][2],j)] - 1)rp[mima(β[j][1],β[j][2])]
for j in tildeV if length(β[j]) > 0]) -
sum([y[j,j]γ[i,j] for i in V, j in V if i != j])
pars.log_level > 1 && @info "Optimality cut found"
nopt_cons += 1
con = @build_constraint(λ ≥ RHS)
MOI.submit(m, MOI.LazyConstraint(cb_data), con)
################ 2-opt
status = callback_node_status(cb_data, m)
if status != MOI.CALLBACK_NODE_STATUS_FRACTIONAL
@show status
end
if status == MOI.CALLBACK_NODE_STATUS_INTEGER
if sum(x̂) > 0
x′, y_sp, sp_cost = compute_sp_res(x̂, ŷ, V, n, tildeV, r, s)
hubs, master_cost = compute_master_res(x̂, ŷ, V, n, tildeV, o, r)
x_opt = run_two_opt_wiki(x̂, x′, hubs, sp_cost+master_cost, pars, n, r, rp, tildeV)[1]
for i in V
status = MOI.submit(
m, MOI.HeuristicSolution(cb_data), [x[i,j] for j in i+1:n], [x_opt[i,j] for j in i+1:n]
)
end
println("I submitted a heuristic solution, and the status was: ", status)
end
end
end
end
MOI.set(m, MOI.LazyConstraintCallback(), call_back_benders)
但是,Julia 告诉我这是一个无效的回调用法:
ERROR: InvalidCallbackUsage: Cannot submit MathOptInterface.HeuristicSolution{Gurobi.CallbackData}(Gurobi.CallbackData( sense : minimize
number of variables = 14952
number of linear constraints = 103
number of quadratic constraints = 0
number of sos constraints = 0
number of non-zero coeffs = 14952
number of non-zero qp objective terms = 0
number of non-zero qp constraint terms = 0
, Ptr{Nothing} @0x000055755c429d40, 4)) inside a
MathOptInterface.LazyConstraintCallback().
这是否意味着我不能使用 JuMP 的独立于求解器的回调?
在Gurobi中,只能将一个回调函数传递给求解器。在此函数内,您可以通过 where
参数将它们分开,从而为不同的场景定义多个操作,如 Python example:
def mycallback(model, where):
if where == GRB.Callback.PRESOLVE:
# Presolve callback
cdels = model.cbGet(GRB.Callback.PRE_COLDEL)
rdels = model.cbGet(GRB.Callback.PRE_ROWDEL)
if cdels or rdels:
print(f"{cdel} columns and {rdel} rows are removed")
elif where == GRB.Callback.SIMPLEX:
# Simplex callback
itcnt = model.cbGet(GRB.Callback.SPX_ITRCNT)
print(f"{itcnt} simplex iterations")
elif where == GRB.Callback.MIP:
# General MIP callback
pass
elif where == GRB.Callback.MIPSOL:
# MIP solution callback
pass
elif where == GRB.Callback.MIPNODE:
# MIP node callback
pass
这应该也可以通过 JuMP 实现,但您需要自己定义回调并将它们组合成一个函数。 JuMP 文档明确警告只能使用 set
函数定义一个回调:
You can only set each callback once. Calling set twice will over-write the earlier callback. In addition, if you use a solver-independent callback, you cannot set a solver-dependent callback.