Get the constraints matrix from a SDP relaxation

hello everyone :slight_smile:
I am using Julia, and I am programming some SDP. For this, I use JuMP to prepare the problem, then Hypatia to solve it. But, after the constraints are cleaned (by Hypatia), I would like to get the matrix of all constraints of the solver.

My setup looks like this:

model = Model( Hypatia.Optimizer)
@variable( model , ... )
@constraint( model , ... )
@objective( model , ... )

optimize!(model)
# 1243 of 2511 primal equality constraints are dependent
# get the matrix of the processed constraints

I found this object : backend(model).optimizer.model.optimizer.solver.
but after there are so many variables, I don’t know which variable is the one I am looking for ! I don’t know neither if it is in the list !
Is there a way to retrieve the constraint matrix that Hypatia uses internally?
Thanks a lot :slight_smile:
Gustave

I’m not sure if there is an easy way to get this from Hypatia.

You can get the Hypatia.Optimizer object with unsafe_backend(model). It has the .solver field.

Perhaps take a look at the fields of unsafe_backend(model).solver.model. But I don’t think there’s an easy way to convert this back to JuMP.

Finally I get the raw constraints, then I process them by myself. Are you interessed in the code ? ChatGPT helped me a bit :slight_smile:
Thanks for your answer :slight_smile:

1 Like

Only if the code is simple. It might be useful to someone else in the future :smile:

OK ! here is the code. Sorry for having made you wait ! :slight_smile:

# get the constraints in some matrix A_sparse
function get_constraints(model::Model)
    rows = Int[]
    cols = Int[]
    A = ComplexF64[]
    b = ComplexF64[]

    i = 1
    for ci in all_constraints(model, include_variable_in_set_constraints=true)
        con = JuMP.constraint_object(ci)
        if con.func isa GenericAffExpr{Complex{Float64}, VariableRef} 

            f = con.func
            rhs = con.set.value
            for (var, coeff) in f.terms
                push!(rows, i)
                push!(cols, var.index.value)
                push!(A, coeff)
            end
            push!(b, rhs - f.constant)
            i += 1
        end
    end

    n_constr = length(b)
    n_vars = num_variables(model)
    A_sparse = sparse(rows, cols, A, n_constr, n_vars)

    return Matrix(A_sparse)
end

# process to clean the constraints
function remove_dependent_constraints(A::Matrix{ComplexF64}; tol=1e-10)
    Q, R, p = qr(A', Val(true)) # A' or A ? I am not sure !
    diagR = abs.(diag(R))
    rankA = count(x -> x > tol, diagR)

    independent_rows = Vector(p)[1:rankA]

    return A[independent_rows, :]  # or select the columns ?
end
1 Like

I assume you’re aware that there will be other constraints in your model that you’re not capturing here? You’re also making an assumption that var.index.value is the column, which may not always be true.

You might want to do something like this:

function get_constraints(model::Model)
    x_to_col = Dict(i => xi for (i, xi) in enumerate(all_variables(model)))
    I, J, V, b = Int[], Int[], ComplexF64[], ComplexF64[]
    F, S = GenericAffExpr{ComplexF64,VariableRef}, MOI.EqualTo{ComplexF64}
    for (row, ci) in enumerate(all_constraints(model, F, S))
        con = JuMP.constraint_object(ci)
        for (var, coeff) in con.func.terms
            push!(I, row)
            push!(J, x_to_col[var])
            push!(V, coeff)
        end
        push!(b, con.set.value - con.func.constant)
    end
    return SparseArrays.sparse(I, J, V, length(b), length(x_to_col))
end
1 Like

I have tested your code, but I found a bug … here is the patch :

x_to_col = Dict(xi => i for (i, xi) in enumerate(all_variables(model)))

I think it works now :slight_smile: Thanks for the help !

1 Like