See Tips and Tricks Ā· JuMP
I think this is just an unavoidable formulation of logsumexp. This helps to show what is happening:
using Convex, JuMP, SCS
function solve_convex(B)
b, n = size(B)
l = Variable(n)
problem = minimize(logsumexp(B * l), [l >= 0])
solve!(problem, SCS.Optimizer)
return problem.optval
end
# Want
# log(sum(exp.(B * l)))
# need affine objective, introduce epigraph
# log(sum(exp.(B * l))) <= t
# exp both sides
# sum(exp.(B * l)) <= exp(t)
# divide by exp(t)
# sum(exp.(B * l - t)) <= 1
# introduce epigraph of exp(B[i, :]' * l - t)
# [B[i, :]' * l - t, 1, u[i]] in ExponentialCone() for all i
# sum(u) <= 1
function solve_jump(B)
b, n = size(B)
model = Model(SCS.Optimizer)
@variables(model, begin
l[1:n] >= 0
t
u[1:b]
end)
@constraints(model, begin
[i in 1:b], [B[i, :]' * l - t, 1, u[i]] in MOI.ExponentialCone()
sum(u) <= 1
end)
@objective(model, Min, t)
optimize!(model)
return objective_value(model)
end
B = rand(10_000, 27);
julia> B = rand(10_000, 27);
julia> solve_convex(B)
[ Info: [Convex.jl] Compilation finished: 0.07 seconds, 63.466 MiB of memory allocated
------------------------------------------------------------------
SCS v3.2.4 - Splitting Conic Solver
(c) Brendan O'Donoghue, Stanford University, 2012
------------------------------------------------------------------
problem: variables n: 10028, constraints m: 30028
cones: l: linear vars: 28
e: exp vars: 30000, dual exp vars: 0
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, rho_x: 1.00e-06
acceleration_lookback: 10, acceleration_interval: 10
compiled with openmp parallelization enabled
lin-sys: sparse-direct-amd-qdldl
nnz(A): 300027, nnz(P): 0
------------------------------------------------------------------
iter | pri res | dua res | gap | obj | scale | time (s)
------------------------------------------------------------------
0| 1.00e+00 3.84e+02 5.49e+02 2.74e+02 1.00e-01 1.08e-01
250| 1.61e-01 8.79e-02 8.53e-02 8.57e+00 6.43e-03 7.21e-01
500| 9.40e-05 8.75e-06 1.04e-05 9.21e+00 6.43e-03 1.32e+00
------------------------------------------------------------------
status: solved
timings: total: 1.32e+00s = setup: 9.95e-02s + solve: 1.22e+00s
lin-sys: 6.82e-01s, cones: 3.44e-01s, accel: 4.79e-02s
------------------------------------------------------------------
objective = 9.210337
------------------------------------------------------------------
9.210331666844379
julia> solve_jump(B)
------------------------------------------------------------------
SCS v3.2.4 - Splitting Conic Solver
(c) Brendan O'Donoghue, Stanford University, 2012
------------------------------------------------------------------
problem: variables n: 10028, constraints m: 30028
cones: l: linear vars: 28
e: exp vars: 30000, dual exp vars: 0
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, rho_x: 1.00e-06
acceleration_lookback: 10, acceleration_interval: 10
compiled with openmp parallelization enabled
lin-sys: sparse-direct-amd-qdldl
nnz(A): 300027, nnz(P): 0
------------------------------------------------------------------
iter | pri res | dua res | gap | obj | scale | time (s)
------------------------------------------------------------------
0| 1.00e+00 3.84e+02 5.49e+02 2.74e+02 1.00e-01 1.04e-01
250| 1.61e-01 8.79e-02 8.53e-02 8.57e+00 6.43e-03 7.15e-01
500| 9.40e-05 8.75e-06 1.04e-05 9.21e+00 6.43e-03 1.30e+00
------------------------------------------------------------------
status: solved
timings: total: 1.30e+00s = setup: 9.45e-02s + solve: 1.21e+00s
lin-sys: 6.90e-01s, cones: 3.30e-01s, accel: 4.79e-02s
------------------------------------------------------------------
objective = 9.210337
------------------------------------------------------------------
9.210331666844379
We need to introduce b + 1
variables, and there are 3b + 1
new rows.