@@ -15,6 +15,10 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
1515 # * `-d` means a diagonal block with diagonal of length `d`
1616 # * `d` means a symmetric `d x d` block
1717 blockdims:: Vector{Int}
18+ # MOI variable index -> rank 1 matrix it corresponds to
19+ rank_one:: Vector{Union{Nothing,MOI.LowRankMatrix{Cdouble}}}
20+ # To avoid it being free'd
21+ cached_ind:: Vector{Vector{Cint}}
1822 varmap:: Vector{Tuple{Int,Int,Int}} # Variable Index vi -> blk, i, j
1923 # If `blockdims[i] < 0`, `blk[i]` is the offset in `lpdvars`.
2024 # That is the **sum of length** of diagonal block before
@@ -32,6 +36,7 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
3236 sdpdinds:: Vector{Vector{Vector{Cint}}}
3337 sdpdcoefs:: Vector{Vector{Vector{Cdouble}}}
3438 y:: Vector{Cdouble}
39+ solve_time:: Float64
3540 silent:: Bool
3641 options:: Dict{Symbol,Any}
3742
@@ -43,6 +48,8 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
4348 1 ,
4449 Cdouble[],
4550 Int[],
51+ Union{Nothing,MOI. LowRankMatrix{Cdouble}}[],
52+ Vector{Cint}[],
4653 Tuple{Int,Int,Int}[],
4754 Int[],
4855 C_NULL ,
@@ -53,6 +60,7 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
5360 Vector{Int}[],
5461 Vector{Cdouble}[],
5562 Cdouble[],
63+ NaN ,
5664 false ,
5765 Dict {Symbol,Any} (),
5866 )
@@ -80,6 +88,8 @@ function MOI.empty!(optimizer::Optimizer)
8088 optimizer. objective_sign = 1
8189 empty! (optimizer. b)
8290 empty! (optimizer. blockdims)
91+ empty! (optimizer. rank_one)
92+ empty! (optimizer. cached_ind)
8393 empty! (optimizer. varmap)
8494 empty! (optimizer. blk)
8595 optimizer. nlpdrows = 0
@@ -89,6 +99,7 @@ function MOI.empty!(optimizer::Optimizer)
8999 empty! (optimizer. sdpdinds)
90100 empty! (optimizer. sdpdcoefs)
91101 empty! (optimizer. y)
102+ optimizer. solve_time = NaN
92103 return
93104end
94105
218229
219230MOI. supports_add_constrained_variables (:: Optimizer , :: Type{MOI.Reals} ) = false
220231
221- const SupportedSets =
222- Union{MOI. Nonnegatives,MOI. PositiveSemidefiniteConeTriangle}
232+ const _SetWithDotProd = MOI. SetWithDotProducts{
233+ MOI. PositiveSemidefiniteConeTriangle,
234+ MOI. TriangleVectorization{Cdouble,MOI. LowRankMatrix{Cdouble}},
235+ }
236+
237+ const SupportedSets = Union{
238+ MOI. Nonnegatives,
239+ MOI. PositiveSemidefiniteConeTriangle,
240+ _SetWithDotProd,
241+ }
223242
224243function MOI. supports_add_constrained_variables (
225244 :: Optimizer ,
@@ -241,6 +260,7 @@ function new_block(optimizer::Optimizer, set::MOI.Nonnegatives)
241260 blk = length (optimizer. blockdims)
242261 for i in 1 : MOI. dimension (set)
243262 push! (optimizer. varmap, (blk, i, i))
263+ push! (optimizer. rank_one, nothing )
244264 end
245265 return
246266end
@@ -254,14 +274,27 @@ function new_block(
254274 for j in 1 : set. side_dimension
255275 for i in 1 : j
256276 push! (optimizer. varmap, (blk, i, j))
277+ push! (optimizer. rank_one, nothing )
257278 end
258279 end
259280 return
260281end
261282
283+ function new_block (model:: Optimizer , set:: _SetWithDotProd )
284+ println (" ______________________Low-Rank" )
285+ blk = length (model. blockdims) + 1
286+ for i in eachindex (set. vectors)
287+ push! (model. varmap, (blk, 0 , 0 ))
288+ push! (model. rank_one, set. vectors[i]. matrix)
289+ end
290+ new_block (model, set. set)
291+ end
292+
262293function _add_constrained_variables (optimizer:: Optimizer , set:: SupportedSets )
263294 offset = length (optimizer. varmap)
264295 new_block (optimizer, set)
296+ @assert length (optimizer. varmap) == offset + MOI. dimension (set)
297+ @assert length (optimizer. rank_one) == offset + MOI. dimension (set)
265298 ci = MOI. ConstraintIndex {MOI.VectorOfVariables,typeof(set)} (offset + 1 )
266299 return [MOI. VariableIndex (i) for i in offset .+ (1 : MOI. dimension (set))], ci
267300end
@@ -306,57 +339,54 @@ function constrain_variables_on_creation(
306339 return
307340end
308341
309- function _setcoefficient! (
310- m:: Optimizer ,
311- coef,
312- constr:: Integer ,
313- blk:: Integer ,
314- i:: Integer ,
315- j:: Integer ,
316- )
317- if m. blockdims[blk] < 0
318- @assert i == j
319- push! (m. lpdvars, constr + 1 )
320- push! (m. lpdrows, m. blk[blk] + i - 1 ) # -1 because indexing starts at 0 in DSDP
321- push! (m. lpcoefs, coef)
322- else
323- sdp = m. blk[blk]
324- push! (m. sdpdinds[end ][sdp], i + (j - 1 ) * m. blockdims[blk] - 1 )
325- if i != j
326- coef /= 2
342+ function _setcoefficient! (dest:: Optimizer , coef, constr:: Integer , vi:: MOI.VariableIndex )
343+ blk, i, j = varmap (dest, vi)
344+ rank_one = dest. rank_one[vi. value]
345+ if isnothing (rank_one)
346+ if dest. blockdims[blk] < 0
347+ @assert i == j
348+ push! (dest. lpdvars, constr + 1 )
349+ push! (dest. lpdrows, dest. blk[blk] + i - 1 ) # -1 because indexing starts at 0 in DSDP
350+ push! (dest. lpcoefs, coef)
351+ else
352+ sdp = dest. blk[blk]
353+ push! (dest. sdpdinds[end ][sdp], i + (j - 1 ) * dest. blockdims[blk] - 1 )
354+ if i != j
355+ coef /= 2
356+ end
357+ push! (dest. sdpdcoefs[end ][sdp], coef)
327358 end
328- push! (m. sdpdcoefs[end ][sdp], coef)
359+ else
360+ d = Cint (dest. blockdims[blk])
361+ push! (dest. cached_ind, collect (Cint (0 ): (d - 1 )))
362+ # We use `Add` and not `Set` because I think (if I interpret the name correctly) that would allow mixing with sparse matrices for the same block and constraint
363+ DSDP. SDPCone. SetARankOneMat (
364+ dest. sdpcone,
365+ dest. blk[blk] - 1 ,
366+ constr,
367+ d,
368+ coef * rank_one. diagonal[],
369+ 0 ,
370+ last (dest. cached_ind),
371+ collect (eachcol (rank_one. factor)[]),
372+ d,
373+ )
329374 end
330375 return
331376end
332377
333378# Loads objective coefficient α * vi
334- function load_objective_term! (
335- optimizer:: Optimizer ,
336- index_map,
337- α,
338- vi:: MOI.VariableIndex ,
339- )
340- blk, i, j = varmap (optimizer, vi)
379+ function load_objective_term! (optimizer:: Optimizer , α, vi:: MOI.VariableIndex )
341380 coef = optimizer. objective_sign * α
342- _setcoefficient! (optimizer, coef, 0 , blk, i, j )
381+ _setcoefficient! (optimizer, coef, 0 , vi )
343382 return
344383end
345384
346385function _set_A_matrices (m:: Optimizer , i)
347386 for (blk, blkdim) in zip (m. blk, m. blockdims)
348- if blkdim > 0
349- SDPCone. SetASparseVecMat (
350- m. sdpcone,
351- blk - 1 ,
352- i,
353- blkdim,
354- 1.0 ,
355- 0 ,
356- m. sdpdinds[end ][blk],
357- m. sdpdcoefs[end ][blk],
358- length (m. sdpdcoefs[end ][blk]),
359- )
387+ if blkdim > 0 && ! isempty (m. sdpdcoefs[end ][blk])
388+ @show (blk - 1 , i, blkdim, 1.0 , 0 , m. sdpdinds[end ][blk], m. sdpdcoefs[end ][blk], length (m. sdpdcoefs[end ][blk]))
389+ SDPCone. SetASparseVecMat (m. sdpcone, blk - 1 , i, blkdim, 1.0 , 0 , m. sdpdinds[end ][blk], m. sdpdcoefs[end ][blk], length (m. sdpdcoefs[end ][blk]))
360390 end
361391 end
362392 return
@@ -386,6 +416,12 @@ function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike)
386416 index_map,
387417 MOI. PositiveSemidefiniteConeTriangle,
388418 )
419+ constrain_variables_on_creation (
420+ dest,
421+ src,
422+ index_map,
423+ _SetWithDotProd,
424+ )
389425 vis_src = MOI. get (src, MOI. ListOfVariableIndices ())
390426 if length (vis_src) < length (index_map. var_map)
391427 _error (
@@ -464,8 +500,7 @@ function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike)
464500 _new_A_matrix (dest)
465501 for t in func. terms
466502 if ! iszero (t. coefficient)
467- blk, i, j = varmap (dest, index_map[t. variable])
468- _setcoefficient! (dest, t. coefficient, k, blk, i, j)
503+ _setcoefficient! (dest, t. coefficient, k, index_map[t. variable])
469504 end
470505 end
471506 _set_A_matrices (dest, k)
@@ -508,7 +543,6 @@ function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike)
508543 if ! iszero (term. coefficient)
509544 load_objective_term! (
510545 dest,
511- index_map,
512546 term. coefficient,
513547 index_map[term. variable],
514548 )
@@ -533,7 +567,9 @@ function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike)
533567end
534568
535569function MOI. optimize! (m:: Optimizer )
570+ start_time = time ()
536571 Solve (m. dsdp)
572+ m. solve_time = time () - start_time
537573 # Calling `ComputeX` not right after `Solve` seems to sometime cause segfaults or weird Heisenbug's
538574 # let's call it directly what `DSDP/examples/readsdpa.c` does
539575 ComputeX (m. dsdp)
@@ -543,6 +579,8 @@ function MOI.optimize!(m::Optimizer)
543579 return
544580end
545581
582+ MOI. get (optimizer:: Optimizer , :: MOI.SolveTimeSec ) = optimizer. solve_time
583+
546584function MOI. get (m:: Optimizer , :: MOI.RawStatusString )
547585 if m. dsdp == C_NULL
548586 return " `optimize!` not called"
0 commit comments