Skip to content

Commit 7abe1c4

Browse files
blegatmatbesancon
andauthored
Add back qp_test (#240)
* Add back qp_test * Fix * Fixes * Fix * move structs to utils * format * Fixes * Small refactor * Fix --------- Co-authored-by: Mathieu Besançon <mathieu.besancon@gmail.com>
1 parent 86a4cf1 commit 7abe1c4

File tree

7 files changed

+167
-138
lines changed

7 files changed

+167
-138
lines changed

src/ConicProgram/ConicProgram.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,9 +398,12 @@ function DiffOpt._get_db(
398398
i = MOI.Utilities.rows(model.model.constraints, ci) # vector
399399
# i = ci.value
400400
n = length(model.x) # columns in A
401+
# Since `b` in https://arxiv.org/pdf/1904.09043.pdf is the constant in the right-hand side and
402+
# `b` in MOI is the constant on the left-hand side, we have the opposite sign here
401403
# db = - dQ[n+1:n+m, end] + dQ[end, n+1:n+m]'
402404
g = model.back_grad_cache.g
403405
πz = model.back_grad_cache.πz
406+
# `g[end] * πz[n .+ i] - πz[end] * g[n .+ i]`
404407
return DiffOpt.lazy_combination(-, πz, g, length(g), n .+ i)
405408
end
406409

@@ -415,7 +418,8 @@ function DiffOpt._get_dA(
415418
# dA = - dQ[1:n, n+1:n+m]' + dQ[n+1:n+m, 1:n]
416419
g = model.back_grad_cache.g
417420
πz = model.back_grad_cache.πz
418-
return DiffOpt.lazy_combination(-, g, πz, i, n .+ (1:n))
421+
#return DiffOpt.lazy_combination(-, g, πz, n .+ i, 1:n)
422+
return g[n.+i] * πz[1:n]' - πz[n.+i] * g[1:n]'
419423
end
420424

421425
function MOI.get(

src/bridges.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ function MOI.set(
1717
)
1818
end
1919

20+
function MOI.get(
21+
model::MOI.ModelLike,
22+
attr::ReverseConstraintFunction,
23+
bridge::MOI.Bridges.Constraint.VectorizeBridge,
24+
)
25+
return MOI.Utilities.eachscalar(
26+
MOI.get(model, attr, bridge.vector_constraint),
27+
)[1]
28+
end
2029
function MOI.get(
2130
model::MOI.ModelLike,
2231
attr::DiffOpt.ReverseConstraintFunction,

src/jump_moi_overloads.jl

Lines changed: 0 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -131,82 +131,6 @@ function Base.isapprox(
131131
return isapprox(standard_form(func1), standard_form(func2); kws...)
132132
end
133133

134-
# In the future, we could replace by https://github.com/jump-dev/MathOptInterface.jl/pull/1238
135-
"""
136-
VectorScalarAffineFunction{T, VT} <: MOI.AbstractScalarFunction
137-
138-
Represents the function `x ⋅ terms + constant`
139-
as an `MOI.AbstractScalarFunction` where `x[i] = MOI.VariableIndex(i)`.
140-
Use [`standard_form`](@ref) to convert it to a `MOI.ScalarAffineFunction{T}`.
141-
"""
142-
struct VectorScalarAffineFunction{T,VT} <: MOI.AbstractScalarFunction
143-
terms::VT
144-
constant::T
145-
end
146-
147-
MOI.constant(func::VectorScalarAffineFunction) = func.constant
148-
149-
function JuMP.coefficient(
150-
func::VectorScalarAffineFunction,
151-
vi::MOI.VariableIndex,
152-
)
153-
return func.terms[vi.value]
154-
end
155-
156-
function Base.convert(
157-
::Type{MOI.ScalarAffineFunction{T}},
158-
func::VectorScalarAffineFunction,
159-
) where {T}
160-
return MOI.ScalarAffineFunction{T}(
161-
# TODO we should do better if the vector is a `SparseVector`, I think
162-
# I have some code working for both vector types in Polyhedra.jl
163-
MOI.ScalarAffineTerm{T}[
164-
MOI.ScalarAffineTerm{T}(func.terms[i], MOI.VariableIndex(i)) for
165-
i in eachindex(func.terms) if !iszero(func.terms[i])
166-
],
167-
func.constant,
168-
)
169-
end
170-
171-
function standard_form(func::VectorScalarAffineFunction{T}) where {T}
172-
return convert(MOI.ScalarAffineFunction{T}, func)
173-
end
174-
175-
function MOI.Utilities.operate(
176-
::typeof(-),
177-
::Type{T},
178-
func::VectorScalarAffineFunction{T},
179-
) where {T}
180-
return VectorScalarAffineFunction(
181-
LazyArrays.ApplyArray(-, func.terms),
182-
-func.constant,
183-
)
184-
end
185-
186-
"""
187-
struct MatrixScalarQuadraticFunction{T, VT, MT} <: MOI.AbstractScalarFunction
188-
affine::VectorScalarAffineFunction{T,VT}
189-
terms::MT
190-
end
191-
192-
Represents the function `x' * terms * x / 2 + affine` as an
193-
`MOI.AbstractScalarFunction` where `x[i] = MOI.VariableIndex(i)`.
194-
Use [`standard_form`](@ref) to convert it to a `MOI.ScalarQuadraticFunction{T}`.
195-
"""
196-
struct MatrixScalarQuadraticFunction{T,VT,MT} <: MOI.AbstractScalarFunction
197-
affine::VectorScalarAffineFunction{T,VT}
198-
terms::MT
199-
end
200-
201-
MOI.constant(func::MatrixScalarQuadraticFunction) = MOI.constant(func.affine)
202-
203-
function JuMP.coefficient(
204-
func::MatrixScalarQuadraticFunction,
205-
vi::MOI.VariableIndex,
206-
)
207-
return JuMP.coefficient(func.affine, vi)
208-
end
209-
210134
function quad_sym_half(
211135
func::MatrixScalarQuadraticFunction,
212136
vi1::MOI.VariableIndex,
@@ -250,50 +174,6 @@ function standard_form(func::MatrixScalarQuadraticFunction{T}) where {T}
250174
return convert(MOI.ScalarQuadraticFunction{T}, func)
251175
end
252176

253-
"""
254-
MatrixVectorAffineFunction{T, VT} <: MOI.AbstractVectorFunction
255-
256-
Represents the function `terms * x + constant`
257-
as an `MOI.AbstractVectorFunction` where `x[i] = MOI.VariableIndex(i)`.
258-
Use [`standard_form`](@ref) to convert it to a `MOI.VectorAffineFunction{T}`.
259-
"""
260-
struct MatrixVectorAffineFunction{AT,VT} <: MOI.AbstractVectorFunction
261-
terms::AT
262-
constants::VT
263-
end
264-
265-
MOI.constant(func::MatrixVectorAffineFunction) = func.constants
266-
267-
function Base.convert(
268-
::Type{MOI.VectorAffineFunction{T}},
269-
func::MatrixVectorAffineFunction,
270-
) where {T}
271-
return MOI.VectorAffineFunction{T}(
272-
MOI.VectorAffineTerm{T}[
273-
# TODO we should do better if the matrix is a `SparseMatrixCSC`
274-
MOI.VectorAffineTerm(
275-
i,
276-
MOI.ScalarAffineTerm{T}(func.terms[i, j], MOI.VariableIndex(j)),
277-
) for i in 1:size(func.terms, 1) for
278-
j in 1:size(func.terms, 2) if !iszero(func.terms[i, j])
279-
],
280-
func.constants,
281-
)
282-
end
283-
284-
function standard_form(func::MatrixVectorAffineFunction{T}) where {T}
285-
return convert(MOI.VectorAffineFunction{T}, func)
286-
end
287-
288-
# Only used for testing at the moment so performance is not critical so
289-
# converting to standard form is ok
290-
function MOI.Utilities.isapprox_zero(
291-
func::Union{VectorScalarAffineFunction,MatrixScalarQuadraticFunction},
292-
tol,
293-
)
294-
return MOI.Utilities.isapprox_zero(standard_form(func), tol)
295-
end
296-
297177
"""
298178
IndexMappedFunction{F<:MOI.AbstractFunction} <: AbstractLazyScalarFunction
299179

src/utils.jl

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,127 @@ function sparse_array_representation(
152152
)
153153
end
154154

155+
# In the future, we could replace by https://github.com/jump-dev/MathOptInterface.jl/pull/1238
156+
"""
157+
VectorScalarAffineFunction{T, VT} <: MOI.AbstractScalarFunction
158+
159+
Represents the function `x ⋅ terms + constant`
160+
as an `MOI.AbstractScalarFunction` where `x[i] = MOI.VariableIndex(i)`.
161+
Use [`standard_form`](@ref) to convert it to a `MOI.ScalarAffineFunction{T}`.
162+
"""
163+
struct VectorScalarAffineFunction{T,VT} <: MOI.AbstractScalarFunction
164+
terms::VT
165+
constant::T
166+
end
167+
MOI.constant(func::VectorScalarAffineFunction) = func.constant
168+
function JuMP.coefficient(
169+
func::VectorScalarAffineFunction,
170+
vi::MOI.VariableIndex,
171+
)
172+
return func.terms[vi.value]
173+
end
174+
function Base.convert(
175+
::Type{MOI.ScalarAffineFunction{T}},
176+
func::VectorScalarAffineFunction,
177+
) where {T}
178+
return MOI.ScalarAffineFunction{T}(
179+
# TODO we should do better if the vector is a `SparseVector`, I think
180+
# I have some code working for both vector types in Polyhedra.jl
181+
MOI.ScalarAffineTerm{T}[
182+
MOI.ScalarAffineTerm{T}(func.terms[i], MOI.VariableIndex(i)) for
183+
i in eachindex(func.terms) if !iszero(func.terms[i])
184+
],
185+
func.constant,
186+
)
187+
end
188+
function standard_form(func::VectorScalarAffineFunction{T}) where {T}
189+
return convert(MOI.ScalarAffineFunction{T}, func)
190+
end
191+
192+
function MOI.Utilities.operate(
193+
::typeof(-),
194+
::Type{T},
195+
func::VectorScalarAffineFunction{T},
196+
) where {T}
197+
return VectorScalarAffineFunction(
198+
LazyArrays.ApplyArray(-, func.terms),
199+
-func.constant,
200+
)
201+
end
202+
203+
"""
204+
struct MatrixScalarQuadraticFunction{T, VT, MT} <: MOI.AbstractScalarFunction
205+
affine::VectorScalarAffineFunction{T,VT}
206+
terms::MT
207+
end
208+
209+
Represents the function `x' * terms * x / 2 + affine` as an
210+
`MOI.AbstractScalarFunction` where `x[i] = MOI.VariableIndex(i)`.
211+
Use [`standard_form`](@ref) to convert it to a `MOI.ScalarQuadraticFunction{T}`.
212+
"""
213+
struct MatrixScalarQuadraticFunction{T,VT,MT} <: MOI.AbstractScalarFunction
214+
affine::VectorScalarAffineFunction{T,VT}
215+
terms::MT
216+
end
217+
MOI.constant(func::MatrixScalarQuadraticFunction) = MOI.constant(func.affine)
218+
function JuMP.coefficient(
219+
func::MatrixScalarQuadraticFunction,
220+
vi::MOI.VariableIndex,
221+
)
222+
return JuMP.coefficient(func.affine, vi)
223+
end
224+
225+
"""
226+
MatrixVectorAffineFunction{T, VT} <: MOI.AbstractVectorFunction
227+
228+
Represents the function `terms * x + constant`
229+
as an `MOI.AbstractVectorFunction` where `x[i] = MOI.VariableIndex(i)`.
230+
Use [`standard_form`](@ref) to convert it to a `MOI.VectorAffineFunction{T}`.
231+
"""
232+
struct MatrixVectorAffineFunction{AT,VT} <: MOI.AbstractVectorFunction
233+
terms::AT
234+
constants::VT
235+
end
236+
MOI.constant(func::MatrixVectorAffineFunction) = func.constants
237+
function Base.convert(
238+
::Type{MOI.VectorAffineFunction{T}},
239+
func::MatrixVectorAffineFunction,
240+
) where {T}
241+
return MOI.VectorAffineFunction{T}(
242+
MOI.VectorAffineTerm{T}[
243+
# TODO we should do better if the matrix is a `SparseMatrixCSC`
244+
MOI.VectorAffineTerm(
245+
i,
246+
MOI.ScalarAffineTerm{T}(func.terms[i, j], MOI.VariableIndex(j)),
247+
) for i in 1:size(func.terms, 1) for
248+
j in 1:size(func.terms, 2) if !iszero(func.terms[i, j])
249+
],
250+
func.constants,
251+
)
252+
end
253+
function standard_form(func::MatrixVectorAffineFunction{T}) where {T}
254+
return convert(MOI.VectorAffineFunction{T}, func)
255+
end
256+
257+
# Only used for testing at the moment so performance is not critical so
258+
# converting to standard form is ok
259+
function MOIU.isapprox_zero(
260+
func::Union{VectorScalarAffineFunction,MatrixScalarQuadraticFunction},
261+
tol,
262+
)
263+
return MOIU.isapprox_zero(standard_form(func), tol)
264+
end
265+
266+
_scalar(::Type{<:MatrixVectorAffineFunction}) = VectorScalarAffineFunction
267+
_scalar(::Type{<:SparseVectorAffineFunction}) = SparseScalarAffineFunction
268+
269+
function Base.getindex(
270+
it::MOI.Utilities.ScalarFunctionIterator{F},
271+
output_index::Integer,
272+
) where {F<:Union{MatrixVectorAffineFunction,SparseVectorAffineFunction}}
273+
return _scalar(F)(it.f.terms[output_index, :], it.f.constants[output_index])
274+
end
275+
155276
function _index_map_to_oneto!(index_map, v::MOI.VariableIndex)
156277
if !haskey(index_map, v)
157278
n = length(index_map.var_map)

test/linear_program.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import Ipopt
1212
import MathOptInterface as MOI
1313
import SCS
1414

15-
const ATOL = 2e-4
16-
const RTOL = 2e-4
15+
const ATOL = 1e-2
16+
const RTOL = 1e-2
1717

1818
function runtests()
1919
for name in names(@__MODULE__; all = true)

test/quadratic_program.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ function test_differentiating_non_trivial_convex_qp_moi()
322322
dbb = vec(grads_actual[6])
323323
qp_test(
324324
Ipopt.Optimizer,
325+
DiffOpt.ConicProgram.Model,
325326
true,
326327
true,
327328
true;

0 commit comments

Comments
 (0)