diff --git a/src/NLSolversBase.jl b/src/NLSolversBase.jl index 9f8161f..8a5b0f0 100644 --- a/src/NLSolversBase.jl +++ b/src/NLSolversBase.jl @@ -41,6 +41,20 @@ export AbstractObjective, export AbstractConstraints, OnceDifferentiableConstraints, TwiceDifferentiableConstraints, ConstraintBounds +function diffeqdiff_fdtype(autodiff) + if autodiff == :finiteforward + fdtype = Val{:forward} + elseif autodiff == :finitecomplex + fdtype = Val{:complex} + elseif any(autodiff .== (:finite, :central, :finitecentral)) + fdtype = Val{:central} + end + fdtype +end + +is_finitediff(autodiff) = autodiff ∈ (:central, :finite, :finiteforward, :finitecomplex) +is_forwarddiff(autodiff) = autodiff ∈ (:forward, :forwarddiff, true) + function x_of_nans(x) x_out = fill!(similar(x), NaN) x_out diff --git a/src/objective_types/oncedifferentiable.jl b/src/objective_types/oncedifferentiable.jl index 5006b29..9790ba3 100644 --- a/src/objective_types/oncedifferentiable.jl +++ b/src/objective_types/oncedifferentiable.jl @@ -34,14 +34,20 @@ function OnceDifferentiable(f, x_seed::AbstractArray{T}, autodiff) where T if typeof(f) <: Union{InplaceObjective, NotInplaceObjective} + fF = make_f(f, x_seed, F) dfF = make_df(f, x_seed, F) fdfF = make_fdf(f, x_seed, F) + return OnceDifferentiable(fF, dfF, fdfF, x_seed, F, DF) else - if any(autodiff .== (:finite, :central)) - # TODO: Allow user to specify Val{:central}, Val{:forward}, :Val{:complex} (requires care when using :forward I think) - gcache = DiffEqDiffTools.GradientCache(x_seed, x_seed, Val{:central}) + if is_finitediff(autodiff) + + # Figure out which Val-type to use for DiffEqDiffTools based on our + # symbol interface. + fdtype = diffeqdiff_fdtype(autodiff) + gcache = DiffEqDiffTools.GradientCache(x_seed, x_seed, fdtype) + function g!(storage, x) DiffEqDiffTools.finite_difference_gradient!(storage, f, x, gcache) return @@ -50,7 +56,7 @@ function OnceDifferentiable(f, x_seed::AbstractArray{T}, g!(storage, x) return f(x) end - elseif autodiff == :forward + elseif is_forwarddiff(autodiff) gcfg = ForwardDiff.GradientConfig(f, x_seed) g! = (out, x) -> ForwardDiff.gradient!(out, f, x, gcfg) @@ -62,6 +68,7 @@ function OnceDifferentiable(f, x_seed::AbstractArray{T}, else error("The autodiff value $autodiff is not support. Use :finite or :forward.") end + return OnceDifferentiable(f, g!, fg!, x_seed, F, DF) end end @@ -87,33 +94,40 @@ function OnceDifferentiable(f, x::AbstractArray, F::AbstractArray, DF::AbstractA fdfF = make_fdf(f, x, F) return OnceDifferentiable(fF, dfF, fdfF, x, F, DF) else - if autodiff == :central || autodiff == :finite - central_cache = DiffEqDiffTools.JacobianCache(similar(x), similar(F), similar(F)) - function fj!(F, J, x) + if is_finitediff(autodiff) + + # Figure out which Val-type to use for DiffEqDiffTools based on our + # symbol interface. + fdtype = diffeqdiff_fdtype(autodiff) + + central_cache = DiffEqDiffTools.JacobianCache(similar(x), similar(F), similar(F), fdtype) + function fj_finitediff!(F, J, x) f(F, x) DiffEqDiffTools.finite_difference_jacobian!(J, f, x, central_cache) F end - function j!(J, x) + function j_finitediff!(J, x) F_cache = similar(F) - fj!(F_cache, J, x) + fj_finitediff!(F_cache, J, x) end - return OnceDifferentiable(f, j!, fj!, x, F, DF) - elseif autodiff == :forward || autodiff == true + return OnceDifferentiable(f, j_finitediff!, fj_finitediff!, x, F, DF) + + elseif is_forwarddiff(autodiff) + jac_cfg = ForwardDiff.JacobianConfig(f, F, x, chunk) ForwardDiff.checktag(jac_cfg, f, x) F2 = copy(F) - function g!(J, x) + function j_forwarddiff!(J, x) ForwardDiff.jacobian!(J, f, F2, x, jac_cfg, Val{false}()) end - function fg!(F, J, x) + function fj_forwarddiff!(F, J, x) jac_res = DiffResults.DiffResult(F, J) ForwardDiff.jacobian!(jac_res, f, F2, x, jac_cfg, Val{false}()) DiffResults.value(jac_res) end - return OnceDifferentiable(f, g!, fg!, x, F, DF) + return OnceDifferentiable(f, j_forwarddiff!, fj_forwarddiff!, x, F, DF) else error("The autodiff value $(autodiff) is not supported. Use :finite or :forward.") end diff --git a/src/objective_types/twicedifferentiable.jl b/src/objective_types/twicedifferentiable.jl index 2660414..ebb8dbf 100644 --- a/src/objective_types/twicedifferentiable.jl +++ b/src/objective_types/twicedifferentiable.jl @@ -50,19 +50,21 @@ function TwiceDifferentiable(f, g, n_x = length(x_seed) g! = df!_from_df(g, F, inplace) - fg! = make_fdf(x_seed, F, f, g!) - if autodiff == :finite - # TODO: Create / request Hessian functionality in DiffEqDiffTools? - # (Or is it better to use the finite difference Jacobian of a gradient?) - # TODO: Allow user to specify Val{:central}, Val{:forward}, :Val{:complex} - jcache = DiffEqDiffTools.JacobianCache(x_seed, Val{:central}) + if is_finitediff(autodiff) + + # Figure out which Val-type to use for DiffEqDiffTools based on our + # symbol interface. + fdtype = diffeqdiff_fdtype(autodiff) + + jcache = DiffEqDiffTools.JacobianCache(x_seed, fdtype) function h!(storage, x) DiffEqDiffTools.finite_difference_jacobian!(storage, g!, x, jcache) return end - elseif autodiff == :forward + + elseif is_forwarddiff(autodiff) hcfg = ForwardDiff.HessianConfig(f, similar(x_seed)) h! = (out, x) -> ForwardDiff.hessian!(out, f, x, hcfg) else @@ -76,16 +78,18 @@ TwiceDifferentiable(d::NonDifferentiable, x_seed::AbstractVector{T} = d.x_f, F:: function TwiceDifferentiable(d::OnceDifferentiable, x_seed::AbstractVector{T} = d.x_f, F::Real = real(zero(T)); autodiff = :finite) where T<:Real - if autodiff == :finite - # TODO: Create / request Hessian functionality in DiffEqDiffTools? - # (Or is it better to use the finite difference Jacobian of a gradient?) - # TODO: Allow user to specify Val{:central}, Val{:forward}, :Val{:complex} - jcache = DiffEqDiffTools.JacobianCache(x_seed, Val{:central}) + if is_finitediff(autodiff) + + # Figure out which Val-type to use for DiffEqDiffTools based on our + # symbol interface. + fdtype = diffeqdiff_fdtype(autodiff) + + jcache = DiffEqDiffTools.JacobianCache(x_seed, fdtype) function h!(storage, x) DiffEqDiffTools.finite_difference_jacobian!(storage, d.df, x, jcache) return end - elseif autodiff == :forward + elseif is_forwarddiff(autodiff) hcfg = ForwardDiff.HessianConfig(d.f, similar(gradient(d))) h! = (out, x) -> ForwardDiff.hessian!(out, d.f, x, hcfg) else @@ -96,9 +100,13 @@ end function TwiceDifferentiable(f, x::AbstractVector, F::Real = real(zero(eltype(x))); autodiff = :finite, inplace = true) - if autodiff == :finite - # TODO: Allow user to specify Val{:central}, Val{:forward}, Val{:complex} - gcache = DiffEqDiffTools.GradientCache(x, x, Val{:central}) + if is_finitediff(autodiff) + + # Figure out which Val-type to use for DiffEqDiffTools based on our + # symbol interface. + fdtype = diffeqdiff_fdtype(autodiff) + gcache = DiffEqDiffTools.GradientCache(x, x, fdtype) + function g!(storage, x) DiffEqDiffTools.finite_difference_gradient!(storage, f, x, gcache) return @@ -107,13 +115,14 @@ function TwiceDifferentiable(f, x::AbstractVector, F::Real = real(zero(eltype(x) g!(storage, x) return f(x) end - # TODO: Allow user to specify Val{:central}, Val{:forward}, :Val{:complex} + function h!(storage::Matrix, x::Vector) # TODO: Wait to use DiffEqDiffTools until they introduce the Hessian feature Calculus.finite_difference_hessian!(f, x, storage) return end - elseif autodiff == :forward + elseif is_forwarddiff(autodiff) + gcfg = ForwardDiff.GradientConfig(f, x) g! = (out, x) -> ForwardDiff.gradient!(out, f, x, gcfg)