Hi Oscar,
Thanks for the reply. But I think it must be somehow the dualization is doing something very weird for the hermitian variable I am actually considering. For real variables, it seems to work OK. But let me attach the following two examples, which really has the essence and also has similar size to the problem I encountered. The first is a julia piece of code. I declared a bunch of matrix called Dmatrix, all of which are Hermitian, then I vectorize them in Dvec. Then using a sparse map M, I map Dvec into T2vec, which is then reshaped into a 300 by 300 matrix, and Hermitianized, and required to be PSD. I then optimize some random linear function of Dvec. For the real problem, the map and the reshaping into 300 by 300 matrix is done in some particular way, but that’s not super important here. So I just make the map random sparse matrix.
using LinearAlgebra
using SparseArrays
using Random
using JuMP
using MosekTools
using Dualization
function minimal_psd_test(; use_dual=false, seed=1234)
Random.seed!(seed)
# Rough toy sizes only
NQ = 12 # number of q sectors
nred = 8 # size of the two small Hermitian blocks
nbig = 18 # size of the one larger Hermitian block
t2n = 300 # T2 matrix size
nzpr = 6 # sparse-map entries per row
# Total length of vectorized D variables
nd = NQ*nred^2 + NQ*nred^2 + NQ*nbig^2
# Random sparse map M : Dvec -> T2vec
rows = Int[]
cols = Int[]
vals = Float64[]
for r in 1:(t2n*t2n)
js = rand(1:nd, nzpr)
vs = randn(nzpr)
for k in 1:nzpr
push!(rows, r)
push!(cols, js[k])
push!(vals, vs[k])
end
end
M = sparse(rows, cols, vals, t2n*t2n, nd)
# Random objective coefficients
c = randn(nd)
if use_dual
model = Model(Dualization.dual_optimizer(Mosek.Optimizer))
else
model = Model(Mosek.Optimizer)
end
set_string_names_on_creation(model, false)
set_attribute(model, "MSK_IPAR_LOG", 10)
# Analog of your Dmatrix
Dmatrix = Vector{Any}(undef, 3)
Dmatrix[1] = Vector{Any}(undef, NQ)
Dmatrix[2] = Vector{Any}(undef, NQ)
Dmatrix[3] = Vector{Any}(undef, NQ)
for q in 1:NQ
Dmatrix[1][q] = @variable(model, [1:nred, 1:nred], Hermitian)
Dmatrix[2][q] = @variable(model, [1:nred, 1:nred], Hermitian)
Dmatrix[3][q] = @variable(model, [1:nbig, 1:nbig], Hermitian)
@constraint(model, Hermitian(Dmatrix[1][q]) in HermitianPSDCone())
@constraint(model, Hermitian(Dmatrix[2][q]) in HermitianPSDCone())
@constraint(model, Hermitian(Dmatrix[3][q]) in HermitianPSDCone())
end
# Vectorize in the same spirit as your real code
Dvec = vcat(
[vec(Dmatrix[1][q]) for q in 1:NQ]...,
[vec(Dmatrix[2][q]) for q in 1:NQ]...,
[vec(Dmatrix[3][q]) for q in 1:NQ]...
)
# Sparse affine map -> large matrix
T2vec = M * Dvec
T2raw = reshape(T2vec, t2n, t2n)
T2herm = 0.5 * (T2raw + T2raw')
@constraint(model, Hermitian(T2herm) in HermitianPSDCone())
@objective(model, Min, sum(c[i] * real(Dvec[i]) for i in 1:nd))
optimize!(model)
return model
end
Now either using dual or not using dual, the above piece of code will produce OOM error. I attach the error message below (using dual):
Problem
Name :
Objective sense : maximize
Type : CONIC (conic optimization problem)
Constraints : 101556
Affine conic cons. : 0
Disjunctive cons. : 0
Cones : 0
Scalar variables : 0
Matrix variables : 37 (scalarized: 191556)
Integer variables : 0
Optimizer started.
Presolve started.
Linear dependency checker started.
Linear dependency checker terminated.
Eliminator started.
Freed constraints in eliminator : 0
Eliminator terminated.
Eliminator - tries : 1 time : 0.00
Lin. dep. - tries : 1 time : 0.02
Lin. dep. - primal attempts : 1 successes : 1
Lin. dep. - dual attempts : 0 successes : 0
Lin. dep. - primal deps. : 0 dual deps. : 0
Presolve terminated. Time: 0.02
Optimizer terminated. Time: 27.53
Mosek.MosekError(1051, "")
Stacktrace:
[1] optimize
@ C:\Users\10519\.julia\packages\Mosek\Gzljt\src\msk_functions.jl:2399 [inlined]
[2] optimize!(m::MosekTools.Optimizer)
@ MosekTools C:\Users\10519\.julia\packages\MosekTools\hgngi\src\MosekTools.jl:356
[3] optimize!(dest::MosekTools.Optimizer, src::MathOptInterface.Utilities.UniversalFallback{…})
@ MathOptInterface C:\Users\10519\.julia\packages\MathOptInterface\7aTit\src\MathOptInterface.jl:122
[4] optimize!(m::MathOptInterface.Utilities.CachingOptimizer{…})
@ MathOptInterface.Utilities C:\Users\10519\.julia\packages\MathOptInterface\7aTit\src\Utilities\cachingoptimizer.jl:370
[5] optimize!
@ C:\Users\10519\.julia\packages\MathOptInterface\7aTit\src\Bridges\bridge_optimizer.jl:367 [inlined]
[6] optimize!
@ C:\Users\10519\.julia\packages\Dualization\WlGYB\src\MOI_wrapper.jl:255 [inlined]
[7] optimize!
@ C:\Users\10519\.julia\packages\MathOptInterface\7aTit\src\MathOptInterface.jl:122 [inlined]
[8] optimize!(m::MathOptInterface.Utilities.CachingOptimizer{…})
@ MathOptInterface.Utilities C:\Users\10519\.julia\packages\MathOptInterface\7aTit\src\Utilities\cachingoptimizer.jl:370
[9] optimize!
@ C:\Users\10519\.julia\packages\MathOptInterface\7aTit\src\Bridges\bridge_optimizer.jl:367 [inlined]
[10] optimize!
@ C:\Users\10519\.julia\packages\MathOptInterface\7aTit\src\MathOptInterface.jl:122 [inlined]
[11] optimize!(m::MathOptInterface.Utilities.CachingOptimizer{…})
@ MathOptInterface.Utilities C:\Users\10519\.julia\packages\MathOptInterface\7aTit\src\Utilities\cachingoptimizer.jl:370
[12] optimize!(model::Model; ignore_optimize_hook::Bool, _differentiation_backend::MathOptInterface.Nonlinear.SparseReverseMode, kwargs::@Kwargs{})
@ JuMP C:\Users\10519\.julia\packages\JuMP\0tD10\src\optimizer_interface.jl:610
[13] optimize!
@ C:\Users\10519\.julia\packages\JuMP\0tD10\src\optimizer_interface.jl:560 [inlined]
[14] minimal_psd_test(; use_dual::Bool, seed::Int64)
@ Main .\In[11]:84
[15] top-level scope
@ In[13]:1
Some type information was truncated. Use `show(err)` to see complete types.
Here is the error mesage for not using dual
Problem
Name :
Objective sense : minimize
Type : CONIC (conic optimization problem)
Constraints : 0
Affine conic cons. : 37 (191556 rows)
Disjunctive cons. : 0
Cones : 0
Scalar variables : 5424
Matrix variables : 0
Integer variables : 0
Optimizer started.
Presolve started.
Linear dependency checker started.
Linear dependency checker terminated.
Eliminator started.
Freed constraints in eliminator : 0
Eliminator terminated.
Eliminator - tries : 1 time : 0.00
Lin. dep. - tries : 1 time : 0.06
Lin. dep. - primal attempts : 1 successes : 1
Lin. dep. - dual attempts : 0 successes : 0
Lin. dep. - primal deps. : 0 dual deps. : 0
Presolve terminated. Time: 0.37
Optimizer terminated. Time: 3.06
Mosek.MosekError(1051, "")
Stacktrace:
[1] optimize
@ C:\Users\10519\.julia\packages\Mosek\Gzljt\src\msk_functions.jl:2399 [inlined]
[2] optimize!(m::MosekTools.Optimizer)
@ MosekTools C:\Users\10519\.julia\packages\MosekTools\hgngi\src\MosekTools.jl:356
[3] optimize!
@ C:\Users\10519\.julia\packages\MathOptInterface\7aTit\src\Bridges\bridge_optimizer.jl:367 [inlined]
[4] optimize!
@ C:\Users\10519\.julia\packages\MathOptInterface\7aTit\src\MathOptInterface.jl:122 [inlined]
[5] optimize!(m::MathOptInterface.Utilities.CachingOptimizer{…})
@ MathOptInterface.Utilities C:\Users\10519\.julia\packages\MathOptInterface\7aTit\src\Utilities\cachingoptimizer.jl:370
[6] optimize!(model::Model; ignore_optimize_hook::Bool, _differentiation_backend::MathOptInterface.Nonlinear.SparseReverseMode, kwargs::@Kwargs{})
@ JuMP C:\Users\10519\.julia\packages\JuMP\0tD10\src\optimizer_interface.jl:610
[7] optimize!
@ C:\Users\10519\.julia\packages\JuMP\0tD10\src\optimizer_interface.jl:560 [inlined]
[8] minimal_psd_test(; use_dual::Bool, seed::Int64)
@ Main .\In[11]:84
[9] top-level scope
@ In[14]:1
Some type information was truncated. Use `show(err)` to see complete types.
Now one can use SCS to as optimizer, that will execute through for this particular example in about 2 minutes. But let’s stick to mosek (I have specific reason I want to use mosek rather than SCS, since for some other problems it seems the mosek converges better), since matlab yalmip seems to use mosek in a different way. The same problem, if written in matlab, using yalmip and mosek, is the follwoing
function [sol, info] = minimal_psd_test_matlab()
rng(1234);
% rough toy sizes
NQ = 12;
nred = 8;
nbig = 18;
t2n = 300;
nzpr = 6;
nd = NQ*nred^2 + NQ*nred^2 + NQ*nbig^2;
% sparse random map M : Dvec -> T2vec
nrow = t2n*t2n;
rows = zeros(nrow*nzpr,1);
cols = zeros(nrow*nzpr,1);
vals = zeros(nrow*nzpr,1);
ptr = 1;
for r = 1:nrow
js = randi(nd, nzpr, 1);
vs = randn(nzpr, 1);
idx = ptr:(ptr+nzpr-1);
rows(idx) = r;
cols(idx) = js;
vals(idx) = vs;
ptr = ptr + nzpr;
end
M = sparse(rows, cols, vals, nrow, nd);
% random objective coefficients
c = randn(nd,1);
yalmip('clear');
Constraints = [];
% analog of Dmatrix
Dmatrix = cell(3, NQ);
for q = 1:NQ
Dmatrix{1,q} = sdpvar(nred, nred, 'hermitian', 'complex');
Dmatrix{2,q} = sdpvar(nred, nred, 'hermitian', 'complex');
Dmatrix{3,q} = sdpvar(nbig, nbig, 'hermitian', 'complex');
Constraints = [Constraints, Dmatrix{1,q} >= 0];
Constraints = [Constraints, Dmatrix{2,q} >= 0];
Constraints = [Constraints, Dmatrix{3,q} >= 0];
end
Dvec = [];
for q = 1:NQ
Dvec = [Dvec; Dmatrix{1,q}(:)];
end
for q = 1:NQ
Dvec = [Dvec; Dmatrix{2,q}(:)];
end
for q = 1:NQ
Dvec = [Dvec; Dmatrix{3,q}(:)];
end
T2vec = M * Dvec;
T2raw = reshape(T2vec, t2n, t2n);
T2herm = 0.5 * (T2raw + T2raw');
Constraints = [Constraints, T2herm >= 0];
Objective = real(c' * Dvec);
ops = sdpsettings('solver','mosek','verbose',1);
fprintf('NQ = %d\n', NQ);
fprintf('nred = %d\n', nred);
fprintf('nbig = %d\n', nbig);
fprintf('nd = %d\n', nd);
fprintf('t2n = %d\n', t2n);
fprintf('nnz(M) = %d\n', nnz(M));
sol = optimize(Constraints, Objective, ops);
info.problem = sol.problem;
info.info = sol.info;
info.yalmiptime = sol.yalmiptime;
info.solvertime = sol.solvertime;
if sol.problem == 0
info.objective_value = value(Objective);
else
info.objective_value = NaN;
end
end
Calling this function in matlab gives me the following output. The optimization is successful and has no OOM issue:
NQ = 12
nred = 8
nbig = 18
nd = 5424
t2n = 300
nnz(M) = 539748
MOSEK Version 11.1.10 (Build date: 2026-3-16 11:37:32)
Copyright (c) MOSEK ApS, Denmark WWW: mosek.com
Platform: Windows/64-X86
Problem
Name :
Objective sense : minimize
Type : CONIC (conic optimization problem)
Constraints : 5424
Affine conic cons. : 0
Disjunctive cons. : 0
Cones : 0
Scalar variables : 0
Matrix variables : 37 (scalarized: 191556)
Integer variables : 0
Optimizer started.
Presolve started.
Linear dependency checker started.
Linear dependency checker terminated.
Eliminator started.
Freed constraints in eliminator : 0
Eliminator terminated.
Eliminator - tries : 1 time : 0.00
Lin. dep. - tries : 1 time : 0.00
Lin. dep. - primal attempts : 1 successes : 1
Lin. dep. - dual attempts : 0 successes : 0
Lin. dep. - primal deps. : 0 dual deps. : 0
Presolve terminated. Time: 0.00
Optimizer - threads : 8
Optimizer - solved problem : the primal
Optimizer - Constraints : 5424
Optimizer - Cones : 0
Optimizer - Scalar variables : 0 conic : 0
Optimizer - Semi-definite variables: 37 scalarized : 191556
Factor - setup time : 1.63
Factor - dense det. time : 0.00 GP order time : 0.00
Factor - nonzeros before factor : 1.47e+07 after factor : 1.47e+07
Factor - dense dim. : 0 flops : 9.02e+11
ITE PFEAS DFEAS GFEAS PRSTATUS POBJ DOBJ MU TIME
0 5.9e+00 1.0e+00 1.0e+00 0.00e+00 0.000000000e+00 0.000000000e+00 1.0e+00 3.33
1 2.3e-01 4.0e-02 1.1e-01 -9.39e-01 0.000000000e+00 7.579916161e+00 4.0e-02 15.32
2 2.1e-04 3.5e-05 1.3e-06 9.52e-01 0.000000000e+00 9.472120673e-04 3.5e-05 27.54
3 1.2e-14 8.0e-19 1.8e-26 1.00e+00 0.000000000e+00 3.575075856e-16 8.0e-19 39.53
Optimizer terminated. Time: 39.54
Interior-point solution summary
Problem status : PRIMAL_AND_DUAL_FEASIBLE
Solution status : OPTIMAL
Primal. obj: 0.0000000000e+00 nrm: 1e+01 Viol. con: 3e-13 barvar: 0e+00
Dual. obj: 3.5750758560e-16 nrm: 8e-18 Viol. con: 0e+00 barvar: 7e-18
Optimizer summary
Optimizer - time: 39.54
Interior-point - iterations : 3 time: 39.54
Simplex - iterations : 0 time: 0.00
Mixed integer - relaxations: 0 time: 0.00
So I am now rather confused what exactly did YALMIP did such that JuMP didn’t do that saves me these memory? It must be something that’s beyond dualization that’s making YALMIP much more memory efficient?