Skip to content

Commit 2401296

Browse files
authored
[Nonlinear] allow univariate operators with only gradient information (#2542)
1 parent 5f5acaa commit 2401296

File tree

3 files changed

+40
-11
lines changed

3 files changed

+40
-11
lines changed

src/Nonlinear/ReverseAD/mathoptinterface_api.jl

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
# Use of this source code is governed by an MIT-style license that can be found
55
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
66

7+
_no_hessian(op::MOI.Nonlinear._UnivariateOperator) = op.f′′ === nothing
8+
_no_hessian(op::MOI.Nonlinear._MultivariateOperator) = op.∇²f === nothing
9+
710
function MOI.features_available(d::NLPEvaluator)
8-
# Check if we are missing any hessians for user-defined multivariate
9-
# operators, in which case we need to disable :Hess and :HessVec.
10-
d.disable_2ndorder = any(
11-
op -> op.∇²f === nothing,
12-
d.data.operators.registered_multivariate_operators,
13-
)
11+
# Check if we are missing any hessians for user-defined operators, in which
12+
# case we need to disable :Hess and :HessVec.
13+
d.disable_2ndorder =
14+
any(_no_hessian, d.data.operators.registered_univariate_operators) ||
15+
any(_no_hessian, d.data.operators.registered_multivariate_operators)
1416
if d.disable_2ndorder
1517
return [:Grad, :Jac, :JacVec]
1618
end

src/Nonlinear/operators.jl

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ struct _UnivariateOperator{F,F′,F′′}
5454
f::F
5555
f′::F′
5656
f′′::F′′
57+
function _UnivariateOperator(
58+
f::Function,
59+
f′::Function,
60+
f′′::Union{Nothing,Function} = nothing,
61+
)
62+
return new{typeof(f),typeof(f′),typeof(f′′)}(f, f′, f′′)
63+
end
5764
end
5865

5966
struct _MultivariateOperator{F,F′,F′′}
@@ -339,14 +346,17 @@ end
339346
function _UnivariateOperator(op::Symbol, f::Function)
340347
_validate_register_assumptions(f, op, 1)
341348
f′ = _checked_derivative(f, op)
342-
f′′ = _checked_derivative(f′, op)
343-
return _UnivariateOperator(f, f′, f′′)
349+
return _UnivariateOperator(op, f, f′)
344350
end
345351

346352
function _UnivariateOperator(op::Symbol, f::Function, f′::Function)
347-
_validate_register_assumptions(f′, op, 1)
348-
f′′ = _checked_derivative(f′, op)
349-
return _UnivariateOperator(f, f′, f′′)
353+
try
354+
_validate_register_assumptions(f′, op, 1)
355+
f′′ = _checked_derivative(f′, op)
356+
return _UnivariateOperator(f, f′, f′′)
357+
catch
358+
return _UnivariateOperator(f, f′, nothing)
359+
end
350360
end
351361

352362
function _UnivariateOperator(::Symbol, f::Function, f′::Function, f′′::Function)

test/Nonlinear/ReverseAD.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,6 +1135,23 @@ function test_varying_length_x()
11351135
return
11361136
end
11371137

1138+
function test_univariate_operator_with_no_second_order()
1139+
f(x::Float64) = x^2
1140+
df(x::Float64) = 2 * x
1141+
model = MOI.Nonlinear.Model()
1142+
MOI.Nonlinear.register_operator(model, :op_f, 1, f, df)
1143+
x = MOI.VariableIndex(1)
1144+
MOI.Nonlinear.add_constraint(model, :(op_f($x)), MOI.LessThan(2.0))
1145+
evaluator =
1146+
MOI.Nonlinear.Evaluator(model, MOI.Nonlinear.SparseReverseMode(), [x])
1147+
@test !(:Hess in MOI.features_available(evaluator))
1148+
MOI.initialize(evaluator, [:Grad, :Jac])
1149+
J = zeros(length(MOI.jacobian_structure(evaluator)))
1150+
MOI.eval_constraint_jacobian(evaluator, J, [2.0])
1151+
@test J == [4.0]
1152+
return
1153+
end
1154+
11381155
end # module
11391156

11401157
TestReverseAD.runtests()

0 commit comments

Comments
 (0)