Skip to content

Commit 7ca79ec

Browse files
authored
Add four functions in the NLP API (#2162)
1 parent d5c3567 commit 7ca79ec

File tree

7 files changed

+198
-8
lines changed

7 files changed

+198
-8
lines changed

docs/src/reference/nonlinear.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,14 @@ eval_objective
3232
eval_constraint
3333
eval_objective_gradient
3434
jacobian_structure
35-
hessian_lagrangian_structure
3635
eval_constraint_jacobian
3736
eval_constraint_jacobian_product
3837
eval_constraint_jacobian_transpose_product
38+
hessian_lagrangian_structure
39+
hessian_objective_structure
40+
hessian_constraint_structure
41+
eval_hessian_objective
42+
eval_hessian_constraint
3943
eval_hessian_lagrangian
4044
eval_hessian_lagrangian_product
4145
objective_expr

src/Nonlinear/ReverseAD/mathoptinterface_api.jl

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,16 @@ function MOI.initialize(d::NLPEvaluator, requested_features::Vector{Symbol})
156156
end
157157
d.max_chunk = max_chunk
158158
if d.want_hess
159-
_compute_hessian_lagrangian_structure(d)
159+
d.hessian_sparsity = Tuple{Int64,Int64}[]
160+
if d.objective !== nothing
161+
append!(
162+
d.hessian_sparsity,
163+
zip(d.objective.hess_I, d.objective.hess_J),
164+
)
165+
end
166+
for c in d.constraints
167+
append!(d.hessian_sparsity, zip(c.hess_I, c.hess_J))
168+
end
160169
end
161170
end
162171
return
@@ -250,22 +259,41 @@ function MOI.eval_constraint_jacobian_transpose_product(
250259
return
251260
end
252261

262+
function MOI.hessian_objective_structure(d::NLPEvaluator)
263+
@assert d.want_hess
264+
obj = d.objective
265+
return Tuple{Int64,Int64}[(i, j) for (i, j) in zip(obj.hess_I, obj.hess_J)]
266+
end
267+
268+
function MOI.hessian_constraint_structure(d::NLPEvaluator, c::Integer)
269+
@assert d.want_hess
270+
con = d.constraints[c]
271+
return Tuple{Int64,Int64}[(i, j) for (i, j) in zip(con.hess_I, con.hess_J)]
272+
end
273+
253274
function MOI.hessian_lagrangian_structure(d::NLPEvaluator)
254275
@assert d.want_hess
255276
return d.hessian_sparsity
256277
end
257278

258-
function _compute_hessian_lagrangian_structure(d::NLPEvaluator)
259-
d.hessian_sparsity = Tuple{Int64,Int64}[]
279+
function MOI.eval_hessian_objective(d::NLPEvaluator, H, x)
280+
@assert d.want_hess
281+
_reverse_mode(d, x)
282+
fill!(d.input_ϵ, 0.0)
260283
if d.objective !== nothing
261-
append!(d.hessian_sparsity, zip(d.objective.hess_I, d.objective.hess_J))
262-
end
263-
for c in d.constraints
264-
append!(d.hessian_sparsity, zip(c.hess_I, c.hess_J))
284+
_eval_hessian(d, d.objective, H, 1.0, 0)
265285
end
266286
return
267287
end
268288

289+
function MOI.eval_hessian_constraint(d::NLPEvaluator, H, x, i)
290+
@assert d.want_hess
291+
_reverse_mode(d, x)
292+
fill!(d.input_ϵ, 0.0)
293+
_eval_hessian(d, d.constraints[i], H, 1.0, 0)
294+
return
295+
end
296+
269297
function MOI.eval_hessian_lagrangian(d::NLPEvaluator, H, x, σ, μ)
270298
@assert d.want_hess
271299
_reverse_mode(d, x)

src/Nonlinear/ReverseAD/precompile.jl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,23 @@
66

77
function _precompile_()
88
ccall(:jl_generating_output, Cint, ()) == 1 || return nothing
9+
Base.precompile(
10+
Tuple{
11+
typeof(MOI.eval_hessian_objective),
12+
NLPEvaluator,
13+
SubArray{Float64,1,Vector{Float64},Tuple{UnitRange{Int64}},true},
14+
Vector{Float64},
15+
},
16+
)
17+
Base.precompile(
18+
Tuple{
19+
typeof(MOI.eval_hessian_constraint),
20+
NLPEvaluator,
21+
SubArray{Float64,1,Vector{Float64},Tuple{UnitRange{Int64}},true},
22+
Vector{Float64},
23+
Int64,
24+
},
25+
)
926
Base.precompile(
1027
Tuple{
1128
typeof(MOI.eval_hessian_lagrangian),
@@ -42,6 +59,13 @@ function _precompile_()
4259
Vector{Float64},
4360
},
4461
)
62+
Base.precompile(Tuple{typeof(MOI.hessian_objective_structure),NLPEvaluator})
63+
Base.precompile(
64+
Tuple{typeof(MOI.hessian_constraint_structure),NLPEvaluator,Int64},
65+
)
66+
Base.precompile(
67+
Tuple{typeof(MOI.hessian_lagrangian_structure),NLPEvaluator},
68+
)
4569
Base.precompile(Tuple{typeof(MOI.jacobian_structure),NLPEvaluator})
4670
Base.precompile(
4771
Tuple{typeof(MOI.eval_objective),NLPEvaluator,Vector{Float64}},

src/Nonlinear/evaluator.jl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ function MOI.initialize(evaluator::Evaluator, features::Vector{Symbol})
6969
evaluator.eval_objective_gradient_timer = 0.0
7070
evaluator.eval_constraint_timer = 0.0
7171
evaluator.eval_constraint_jacobian_timer = 0.0
72+
evaluator.eval_hessian_objective_timer = 0.0
73+
evaluator.eval_hessian_constraint_timer = 0.0
7274
evaluator.eval_hessian_lagrangian_timer = 0.0
7375
append!(evaluator.ordered_constraints, keys(evaluator.model.constraints))
7476
filter!(f -> f != :ExprGraph, features)
@@ -143,10 +145,32 @@ function MOI.eval_constraint_jacobian(evaluator::Evaluator, J, x)
143145
return
144146
end
145147

148+
function MOI.hessian_objective_structure(evaluator::Evaluator)
149+
return MOI.hessian_objective_structure(evaluator.backend)
150+
end
151+
152+
function MOI.hessian_constraint_structure(evaluator::Evaluator, i)
153+
return MOI.hessian_constraint_structure(evaluator.backend, i)
154+
end
155+
146156
function MOI.hessian_lagrangian_structure(evaluator::Evaluator)
147157
return MOI.hessian_lagrangian_structure(evaluator.backend)
148158
end
149159

160+
function MOI.eval_hessian_objective(evaluator::Evaluator, H, x)
161+
start = time()
162+
MOI.eval_hessian_objective(evaluator.backend, H, x)
163+
evaluator.eval_hessian_objective_timer += time() - start
164+
return
165+
end
166+
167+
function MOI.eval_hessian_constraint(evaluator::Evaluator, H, x, i)
168+
start = time()
169+
MOI.eval_hessian_constraint(evaluator.backend, H, x, i)
170+
evaluator.eval_hessian_constraint_timer += time() - start
171+
return
172+
end
173+
150174
function MOI.eval_hessian_lagrangian(evaluator::Evaluator, H, x, σ, μ)
151175
start = time()
152176
MOI.eval_hessian_lagrangian(evaluator.backend, H, x, σ, μ)

src/Nonlinear/types.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,8 @@ mutable struct Evaluator{B} <: MOI.AbstractNLPEvaluator
209209
eval_constraint_timer::Float64
210210
eval_objective_gradient_timer::Float64
211211
eval_constraint_jacobian_timer::Float64
212+
eval_hessian_objective_timer::Float64
213+
eval_hessian_constraint_timer::Float64
212214
eval_hessian_lagrangian_timer::Float64
213215

214216
function Evaluator(
@@ -225,6 +227,8 @@ mutable struct Evaluator{B} <: MOI.AbstractNLPEvaluator
225227
0.0,
226228
0.0,
227229
0.0,
230+
0.0,
231+
0.0,
228232
)
229233
end
230234
end

src/nlp.jl

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,47 @@ The sparsity structure is assumed to be independent of the point ``x``.
203203
"""
204204
function jacobian_structure end
205205

206+
"""
207+
hessian_objective_structure(
208+
d::AbstractNLPEvaluator,
209+
)::Vector{Tuple{Int64,Int64}}
210+
211+
Returns a vector of tuples, `(row, column)`, where each indicates the position
212+
of a structurally nonzero element in the Hessian matrix:
213+
``\\nabla^2 f(x)``.
214+
215+
The indices are not required to be sorted and can contain duplicates, in which
216+
case the solver should combine the corresponding elements by adding them
217+
together.
218+
219+
Any mix of lower and upper-triangular indices is valid. Elements `(i,j)` and
220+
`(j,i)`, if both present, should be treated as duplicates.
221+
222+
The sparsity structure is assumed to be independent of the point ``x``.
223+
"""
224+
function hessian_objective_structure end
225+
226+
"""
227+
hessian_constraint_structure(
228+
d::AbstractNLPEvaluator,
229+
i::Int64,
230+
)::Vector{Tuple{Int64,Int64}}
231+
232+
Returns a vector of tuples, `(row, column)`, where each indicates the position
233+
of a structurally nonzero element in the Hessian matrix:
234+
``nabla^2 g_i(x)``.
235+
236+
The indices are not required to be sorted and can contain duplicates, in which
237+
case the solver should combine the corresponding elements by adding them
238+
together.
239+
240+
Any mix of lower and upper-triangular indices is valid. Elements `(i,j)` and
241+
`(j,i)`, if both present, should be treated as duplicates.
242+
243+
The sparsity structure is assumed to be independent of the point ``x``.
244+
"""
245+
function hessian_constraint_structure end
246+
206247
"""
207248
hessian_lagrangian_structure(
208249
d::AbstractNLPEvaluator,
@@ -312,6 +353,47 @@ For example, it may be the `view` of a vector.
312353
"""
313354
function eval_hessian_lagrangian_product end
314355

356+
"""
357+
eval_hessian_objective(
358+
d::AbstractNLPEvaluator,
359+
H::AbstractVector{T},
360+
x::AbstractVector{T},
361+
)::Nothing where {T}
362+
363+
This function computes the sparse Hessian matrix:
364+
``\\nabla^2 f(x)``,
365+
storing the result in the vector `H` in the same order as the indices
366+
returned by [`hessian_objective_structure`](@ref).
367+
368+
## Implementation notes
369+
370+
When implementing this method, you must not assume that `H` is
371+
`Vector{Float64}`, but you may assume that it supports `setindex!` and `length`.
372+
For example, it may be the `view` of a vector.
373+
"""
374+
function eval_hessian_objective end
375+
376+
"""
377+
eval_hessian_constraint(
378+
d::AbstractNLPEvaluator,
379+
H::AbstractVector{T},
380+
x::AbstractVector{T},
381+
i::Int64,
382+
)::Nothing where {T}
383+
384+
This function computes the sparse Hessian matrix:
385+
``\\nabla^2 g_i(x)``,
386+
storing the result in the vector `H` in the same order as the indices
387+
returned by [`hessian_constraint_structure`](@ref).
388+
389+
## Implementation notes
390+
391+
When implementing this method, you must not assume that `H` is
392+
`Vector{Float64}`, but you may assume that it supports `setindex!` and `length`.
393+
For example, it may be the `view` of a vector.
394+
"""
395+
function eval_hessian_constraint end
396+
315397
"""
316398
eval_hessian_lagrangian(
317399
d::AbstractNLPEvaluator,

test/Nonlinear/ReverseAD.jl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ function test_objective_quadratic_univariate()
3636
g = [NaN]
3737
MOI.eval_objective_gradient(evaluator, g, [1.2])
3838
@test g == [2.4]
39+
@test MOI.hessian_objective_structure(evaluator) == [(1, 1)]
40+
H = [NaN]
41+
MOI.eval_hessian_objective(evaluator, H, [1.2])
42+
@test H == [2.0]
3943
@test MOI.hessian_lagrangian_structure(evaluator) == [(1, 1)]
4044
H = [NaN]
4145
MOI.eval_hessian_lagrangian(evaluator, H, [1.2], 1.5, Float64[])
@@ -63,6 +67,14 @@ function test_objective_and_constraints_quadratic_univariate()
6367
g = [NaN]
6468
MOI.eval_objective_gradient(evaluator, g, [1.2])
6569
@test g == [2.4]
70+
@test MOI.hessian_objective_structure(evaluator) == [(1, 1)]
71+
H = [NaN]
72+
MOI.eval_hessian_objective(evaluator, H, [1.2])
73+
@test H == [2.0]
74+
@test MOI.hessian_constraint_structure(evaluator, 1) == [(1, 1)]
75+
H = [NaN]
76+
MOI.eval_hessian_constraint(evaluator, H, [1.3], 1)
77+
@test H == [2.0]
6678
@test MOI.hessian_lagrangian_structure(evaluator) == [(1, 1), (1, 1)]
6779
H = [NaN, NaN]
6880
MOI.eval_hessian_lagrangian(evaluator, H, [1.2], 1.5, Float64[1.3])
@@ -92,6 +104,10 @@ function test_objective_quadratic_multivariate()
92104
g = [NaN, NaN]
93105
MOI.eval_objective_gradient(evaluator, g, [1.2, 2.3])
94106
@test g == [2 * 1.2 + 2.3, 1.2 + 2 * 2.3]
107+
@test MOI.hessian_objective_structure(evaluator) == [(1, 1), (2, 2), (2, 1)]
108+
H = [NaN, NaN, NaN]
109+
MOI.eval_hessian_objective(evaluator, H, [1.2, 2.3])
110+
@test H == [2.0, 2.0, 1.0]
95111
@test MOI.hessian_lagrangian_structure(evaluator) ==
96112
[(1, 1), (2, 2), (2, 1)]
97113
H = [NaN, NaN, NaN]
@@ -126,6 +142,10 @@ function test_objective_quadratic_multivariate_subexpressions()
126142
g = [NaN, NaN]
127143
MOI.eval_objective_gradient(evaluator, g, [1.2, 2.3])
128144
@test g == [2 * 1.2 + 2.3, 1.2 + 2 * 2.3]
145+
@test MOI.hessian_objective_structure(evaluator) == [(1, 1), (2, 2), (2, 1)]
146+
H = [NaN, NaN, NaN]
147+
MOI.eval_hessian_objective(evaluator, H, [1.2, 2.3])
148+
@test H == [2.0, 2.0, 1.0]
129149
@test MOI.hessian_lagrangian_structure(evaluator) ==
130150
[(1, 1), (2, 2), (2, 1)]
131151
H = [NaN, NaN, NaN]
@@ -225,6 +245,10 @@ function test_constraint_quadratic_univariate()
225245
J = [NaN]
226246
MOI.eval_constraint_jacobian(evaluator, J, x_val)
227247
@test J == 2 .* x_val
248+
@test MOI.hessian_constraint_structure(evaluator, 1) == [(1, 1)]
249+
H = [NaN]
250+
MOI.eval_hessian_constraint(evaluator, H, x_val, 1)
251+
@test H == [2.0]
228252
@test MOI.hessian_lagrangian_structure(evaluator) == [(1, 1)]
229253
H = [NaN]
230254
MOI.eval_hessian_lagrangian(evaluator, H, x_val, 0.0, [1.5])

0 commit comments

Comments
 (0)