-
Notifications
You must be signed in to change notification settings - Fork 3
/
mock.jl
244 lines (227 loc) · 9.63 KB
/
mock.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
abstract type AbstractBlockMatrix{T} <: AbstractMatrix{T} end
struct BlockMatrix{T} <: AbstractBlockMatrix{T}
blocks::Vector{Matrix{T}}
end
nblocks(bm::BlockMatrix) = length(bm.blocks)
block(bm::BlockMatrix, i::Integer) = bm.blocks[i]
function Base.size(bm::AbstractBlockMatrix)
n = sum(blk -> Compat.LinearAlgebra.checksquare(block(bm, blk)),
1:nblocks(bm))
return (n, n)
end
function Base.getindex(bm::AbstractBlockMatrix, i::Integer, j::Integer)
(i < 0 || j < 0) && throw(BoundsError(i, j))
for k in 1:nblocks(bm)
blk = block(bm, k)
n = size(blk, 1)
if i <= n && j <= n
return blk[i, j]
elseif i <= n || j <= n
return 0
else
i -= n
j -= n
end
end
i, j = (i, j) .+ size(bm)
throw(BoundsError(i, j))
end
Base.getindex(A::AbstractBlockMatrix, I::Tuple) = getindex(A, I...)
mutable struct MockSDOptimizer{T} <: AbstractSDOptimizer
nconstrs::Int
blkdims::Vector{Int}
constraint_constants::Vector{T}
objective_coefficients::Vector{Tuple{T, Int, Int, Int}}
constraint_coefficients::Vector{Vector{Tuple{T, Int, Int, Int}}}
optimize!::Function # Function used to set dummy primal/dual values and statuses
hasprimal::Bool
hasdual::Bool
terminationstatus::MOI.TerminationStatusCode
primalstatus::MOI.ResultStatusCode
dualstatus::MOI.ResultStatusCode
X::BlockMatrix{T}
Z::BlockMatrix{T}
y::Vector{T}
end
MockSDOptimizer{T}() where T = MockSDOptimizer{T}(0,
Int[],
T[],
Tuple{T, Int, Int, Int}[],
Vector{Tuple{T, Int, Int, Int}}[],
(::MockSDOptimizer) -> begin end,
false,
false,
MOI.OptimizeNotCalled,
MOI.NoSolution,
MOI.NoSolution,
BlockMatrix{T}(Matrix{T}[]),
BlockMatrix{T}(Matrix{T}[]),
T[])
mockSDoptimizer(T::Type) = SDOIOptimizer(MockSDOptimizer{T}(), T)
MOI.get(::MockSDOptimizer, ::MOI.SolverName) = "MockSD"
function MOI.empty!(mock::MockSDOptimizer{T}) where T
mock.nconstrs = 0
mock.blkdims = Int[]
mock.constraint_constants = T[]
mock.objective_coefficients = Tuple{T, Int, Int, Int}[]
mock.constraint_coefficients = Vector{Tuple{T, Int, Int, Int}}[]
mock.hasprimal = false
mock.hasdual = false
mock.terminationstatus = MOI.OptimizeNotCalled
mock.primalstatus = MOI.NoSolution
mock.dualstatus = MOI.NoSolution
mock.X = BlockMatrix{T}(Matrix{T}[])
mock.Z = BlockMatrix{T}(Matrix{T}[])
mock.y = T[]
end
coefficienttype(::MockSDOptimizer{T}) where T = T
getnumberofconstraints(optimizer::MockSDOptimizer) = optimizer.nconstrs
getnumberofblocks(optimizer::MockSDOptimizer) = length(optimizer.blkdims)
getblockdimension(optimizer::MockSDOptimizer, blk) = optimizer.blkdims[blk]
function init!(optimizer::MockSDOptimizer{T}, blkdims::Vector{Int},
nconstrs::Integer) where T
optimizer.nconstrs = nconstrs
optimizer.blkdims = blkdims
optimizer.constraint_constants = zeros(T, nconstrs)
optimizer.objective_coefficients = Tuple{T, Int, Int, Int}[]
optimizer.constraint_coefficients = map(i -> Tuple{T, Int, Int, Int}[], 1:nconstrs)
end
getconstraintconstant(optimizer::MockSDOptimizer, c) = optimizer.constraint_constants[c]
function setconstraintconstant!(optimizer::MockSDOptimizer, val, c::Integer)
optimizer.constraint_constants[c] = val
end
getobjectivecoefficients(optimizer::MockSDOptimizer) = optimizer.objective_coefficients
function setobjectivecoefficient!(optimizer::MockSDOptimizer, val, blk::Integer, i::Integer, j::Integer)
push!(optimizer.objective_coefficients, (val, blk, i, j))
end
getconstraintcoefficients(optimizer::MockSDOptimizer, c) = optimizer.constraint_coefficients[c]
function setconstraintcoefficient!(optimizer::MockSDOptimizer, val, c::Integer,
blk::Integer, i::Integer, j::Integer)
push!(optimizer.constraint_coefficients[c], (val, blk, i, j))
end
MOI.get(mock::MockSDOptimizer, ::MOI.TerminationStatus) = mock.terminationstatus
function MOI.set(mock::MockSDOptimizer, ::MOI.TerminationStatus,
value::MOI.TerminationStatusCode)
mock.terminationstatus = value
end
MOI.get(mock::MockSDOptimizer, ::MOI.PrimalStatus) = mock.primalstatus
MOI.set(mock::MockSDOptimizer, ::MOI.PrimalStatus, value::MOI.ResultStatusCode) = (mock.primalstatus = value)
MOI.get(mock::MockSDOptimizer, ::MOI.DualStatus) = mock.dualstatus
MOI.set(mock::MockSDOptimizer, ::MOI.DualStatus, value::MOI.ResultStatusCode) = (mock.dualstatus = value)
getX(mock::MockSDOptimizer) = mock.X
getZ(mock::MockSDOptimizer) = mock.Z
gety(mock::MockSDOptimizer) = mock.y
function getprimalobjectivevalue(mock::MockSDOptimizer{T}) where T
v = zero(T)
for (α, blk, i, j) in mock.objective_coefficients
v += α * block(mock.X, blk)[i, j]
if i != j
v += α * block(mock.X, blk)[j, i]
end
end
v
end
function getdualobjectivevalue(mock::MockSDOptimizer{T}) where T
v = zero(T)
for c in 1:mock.nconstrs
v += mock.constraint_constants[c] * mock.y[c]
end
v
end
function MOI.optimize!(mock::MockSDOptimizer)
mock.hasprimal = true
mock.hasdual = true
mock.optimize!(mock)
end
function MOIU.set_mock_optimize!(mock::MockSDOptimizer, opts::Function...)
mock.optimize! = MOIU.rec_mock_optimize(mock, opts...)
end
# TODO remove the following methods once it is defined for AbstractMockOptimizer in MOIU
function MOIU.rec_mock_optimize(mock::MockSDOptimizer, opt::Function, opts::Function...)
# TODO replace mock.optimize! = ... by MOI.set(..., MOIU.MockOptimizeFunction, ...)
# where MOIU.MockOptimizeFunction is a MockModelAttribute
(mock::MockSDOptimizer) -> begin
opt(mock)
mock.optimize! = MOIU.rec_mock_optimize(mock, opts...)
end
end
MOIU.rec_mock_optimize(mock::MockSDOptimizer, opt::Function) = opt
# TOD remove the following methods once it is defined for AbstractMockSDOptimizer in MOIU
function MOIU.mock_optimize!(mock::MockSDOptimizer, termstatus::MOI.TerminationStatusCode, primal, dual...)
MOI.set(mock, MOI.TerminationStatus(), termstatus)
MOIU.mock_primal!(mock, primal)
MOIU.mock_dual!(mock, dual...)
end
# Default termination status
function MOIU.mock_optimize!(mock::MockSDOptimizer, primdual...)
MOIU.mock_optimize!(mock, MOI.Optimal, primdual...)
end
function MOIU.mock_optimize!(mock::MockSDOptimizer, termstatus::MOI.TerminationStatusCode)
MOI.set(mock, MOI.TerminationStatus(), termstatus)
end
# Primal
function MOIU.mock_primal!(mock::MockSDOptimizer, primstatus::MOI.ResultStatusCode, varprim...)
MOI.set(mock, MOI.PrimalStatus(), primstatus)
MOIU.mock_varprimal!(mock, varprim...)
end
# Default primal status
MOIU.mock_primal!(mock::MockSDOptimizer, varprim::Vector) = MOIU.mock_primal!(mock, MOI.FeasiblePoint, varprim)
function MOIU.mock_primal!(mock::MockSDOptimizer)
# No primal solution
mock.hasprimal = false
end
# Sets variable primal to varprim
function MOIU.mock_varprimal!(mock::MockSDOptimizer) end
function MOIU.mock_varprimal!(mock::MockSDOptimizer{T}, X::Vector{Matrix{T}}) where T
mock.X = BlockMatrix{T}(X)
end
to_matrix(X::Matrix) = X
function to_matrix(X::Vector)
@assert length(X) == 1
reshape(X, 1, 1)
end
function MOIU.mock_varprimal!(mock::MockSDOptimizer{T}, X::Vector) where T
MOIU.mock_varprimal!(mock, to_matrix.(X))
end
# Dual
function MOIU.mock_dual!(mock::MockSDOptimizer, dualstatus::MOI.ResultStatusCode, conduals...)
MOI.set(mock, MOI.DualStatus(), dualstatus)
MOIU.mock_condual!(mock, conduals...)
end
# Default dual status
function MOIU.mock_dual!(mock::MockSDOptimizer, conduals...)
status = !mock.hasprimal || MOI.get(mock, MOI.PrimalStatus()) == MOI.InfeasiblePoint ? MOI.InfeasibilityCertificate : MOI.FeasiblePoint
MOIU.mock_dual!(mock, status, conduals...)
end
function MOIU.mock_dual!(mock::MockSDOptimizer)
# No dual solution
mock.hasdual = false
end
# Sets constraint dual to conduals
function MOIU.mock_condual!(mock::MockSDOptimizer) end
function MOIU.mock_condual!(mock::MockSDOptimizer{T}, y::Vector{T}) where T
mock.y = y
# blockdims can be negative for diagonal blocks. We allocate full blocks
# here. As mock optimizers are used only for testing, we favor simplicity.
mock.Z = BlockMatrix{T}(map(n -> zeros(T, abs(n), abs(n)), mock.blkdims))
# FIXME shouldn't Z be defined as the opposite i.e. Z = C - sum y_i A_i >= 0
# instead of sum y_i A_i - C <= 0 ?
if mock.dualstatus != MOI.InfeasibilityCertificate
# Infeasibility certificate is a ray so we only take the homogeneous part
# FIXME:check that this is also done IN MOIU.MockOptimizer
for (α, blk, i, j) in mock.objective_coefficients
block(mock.Z, blk)[i, j] -= α
if i != j
block(mock.Z, blk)[j, i] -= α
end
end
end
for (c, constraint_coefficients) in enumerate(mock.constraint_coefficients)
for (α, blk, i, j) in constraint_coefficients
block(mock.Z, blk)[i, j] += α * y[c]
if i != j
block(mock.Z, blk)[j, i] += α * y[c]
end
end
end
end