Resolving segfault error when attempting to sequentially add constraints to a JuMP model

I am attempting to resolve a segfault error, which seems to be stemming from a line of code that tries to add to an empty dictionary new constraints iteratively in a while loop. I am wondering what the best way would be to add new constraints to an existing JuMP model, so that the updated model can be resolved at each iteration while considering both new constraints and old constraints previously used in a past solve.

My attempt consists of defining an empty dictionary, and then passing that dictionary into a function which solves a feasibility problem, which returns the updated dictionary of constraints for that feasibility problem. This updated dictionary should then be passed back into the function at another iteration, however, does not get past the first iteration and is returning the following error:

MethodError: no method matching setindex!(::Nothing, ::ConstraintRef{Model, MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.GreaterThan{Float64}}, ScalarShape}, ::Int64)

There are two functions in question (a primary one and another where the error is located it looks like) which are constructed as follows:

function SLBLR(λ0, s0, γ, ν, ζ, q0, ϵ_gap, ϵ, max_iter, time_limit, qmax, A, b, c) # ζ = 1/1.5, ν = 2, s0 = 0.02, γ = 1/I
    m = size(A,1) 
    n = size(A,2)
    ck = Dict() 
    iter_counter = 1 
    i = 1
    k = 1
    j = 1
    λk = λ0
    sk = s0
    qj = q0 
    x̃k = solve_relaxed_problem(A,b,c,λk,true) 
    timer = time() 
    while true
        if k != 1
            x̃k_new = solve_subproblem(A,b,c, λk, x̃k, i)
            x̃k = x̃k_new
        end

        gx̃ = [] 
        for i in 1:m
            push!(gx̃, sum(A[i,j] * x̃k[i,j] for j in 1:n) - b[i]) 
        end 
        Lx̃λ̃ = sum(sum(  c[i,j] * x̃k[i,j] for j in 1:n) for i in 1:m) + sum(λk[i] * gx̃[i] for i in 1:m) 
        sk = ζ * γ * (qj - Lx̃λ̃) / norm(gx̃)^2  

        λk_new = λk + sk * gx̃ 

        if qmax < sk * norm(gx̃)^2 / γ + Lx̃λ̃
            qmax = sk * norm(gx̃)^2 / γ + Lx̃λ̃
        end
        # update indices

        if i == m
            i = 1
        else 
            i += 1
        end
        k += 1
        if constraint_satisfiability_problem(λk, λk_new, m, k, ck, ν, sk, gx̃) == "INFEASIBLE" 
            qj = qmax
            qmax = -1e9 
            j += 1
            ck = Dict() ## reset the constraint set for the feasibility problem 
        else
            constraints_λ = constraint_satisfiability_problem(λk, λk_new, m, k, ck, ν, sk, gx̃)
            ck = constraints_λ
        end

        λk = λk_new 

        qλk = solve_relaxed_problem(A,b,c,λk, false)

        if (qj - qλk)/qj < ϵ 
            x̂ = feasibility_milp(A,b, x̃k, 3) 
            fx̂ = sum( sum(c[i,j]*x̂[i,j] for i in 1:m ) for j in 1:n)

            if (fx̂ - qλk)/fx̂ <= ϵ_gap
                println("feasible objective value: ", fx̂) 
                return args["elapsed_time"], iter_counter
                break
            end 

        end
        
        args["elapsed_time"] = timer - time() ## update the timer 
        iter_counter += 1 ## update the iteration count 
    end
end
function constraint_satisfiability_problem(λk, λk_new, m, k, ck, ν, sk, gx̃) 
    model = Model(CPLEX.Optimizer) 

    @variable(model, λ[1:m] >= 0)

    ck[k] = @constraint(model, 2*(λ - λk)' * gx̃ >= sk 
    optimize!(model)

    ck_new = ck 

    if termination_status(model) == "INFEASIBLE"
        return termination_status(model)
    elseif termination_status(model) == "OPTIMAL"
        λ_out = value.(λ) 
        return λ_out, ck_new 
    end
end 

The line that is returning the error is this one:

ck[k] = @constraint(model, 2*(λ - λk)' * gx̃ >= sk * norm(gx̃)^2)

and the debugger is showing that the dictionary in question, ck, has value nothing, which I am thinking may be related to the error mentioned previously.

There are several other “helper” functions which I can include as well, if they are helpful. Though, I am mainly wondering if there is an efficient way to add constraints to the subproblem outlined in constraint_satisfiability_problem at each new iteration and subsequently resolve the segfault error.

Hi @jcclug, welcome to the forum.

Your error is that ck has become nothing.

It’s probably this line:

ck = constraints_λ

but you have much bigger issues with your function constraint_satisfiability_problem . This part is wrong:

    if termination_status(model) == "INFEASIBLE"
        return termination_status(model)
    elseif termination_status(model) == "OPTIMAL"

This should be == INFEASIBLE and == OPTIMAL. INFEASIBLE and OPTIMAL are objects from JuMP. They are not Strings.

You should also re-think how and what you are returning. You probably want something like:

        ret = constraint_satisfiability_problem(λk, λk_new, m, k, ck, ν, sk, gx̃)
        if ret == JuMP.INFEASIBLE
            qj = qmax
            qmax = -1e9 
            j += 1
            ck = Dict() ## reset the constraint set for the feasibility problem 
        else
            λk, ck = ret  # Perhaps?
        end

Before trying to solve a big problem end-to-end, I would test your constraint_satisfiability_problem function in isolation. Does it do what you expect it should do?

For example, another issue is a missing closing parenthesis in: ck[k] = @constraint(model, 2*(λ - λk)' * gx̃ >= sk

Hi @odow, thank you for the quick response, feedback on the errors and general advice. This resolved the issue I was having.

1 Like

No problem. From a glance there are plenty of other things to improve, but they can wait until you’ve got something working. Feel free to post other questions. (Just as an aside: reproducible examples that someone can copy-paste and run into the same problem as you are always more helpful than small code snippets that do not run.)

As some hints, you might consider replacing

        if qmax < sk * norm(gx̃)^2 / γ + Lx̃λ̃
            qmax = sk * norm(gx̃)^2 / γ + Lx̃λ̃
        end

with

        qmax = max(qmax, sk * norm(gx̃)^2 / γ + Lx̃λ̃)

Instead of

ck = Dict() ## reset the constraint set for the feasibility problem 

you can do

empty!(ck)

and you may want to strongly type

gx̃ = [] 

as

gx̃ = Float64[] 

(I’m assuming this last part because I can’t run your code. You might have something weird going on with A and b.)

1 Like

These are some great suggestions, thank you for that. I will admit that the code is a work in progress and could use some improvement overall (in addition to the original errors, of course), so I appreciate the feedback.

I was not aware of empty!, and the suggestion to write the if statement in that way is nice. gx̃ is a vector of integers since A, b, and x̃k are all integer. (This would be clear if I provided more of the code, sorry about not doing that.) However, I will take the suggestion of strongly defining

gx̃ = Int64[]

The rest of the script is lengthy, so it was originally excluded for brevity. Though, in the future I will do my best to provide a minimum working example. :smile:

Thank you again!

1 Like