I’m trying to solve the following SDP problem
\begin{align*}
maximize \ & \sum_j s_j\\
\text{subject to } \ & 2\Sigma - diag(s) \succeq 0,\\
& 0 \le s_j \le 1
\end{align*}
Julia’s SCS solver with Convex.jl is 100x slower than Matlab’s CVX, and it seems like this is because the latter solves the dual problem (which requires a lot less iteration?). How can I get Julia’s solver to be as fast as Matlab?
MWE:
using LinearAlgebra
using Random
using ToeplitzMatrices
using MATLAB
using Convex
using SCS
# simulate data
Random.seed!(2022)
n = 100
p = 200
ρ = 0.4
Sigma = Matrix(SymmetricToeplitz(ρ.^(0:(p-1))))
Solve in Julia (109 seconds):
function julia_sdp(Sigma::Matrix)
p = size(Sigma, 1)
svar = Variable(p)
problem = maximize(sum(svar), svar ≥ 0, 1 ≥ svar, 2*Sigma - Diagonal(svar) == Semidefinite(p))
solve!(problem, SCS.Optimizer())
return evaluate(svar)
end
@time s_julia = julia_sdp(Sigma) # compile
@time s_julia = julia_sdp(Sigma)
------------------------------------------------------------------
SCS v3.0.0 - Splitting Conic Solver
(c) Brendan O'Donoghue, Stanford University, 2012
------------------------------------------------------------------
problem: variables n: 40201, constraints m: 80401
cones: z: primal zero / dual free vars: 59901
l: linear vars: 400
s: psd vars: 20100, ssize: 1
settings: eps_abs: 1.0e-04, eps_rel: 1.0e-04, eps_infeas: 1.0e-07
alpha: 1.50, scale: 1.00e-01, adaptive_scale: 1
max_iters: 100000, normalize: 1, warm_start: 0
acceleration_lookback: 10, acceleration_interval: 10
lin-sys: sparse-direct
nnz(A): 100701, nnz(P): 0
------------------------------------------------------------------
iter | pri res | dua res | gap | obj | scale | time (s)
------------------------------------------------------------------
0| 8.69e+00 9.98e-01 1.69e+03 -1.09e+03 1.00e-01 2.40e-02
250| 6.13e-03 4.90e-04 2.35e-01 -1.75e+02 5.22e-01 4.40e+00
500| 2.62e-03 1.79e-04 1.02e-01 -1.73e+02 5.22e-01 9.49e+00
750| 1.72e-03 9.00e-05 6.30e-02 -1.73e+02 5.22e-01 1.51e+01
1000| 1.36e-03 6.81e-05 5.18e-02 -1.73e+02 5.22e-01 2.17e+01
1250| 1.02e-03 5.15e-05 3.82e-02 -1.73e+02 5.22e-01 2.85e+01
1500| 8.86e-04 4.53e-05 3.45e-02 -1.73e+02 5.22e-01 3.61e+01
1750| 8.61e+00 4.50e+00 4.15e-01 -1.75e+02 5.22e-01 4.42e+01
2000| 6.42e-04 3.26e-05 2.48e-02 -1.72e+02 5.22e-01 5.25e+01
2250| 5.36e-04 2.66e-05 1.99e-02 -1.72e+02 5.22e-01 6.06e+01
2500| 5.53e-04 2.89e-05 2.20e-02 -1.72e+02 5.22e-01 6.82e+01
2750| 4.47e-04 2.23e-05 1.70e-02 -1.72e+02 5.22e-01 7.48e+01
3000| 4.60e-04 2.41e-05 1.84e-02 -1.72e+02 5.22e-01 8.10e+01
3250| 3.65e-04 1.81e-05 1.37e-02 -1.72e+02 5.22e-01 8.70e+01
3500| 3.71e-04 1.92e-05 1.47e-02 -1.72e+02 5.22e-01 9.29e+01
3700| 2.99e-04 8.99e-05 9.86e-03 -1.72e+02 5.22e-01 9.76e+01
------------------------------------------------------------------
status: solved
timings: total: 9.77e+01s = setup: 8.60e-02s + solve: 9.76e+01s
lin-sys: 1.09e+01s, cones: 8.10e+01s, accel: 7.57e-01s
------------------------------------------------------------------
objective = -172.217851
------------------------------------------------------------------
109.175072 seconds (891.39 k allocations: 88.280 MiB, 0.05% gc time, 0.03% compilation time)
Solve with Matlab’s CVX (1.1 seconds)
function matlab_sdp(Sigma::Matrix)
p = Float64(size(Sigma, 1))
@mput Sigma p
mat"""
cvx_begin
variable s(p);
maximize(sum(s));
2*Sigma-diag(s) == semidefinite(p);
0 <= s <= 1;
cvx_end
"""
@mget s
return s
end
@time s_matlab = matlab_sdp(Sigma) # compile
@time s_matlab = matlab_sdp(Sigma)
>> >> >> >> >> >> >> >>
Calling SDPT3 4.0: 20500 variables, 200 equality constraints
For improved efficiency, SDPT3 is solving the dual problem.
------------------------------------------------------------
num. of constraints = 200
dim. of sdp var = 200, num. of sdp blk = 1
dim. of linear var = 400
*******************************************************************
SDPT3: Infeasible path-following algorithms
*******************************************************************
version predcorr gam expon scale_data
HKM 1 0.000 1 0
it pstep dstep pinfeas dinfeas gap prim-obj dual-obj cputime
-------------------------------------------------------------------
0|0.000|0.000|1.9e+02|1.6e+01|1.5e+06| 8.400000e+04 0.000000e+00| 0:0:00| chol 1 1
1|1.000|0.524|4.4e-05|7.8e+00|6.2e+05| 4.042192e+04 -1.699825e+03| 0:0:00| chol 1 1
2|1.000|1.000|5.2e-05|5.9e-02|3.7e+04| 3.332107e+04 -6.961080e-01| 0:0:00| chol 1 1
3|0.990|1.000|1.2e-06|1.8e-02|5.9e+02| 5.836344e+02 1.060232e+01| 0:0:00| chol 1 1
4|1.000|1.000|9.2e-08|1.8e-03|2.4e+02| 3.333237e+02 9.407462e+01| 0:0:00| chol 1 1
5|1.000|0.807|5.8e-10|4.9e-04|7.2e+01| 2.405317e+02 1.682483e+02| 0:0:00| chol 1 1
6|0.798|1.000|1.0e-10|1.8e-05|3.0e+01| 1.946140e+02 1.646145e+02| 0:0:00| chol 1 1
7|0.888|0.866|2.1e-11|3.9e-06|3.8e+00| 1.754671e+02 1.716357e+02| 0:0:00| chol 1 1
8|0.894|0.907|3.7e-12|5.3e-07|1.2e+00| 1.730688e+02 1.718583e+02| 0:0:00| chol 1 1
9|0.845|1.000|1.2e-12|1.8e-08|3.3e-01| 1.722023e+02 1.718724e+02| 0:0:00| chol 1 1
10|0.933|0.938|8.0e-14|2.8e-09|2.5e-02| 1.719021e+02 1.718774e+02| 0:0:00| chol 1 1
11|0.940|0.993|1.2e-14|2.0e-11|1.7e-03| 1.718792e+02 1.718775e+02| 0:0:00| chol 1 1
12|0.964|0.988|1.0e-14|1.2e-12|5.9e-05| 1.718776e+02 1.718776e+02| 0:0:01| chol 1 1
13|1.000|1.000|9.6e-12|1.0e-12|7.5e-06| 1.718776e+02 1.718776e+02| 0:0:01| chol 1 1
14|1.000|1.000|3.8e-13|1.5e-12|1.3e-07| 1.718776e+02 1.718776e+02| 0:0:01|
stop: max(relative gap, infeasibilities) < 1.49e-08
-------------------------------------------------------------------
number of iterations = 14
primal objective value = 1.71877551e+02
dual objective value = 1.71877551e+02
gap := trace(XZ) = 1.33e-07
relative gap = 3.87e-10
actual relative gap = 3.86e-10
rel. primal infeas (scaled problem) = 3.78e-13
rel. dual " " " = 1.50e-12
rel. primal infeas (unscaled problem) = 0.00e+00
rel. dual " " " = 0.00e+00
norm(X), norm(y), norm(Z) = 2.0e+02, 1.2e+01, 2.7e+01
norm(A), norm(b), norm(C) = 2.5e+01, 1.5e+01, 3.7e+01
Total CPU time (secs) = 0.56
CPU time per iteration = 0.04
termination code = 0
DIMACS: 2.9e-12 0.0e+00 1.9e-11 0.0e+00 3.9e-10 3.9e-10
-------------------------------------------------------------------
------------------------------------------------------------
Status: Solved
Optimal value (cvx_optval): +171.878
1.157232 seconds (17.41 k allocations: 957.003 KiB, 1.95% compilation time)
Check answer is the same:
[s_julia s_matlab]
200×2 Matrix{Float64}:
0.999983 1.0
0.97131 0.971428
0.811161 0.811429
0.875385 0.875428
0.849962 0.849829
0.86042 0.860068
0.85656 0.855973
0.858421 0.857611
0.857984 0.856956
0.858443 0.857218
0.858523 0.857113
0.858736 0.857155
0.858881 0.857138
⋮
Any help in speeding up the Julia code would be greatly appreciated.