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.