Skip to content

Commit 14c9a72

Browse files
authored
Merge pull request #27 from JuliaOpt/bl/rmslack
Simplify thanks to bridges
2 parents 7424e2f + 66bde83 commit 14c9a72

10 files changed

+488
-543
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
*.jl.cov
22
*.jl.*.cov
33
*.jl.mem
4+
.ipynb_checkpoints

src/SemidefiniteOptInterface.jl

+60-87
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,7 @@ const SVF = MOI.SingleVariable
1515
const VVF = MOI.VectorOfVariables
1616
const VF = Union{SVF, VVF}
1717
const SAF{T} = MOI.ScalarAffineFunction{T}
18-
const VAF{T} = MOI.VectorAffineFunction{T}
19-
const AF{T} = Union{SAF{T}, VAF{T}}
2018
const ASF{T} = Union{SVF, SAF{T}}
21-
const AVF{T} = Union{VVF, VAF{T}}
2219

2320
const ZS = Union{MOI.EqualTo, MOI.Zeros}
2421
const NS = Union{MOI.GreaterThan, MOI.Nonnegatives}
@@ -43,7 +40,6 @@ mutable struct SOItoMOIBridge{T, SIT <: AbstractSDOptimizer} <: MOI.AbstractOpti
4340
varmap::Vector{Vector{Tuple{Int, Int, Int, T, T}}} # Variable Index vi -> blk, i, j, coef, shift # x = sum coef * block(X, blk)[i, j] + shift
4441
zeroblock::Dict{CI, Int}
4542
constrmap::Dict{CI, UnitRange{Int}} # Constraint Index ci -> cs
46-
slackmap::Vector{Tuple{Int, Int, Int, T}} # c -> blk, i, j, coef
4743
double::Vector{CI} # created when there are two cones for same variable
4844
function SOItoMOIBridge{T}(sdoptimizer::SIT) where {T, SIT}
4945
new{T, SIT}(sdoptimizer, Dict{Int64, T}(), Dict{Int, T}(),
@@ -53,7 +49,6 @@ mutable struct SOItoMOIBridge{T, SIT <: AbstractSDOptimizer} <: MOI.AbstractOpti
5349
Vector{Tuple{Int, Int, Int, T}}[],
5450
Dict{CI, Int}(),
5551
Dict{CI, UnitRange{Int}}(),
56-
Tuple{Int, Int, Int, T}[],
5752
CI[])
5853
end
5954
end
@@ -86,8 +81,7 @@ function MOI.is_empty(optimizer::SOItoMOIBridge)
8681
isempty(optimizer.free) &&
8782
isempty(optimizer.varmap) &&
8883
isempty(optimizer.zeroblock) &&
89-
isempty(optimizer.constrmap) &&
90-
isempty(optimizer.slackmap)
84+
isempty(optimizer.constrmap)
9185
end
9286
function MOI.empty!(optimizer::SOItoMOIBridge{T}) where T
9387
for s in optimizer.double
@@ -107,37 +101,57 @@ function MOI.empty!(optimizer::SOItoMOIBridge{T}) where T
107101
optimizer.varmap = Vector{Tuple{Int, Int, Int, T}}[]
108102
optimizer.zeroblock = Dict{CI, Int}()
109103
optimizer.constrmap = Dict{CI, UnitRange{Int}}()
110-
optimizer.slackmap = Tuple{Int, Int, Int, T}[]
111104
end
112105

113106
function setconstant!(optimizer::SOItoMOIBridge, ci::CI, s) end
114107
function setconstant!(optimizer::SOItoMOIBridge, ci::CI, s::MOI.AbstractScalarSet)
115108
optimizer.setconstant[ci.value] = MOIU.getconstant(s)
116109
end
117-
function addsetconstant(optimizer::SOItoMOIBridge, ci::CI{<:Any, <:MOI.AbstractScalarSet}, x)
118-
x + optimizer.setconstant[ci.value]
110+
function set_constant(optimizer::SOItoMOIBridge,
111+
ci::CI{<:MOI.AbstractScalarFunction,
112+
<:MOI.AbstractScalarSet})
113+
return optimizer.setconstant[ci.value]
119114
end
120-
function addsetconstant(optimizer::SOItoMOIBridge, ci::CI, x)
121-
x
115+
function set_constant(optimizer::SOItoMOIBridge{T}, ci::CI) where T
116+
return zeros(T, length(optimizer.constrmap[ci]))
122117
end
123118
function addblkconstant(optimizer::SOItoMOIBridge, ci::CI{<:Any, <:Union{NS, PS}}, x)
124119
blk = -ci.value
125-
x .+ optimizer.blkconstant[blk]
126-
end
127-
function addblkconstant(optimizer::SOItoMOIBridge, ci::CI, x)
128-
x
120+
return x .+ optimizer.blkconstant[blk]
129121
end
122+
addblkconstant(optimizer::SOItoMOIBridge, ci::CI, x) = x
130123

131-
function MOI.supports(optimizer::SOItoMOIBridge{T},
132-
::Union{MOI.ObjectiveSense,
133-
MOI.ObjectiveFunction{<:Union{MOI.SingleVariable,
134-
MOI.ScalarAffineFunction{T}}}}) where T
124+
function MOI.supports(
125+
optimizer::SOItoMOIBridge{T},
126+
::Union{MOI.ObjectiveSense,
127+
MOI.ObjectiveFunction{<:Union{MOI.SingleVariable,
128+
MOI.ScalarAffineFunction{T}}}}) where T
135129
return true
136130
end
137131

138-
function MOI.supports_constraint(::SOItoMOIBridge{T},
139-
::Type{<:Union{VF, AF{T}}},
140-
::Type{<:SupportedSets}) where T
132+
# Zeros and Nonpositives supports could be removed thanks to variable bridges
133+
# * `VectorOfVariables`-in-`Zeros` would return a `VectorAffineFunction` with
134+
# zero constant and no variable created.
135+
# * `VectorOfVariables`-in-`Nonpositives` would create variables in
136+
# `Nonnegatives` and return a `VectorAffineFunction` containing `-` the
137+
# variables.
138+
function MOI.supports_constraint(
139+
::SOItoMOIBridge, ::Type{MOI.VectorOfVariables},
140+
::Type{<:Union{MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives,
141+
MOI.PositiveSemidefiniteConeTriangle}})
142+
return true
143+
end
144+
# This support could be remove thanks to variable bridges.
145+
# The VectorizeVariableBridge would redirect to the above case and then the
146+
# resulting function would be shifted by the constant.
147+
function MOI.supports_constraint(
148+
::SOItoMOIBridge{T}, ::Type{MOI.SingleVariable},
149+
::Type{<:Union{MOI.EqualTo{T}, MOI.GreaterThan{T}, MOI.LessThan{T}}}) where T
150+
return true
151+
end
152+
function MOI.supports_constraint(
153+
::SOItoMOIBridge{T}, ::Type{MOI.ScalarAffineFunction{T}},
154+
::Type{MOI.EqualTo{T}}) where T
141155
return true
142156
end
143157

@@ -163,10 +177,10 @@ MOI.get(m::SOItoMOIBridge, s::SolverStatus) = MOI.get(m.sdoptimizer, s)
163177
MOI.get(m::SOItoMOIBridge, ::MOI.ResultCount) = 1
164178

165179
function _getblock(M, blk::Integer, s::Type{<:Union{NS, ZS}})
166-
diag(block(M, blk))
180+
return diag(block(M, blk))
167181
end
168182
function _getblock(M, blk::Integer, s::Type{<:PS})
169-
-diag(block(M, blk))
183+
return -diag(block(M, blk))
170184
end
171185
# Vectorized length for matrix dimension d
172186
sympackedlen(d::Integer) = (d*(d+1)) >> 1
@@ -183,20 +197,22 @@ function _getblock(M::AbstractMatrix{T}, blk::Integer, s::Type{<:DS}) where T
183197
end
184198
end
185199
@assert k == n
186-
v
200+
return v
187201
end
188202
function getblock(M, blk::Integer, s::Type{<:MOI.AbstractScalarSet})
189203
vd = _getblock(M, blk, s)
190204
@assert length(vd) == 1
191-
vd[1]
205+
return vd[1]
192206
end
193207
function getblock(M, blk::Integer, s::Type{<:MOI.AbstractVectorSet})
194-
_getblock(M, blk, s)
208+
return _getblock(M, blk, s)
195209
end
196210

197211
getvarprimal(m::SOItoMOIBridge, blk::Integer, S) = getblock(getX(m.sdoptimizer), blk, S)
198212
function getvardual(m::SOItoMOIBridge, blk::Integer, S)
199-
getblock(getZ(m.sdoptimizer), blk, S)
213+
z = getZ(m.sdoptimizer)
214+
b = getblock(z, blk, S)
215+
return getblock(getZ(m.sdoptimizer), blk, S)
200216
end
201217

202218
function MOI.get(m::SOItoMOIBridge{T}, ::MOI.VariablePrimal, vi::VI) where T
@@ -208,97 +224,54 @@ function MOI.get(m::SOItoMOIBridge{T}, ::MOI.VariablePrimal, vi::VI) where T
208224
x += block(X, blk)[i, j] * sign(coef)
209225
end
210226
end
211-
x
227+
return x
212228
end
213229
function MOI.get(m::SOItoMOIBridge, vp::MOI.VariablePrimal, vi::Vector{VI})
214-
MOI.get.(m, vp, vi)
230+
return MOI.get.(m, vp, vi)
215231
end
216232

217233
function _getattribute(m::SOItoMOIBridge, ci::CI{<:ASF}, f)
218234
cs = m.constrmap[ci]
219235
@assert length(cs) == 1
220-
f(m, first(cs))
236+
return f(m, first(cs))
221237
end
222-
function _getattribute(m::SOItoMOIBridge, ci::CI{<:AVF}, f)
223-
f.(m, m.constrmap[ci])
224-
end
225-
226-
function getslack(m::SOItoMOIBridge{T}, c::Integer) where T
227-
X = getX(m.sdoptimizer)
228-
blk, i, j, coef = m.slackmap[c]
229-
if iszero(blk)
230-
zero(T)
231-
else
232-
if i != j
233-
coef *= 2 # We should take block(X, blk)[i, j] + block(X, blk)[j, i] but they are equal
234-
end
235-
coef * block(X, blk)[i, j]
236-
end
238+
function _getattribute(m::SOItoMOIBridge, ci::CI{<:VVF}, f)
239+
return f.(m, m.constrmap[ci])
237240
end
238241

239-
function MOI.get(m::SOItoMOIBridge, a::MOI.ConstraintPrimal, ci::CI{F, S}) where {F, S}
242+
function MOI.get(m::SOItoMOIBridge, a::MOI.ConstraintPrimal,
243+
ci::CI{F, S}) where {F, S}
240244
if ci.value >= 0
241-
addsetconstant(m, ci, _getattribute(m, ci, getslack))
245+
return set_constant(m, ci)
242246
else
243247
# Variable Function-in-S with S different from Zeros and EqualTo and not a double variable constraint
244248
blk = -ci.value
245-
addblkconstant(m, ci, getvarprimal(m, blk, S))
249+
return addblkconstant(m, ci, getvarprimal(m, blk, S))
246250
end
247251
end
248252

249-
function getvardual(m::SOItoMOIBridge{T}, vi::VI) where T
250-
Z = getZ(m.sdoptimizer)
251-
z = zero(T)
252-
for (blk, i, j, coef) in varmap(m, vi)
253-
if blk != 0
254-
z += block(Z, blk)[i, j] * sign(coef)
255-
end
256-
end
257-
z
258-
end
259-
getvardual(m::SOItoMOIBridge, f::SVF) = getvardual(m, f.variable)
260-
getvardual(m::SOItoMOIBridge, f::VVF) = map(vi -> getvardual(m, vi), f.variables)
261-
#function MOI.get(m::SOItoMOIBridge, ::MOI.ConstraintDual, ci::CI{<:VF, S})
262-
# _getattribute(m, ci, getdual) + getvardual(m, MOI.get(m, MOI.ConstraintFunction(), ci))
263-
#end
264253
function MOI.get(m::SOItoMOIBridge, ::MOI.ConstraintDual, ci::CI{<:VF, S}) where S<:SupportedSets
265254
if ci.value < 0
266-
getvardual(m, -ci.value, S)
255+
return getvardual(m, -ci.value, S)
267256
else
268257
dual = _getattribute(m, ci, getdual)
269258
if haskey(m.zeroblock, ci) # ZS
270-
dual + getvardual(m, m.zeroblock[ci], S)
259+
return dual + getvardual(m, m.zeroblock[ci], S)
271260
else # var constraint on unfree constraint
272-
dual
261+
return dual
273262
end
274263
end
275264
end
276265

277266
function getdual(m::SOItoMOIBridge{T}, c::Integer) where T
278267
if c == 0
279-
zero(T)
268+
return zero(T)
280269
else
281-
-gety(m.sdoptimizer)[c]
270+
return -gety(m.sdoptimizer)[c]
282271
end
283272
end
284273
function MOI.get(m::SOItoMOIBridge, ::MOI.ConstraintDual, ci::CI)
285-
_getattribute(m, ci, getdual)
286-
end
287-
function scalevec!(v, c)
288-
d = div(isqrt(1+8length(v))-1, 2)
289-
@assert div(d*(d+1), 2) == length(v)
290-
i = 1
291-
for j in 1:d
292-
for k in i:(i+j-2)
293-
v[k] *= c
294-
end
295-
i += j
296-
end
297-
v
298-
end
299-
function MOI.get(m::SOItoMOIBridge{T}, ::MOI.ConstraintDual,
300-
ci::CI{<:AF{T}, DS}) where T
301-
scalevec!(_getattribute(m, ci, getdual), one(T)/2)
274+
return _getattribute(m, ci, getdual)
302275
end
303276

304277
include("sdpa.jl")

src/constraint.jl

+16-75
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,5 @@
1-
function createslack!(m::SOItoMOIBridge{T}, cs, ::ZS) where T
2-
m.slackmap[cs] .= ((0, 0, 0, zero(T)),)
3-
end
4-
function createslack!(m::SOItoMOIBridge, cs, ::S) where S <: Union{NS, PS}
5-
blk = newblock(m, -length(cs))
6-
for (i, c) in enumerate(cs)
7-
m.slackmap[c] = (blk, i, i, vscaling(S))
8-
end
9-
end
10-
function createslack!(m::SOItoMOIBridge{T}, cs, ::DS) where T
11-
d = getmatdim(length(cs))
12-
k = 0
13-
blk = newblock(m, d)
14-
for i in 1:d
15-
for j in 1:i
16-
k += 1
17-
m.slackmap[cs[k]] = (blk, i, j, i == j ? one(T) : one(T)/2)
18-
end
19-
end
20-
end
21-
22-
function createslack!(m::SOItoMOIBridge, ci::CI, f, s)
23-
cs = m.constrmap[ci]
24-
createslack!(m, cs, s)
25-
end
26-
271
nconstraints(f::Union{SVF, SAF}, s) = 1
282
nconstraints(f::VVF, s) = length(f.variables)
29-
nconstraints(f::VAF, s) = MOI.output_dimension(f)
303

314
function _allocate_constraint(m::SOItoMOIBridge, f, s)
325
ci = CI{typeof(f), typeof(s)}(m.nconstrs)
@@ -35,67 +8,35 @@ function _allocate_constraint(m::SOItoMOIBridge, f, s)
358
#m.constrmap[ci] = m.nconstrs .+ (1:n)
369
m.constrmap[ci] = (m.nconstrs + 1):(m.nconstrs + n)
3710
m.nconstrs += n
38-
resize!(m.slackmap, m.nconstrs)
39-
createslack!(m, ci, f, s)
40-
ci
11+
return ci
4112
end
42-
function MOIU.allocate_constraint(m::SOItoMOIBridge, f::AF, s::SupportedSets)
13+
function MOIU.allocate_constraint(m::SOItoMOIBridge, f::SAF, s::SupportedSets)
4314
_allocate_constraint(m::SOItoMOIBridge, f, s)
4415
end
4516

46-
function loadslack!(m::SOItoMOIBridge, c::Integer)
47-
blk, i, j, coef = m.slackmap[c]
48-
if blk != 0
49-
@assert !iszero(coef)
50-
setconstraintcoefficient!(m.sdoptimizer, -coef, c, blk, i, j)
51-
end
52-
end
53-
function loadslacks!(m::SOItoMOIBridge, cs)
54-
for c in cs
55-
loadslack!(m, c)
56-
end
57-
end
58-
59-
output_index(::MOI.ScalarAffineTerm) = 1
60-
output_index(t::MOI.VectorAffineTerm) = t.output_index
61-
scalar_term(t::MOI.ScalarAffineTerm) = t
62-
scalar_term(t::MOI.VectorAffineTerm) = t.scalar_term
63-
64-
_getconstant(m::SOItoMOIBridge, s::MOI.AbstractScalarSet) = MOIU.getconstant(s)
65-
_getconstant(m::SOItoMOIBridge{T}, s::MOI.AbstractSet) where T = zero(T)
66-
67-
function loadcoefficients!(m::SOItoMOIBridge, cs::UnitRange, f::AF, s)
17+
function loadcoefficients!(m::SOItoMOIBridge, cs::UnitRange,
18+
f::MOI.ScalarAffineFunction, s)
6819
f = MOIU.canonical(f) # sum terms with same variables and same outputindex
69-
if !isempty(cs)
70-
rhs = _getconstant(m, s) .- MOI._constant(f)
71-
for t in f.terms
72-
st = scalar_term(t)
73-
if !iszero(st.coefficient)
74-
c = cs[output_index(t)]
75-
for (blk, i, j, coef, shift) in varmap(m, st.variable_index)
76-
if !iszero(blk)
77-
@assert !iszero(coef)
78-
setconstraintcoefficient!(m.sdoptimizer, st.coefficient*coef, c, blk, i, j)
79-
end
80-
if isa(rhs, Vector)
81-
rhs[output_index(t)] -= st.coefficient * shift
82-
else
83-
rhs -= st.coefficient * shift
84-
end
20+
@assert length(cs) == 1
21+
c = first(cs)
22+
rhs = MOIU.getconstant(s) - MOI._constant(f)
23+
for t in f.terms
24+
if !iszero(t.coefficient)
25+
for (blk, i, j, coef, shift) in varmap(m, t.variable_index)
26+
if !iszero(blk)
27+
@assert !iszero(coef)
28+
setconstraintcoefficient!(m.sdoptimizer, t.coefficient*coef, c, blk, i, j)
8529
end
30+
rhs -= t.coefficient * shift
8631
end
8732
end
88-
for j in 1:MOI.output_dimension(f)
89-
c = cs[j]
90-
setconstraintconstant!(m.sdoptimizer, rhs[j], c)
91-
end
9233
end
34+
setconstraintconstant!(m.sdoptimizer, rhs, c)
9335
end
9436

95-
function MOIU.load_constraint(m::SOItoMOIBridge, ci::CI, f::AF, s::SupportedSets)
37+
function MOIU.load_constraint(m::SOItoMOIBridge, ci::CI, f::SAF, s::SupportedSets)
9638
setconstant!(m, ci, s)
9739
cs = m.constrmap[ci]
9840
@assert !isempty(cs)
99-
loadslacks!(m, cs)
10041
loadcoefficients!(m, cs, f, s)
10142
end

src/mock.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ nblocks(bm::BlockMatrix) = length(bm.blocks)
66
block(bm::BlockMatrix, i::Integer) = bm.blocks[i]
77

88
function Base.size(bm::AbstractBlockMatrix)
9-
n = sum(blk -> Compat.LinearAlgebra.checksquare(block(bm, blk)),
10-
1:nblocks(bm))
9+
n = Compat.mapreduce(blk -> Compat.LinearAlgebra.checksquare(block(bm, blk)),
10+
+, 1:nblocks(bm), init=0)
1111
return (n, n)
1212
end
1313
function Base.getindex(bm::AbstractBlockMatrix, i::Integer, j::Integer)

0 commit comments

Comments
 (0)