Hi everyone. I need to solve tens of thousands of second order cone problems, so I need my code to be as fast as possible. Currently, my main bottleneck is actually building the problem in JuMP and I would appreciate any help with speeding it up.
So suppose I have some list of matrices E = [ E_i ]_{i=1}^{10^n} for any given n and that, for some fixed \epsilon, I want to compute
for some positive semidefinite X. What I mean by [ \text{tr}(E_i X) ]_i is the 10^n-dimensional vector obtained by multiplying each E_i with the matrix variable X and taking the trace, while \lVert \cdot \rVert_2 is the Frobenius norm.
Here is how I am currently implementing this:
function f(ϵ, E)
n = size(E)[1]
model = Model(SCS.Optimizer)
X = @variable(model, [1:n,1:n] in PSDCone())
@variable(model, η >= 0)
# Construct the vector of traces:
idx = 1
s = Vector{AffExpr}(undef, 10^n)
for e in E
@inbounds s[idx] = tr(e * X)
idx += 1
end
# Frobenius norm of s is leq. ϵη:
@constraint(model, [ϵ * η; s] in SecondOrderCone())
@objective(model, Min, η)
optimize!(model)
end
However, this implementation is too slow. Already for n \geq 5 it can take several minutes to build the model. Also, I get JuMP warnings about too many uses of the addition operator.
So how could I speedup this implementation? And why am I getting JuMP warnings? Is it because of the tr
method not operating in-place? Can that also be improved?
Equivalently, one can write the problem as
then \sqrt{\eta^{*}} is equal to the optimal solution of the first formulation. A possible implementation is:
function g(ϵ, E)
n = size(E)[1]
model = Model(SCS.Optimizer)
X = @variable(model, [1:n,1:n] in PSDCone())
@variable(model, η >= 0)
s = QuadExpr()
for e in E
tmp = tr(e * X)
add_to_expression!(s, tmp, tmp)
end
@constraint(model, s <= η * ϵ^2)
@objective(model, Min, η)
optimize!(model)
This leads to a smaller problem for the solver, but model building in my implementation is even slower than the first attempt.
So any hints on how to make this one faster are also welcome
Note: Since the vector E of matrices can be very large, in my actual code it is implemented as a generator. This must be kept that way. Therefore, any suggestions that depend on collecting E would not work.