The source files for all examples can be found in /examples.
Semidefinite Program Decomposition
This example illustrates how to decompose a positive semidefinite constraint with Chordal.jl
. The decomposed constraint can then be plugged into your favorite semidefinite program (SDP) solver.
Problem Setup
We first generate a random SDP using generate_random_sdp(n)
, which constructs problems of the form
\[\begin{array}{ll} \text{minimize} &c^Tx \\ \text{subject to} & \sum_{i=1}^m F_ix_i + G \succeq 0. \end{array}\]
using Chordal
using JuMP, SCS
using SparseArrays, LinearAlgebra, Random
rand_seed = 1234
n = 500
optimizer = SCS.Optimizer
# D is the dual variable and xstar is an optimal solution
c, F, G, xstar, D = Chordal.generate_random_sdp(n, rand_seed=rand_seed);
Standard Solve
First, we build the SDP using JuMP
and solve it using SCS
without chordal decomposition.
# Without decomposition
m = Model(optimizer)
JuMP.set_silent(m)
@variable(m, x[1:n])
@constraint(m, sum(F[i]*x[i] for i in 1:n) + G ∈ PSDCone())
@objective(m, Min, dot(c, x))
time_non_chordal = @elapsed optimize!(m)
xv = value.(x)
pstar = dot(c,xv)
@info "termination status: $(termination_status(m))"
@info "solution status: $(primal_status(m))"
@info "Difference with true optimal: $(norm(xv - xstar))"
@info "Optimal value: $pstar"
[ Info: termination status: OPTIMAL
[ Info: solution status: FEASIBLE_POINT
[ Info: Difference with true optimal: 2.29364353978583e-8
[ Info: Optimal value: 1323.6334474977025
Solving after Chordal Decomposition
Next, we use chordal decomposition tools from Chordal.jl
to decompose the PSD constraint into smaller blocks.
# Chordal Decomposition Setup
m2 = Model(optimizer)
JuMP.set_silent(m2)
@variable(m2, y[1:n])
# build_A is a helper function to construct the SDP
# A = sum(F[i]*y[i] for i in 1:n) + G
# A + S ∈ PSDCone(), where S ⪰ 0
A = Chordal.build_A(y, F, G)
nnzA = nnz(A)
@info "There are $nnzA nonzeros; density = $(round(nnzA/n^2, digits=3))"
time_constraints = @elapsed build_constraints_lmi!(m2, A)
# NOTE: Can also call build_constraints_lmi!(m2, y, F, G)
@info "Finished adding constraints, time = $(round(time_constraints, digits=3))s"
@objective(m2, Min, dot(c,y))
@info "Finished setup"
[ Info: There are 4898 nonzeros; density = 0.02
[ Info: Finished adding constraints, time = 6.031s
[ Info: Finished setup
We can solve the decomposed model with any SDP solver (here we use SCS).
# Chordal Decomposition Solve
time_chordal = @elapsed optimize!(m2)
yv = value.(y)
pstar_chordal = dot(c,yv)
@info "termination status: $(termination_status(m2))"
@info "solution status: $(primal_status(m2))"
@info "difference with non-chordal: $(norm(yv - xv))"
@info "difference with true variable: $(norm(yv - xstar))"
@info "Time with chordal is $(round(time_chordal, digits=3))s vs $(round(time_non_chordal, digits=3))s"
[ Info: termination status: OPTIMAL
[ Info: solution status: FEASIBLE_POINT
[ Info: difference with non-chordal: 8.23848422119879e-7
[ Info: difference with true variable: 8.317162905774553e-7
[ Info: Time with chordal is 2.585s vs 12.748s
This page was generated using Literate.jl.