3
3
# Use of this source code is governed by an MIT-style license that can be found
4
4
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
5
5
6
+ function MOI. get (
7
+ model:: MOI.ModelLike ,
8
+ :: ObjectiveFunctionAttribute{ReverseObjectiveFunction} ,
9
+ bridge:: MOI.Bridges.Objective.SlackBridge ,
10
+ )
11
+ return MOI. get (model, ReverseConstraintFunction (), bridge. constraint)
12
+ end
13
+
14
+ function MOI. set (
15
+ model:: MOI.ModelLike ,
16
+ :: ObjectiveFunctionAttribute{ForwardObjectiveFunction} ,
17
+ bridge:: MOI.Bridges.Objective.SlackBridge ,
18
+ value,
19
+ )
20
+ return MOI. set (model, ForwardConstraintFunction (), bridge. constraint, value)
21
+ end
22
+
6
23
function MOI. set (
7
24
model:: MOI.ModelLike ,
8
25
attr:: ForwardConstraintFunction ,
@@ -44,6 +61,23 @@ function MOI.set(
44
61
return MOI. set (model, attr, bridge. constraint, mapped_func)
45
62
end
46
63
64
+ function _variable_to_index_map (bridge)
65
+ return Dict {MOI.VariableIndex,MOI.VariableIndex} (
66
+ v => MOI. VariableIndex (i) for
67
+ (i, v) in enumerate (bridge. index_to_variable_map)
68
+ )
69
+ end
70
+
71
+ function _U (func:: MOI.VectorAffineFunction , n, variable_to_index_map)
72
+ # x'Qx/2 + a'x + β is bridged into
73
+ # (1, -a'x - β, U*x) in RSOC
74
+ # where Q = U' * U
75
+ # The linear part of `func` is `[0; -a'; U]`
76
+ Ux = MOI. Utilities. eachscalar (func)[3 : end ]
77
+ func_array = sparse_array_representation (Ux, n, variable_to_index_map)
78
+ return func_array. terms
79
+ end
80
+
47
81
function MOI. get (
48
82
model:: MOI.ModelLike ,
49
83
attr:: DiffOpt.ReverseConstraintFunction ,
@@ -53,55 +87,167 @@ function MOI.get(
53
87
return MOI. Bridges. adjoint_map_function (typeof (bridge), func)
54
88
end
55
89
56
- function MOI. set (
90
+ function MOI. get (
57
91
model:: MOI.ModelLike ,
58
- attr:: DiffOpt.ForwardConstraintFunction ,
92
+ attr:: DiffOpt.ReverseConstraintFunction ,
59
93
bridge:: MOI.Bridges.Constraint.QuadtoSOCBridge{T} ,
60
- diff:: MOI.ScalarQuadraticFunction ,
61
94
) where {T}
62
- func = MOI. get (model, MOI. ConstraintFunction (), bridge. soc)
63
- index_map = index_map_to_oneto (func)
64
- index_map_to_oneto! (index_map, diff)
65
- n = length (index_map. var_map)
66
- func_array = sparse_array_representation (func, n, index_map)
67
- # x'Qx/2 + a'x + β is bridged into
68
- # (1, -a'x - β, U*x) in RSOC
69
- # where Q = U' * U
70
- # `func_array.terms` is `[0; -a'; U]`
71
- U = func_array. terms[3 : end , :]
72
- diff_array = sparse_array_representation (diff, n, index_map)
73
- dU = Matrix (diff_array. quadratic_terms)
74
- dU = dU_from_dQ! (Matrix (diff_array. quadratic_terms), U)
75
- x = Vector {MOI.VariableIndex} (undef, n)
76
- for v in keys (index_map. var_map)
77
- x[index_map[v]. value] = v
95
+ variable_to_index_map = _variable_to_index_map (bridge)
96
+ n = length (bridge. index_to_variable_map)
97
+ U = _U (
98
+ MOI. get (model, MOI. ConstraintFunction (), bridge. soc),
99
+ n,
100
+ variable_to_index_map,
101
+ )
102
+ Δfunc =
103
+ convert (MOI. VectorAffineFunction{T}, MOI. get (model, attr, bridge. soc))
104
+ filter! (Δfunc. terms) do t
105
+ return haskey (variable_to_index_map, t. scalar_term. variable)
78
106
end
79
- diff_aff = MOI. VectorAffineFunction {T} (
80
- MOI. VectorAffineTerm{T}[],
81
- zeros (T, size (dU, 1 ) + 2 ),
107
+ aff = sparse_array_representation (
108
+ - MOI. Utilities. eachscalar (Δfunc)[2 ],
109
+ n,
110
+ variable_to_index_map,
111
+ )
112
+ ΔU = _U (Δfunc, n, variable_to_index_map)
113
+ ΔQ = ΔQ_from_ΔU! (ΔU, U)
114
+ func = MatrixScalarQuadraticFunction (
115
+ convert (VectorScalarAffineFunction{T,Vector{T}}, aff),
116
+ ΔQ,
82
117
)
83
- for t in diff. affine_terms
118
+ index_map = MOI. Utilities. IndexMap ()
119
+ for (i, vi) in enumerate (bridge. index_to_variable_map)
120
+ index_map[MOI. VariableIndex (i)] = vi
121
+ end
122
+ Δ = standard_form (IndexMappedFunction (func, index_map))
123
+ if ! bridge. less_than
124
+ Δ = - Δ
125
+ end
126
+ return Δ
127
+ end
128
+
129
+ function _quad_to_soc_diff (
130
+ :: MOI.ModelLike ,
131
+ bridge:: MOI.Bridges.Constraint.QuadtoSOCBridge{T} ,
132
+ diff:: MOI.ScalarAffineFunction{T} ,
133
+ ) where {T}
134
+ n = length (bridge. index_to_variable_map)
135
+ diff_soc =
136
+ MOI. VectorAffineFunction {T} (MOI. VectorAffineTerm{T}[], zeros (T, n + 2 ))
137
+ for t in diff. terms
84
138
push! (
85
- diff_aff . terms,
139
+ diff_soc . terms,
86
140
MOI. VectorAffineTerm (
87
141
2 ,
88
142
MOI. ScalarAffineTerm (- t. coefficient, t. variable),
89
143
),
90
144
)
91
145
end
92
- diff_aff. constants[2 ] = - diff. constant
146
+ diff_soc. constants[2 ] = - diff. constant
147
+ return diff_soc
148
+ end
149
+
150
+ function _quad_to_soc_diff (
151
+ model:: MOI.ModelLike ,
152
+ bridge:: MOI.Bridges.Constraint.QuadtoSOCBridge{T} ,
153
+ diff:: MOI.ScalarQuadraticFunction{T} ,
154
+ ) where {T}
155
+ variable_to_index_map = _variable_to_index_map (bridge)
156
+ n = length (bridge. index_to_variable_map)
157
+ U = _U (
158
+ MOI. get (model, MOI. ConstraintFunction (), bridge. soc),
159
+ n,
160
+ variable_to_index_map,
161
+ )
162
+ # We assume `diff.quadratic_terms` only contains quadratic terms with
163
+ # variables already in a quadratic term of the initial constraint
164
+ # Otherwise, the `U` matrix will not be square so it's a TODO
165
+ # We remove the affine terms here as they might have other variables not
166
+ # in `variable_to_index_map`
167
+ diff_quad = MOI. ScalarQuadraticFunction (
168
+ diff. quadratic_terms,
169
+ MOI. ScalarAffineTerm{T}[],
170
+ zero (T),
171
+ )
172
+ diff_aff = MOI. ScalarAffineFunction (diff. affine_terms, diff. constant)
173
+ diff_array =
174
+ sparse_array_representation (diff_quad, n, variable_to_index_map)
175
+ dU = dU_from_dQ! (Matrix (diff_array. quadratic_terms), U)
176
+ diff_soc = _quad_to_soc_diff (model, bridge, diff_aff)
93
177
for i in axes (dU, 1 )
94
178
for j in axes (dU, 2 )
95
179
if ! iszero (dU[i, j])
96
- scalar = MOI. ScalarAffineTerm (dU[i, j], x[j])
97
- push! (diff_aff. terms, MOI. VectorAffineTerm (2 + i, scalar))
180
+ scalar = MOI. ScalarAffineTerm (
181
+ dU[i, j],
182
+ bridge. index_to_variable_map[j],
183
+ )
184
+ push! (diff_soc. terms, MOI. VectorAffineTerm (2 + i, scalar))
98
185
end
99
186
end
100
187
end
101
- MOI. set (model, attr, bridge. soc, diff_aff)
188
+ return diff_soc
189
+ end
190
+
191
+ function MOI. set (
192
+ model:: MOI.ModelLike ,
193
+ attr:: DiffOpt.ForwardConstraintFunction ,
194
+ bridge:: MOI.Bridges.Constraint.QuadtoSOCBridge{T} ,
195
+ diff:: MOI.AbstractScalarFunction ,
196
+ ) where {T}
197
+ if ! bridge. less_than
198
+ diff = - diff
199
+ end
200
+ diff_soc = _quad_to_soc_diff (model, bridge, diff)
201
+ MOI. set (model, attr, bridge. soc, diff_soc)
102
202
return
103
203
end
104
204
205
+ """
206
+ ΔQ_from_ΔU!(ΔU, U)
207
+
208
+ Return the symmetric solution `ΔQ` of the matrix equation
209
+ `triu(ΔU) = 2triu(U * ΔQ)`
210
+ where `ΔU` and `U` are the two argument of the function.
211
+
212
+ This function overwrites the first argument `ΔU` to store the solution.
213
+ The matrix `U` is not however modified.
214
+
215
+ The matrix `U` is assumed to be upper triangular.
216
+
217
+ We can exploit the structure of `U` here:
218
+
219
+ * If the factorization was obtained from SVD, `U` would be orthogonal
220
+ * If the factorization was obtained from Cholesky, `U` would be upper triangular.
221
+
222
+ The MOI bridge uses Cholesky in order to exploit sparsity so we are in the
223
+ second case.
224
+
225
+ We can find each column of `ΔQ` by solving a triangular linear system.
226
+ """
227
+ function ΔQ_from_ΔU! (ΔU, U)
228
+ n = LinearAlgebra. checksquare (ΔU)
229
+ LinearAlgebra. rdiv! (ΔU, 2 )
230
+ for j in n: - 1 : 1
231
+ # FIXME MA does not support mutating subarray
232
+ # MA.operate!(MA.sub_mul, view(ΔU, 1:j, j), view(U, 1:j, (j + 1):n), view(ΔU, j, (j+1):n))
233
+ for row in 1 : j
234
+ for col in (j+ 1 ): n
235
+ ΔU[row, j] -= U[row, col] * ΔU[j, col]
236
+ end
237
+ end
238
+ _U = LinearAlgebra. UpperTriangular (view (U, 1 : j, 1 : j))
239
+ LinearAlgebra. ldiv! (_U, view (ΔU, 1 : j, j))
240
+ end
241
+ for j in 1 : n
242
+ for i in 1 : (j- 1 )
243
+ if i != j
244
+ ΔU[j, i] = ΔU[i, j]
245
+ end
246
+ end
247
+ end
248
+ return ΔU
249
+ end
250
+
105
251
"""
106
252
dU_from_dQ!(dQ, U)
107
253
0 commit comments