@@ -14,6 +14,10 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
1414 # * `-d` means a diagonal block with diagonal of length `d`
1515 # * `d` means a symmetric `d x d` block
1616 blockdims:: Vector{Int}
17+ # MOI variable index -> rank 1 matrix it corresponds to
18+ rank_one:: Vector{Union{Nothing,MOI.LowRankMatrix{Cdouble}}}
19+ # To avoid it being free'd
20+ cached_ind:: Vector{Vector{Cint}}
1721 varmap:: Vector{Tuple{Int, Int, Int}} # Variable Index vi -> blk, i, j
1822 # If `blockdims[i] < 0`, `blk[i]` is the offset in `lpdvars`.
1923 # That is the **sum of length** of diagonal block before
@@ -33,15 +37,18 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
3337
3438 y:: Vector{Cdouble}
3539
40+ solve_time:: Float64
3641 silent:: Bool
3742 options:: Dict{Symbol,Any}
3843 function Optimizer ()
3944 optimizer = new (
4045 C_NULL , C_NULL , 0.0 , 1 , Cdouble[], Int[],
46+ Union{Nothing,MOI. LowRankMatrix{Cdouble}}[],
47+ Vector{Cint}[],
4148 Tuple{Int, Int, Int}[], Int[], C_NULL , 0 ,
4249 Int[], Int[], Cdouble[],
4350 Vector{Int}[], Vector{Cdouble}[],
44- Cdouble[], false , Dict {Symbol, Any} (),
51+ Cdouble[], NaN , false , Dict {Symbol, Any} (),
4552 )
4653 finalizer (_free, optimizer)
4754 return optimizer
@@ -64,6 +71,8 @@ function MOI.empty!(optimizer::Optimizer)
6471 optimizer. objective_sign = 1
6572 empty! (optimizer. b)
6673 empty! (optimizer. blockdims)
74+ empty! (optimizer. rank_one)
75+ empty! (optimizer. cached_ind)
6776 empty! (optimizer. varmap)
6877 empty! (optimizer. blk)
6978 optimizer. nlpdrows = 0
@@ -73,6 +82,8 @@ function MOI.empty!(optimizer::Optimizer)
7382 empty! (optimizer. sdpdinds)
7483 empty! (optimizer. sdpdcoefs)
7584 empty! (optimizer. y)
85+ optimizer. solve_time = NaN
86+ return
7687end
7788
7889function MOI. is_empty (optimizer:: Optimizer )
@@ -193,8 +204,20 @@ function MOI.supports(
193204end
194205
195206MOI. supports_add_constrained_variables (:: Optimizer , :: Type{MOI.Reals} ) = false
196- const SupportedSets = Union{MOI. Nonnegatives, MOI. PositiveSemidefiniteConeTriangle}
207+
208+ const _SetWithDotProd = MOI. SetWithDotProducts{
209+ MOI. PositiveSemidefiniteConeTriangle,
210+ MOI. TriangleVectorization{Cdouble,MOI. LowRankMatrix{Cdouble}},
211+ }
212+
213+ const SupportedSets = Union{
214+ MOI. Nonnegatives,
215+ MOI. PositiveSemidefiniteConeTriangle,
216+ _SetWithDotProd,
217+ }
218+
197219MOI. supports_add_constrained_variables (:: Optimizer , :: Type{<:SupportedSets} ) = true
220+
198221function MOI. supports_constraint (
199222 :: Optimizer , :: Type{MOI.ScalarAffineFunction{Cdouble}} ,
200223 :: Type{MOI.EqualTo{Cdouble}} )
@@ -206,6 +229,7 @@ function new_block(optimizer::Optimizer, set::MOI.Nonnegatives)
206229 blk = length (optimizer. blockdims)
207230 for i in 1 : MOI. dimension (set)
208231 push! (optimizer. varmap, (blk, i, i))
232+ push! (optimizer. rank_one, nothing )
209233 end
210234end
211235
@@ -215,13 +239,26 @@ function new_block(optimizer::Optimizer, set::MOI.PositiveSemidefiniteConeTriang
215239 for j in 1 : set. side_dimension
216240 for i in 1 : j
217241 push! (optimizer. varmap, (blk, i, j))
242+ push! (optimizer. rank_one, nothing )
218243 end
219244 end
220245end
221246
247+ function new_block (model:: Optimizer , set:: _SetWithDotProd )
248+ println (" ______________________Low-Rank" )
249+ blk = length (model. blockdims) + 1
250+ for i in eachindex (set. vectors)
251+ push! (model. varmap, (blk, 0 , 0 ))
252+ push! (model. rank_one, set. vectors[i]. matrix)
253+ end
254+ new_block (model, set. set)
255+ end
256+
222257function _add_constrained_variables (optimizer:: Optimizer , set:: SupportedSets )
223258 offset = length (optimizer. varmap)
224259 new_block (optimizer, set)
260+ @assert length (optimizer. varmap) == offset + MOI. dimension (set)
261+ @assert length (optimizer. rank_one) == offset + MOI. dimension (set)
225262 ci = MOI. ConstraintIndex {MOI.VectorOfVariables, typeof(set)} (offset + 1 )
226263 return [MOI. VariableIndex (i) for i in offset .+ (1 : MOI. dimension (set))], ci
227264end
@@ -265,32 +302,51 @@ function constrain_variables_on_creation(
265302 end
266303end
267304
268- function _setcoefficient! (m:: Optimizer , coef, constr:: Integer , blk:: Integer , i:: Integer , j:: Integer )
269- if m. blockdims[blk] < 0
270- @assert i == j
271- push! (m. lpdvars, constr + 1 )
272- push! (m. lpdrows, m. blk[blk] + i - 1 ) # -1 because indexing starts at 0 in DSDP
273- push! (m. lpcoefs, coef)
274- else
275- sdp = m. blk[blk]
276- push! (m. sdpdinds[end ][sdp], i + (j - 1 ) * m. blockdims[blk] - 1 )
277- if i != j
278- coef /= 2
305+ function _setcoefficient! (dest:: Optimizer , coef, constr:: Integer , vi:: MOI.VariableIndex )
306+ blk, i, j = varmap (dest, vi)
307+ rank_one = dest. rank_one[vi. value]
308+ if isnothing (rank_one)
309+ if dest. blockdims[blk] < 0
310+ @assert i == j
311+ push! (dest. lpdvars, constr + 1 )
312+ push! (dest. lpdrows, dest. blk[blk] + i - 1 ) # -1 because indexing starts at 0 in DSDP
313+ push! (dest. lpcoefs, coef)
314+ else
315+ sdp = dest. blk[blk]
316+ push! (dest. sdpdinds[end ][sdp], i + (j - 1 ) * dest. blockdims[blk] - 1 )
317+ if i != j
318+ coef /= 2
319+ end
320+ push! (dest. sdpdcoefs[end ][sdp], coef)
279321 end
280- push! (m. sdpdcoefs[end ][sdp], coef)
322+ else
323+ d = Cint (dest. blockdims[blk])
324+ push! (dest. cached_ind, collect (Cint (0 ): (d - 1 )))
325+ # 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
326+ DSDP. SDPCone. SetARankOneMat (
327+ dest. sdpcone,
328+ dest. blk[blk] - 1 ,
329+ constr,
330+ d,
331+ coef * rank_one. diagonal[],
332+ 0 ,
333+ last (dest. cached_ind),
334+ collect (eachcol (rank_one. factor)[]),
335+ d,
336+ )
281337 end
282338end
283339
284340# Loads objective coefficient α * vi
285- function load_objective_term! (optimizer:: Optimizer , index_map, α, vi:: MOI.VariableIndex )
286- blk, i, j = varmap (optimizer, vi)
341+ function load_objective_term! (optimizer:: Optimizer , α, vi:: MOI.VariableIndex )
287342 coef = optimizer. objective_sign * α
288- _setcoefficient! (optimizer, coef, 0 , blk, i, j )
343+ _setcoefficient! (optimizer, coef, 0 , vi )
289344end
290345
291346function _set_A_matrices (m:: Optimizer , i)
292347 for (blk, blkdim) in zip (m. blk, m. blockdims)
293- if blkdim > 0
348+ if blkdim > 0 && ! isempty (m. sdpdcoefs[end ][blk])
349+ @show (blk - 1 , i, blkdim, 1.0 , 0 , m. sdpdinds[end ][blk], m. sdpdcoefs[end ][blk], length (m. sdpdcoefs[end ][blk]))
294350 SDPCone. SetASparseVecMat (m. sdpcone, blk - 1 , i, blkdim, 1.0 , 0 , m. sdpdinds[end ][blk], m. sdpdcoefs[end ][blk], length (m. sdpdcoefs[end ][blk]))
295351 end
296352 end
@@ -321,6 +377,12 @@ function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike)
321377 index_map,
322378 MOI. PositiveSemidefiniteConeTriangle,
323379 )
380+ constrain_variables_on_creation (
381+ dest,
382+ src,
383+ index_map,
384+ _SetWithDotProd,
385+ )
324386 vis_src = MOI. get (src, MOI. ListOfVariableIndices ())
325387 if length (vis_src) < length (index_map. var_map)
326388 _error (
@@ -392,8 +454,7 @@ function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike)
392454 _new_A_matrix (dest)
393455 for t in func. terms
394456 if ! iszero (t. coefficient)
395- blk, i, j = varmap (dest, index_map[t. variable])
396- _setcoefficient! (dest, t. coefficient, k, blk, i, j)
457+ _setcoefficient! (dest, t. coefficient, k, index_map[t. variable])
397458 end
398459 end
399460 _set_A_matrices (dest, k)
@@ -428,7 +489,6 @@ function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike)
428489 if ! iszero (term. coefficient)
429490 load_objective_term! (
430491 dest,
431- index_map,
432492 term. coefficient,
433493 index_map[term. variable],
434494 )
@@ -449,7 +509,9 @@ function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike)
449509end
450510
451511function MOI. optimize! (m:: Optimizer )
512+ start_time = time ()
452513 Solve (m. dsdp)
514+ m. solve_time = time () - start_time
453515 # Calling `ComputeX` not right after `Solve` seems to sometime cause segfaults or weird Heisenbug's
454516 # let's call it directly what `DSDP/examples/readsdpa.c` does
455517 ComputeX (m. dsdp)
@@ -459,6 +521,8 @@ function MOI.optimize!(m::Optimizer)
459521 return
460522end
461523
524+ MOI. get (optimizer:: Optimizer , :: MOI.SolveTimeSec ) = optimizer. solve_time
525+
462526function MOI. get (m:: Optimizer , :: MOI.RawStatusString )
463527 if m. dsdp == C_NULL
464528 return " `optimize!` not called"
0 commit comments