Default backend and performance in ADNLPModels

As illustrated in the tutorial on backends, ADNLPModels.jl use different backend for each method from the NLPModel API that are implemented. By default, it uses the following:

using ADNLPModels, NLPModels

f(x) = 100 * (x[2] - x[1]^2)^2 + (x[1] - 1)^2
T = Float64
x0 = T[-1.2; 1.0]
lvar, uvar = zeros(T, 2), ones(T, 2) # must be of same type than `x0`
lcon, ucon = -T[0.5], T[0.5]
c!(cx, x) = begin
  cx[1] = x[1] + x[2]
  return cx
end
nlp = ADNLPModel!(f, x0, lvar, uvar, c!, lcon, ucon)
get_adbackend(nlp)
ADModelBackend{
  ForwardDiffADGradient,
  ForwardDiffADHvprod,
  ForwardDiffADJprod,
  ForwardDiffADJtprod,
  SparseADJacobian,
  SparseADHessian,
  ForwardDiffADGHjvprod,
}

Note that ForwardDiff.jl is mainly used as it is efficient and stable.

Predefined backends

Another way to know the default backends used is to check the constant ADNLPModels.default_backend.

ADNLPModels.default_backend
Dict{Symbol, Type} with 12 entries:
  :hprod_residual_backend    => ForwardDiffADHvprod
  :jprod_backend             => ForwardDiffADJprod
  :jtprod_backend            => ForwardDiffADJtprod
  :gradient_backend          => ForwardDiffADGradient
  :hprod_backend             => ForwardDiffADHvprod
  :hessian_residual_backend  => SparseADHessian
  :ghjvprod_backend          => ForwardDiffADGHjvprod
  :hessian_backend           => SparseADHessian
  :jprod_residual_backend    => ForwardDiffADJprod
  :jtprod_residual_backend   => ForwardDiffADJtprod
  :jacobian_residual_backend => SparseADJacobian
  :jacobian_backend          => SparseADJacobian

More generally, the package anticipates more uses

ADNLPModels.predefined_backend
Dict{Symbol, Dict{Symbol}} with 3 entries:
  :default   => Dict{Symbol, Type}(:hprod_residual_backend=>ForwardDiffADHvprod…
  :generic   => Dict{Symbol, DataType}(:hprod_residual_backend=>GenericForwardD…
  :optimized => Dict{Symbol, Type}(:hprod_residual_backend=>ReverseDiffADHvprod…

The backend :optimized will mainly focus on the most efficient approaches, for instance using ReverseDiff to compute the gradient instead of ForwardDiff.

ADNLPModels.predefined_backend[:optimized]
Dict{Symbol, Type} with 12 entries:
  :hprod_residual_backend    => ReverseDiffADHvprod
  :jprod_backend             => ForwardDiffADJprod
  :jtprod_backend            => ReverseDiffADJtprod
  :gradient_backend          => ReverseDiffADGradient
  :hprod_backend             => ReverseDiffADHvprod
  :hessian_residual_backend  => SparseReverseADHessian
  :ghjvprod_backend          => ForwardDiffADGHjvprod
  :hessian_backend           => SparseReverseADHessian
  :jprod_residual_backend    => ForwardDiffADJprod
  :jtprod_residual_backend   => ReverseDiffADJtprod
  :jacobian_residual_backend => SparseADJacobian
  :jacobian_backend          => SparseADJacobian

The backend :generic focuses on backend that make no assumptions on the element type, see Creating an ADNLPModels backend that supports multiple precisions.

It is possible to use these pre-defined backends using the keyword argument backend when instantiating the model.

nlp = ADNLPModel!(f, x0, lvar, uvar, c!, lcon, ucon, backend = :optimized)
get_adbackend(nlp)
ADModelBackend{
  ReverseDiffADGradient,
  ReverseDiffADHvprod,
  ForwardDiffADJprod,
  ReverseDiffADJtprod,
  SparseADJacobian,
  SparseReverseADHessian,
  ForwardDiffADGHjvprod,
}