Seeking low rank solution for two PSD matrices via `SDPLR.jl` in `JuMP`

I am trying the SDPLR.jl solver in JuMP for finding low-rank solutions to SDPs and it is working quite nicely when the underlying semidefinite optimization problem has only one PSD variable X. How do I apply SDPLR.jl in JuMP when I have two SDP matrices X and Y, where I want to seek a rank 1 solution for X and say rank 2 solution on Y. The README.md file for SDPLR.jl is not super clear in this regard of this can be done.

I will appreciate any tips/suggestions on this. If there are other SDP solvers that gives sparse/low-rank solutions to SDPs in JuMP ecosystem, please let me know.

This is a question for @blegat. He’s been working on the low-rank stuff.

Have you tried something along these lines?

using LinearAlgebra, JuMP, SDPLR, MathOptInterface
const MOI = MathOptInterface

nx = 4   # size of X
ny = 6   # size of Y

model = Model(SDPLR.Optimizer)

@variable(model, X[1:nx, 1:nx], PSD)
@variable(model, Y[1:ny, 1:ny], PSD)

# objective and constraints ...

set_attribute(model, "maxrank", function(m, n)
    if n == nx
        return 1   # rank-1 factor for X
    elseif n == ny
        return 2   # rank-2 factor for Y
    else
        return SDPLR.default_maxrank(m, n)   # fallback
    end
end)

optimize!(model)

Fx = MOI.get(model, SDPLR.Factor(), VariableInSetRef(X))  # nx×1
Fy = MOI.get(model, SDPLR.Factor(), VariableInSetRef(Y))  # ny×2 or paste code here

            ***   SDPLR 1.03-beta   ***

===================================================
 major   minor        val        infeas      time  
---------------------------------------------------
    1        0   0.00000000e+00  0.0e+00       0
===================================================

DIMACS error measures: 0.00e+00 0.00e+00 0.00e+00 -0.00e+00 0.00e+00 0.00e+00


julia> Fx = MOI.get(model, SDPLR.Factor(), VariableInSetRef(X))  # nx×1
4×1 Matrix{Float64}:
 -0.5556374349255623
 -0.20338580709894138
 -0.050108647317640864
 -0.25648261076643797

julia> Fy = MOI.get(model, SDPLR.Factor(), VariableInSetRef(Y))  # ny×2
6×2 Matrix{Float64}:
 -0.318264   -0.482569
 -0.413709    0.709417
  0.264882    0.686019
 -0.125805   -0.170905
  0.0918272   0.0715626
 -0.650191   -0.139925

Thanks for your response. In my test case, the size of the matrices could be the same, i.e., nx can be equal to ny, so it would be great if there is a way to seek low rank solution to matrices of same size.

ny = 4 in the example doesn’t behave as expected?

Good questions. Indeed if they have the same dimension, setting a function probably doesn’t work. My plan is to define variable attributes for this in LowRankOpt.jl that would then behave uniformly across Burer-Motneiro solvers such as SDPLR and SDPLRPlus.
At the moment, if you use SDPLRPlus using the LowRankOpt interface, you can use the ranks optimizer attribute:

It is not ideal since you’re giving a list of ranks that should be of length equal to the number of PSD variables which is why the goal is to have variable attributes instead.
This will be discussed during my talk at JuMP-dev 2026 and hopefully there will be variable attributes by then :slight_smile:

1 Like

Thanks for your response. When nx = ny then the code will just impose rank 2 on both, I think.

Thanks @blegat , for now I will try the LowRankOpt.jl approach that you suggested. Looking forward to your talk at JuMP-dev!

Thanks, also looking forward to your talk on PEPit.jl, are your trying to find low-rank solutions for PEPit ?

1 Like

Thank you @blegat !PEPit.jl mostly involves single PSD matrix and for that SDPLR.jl works well already, logdet type heuristics also work well. Actually I am actually trying to apply SDPLR.jl to Problem (63) of https://arxiv.org/pdf/2510.03855 , which has 4 interconnected PSD matrices. I have found GitHub - nanleij/ClusteredLowRankSolver.jl: A semidefinite programming solver for clustered low-rank SDPs very useful in this regard so far, it does not have direct JuMP connection and the built-in interior-point solver is not robust, but the rounding procedure described at ClusteredLowRankSolver.jl/src/rounding.jl at main · nanleij/ClusteredLowRankSolver.jl · GitHub was very useful for me (I had to modify it for my setup to use it). It may be useful if some variant of it can be added to LowRankOpt.jl.

1 Like