Skip to content

Commit fe27396

Browse files
authored
Add constraint_gradient_structure and eval_constraint_gradient (#2200)
1 parent 7238aef commit fe27396

File tree

6 files changed

+88
-0
lines changed

6 files changed

+88
-0
lines changed

docs/src/reference/nonlinear.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ eval_objective
3232
eval_constraint
3333
eval_objective_gradient
3434
jacobian_structure
35+
eval_constraint_gradient
36+
constraint_gradient_structure
3537
eval_constraint_jacobian
3638
eval_constraint_jacobian_product
3739
eval_constraint_jacobian_transpose_product

src/Nonlinear/ReverseAD/mathoptinterface_api.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,22 @@ function MOI.jacobian_structure(d::NLPEvaluator)
207207
return J
208208
end
209209

210+
function MOI.constraint_gradient_structure(d::NLPEvaluator, i)
211+
return copy(d.constraints[i].grad_sparsity)
212+
end
213+
214+
function MOI.eval_constraint_gradient(d::NLPEvaluator, ∇g, x, i)
215+
_reverse_mode(d, x)
216+
for k in d.constraints[i].grad_sparsity
217+
d.jac_storage[k] = 0.0
218+
end
219+
_extract_reverse_pass(d.jac_storage, d, d.constraints[i])
220+
for (j, k) in enumerate(d.constraints[i].grad_sparsity)
221+
∇g[j] = d.jac_storage[k]
222+
end
223+
return
224+
end
225+
210226
function MOI.eval_constraint_jacobian(d::NLPEvaluator, J, x)
211227
_reverse_mode(d, x)
212228
fill!(J, 0.0)

src/Nonlinear/evaluator.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ function MOI.initialize(evaluator::Evaluator, features::Vector{Symbol})
6868
evaluator.eval_objective_timer = 0.0
6969
evaluator.eval_objective_gradient_timer = 0.0
7070
evaluator.eval_constraint_timer = 0.0
71+
evaluator.eval_constraint_gradient_timer = 0.0
7172
evaluator.eval_constraint_jacobian_timer = 0.0
7273
evaluator.eval_hessian_objective_timer = 0.0
7374
evaluator.eval_hessian_constraint_timer = 0.0
@@ -134,6 +135,17 @@ function MOI.eval_constraint(evaluator::Evaluator, g, x)
134135
return
135136
end
136137

138+
function MOI.eval_constraint_gradient(evaluator::Evaluator, ∇g, x, i)
139+
start = time()
140+
MOI.eval_constraint_gradient(evaluator.backend, ∇g, x, i)
141+
evaluator.eval_constraint_gradient_timer += time() - start
142+
return
143+
end
144+
145+
function MOI.constraint_gradient_structure(evaluator::Evaluator, i)
146+
return MOI.constraint_gradient_structure(evaluator.backend, i)
147+
end
148+
137149
function MOI.jacobian_structure(evaluator::Evaluator)
138150
return MOI.jacobian_structure(evaluator.backend)
139151
end

src/Nonlinear/types.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ mutable struct Evaluator{B} <: MOI.AbstractNLPEvaluator
208208
eval_objective_timer::Float64
209209
eval_constraint_timer::Float64
210210
eval_objective_gradient_timer::Float64
211+
eval_constraint_gradient_timer::Float64
211212
eval_constraint_jacobian_timer::Float64
212213
eval_hessian_objective_timer::Float64
213214
eval_hessian_constraint_timer::Float64
@@ -229,6 +230,7 @@ mutable struct Evaluator{B} <: MOI.AbstractNLPEvaluator
229230
0.0,
230231
0.0,
231232
0.0,
233+
0.0,
232234
)
233235
end
234236
end

src/nlp.jl

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,40 @@ The sparsity structure is assumed to be independent of the point ``x``.
264264
"""
265265
function hessian_lagrangian_structure end
266266

267+
"""
268+
constraint_gradient_structure(d::AbstractNLPEvaluator, i::Int)::Vector{Int64}
269+
270+
Returns a vector of indices, where each element indicates the position of a
271+
structurally nonzero element in the gradient of constraint ``\\nabla g_i(x)``.
272+
273+
The indices are not required to be sorted and can contain duplicates, in which
274+
case the solver should combine the corresponding elements by adding them
275+
together.
276+
277+
The sparsity structure is assumed to be independent of the point ``x``.
278+
"""
279+
function constraint_gradient_structure end
280+
281+
"""
282+
eval_constraint_gradient(
283+
d::AbstractNLPEvaluator,
284+
∇g::AbstractVector{T},
285+
x::AbstractVector{T},
286+
i::Int,
287+
)::Nothing where {T}
288+
289+
Evaluate the gradient of constraint `i`, ``\\nabla g_i(x)``, and store the
290+
non-zero values in `∇g`, corresponding to the structure returned by
291+
[`constraint_gradient_structure`](@ref).
292+
293+
## Implementation notes
294+
295+
When implementing this method, you must not assume that `∇g` is
296+
`Vector{Float64}`, but you may assume that it supports `setindex!` and `length`.
297+
For example, it may be the `view` of a vector.
298+
"""
299+
function eval_constraint_gradient end
300+
267301
"""
268302
eval_constraint_jacobian(d::AbstractNLPEvaluator,
269303
J::AbstractVector{T},

test/Nonlinear/ReverseAD.jl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,28 @@ function test_pow_complex_result()
10291029
return
10301030
end
10311031

1032+
function test_constraint_gradient()
1033+
x = MOI.VariableIndex(1)
1034+
y = MOI.VariableIndex(2)
1035+
model = Nonlinear.Model()
1036+
Nonlinear.add_constraint(model, :($x^2 + $x * $y + $y^2), MOI.LessThan(2.0))
1037+
Nonlinear.add_constraint(model, :(cos($y)), MOI.LessThan(2.0))
1038+
evaluator =
1039+
Nonlinear.Evaluator(model, Nonlinear.SparseReverseMode(), [x, y])
1040+
MOI.initialize(evaluator, [:Grad, :Jac])
1041+
@test MOI.constraint_gradient_structure(evaluator, 1) == [1, 2]
1042+
@test MOI.constraint_gradient_structure(evaluator, 2) == [2]
1043+
x_val = [1.2, 2.3]
1044+
∇g = [NaN, NaN]
1045+
MOI.eval_constraint_gradient(evaluator, ∇g, x_val, 1)
1046+
@test ∇g [2 * x_val[1] + x_val[2], x_val[1] + 2 * x_val[2]]
1047+
x_val = [1.2, 2.3]
1048+
∇g = [NaN]
1049+
MOI.eval_constraint_gradient(evaluator, ∇g, x_val, 2)
1050+
@test ∇g [-sin(x_val[2])]
1051+
return
1052+
end
1053+
10321054
end # module
10331055

10341056
TestReverseAD.runtests()

0 commit comments

Comments
 (0)