Skip to content

Commit

Permalink
[Nonlinear] allow univariate operators with only gradient information
Browse files Browse the repository at this point in the history
  • Loading branch information
odow committed Sep 2, 2024
1 parent 8950c80 commit b1d9b9b
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 11 deletions.
14 changes: 8 additions & 6 deletions src/Nonlinear/ReverseAD/mathoptinterface_api.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
# Use of this source code is governed by an MIT-style license that can be found
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.

_no_hessian(op::MOI.Nonlinear._UnivariateOperator) = op.f′′ === nothing
_no_hessian(op::MOI.Nonlinear._MultivariateOperator) = op.∇²f === nothing

function MOI.features_available(d::NLPEvaluator)
# Check if we are missing any hessians for user-defined multivariate
# operators, in which case we need to disable :Hess and :HessVec.
d.disable_2ndorder = any(
op -> op.∇²f === nothing,
d.data.operators.registered_multivariate_operators,
)
# Check if we are missing any hessians for user-defined operators, in which
# case we need to disable :Hess and :HessVec.
d.disable_2ndorder =
any(_no_hessian, d.data.operators.registered_univariate_operators) ||
any(_no_hessian, d.data.operators.registered_multivariate_operators)
if d.disable_2ndorder
return [:Grad, :Jac, :JacVec]
end
Expand Down
20 changes: 15 additions & 5 deletions src/Nonlinear/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ struct _UnivariateOperator{F,F′,F′′}
f::F
f′::F′
f′′::F′′
function _UnivariateOperator(
f::Function,
f′::Function,
f′′::Union{Nothing,Function} = nothing,
)
return new{typeof(f),typeof(f′),typeof(f′′)}(f, f′, f′′)
end
end

struct _MultivariateOperator{F,F′,F′′}
Expand Down Expand Up @@ -339,14 +346,17 @@ end
function _UnivariateOperator(op::Symbol, f::Function)
_validate_register_assumptions(f, op, 1)
f′ = _checked_derivative(f, op)
f′′ = _checked_derivative(f′, op)
return _UnivariateOperator(f, f′, f′′)
return _UnivariateOperator(op, f, f′)
end

function _UnivariateOperator(op::Symbol, f::Function, f′::Function)
_validate_register_assumptions(f′, op, 1)
f′′ = _checked_derivative(f′, op)
return _UnivariateOperator(f, f′, f′′)
try
_validate_register_assumptions(f′, op, 1)
f′′ = _checked_derivative(f′, op)
return _UnivariateOperator(f, f′, f′′)
catch
return _UnivariateOperator(f, f′, nothing)
end
end

function _UnivariateOperator(::Symbol, f::Function, f′::Function, f′′::Function)
Expand Down
20 changes: 20 additions & 0 deletions test/Nonlinear/ReverseAD.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1135,6 +1135,26 @@ function test_varying_length_x()
return
end

function test_univariate_operator_with_no_second_order()
f(x::Float64) = x^2
df(x::Float64) = 2 * x
model = MOI.Nonlinear.Model()
MOI.Nonlinear.register_operator(model, :op_f, 1, f, df)
x = MOI.VariableIndex(1)
MOI.Nonlinear.add_constraint(model, :(op_f($x)), MOI.LessThan(2.0))
evaluator = MOI.Nonlinear.Evaluator(
model,
MOI.Nonlinear.SparseReverseMode(),
[x],
)
@test !(:Hess in MOI.features_available(evaluator))
MOI.initialize(evaluator, [:Grad, :Jac])
J = zeros(length(MOI.jacobian_structure(evaluator)))
MOI.eval_constraint_jacobian(evaluator, J, [2.0])
@test J == [4.0]
return
end

end # module

TestReverseAD.runtests()

0 comments on commit b1d9b9b

Please sign in to comment.