Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,33 @@ version = "7.11.0"
[deps]
ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b"
DifferentiationInterface = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63"
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
FiniteDiff = "6a86dc24-6348-571c-b903-95158fe2bd41"
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

[compat]
ADTypes = "1.11.0"
Aqua = "0.8.14"
ComponentArrays = "0.15.30"
DifferentiationInterface = "0.6.43, 0.7"
ExplicitImports = "1.13.2"
FiniteDiff = "2.0"
ForwardDiff = "0.10, 1.0"
JET = "0.9, 0.10"
OptimTestProblems = "2.0.3"
LinearAlgebra = "<0.0.1, 1"
Random = "<0.0.1, 1"
RecursiveArrayTools = "3.39"
SparseArrays = "<0.0.1, 1"
StaticArrays = "1.9"
Test = "<0.0.1, 1"
julia = "1.10"

[extras]
ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b"
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66"
ExplicitImports = "7d51a73a-1435-4ff3-83d9-f097790105c7"
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
OptimTestProblems = "cec144fc-5a64-5bc6-99fb-dde8f63e154c"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd"
Expand All @@ -30,4 +40,4 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["ADTypes", "ComponentArrays", "OptimTestProblems", "Random", "RecursiveArrayTools", "SparseArrays", "StaticArrays", "Test"]
test = ["Aqua", "ComponentArrays", "ExplicitImports", "JET", "OptimTestProblems", "Random", "RecursiveArrayTools", "SparseArrays", "StaticArrays", "Test"]
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
NLSolversBase.jl
========

[![Aqua QA](https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg)](https://github.com/JuliaTesting/Aqua.jl)

Base functionality for optimization and solving systems of equations in Julia.

NLSolversBase.jl is the core, common dependency of several packages in the [JuliaNLSolvers](https://github.com/JuliaNLSolvers/) family.
Expand Down
11 changes: 6 additions & 5 deletions src/NLSolversBase.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import DifferentiationInterface as DI
using FiniteDiff: FiniteDiff
using ForwardDiff: ForwardDiff
using LinearAlgebra: LinearAlgebra
import Distributed: clear!

export AbstractObjective,
NonDifferentiable,
OnceDifferentiable,
Expand Down Expand Up @@ -47,13 +47,14 @@ export AbstractConstraints, OnceDifferentiableConstraints,

function finitediff_fdtype(autodiff)
if autodiff == :finiteforward
fdtype = Val{:forward}
return Val{:forward}
elseif autodiff == :finitecomplex
fdtype = Val{:complex}
return Val{:complex}
elseif autodiff == :finite || autodiff == :central || autodiff == :finitecentral
fdtype = Val{:central}
return Val{:central}
else
throw(ArgumentError(LazyString("The autodiff value `", repr(autodiff), "` is not supported. Use `:finite` or `:forward`.")))
end
fdtype
end

forwarddiff_chunksize(::Nothing) = nothing
Expand Down
8 changes: 4 additions & 4 deletions src/interface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ function value!!(obj::AbstractObjective, F, x)
F
end

function _clear_f!(d::NLSolversBase.AbstractObjective)
function _clear_f!(d::AbstractObjective)
d.f_calls = 0
if d.F isa AbstractArray
fill!(d.F, NaN)
Expand All @@ -181,21 +181,21 @@ function _clear_f!(d::NLSolversBase.AbstractObjective)
nothing
end

function _clear_df!(d::NLSolversBase.AbstractObjective)
function _clear_df!(d::AbstractObjective)
d.df_calls = 0
fill!(d.DF, NaN)
fill!(d.x_df, NaN)
nothing
end

function _clear_h!(d::NLSolversBase.AbstractObjective)
function _clear_h!(d::AbstractObjective)
d.h_calls = 0
fill!(d.H, NaN)
fill!(d.x_h, NaN)
nothing
end

function _clear_hv!(d::NLSolversBase.AbstractObjective)
function _clear_hv!(d::AbstractObjective)
d.hv_calls = 0
fill!(d.Hv, NaN)
fill!(d.x_hv, NaN)
Expand Down
70 changes: 57 additions & 13 deletions src/objective_types/incomplete.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,63 @@ df(t::Union{InplaceObjective, NotInplaceObjective}) = t.df
fdf(t::Union{InplaceObjective, NotInplaceObjective}) = t.fdf

# Mutating version
make_f(t::InplaceObjective, x, F::Real) = x -> fdf(t)(F, nothing, x)
make_f(t::InplaceObjective, x, F) = (F, x) -> fdf(t)(F, nothing, x)
make_f(t::InPlaceObjectiveFGH, x, F::Real) = x -> t.fgh(F, nothing, nothing, x)
make_f(t::InPlaceObjectiveFGHv, x, F::Real) = x -> t.fghv(F, nothing, nothing, x, nothing)


make_df(t::InplaceObjective, x, F) = (DF, x) -> fdf(t)(nothing, DF, x)
make_df(t::InPlaceObjectiveFGH, x, F) = (DF, x) -> t.fgh(nothing, DF, nothing, x)
make_df(t::InPlaceObjectiveFGHv, x, F) = (DF, x) -> t.fghv(nothing, DF, nothing, x, nothing)
function make_f(t::InplaceObjective, x, F::Real)
(; fdf, fgh, fghv) = t
if fdf !== nothing
return let fdf = fdf, F = F
x -> fdf(F, nothing, x)
end
elseif fgh !== nothing
return let fgh = fgh, F = F
x -> fgh(F, nothing, nothing, x)
end
elseif fghv !== nothing
return let fghv = fghv, F = F
x -> fghv(F, nothing, nothing, x, nothing)
end
else
throw(ArgumentError("Cannot construct function for evaluating the objective function: No suitable function was provided."))
end
end
make_f(t::InplaceObjective, x, F) = let fdf = t.fdf; (F, x) -> fdf(F, nothing, x); end

function make_df(t::InplaceObjective, x, F)
(; fdf, fgh, fghv) = t
if fdf !== nothing
return let fdf = fdf
(DF, x) -> fdf(nothing, DF, x)
end
elseif fgh !== nothing
return let fgh = fgh
(DF, x) -> fgh(nothing, DF, nothing, x)
end
elseif fghv !== nothing
return let fghv = fghv
(DF, x) -> fghv(nothing, DF, nothing, x, nothing)
end
else
throw(ArgumentError("Cannot construct function for evaluating the gradient of the objective function: No suitable function was provided.."))
end
end

make_fdf(t::InplaceObjective, x, F::Real) = (G, x) -> fdf(t)(F, G, x)
make_fdf(t::InPlaceObjectiveFGH, x, F::Real) = (G, x) -> t.fgh(F, G, nothing, x)
make_fdf(t::InPlaceObjectiveFGHv, x, F::Real) = (G, x) -> t.fghv(F, G, nothing, x, nothing)
function make_fdf(t::InplaceObjective, x, F::Real)
(; fdf, fgh, fghv) = t
if fdf !== nothing
return let fdf = fdf, F = F
(G, x) -> fdf(F, G, x)
end
elseif fgh !== nothing
return let fgh = fgh, F = F
(G, x) -> fgh(F, G, nothing, x)
end
elseif fghv !== nothing
return let fghv = fghv, F = F
(G, x) -> fghv(F, G, nothing, x, nothing)
end
else
throw(ArgumentError("Cannot construct function that evaluates both the objective function and its gradient: No suitable function was provided."))
end
end
make_fdf(t::InplaceObjective, x, F) = fdf(t)

# Non-mutating version
Expand Down Expand Up @@ -93,7 +137,7 @@ end
const InPlaceFGH = InplaceObjective{<:Nothing,<:Nothing,TH,<:Nothing,<:Nothing} where {TH}
const InPlaceFG_HV = InplaceObjective{<:Nothing,TFG,<:Nothing,THv,<:Nothing} where {TFG,THv}
const InPlaceFGHV = InplaceObjective{<:Nothing,<:Nothing,<:Nothing,<:Nothing,TFGHv} where {TFGHv}
function TwiceDifferentiable(t::InPlaceFGH, x::AbstractArray, F::Real = real(zero(eltype(x))), G::AbstractArray = alloc_DF(x, F), H = alloc_H(x, F))
function TwiceDifferentiable(t::InPlaceFGH, x::AbstractArray, F::Real = real(zero(eltype(x))), G::AbstractArray = alloc_DF(x, F), H::AbstractMatrix = alloc_H(x, F))
f = x -> t.fgh(F, nothing, nothing, x)
df = (G, x) -> t.fgh(nothing, G, nothing, x)
fdf = (G, x) -> t.fgh(F, G, nothing, x)
Expand Down
2 changes: 1 addition & 1 deletion src/objective_types/nondifferentiable.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Used for objectives and solvers where no gradient is available/exists
mutable struct NonDifferentiable{TF,TX} <: AbstractObjective
mutable struct NonDifferentiable{TF<:Union{AbstractArray,Real},TX<:AbstractArray} <: AbstractObjective
f
F::TF
x_f::TX
Expand Down
22 changes: 11 additions & 11 deletions src/objective_types/oncedifferentiable.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Used for objectives and solvers where the gradient is available/exists
mutable struct OnceDifferentiable{TF, TDF, TX} <: AbstractObjective
mutable struct OnceDifferentiable{TF<:Union{AbstractArray,Real}, TDF<:AbstractArray, TX<:AbstractArray} <: AbstractObjective
f # objective
df # (partial) derivative of objective
fdf # objective and (partial) derivative of objective
Expand All @@ -15,24 +15,24 @@ end
# Ambiguity
OnceDifferentiable(f, x::AbstractArray,
F::Real = real(zero(eltype(x))),
DF::AbstractArray = alloc_DF(x, F); inplace = true, autodiff = :finite,
DF::AbstractArray = alloc_DF(x, F); inplace::Bool = true, autodiff::Union{AbstractADType,Symbol,Bool} = :finite,
chunk::ForwardDiff.Chunk = ForwardDiff.Chunk(x)) =
OnceDifferentiable(f, x, F, DF, autodiff, chunk)
#OnceDifferentiable(f, x::AbstractArray, F::AbstractArray; autodiff = :finite) =
# OnceDifferentiable(f, x::AbstractArray, F::AbstractArray, alloc_DF(x, F))
function OnceDifferentiable(f, x::AbstractArray,
F::AbstractArray, DF::AbstractArray = alloc_DF(x, F);
inplace = true, autodiff = :finite)
inplace::Bool = true, autodiff::Union{AbstractADType,Symbol,Bool} = :finite)
f! = f!_from_f(f, F, inplace)

OnceDifferentiable(f!, x::AbstractArray, F::AbstractArray, DF, autodiff)
OnceDifferentiable(f!, x, F, DF, autodiff)
end


function OnceDifferentiable(f, x_seed::AbstractArray{T},
function OnceDifferentiable(f, x_seed::AbstractArray,
F::Real,
DF::AbstractArray,
autodiff, chunk) where T
autodiff::Union{AbstractADType,Symbol,Bool}, chunk::ForwardDiff.Chunk)
# When here, at the constructor with positional autodiff, it should already
# be the case, that f is inplace.
if f isa Union{InplaceObjective, NotInplaceObjective}
Expand Down Expand Up @@ -71,7 +71,7 @@ function OnceDifferentiable(f, x::AbstractArray, F::AbstractArray,
OnceDifferentiable(f, x, F, alloc_DF(x, F), :forward, chunk)
end
function OnceDifferentiable(f, x_seed::AbstractArray, F::AbstractArray, DF::AbstractArray,
autodiff::Symbol , chunk::ForwardDiff.Chunk = ForwardDiff.Chunk(x_seed))
autodiff::Union{AbstractADType,Symbol,Bool}, chunk::ForwardDiff.Chunk = ForwardDiff.Chunk(x_seed))
if f isa Union{InplaceObjective, NotInplaceObjective}
fF = make_f(f, x_seed, F)
dfF = make_df(f, x_seed, F)
Expand All @@ -98,7 +98,7 @@ function OnceDifferentiable(f, df,
x::AbstractArray,
F::Real = real(zero(eltype(x))),
DF::AbstractArray = alloc_DF(x, F);
inplace = true)
inplace::Bool = true)


df! = df!_from_df(df, F, inplace)
Expand All @@ -112,7 +112,7 @@ function OnceDifferentiable(f, j,
x::AbstractArray,
F::AbstractArray,
J::AbstractArray = alloc_DF(x, F);
inplace = true)
inplace::Bool = true)

f! = f!_from_f(f, F, inplace)
j! = df!_from_df(j, F, inplace)
Expand All @@ -127,7 +127,7 @@ function OnceDifferentiable(f, df, fdf,
x::AbstractArray,
F::Real = real(zero(eltype(x))),
DF::AbstractArray = alloc_DF(x, F);
inplace = true)
inplace::Bool = true)

# f is never "inplace" since F is scalar
df! = df!_from_df(df, F, inplace)
Expand All @@ -145,7 +145,7 @@ function OnceDifferentiable(f, df, fdf,
x::AbstractArray,
F::AbstractArray,
DF::AbstractArray = alloc_DF(x, F);
inplace = true)
inplace::Bool = true)

f = f!_from_f(f, F, inplace)
df! = df!_from_df(df, F, inplace)
Expand Down
24 changes: 11 additions & 13 deletions src/objective_types/twicedifferentiable.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Used for objectives and solvers where the gradient and Hessian is available/exists
mutable struct TwiceDifferentiable{T,TDF,TH,TX} <: AbstractObjective
mutable struct TwiceDifferentiable{T<:Real,TDF<:AbstractArray,TH<:AbstractMatrix,TX<:AbstractArray} <: AbstractObjective
f
df
fdf
Expand All @@ -17,7 +17,7 @@ mutable struct TwiceDifferentiable{T,TDF,TH,TX} <: AbstractObjective
h_calls::Int
end
# compatibility with old constructor
function TwiceDifferentiable(f, g, fg, h, x::TX, F::T = real(zero(eltype(x))), G::TG = alloc_DF(x, F), H::TH = alloc_H(x, F); inplace = true) where {T, TG, TH, TX}
function TwiceDifferentiable(f, g, fg, h, x::TX, F::T = real(zero(eltype(x))), G::TG = alloc_DF(x, F), H::TH = alloc_H(x, F); inplace::Bool = true) where {T<:Real, TG<:AbstractArray, TH<:AbstractMatrix, TX<:AbstractArray}
x_f, x_df, x_h = x_of_nans(x), x_of_nans(x), x_of_nans(x)

g! = df!_from_df(g, F, inplace)
Expand All @@ -31,10 +31,10 @@ function TwiceDifferentiable(f, g, fg, h, x::TX, F::T = real(zero(eltype(x))), G
end

function TwiceDifferentiable(f, g, h,
x::AbstractVector{TX},
x::AbstractArray,
F::Real = real(zero(eltype(x))),
G = alloc_DF(x, F),
H = alloc_H(x, F); inplace = true) where {TX}
G::AbstractArray = alloc_DF(x, F),
H::AbstractMatrix = alloc_H(x, F); inplace = true)
g! = df!_from_df(g, F, inplace)
h! = h!_from_h(h, F, inplace)

Expand All @@ -47,10 +47,8 @@ end


function TwiceDifferentiable(f, g,
x_seed::AbstractVector{T},
F::Real = real(zero(T)); autodiff = :finite, inplace = true) where T
n_x = length(x_seed)

x_seed::AbstractArray,
F::Real = real(zero(eltype(x_seed))); autodiff::Union{AbstractADType,Symbol,Bool} = :finite, inplace::Bool = true)
g! = df!_from_df(g, F, inplace)
fg! = make_fdf(x_seed, F, f, g!)

Expand All @@ -63,11 +61,11 @@ function TwiceDifferentiable(f, g,
TwiceDifferentiable(f, g!, fg!, h!, x_seed, F)
end

TwiceDifferentiable(d::NonDifferentiable, x_seed::AbstractVector{T} = d.x_f, F::Real = real(zero(T)); autodiff = :finite) where {T<:Real} =
TwiceDifferentiable(d::NonDifferentiable, x_seed::AbstractArray = d.x_f, F::Real = real(zero(eltype(x_seed))); autodiff::Union{AbstractADType,Symbol,Bool} = :finite) =
TwiceDifferentiable(d.f, x_seed, F; autodiff = autodiff)

function TwiceDifferentiable(d::OnceDifferentiable, x_seed::AbstractVector{T} = d.x_f,
F::Real = real(zero(T)); autodiff = :finite) where T<:Real
function TwiceDifferentiable(d::OnceDifferentiable, x_seed::AbstractArray = d.x_f,
F::Real = real(zero(eltype(x_seed))); autodiff::Union{AbstractADType,Symbol,Bool} = :finite)
backend = get_adtype(autodiff)
hess_prep = DI.prepare_hessian(d.f, backend, x_seed)
function h!(_h, _x)
Expand All @@ -78,7 +76,7 @@ function TwiceDifferentiable(d::OnceDifferentiable, x_seed::AbstractVector{T} =
end

function TwiceDifferentiable(f, x::AbstractArray, F::Real = real(zero(eltype(x)));
autodiff = :finite, inplace = true)
autodiff::Union{AbstractADType,Symbol,Bool} = :finite, inplace::Bool = true)
backend = get_adtype(autodiff)
grad_prep = DI.prepare_gradient(f, backend, x)
hess_prep = DI.prepare_hessian(f, backend, x)
Expand Down
2 changes: 1 addition & 1 deletion test/incomplete.jl
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
_F = zero(eltype(x))
od_fgh! = TwiceDifferentiable(only_fgh!(just_fgh!), x, _F)
od_fgh! = TwiceDifferentiable(only_fgh!(just_fgh!), x, _F, similar(x))
od_fgh! = TwiceDifferentiable(only_fgh!(just_fgh!), x, _F, similar(x), NLSolversBase.alloc_DF(x, _F))
od_fgh! = TwiceDifferentiable(only_fgh!(just_fgh!), x, _F, similar(x), NLSolversBase.alloc_H(x, _F))
# od_fgh = TwiceDifferentiable(only_fgh(fgh), x)
for OD in (od_fg, od_fg!, od_fgh!)#, od_fgh)
value!(OD, x)
Expand Down
34 changes: 34 additions & 0 deletions test/qa.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@testset "QA" begin
@testset "Aqua" begin
Aqua.test_all(NLSolversBase)
end

@testset "ExplicitImports" begin
# No implicit imports (`using XY`)
@test ExplicitImports.check_no_implicit_imports(NLSolversBase) === nothing

# All explicit imports (`using XY: Z`) are loaded via their owners
@test ExplicitImports.check_all_explicit_imports_via_owners(NLSolversBase) === nothing

# No explicit imports (`using XY: Z`) that are not used
@test ExplicitImports.check_no_stale_explicit_imports(NLSolversBase) === nothing

# Nothing is accessed via modules other than its owner
@test ExplicitImports.check_all_qualified_accesses_via_owners(NLSolversBase) === nothing

# NLSolversBase accesses almost no non-public names
# The only exception is `ForwardDiff.Chunk`
@test ExplicitImports.check_all_qualified_accesses_are_public(NLSolversBase; ignore = (:Chunk,)) === nothing

# No self-qualified accesses
@test ExplicitImports.check_no_self_qualified_accesses(NLSolversBase) === nothing
end

@testset "JET" begin
# Check that there are no undefined global references and undefined field accesses
JET.test_package(NLSolversBase; target_defined_modules = true, mode = :typo, toplevel_logger = nothing)

# Analyze methods based on their declared signature
JET.test_package(NLSolversBase; target_defined_modules = true, toplevel_logger = nothing)
end
end
Loading