# Memory blow-up when solving a sparse affine Hermitian SDP translated from YALMIP to JuMP

**URL:** <https://discourse.julialang.org/t/memory-blow-up-when-solving-a-sparse-affine-hermitian-sdp-translated-from-yalmip-to-jump/137744>\
**Category:** Optimization (Mathematical)\
**Tags:** jump, memory, dualization\
**Created:** [June 22, 2026, 3:10pm UTC](https://discourse.julialang.org/t/memory-blow-up-when-solving-a-sparse-affine-hermitian-sdp-translated-from-yalmip-to-jump/137744 "2026-06-22T15:10:11Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![hz-xiaxz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hz-xiaxz/32/209585_2.png) [@hz-xiaxz](https://discourse.julialang.org/u/hz-xiaxz)\
**Post date:** [June 22, 2026, 3:10pm UTC](https://discourse.julialang.org/t/memory-blow-up-when-solving-a-sparse-affine-hermitian-sdp-translated-from-yalmip-to-jump/137744/1 "2026-06-22T15:10:12Z")

</div>

Hi JuMP developers,

I am translating a MATLAB/YALMIP semidefinite program to Julia/JuMP + MosekTools. The model comes from a many-body bootstrap calculation. The MATLAB/YALMIP version can solve a small `3 x 3` lattice case, but my direct JuMP translation runs into severe memory issues when solving the full SDP.

I would appreciate advice on whether my JuMP formulation is inefficient, whether I should use MathOptInterface directly, or whether this is an expected solver-side factorization issue.

Disclosure: I prepared this post with AI assistance to make the description precise and reproducible, but the code, measurements, and problem setup are from my local experiments.

## Problem summary

The model has:

- complex-valued decision variables,
- sparse affine maps from those variables into large Hermitian PSD constraints,
- two large T2 PSD blocks in the `3 x 3` case:
  - one `81 x 81` Hermitian PSD block,
  - one `162 x 162` Hermitian PSD block.

The JuMP model can be constructed successfully. The problem occurs during `optimize!`, apparently in the MOSEK/MOI bridge/solver stage.

## Current modeling approach

I use complex JuMP variables:

```julia
using JuMP
import MathOptInterface as MOI
import Dualization
import Mosek
import MosekTools

model = use_dual ?
    Model(Dualization.dual_optimizer(Mosek.Optimizer)) :
    Model(Mosek.Optimizer)

@variable(model, M_D[1:Nk, 1:Nk, 1:Nk, 1:3] in ComplexPlane())
@variable(model, rho_k_sz[1:Nk, 1:2])

```

The difficult part is a sparse affine map into the T2 constraints:

```julia
D_vec = vec(M_D)
rho_var_vec = vec(rho_k_sz)

T2_vec = affine_map(A, D_vec) .+
         affine_map(B, rho_var_vec) .+
         extra

M_T2 = reshape(T2_vec, Nk^2, Nk^2, 1, 5)

```

For the `3 x 3` case, `Nk = 9`, so:

```plaintext
A size = (32805, 2187), nnz = 7481
B size = (32805, 18), nnz = 986
length(T2_vec) = 32805

```

The two T2 PSD constraints are:

```julia
# block 1: 81 x 81 Hermitian PSD
M_T2[:, :, 1, 1] + M_T2[:, :, 1, 1]'

# block 2: 162 x 162 Hermitian PSD
[
    M_T2[:, :, 1, 2] M_T2[:, :, 1, 3]
    M_T2[:, :, 1, 4] M_T2[:, :, 1, 5]
]

```

Initially I used:

```julia
@constraint(model, LinearAlgebra.Hermitian(H) in HermitianPSDCone())

```

Then I rewrote this to avoid constructing the full Hermitian matrix and instead pass the triangular vector directly to MathOptInterface:

```julia
@constraint(
    model,
    terms in MOI.HermitianPositiveSemidefiniteConeTriangle(n),
)

```

where `terms` is ordered as required by MOI:

1. real parts of the upper triangular entries, column by column;
2. imaginary parts of the strict upper triangular entries, column by column.

This avoids an extra full matrix allocation, but the solver-side problem remains large.

## What I tried

### 1. Direct primal formulation

With:

```julia
model = Model(Mosek.Optimizer)

```

the `3 x 3` model builds, but the process is killed by the Linux OOM killer during `optimize!`.

A build-only diagnostic succeeds:

```julia
model, t = bootstrapSC(
    3, 3, 2, [0.01, 0.0];
    use_T2 = true,
    build_only = true,
)

```

Result:

```plaintext
vars = 4392
constraints = 1290

```

So the model construction itself completes.

### 2. Dualization.jl

Using:

```julia
model = Model(Dualization.dual_optimizer(Mosek.Optimizer))

```

improves the situation. The same `3 x 3` model reaches MOSEK and can complete one interior-point iteration if I set:

```julia
set_attribute(model, "MSK_IPAR_INTPNT_MAX_ITERATIONS", 1)

```

The MOSEK log for the dualized model is:

```plaintext
Problem
  Objective sense : maximize
  Type : CONIC
  Constraints : 46998
  Scalar variables : 2377
  Matrix variables : 83 (scalarized: 84159)

Optimizer
  Constraints : 46908
  Scalar variables : 2288
  Semi-definite variables: 83
  scalarized : 84159

Factor
  setup time : 61.26
  dense det. time : 20.98
  nonzeros before factor : 4.35e+08
  flops : 7.75e+12

```

This confirms that `dual_optimizer` helps avoid immediate OOM, but the factorization is still very large.

## Comparison with MATLAB/YALMIP

In MATLAB/YALMIP, the corresponding code is essentially:

```matlab
M_T2 = reshape( ...
    A * reshape(M_D, [], 1) + ...
    B * reshape(rho, [], 1) + ...
    extra, ...
    [Nk^2, Nk^2, 1, num_Sz_T2] ...
);

Constraints = [
    Constraints,
    M_T2(:,:,1,1) + M_T2(:,:,1,1)' >= 0,
    [
        M_T2(:,:,1,2), M_T2(:,:,1,3);
        M_T2(:,:,1,4), M_T2(:,:,1,5)
    ] >= 0
];

```

Here `A` and `B` are sparse matrices. YALMIP appears to handle the sparse symbolic affine maps more efficiently for this small case.

## Questions

1. Is `Dualization.dual_optimizer(Mosek.Optimizer)` the recommended approach for this type of SDP?

2. Is there a more memory-efficient way in JuMP to model sparse affine maps into Hermitian PSD cones?

3. Should I bypass JuMP expressions and build an `MOI.VectorAffineFunction` manually for the PSD cone constraints?

4. Would manually reformulating the Hermitian PSD constraints as real symmetric PSD constraints reduce memory or factorization size, or does MOI already do this optimally?

5. Are there best practices for avoiding huge factorization sizes in structured SDP models with a small number of large PSD blocks?

6. Is this likely a JuMP/MOI modeling issue, a bridge issue, or simply the expected cost of this SDP formulation as passed to MOSEK?

I am happy to provide the full reproducible code if useful. Any advice on the preferred JuMP/MOI formulation would be greatly appreciated.

---

<div class="post-metadata">

**Author:** ![araujoms](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/araujoms/32/217734_2.png) [@araujoms](https://discourse.julialang.org/u/araujoms)\
**Post date:** [June 22, 2026, 4:23pm UTC](https://discourse.julialang.org/t/memory-blow-up-when-solving-a-sparse-affine-hermitian-sdp-translated-from-yalmip-to-jump/137744/2 "2026-06-22T16:23:16Z")

</div>

I would try SCS instead, as it’s better suited for large scale problems and has a native complex PSD cone, which cuts dimension in half.

---

<div class="post-metadata">

**Author:** ![hz-xiaxz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hz-xiaxz/32/209585_2.png) [@hz-xiaxz](https://discourse.julialang.org/u/hz-xiaxz)\
**Post date:** [June 22, 2026, 5:30pm UTC](https://discourse.julialang.org/t/memory-blow-up-when-solving-a-sparse-affine-hermitian-sdp-translated-from-yalmip-to-jump/137744/3 "2026-06-22T17:30:41Z")

</div>

Yes this did solve the OOM problem with the cost of speed… Thanks for mentioning SCS and thank you Mateus your blog on SDP also helps a lot!

---

<div class="post-metadata">

**Author:** ![hz-xiaxz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hz-xiaxz/32/209585_2.png) [@hz-xiaxz](https://discourse.julialang.org/u/hz-xiaxz)\
**Post date:** [June 22, 2026, 5:36pm UTC](https://discourse.julialang.org/t/memory-blow-up-when-solving-a-sparse-affine-hermitian-sdp-translated-from-yalmip-to-jump/137744/4 "2026-06-22T17:36:26Z")

</div>

However, I still find it puzzling that calling MOSEK through YALMIP leads to smooth memory allocation, whereas calling it through JuMP causes a sudden spike in memory usage. Is this due to fundamental design differences between YALMIP and JuMP, or is it more likely caused by the way the problem is formulated on the user side?

---

<div class="post-metadata">

**Author:** ![araujoms](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/araujoms/32/217734_2.png) [@araujoms](https://discourse.julialang.org/u/araujoms)\
**Post date:** [June 22, 2026, 8:27pm UTC](https://discourse.julialang.org/t/memory-blow-up-when-solving-a-sparse-affine-hermitian-sdp-translated-from-yalmip-to-jump/137744/5 "2026-06-22T20:27:47Z")

</div>

My pleasure.

See this thread: [Bottleneck of JuMP](https://discourse.julialang.org/t/bottleneck-of-jump/136609) In a nutshell, there are two related problems: the first is that JuMP solves the primal problem by default, and YALMIP the dual. Here the dual happens to be more efficient. The second problem is a bug in Dualization.jl when dualising problems that use a non-native cone (here the complex PSD cone). There is a PR to fix it though: [[breaking] change DualOptimizer to not add bridges by default - Pull Request #209 - jump-dev/Dualization.jl - GitHub](https://github.com/jump-dev/Dualization.jl/pull/209)

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [June 23, 2026, 12:14am UTC](https://discourse.julialang.org/t/memory-blow-up-when-solving-a-sparse-affine-hermitian-sdp-translated-from-yalmip-to-jump/137744/6 "2026-06-23T00:14:05Z")

</div>

Hi @hz-xiaxz, try with this code:

> [@Bottleneck of JuMP](https://discourse.julialang.org/t/bottleneck-of-jump/136609/21):
>
> Try with: function dual\_optimizer() inner = MosekTools.Optimizer() dual\_problem = Dualization.DualProblem{Float64}(inner) return Dualization.DualOptimizer{Float64,typeof(inner)}(dual\_problem) end model = Model(dual\_optimizer) The underlying issue is: Mosek does not support HermitianPSDCone directly, so JuMP reformulates the problem into a larger PSD model. Model(Dualization.dual\_optimizer(Mosek.Optimizer)) creates two sets of bridges: one before the dual\_optimizer and one betwee…

---

<div class="post-metadata">

**Author:** ![hz-xiaxz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hz-xiaxz/32/209585_2.png) [@hz-xiaxz](https://discourse.julialang.org/u/hz-xiaxz)\
**Post date:** [June 23, 2026, 5:00am UTC](https://discourse.julialang.org/t/memory-blow-up-when-solving-a-sparse-affine-hermitian-sdp-translated-from-yalmip-to-jump/137744/7 "2026-06-23T05:00:59Z")

</div>

Thank you for the reply! It looks like I am running into the exact same issue mentioned in that post—my apologies for not finding it before asking.

I do have a follow-up question regarding JuMP’s approach to formulating this problem. Specifically, regarding this point:

> _Mosek does not support HermitianPSDCone directly, so JuMP reformulates the problem into a larger PSD model._

Out of curiosity, does YALMIP use a different or more efficient approach for this? I’m cc’ing @araujoms, as he might be familiar with the YALMIP implementation as well.

In the meantime, I will try using the PR branch in Dualization.jl to see if that resolves the issue for me. Thank you again for all your help and suggestions!

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [June 23, 2026, 5:04am UTC](https://discourse.julialang.org/t/memory-blow-up-when-solving-a-sparse-affine-hermitian-sdp-translated-from-yalmip-to-jump/137744/8 "2026-06-23T05:04:27Z")

</div>

> Out of curiosity, does YALMIP use a different or more efficient approach for this?

YALMIP automatically solves the dual.

Which would be exactly equivalent to JuMP with `Dualization.dual_optimizer`.

Except there is a bug (not a correctness, just a missing performance optimization) in Dualization where we reformulate the problem at the “wrong” stage, and thus miss out passing the “correct” model to Mosek.

---

<div class="post-metadata">

**Author:** ![hz-xiaxz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hz-xiaxz/32/209585_2.png) [@hz-xiaxz](https://discourse.julialang.org/u/hz-xiaxz)\
**Post date:** [June 23, 2026, 5:06am UTC](https://discourse.julialang.org/t/memory-blow-up-when-solving-a-sparse-affine-hermitian-sdp-translated-from-yalmip-to-jump/137744/9 "2026-06-23T05:06:41Z")

</div>

Thanks! If I end up trying to use SCS backend, is there an easy way to make it parrellel (multithreading or MPI)? Mosek is natively parrellel.

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [June 23, 2026, 5:07am UTC](https://discourse.julialang.org/t/memory-blow-up-when-solving-a-sparse-affine-hermitian-sdp-translated-from-yalmip-to-jump/137744/10 "2026-06-23T05:07:36Z")

</div>

See the options to change the linear solver used by SCS: [GitHub - jump-dev/SCS.jl: A Julia interface for the SCS conic programming solver · GitHub](https://github.com/jump-dev/SCS.jl#linear-solvers)

---

<div class="post-metadata">

**Author:** ![araujoms](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/araujoms/32/217734_2.png) [@araujoms](https://discourse.julialang.org/u/araujoms)\
**Post date:** [June 23, 2026, 7:24am UTC](https://discourse.julialang.org/t/memory-blow-up-when-solving-a-sparse-affine-hermitian-sdp-translated-from-yalmip-to-jump/137744/11 "2026-06-23T07:24:42Z")

</div>

Fundamentally both JuMP and YALMIP use the dimension-doubling technique I explain here: [SDPs with complex numbers | More Quantum](https://mateusaraujo.info/2023/03/14/sdps-with-complex-numbers/)

As far as I know there is no better way of mapping the complex PSD cone onto the real one. The only difference is when dualising: YALMIP first dualises, then doubles the dimension, whereas Dualization.jl first doubles the dimension, and then dualises.
