Skip to content
Closed
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
14 changes: 12 additions & 2 deletions .buildkite/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,23 @@ steps:
plugins:
- JuliaCI/julia#v1:
version: "1"
- JuliaCI/julia-test#v1:
test_args: "GROUP=cuda"
- JuliaCI/julia-coverage#v1:
codecov: true
dirs:
- src
- ext
command: |
julia --color=yes --code-coverage=user --depwarn=yes --project=lib/SimpleNonlinearSolve -e '
import Pkg;
Pkg.Registry.update();
# Install packages present in subdirectories
dev_pks = Pkg.PackageSpec[];
for path in ("lib/NonlinearSolveBase", "lib/BracketingNonlinearSolve", "lib/SciMLJacobianOperators")
push!(dev_pks, Pkg.PackageSpec(; path))
end
Pkg.develop(dev_pks);
Pkg.instantiate();
Pkg.test(; coverage="user")'
agents:
queue: "juliagpu"
cuda: "*"
Expand Down
4 changes: 4 additions & 0 deletions lib/NonlinearSolveBase/src/autodiff.jl
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ function incompatible_backend_and_problem(
return additional_incompatible_backend_check(prob, ad)
end

incompatible_backend_and_problem(::AbstractNonlinearProblem, ::ADTypes.AutoForwardDiff) = false
incompatible_backend_and_problem(::AbstractNonlinearProblem, ::ADTypes.AutoFiniteDiff) = false
incompatible_backend_and_problem(::AbstractNonlinearProblem, ::ADTypes.AutoEnzyme) = false

additional_incompatible_backend_check(::AbstractNonlinearProblem, ::AbstractADType) = false
function additional_incompatible_backend_check(
prob::AbstractNonlinearProblem, ::ADTypes.AutoPolyesterForwardDiff
Expand Down
20 changes: 10 additions & 10 deletions lib/NonlinearSolveBase/src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -173,25 +173,25 @@ can_setindex(::Number) = false
function evaluate_f!!(prob::AbstractNonlinearProblem, fu, u, p = prob.p)
return evaluate_f!!(prob.f, fu, u, p)
end
function evaluate_f!!(f::NonlinearFunction, fu, u, p)
if SciMLBase.isinplace(f)
f(fu, u, p)

@inline function evaluate_f!!(f::NonlinearFunction, fu, u, p)
if f isa NonlinearFunction{true}
f.f(fu, u, p)
return fu
end
return f(u, p)
return f.f(u, p)
end

function evaluate_f(prob::AbstractNonlinearProblem, u)
if SciMLBase.isinplace(prob)
@inline function evaluate_f(prob::AbstractNonlinearProblem, u)
if prob.f isa NonlinearFunction{true}
fu = prob.f.resid_prototype === nothing ? similar(u) :
similar(prob.f.resid_prototype)
prob.f(fu, u, prob.p)
prob.f.f(fu, u, prob.p)
return fu
else
fu = prob.f(u, prob.p)
return prob.f.f(u, prob.p)
end
return fu
end

function evaluate_f!(cache, u, p)
cache.stats.nf += 1
return if SciMLBase.isinplace(cache)
Expand Down
8 changes: 6 additions & 2 deletions lib/SimpleNonlinearSolve/src/broyden.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@ end
function SciMLBase.__solve(
prob::ImmutableNonlinearProblem, alg::SimpleBroyden, args...;
abstol = nothing, reltol = nothing, maxiters = 1000,
alias_u0 = false, termination_condition = nothing, kwargs...
alias::Union{Nothing, SciMLBase.NonlinearAliasSpecifier} = nothing,
alias_u0 = false,
termination_condition = nothing, kwargs...
)
x = NLBUtils.maybe_unaliased(prob.u0, alias_u0)
# Extract alias_u0: if alias struct provided, use it; otherwise use alias_u0 kwarg
_alias_u0 = alias === nothing ? alias_u0 : Utils.get_alias_u0(alias, alias_u0)
x = NLBUtils.maybe_unaliased(prob.u0, _alias_u0)
fx = NLBUtils.evaluate_f(prob, x)
T = promote_type(eltype(fx), eltype(x))

Expand Down
12 changes: 6 additions & 6 deletions lib/SimpleNonlinearSolve/src/dfsane.jl
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,14 @@ end

function SciMLBase.__solve(
prob::ImmutableNonlinearProblem, alg::SimpleDFSane, args...;
abstol = nothing, reltol = nothing, maxiters = 1000, alias = SciMLBase.NonlinearAliasSpecifier(alias_u0 = false),
abstol = nothing, reltol = nothing, maxiters = 1000,
alias::Union{Nothing, SciMLBase.NonlinearAliasSpecifier} = nothing,
alias_u0 = false,
termination_condition = nothing, kwargs...
)
if haskey(kwargs, :alias_u0)
alias = SciMLBase.NonlinearAliasSpecifier(alias_u0 = kwargs[:alias_u0])
end
alias_u0 = alias.alias_u0
x = NLBUtils.maybe_unaliased(prob.u0, alias_u0)
# Extract alias_u0: if alias struct provided, use it; otherwise use alias_u0 kwarg
_alias_u0 = alias === nothing ? alias_u0 : Utils.get_alias_u0(alias, alias_u0)
x = NLBUtils.maybe_unaliased(prob.u0, _alias_u0)
fx = NLBUtils.evaluate_f(prob, x)
T = promote_type(eltype(fx), eltype(x))

Expand Down
8 changes: 6 additions & 2 deletions lib/SimpleNonlinearSolve/src/klement.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ struct SimpleKlement <: AbstractSimpleNonlinearSolveAlgorithm end
function SciMLBase.__solve(
prob::ImmutableNonlinearProblem, alg::SimpleKlement, args...;
abstol = nothing, reltol = nothing, maxiters = 1000,
alias_u0 = false, termination_condition = nothing, kwargs...
alias::Union{Nothing, SciMLBase.NonlinearAliasSpecifier} = nothing,
alias_u0 = false,
termination_condition = nothing, kwargs...
)
x = NLBUtils.maybe_unaliased(prob.u0, alias_u0)
# Extract alias_u0: if alias struct provided, use it; otherwise use alias_u0 kwarg
_alias_u0 = alias === nothing ? alias_u0 : Utils.get_alias_u0(alias, alias_u0)
x = NLBUtils.maybe_unaliased(prob.u0, _alias_u0)
T = eltype(x)
fx = NLBUtils.evaluate_f(prob, x)

Expand Down
12 changes: 6 additions & 6 deletions lib/SimpleNonlinearSolve/src/lbroyden.jl
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,13 @@ end
@views function internal_generic_solve(
prob::ImmutableNonlinearProblem, alg::SimpleLimitedMemoryBroyden,
args...; abstol = nothing, reltol = nothing, maxiters = 1000,
alias = SciMLBase.NonlinearAliasSpecifier(alias_u0 = false), termination_condition = nothing, kwargs...
alias::Union{Nothing, SciMLBase.NonlinearAliasSpecifier} = nothing,
alias_u0 = false,
termination_condition = nothing, kwargs...
)
if haskey(kwargs, :alias_u0)
alias = SciMLBase.NonlinearAliasSpecifier(alias_u0 = kwargs[:alias_u0])
end
alias_u0 = alias.alias_u0
x = NLBUtils.maybe_unaliased(prob.u0, alias_u0)
# Extract alias_u0: if alias struct provided, use it; otherwise use alias_u0 kwarg
_alias_u0 = alias === nothing ? alias_u0 : Utils.get_alias_u0(alias, alias_u0)
x = NLBUtils.maybe_unaliased(prob.u0, _alias_u0)
η = min(NLBUtils.unwrap_val(alg.threshold), maxiters)

# For scalar problems / if the threshold is larger than problem size just use Broyden
Expand Down
14 changes: 7 additions & 7 deletions lib/SimpleNonlinearSolve/src/raphson.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ function SciMLBase.__solve(
prob::Union{ImmutableNonlinearProblem, NonlinearLeastSquaresProblem},
alg::SimpleNewtonRaphson, args...;
abstol = nothing, reltol = nothing, maxiters = 1000,
alias = SciMLBase.NonlinearAliasSpecifier(alias_u0 = false), termination_condition = nothing, kwargs...
alias::Union{Nothing, SciMLBase.NonlinearAliasSpecifier} = nothing,
alias_u0 = false,
termination_condition = nothing, kwargs...
)
if haskey(kwargs, :alias_u0)
alias = SciMLBase.NonlinearAliasSpecifier(alias_u0 = kwargs[:alias_u0])
end
alias_u0 = alias.alias_u0
# Extract alias_u0: if alias struct provided, use it; otherwise use alias_u0 kwarg
_alias_u0 = alias === nothing ? alias_u0 : Utils.get_alias_u0(alias, alias_u0)
autodiff = alg.autodiff
x = NLBUtils.maybe_unaliased(prob.u0, alias_u0)
x = NLBUtils.maybe_unaliased(prob.u0, _alias_u0)
fx = NLBUtils.evaluate_f(prob, x)

iszero(fx) &&
Expand All @@ -54,7 +54,7 @@ function SciMLBase.__solve(
)

@bb xo = similar(x)
fx_cache = (SciMLBase.isinplace(prob) && !SciMLBase.has_jac(prob.f)) ?
fx_cache = Utils.should_cache_fx(prob, prob.f) ?
NLBUtils.safe_similar(fx) : fx
jac_cache = Utils.prepare_jacobian(prob, autodiff, fx_cache, x)
J = Utils.compute_jacobian!!(nothing, prob, autodiff, fx_cache, x, jac_cache)
Expand Down
14 changes: 7 additions & 7 deletions lib/SimpleNonlinearSolve/src/trust_region.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,13 @@ function SciMLBase.__solve(
prob::Union{ImmutableNonlinearProblem, NonlinearLeastSquaresProblem},
alg::SimpleTrustRegion, args...;
abstol = nothing, reltol = nothing, maxiters = 1000,
alias = SciMLBase.NonlinearAliasSpecifier(alias_u0 = false), termination_condition = nothing, kwargs...
alias::Union{Nothing, SciMLBase.NonlinearAliasSpecifier} = nothing,
alias_u0 = false,
termination_condition = nothing, kwargs...
)
if haskey(kwargs, :alias_u0)
alias = SciMLBase.NonlinearAliasSpecifier(alias_u0 = kwargs[:alias_u0])
end
alias_u0 = alias.alias_u0
x = NLBUtils.maybe_unaliased(prob.u0, alias_u0)
# Extract alias_u0: if alias struct provided, use it; otherwise use alias_u0 kwarg
_alias_u0 = alias === nothing ? alias_u0 : Utils.get_alias_u0(alias, alias_u0)
x = NLBUtils.maybe_unaliased(prob.u0, _alias_u0)
T = eltype(x)
Δₘₐₓ = T(alg.max_trust_radius)
Δ = T(alg.initial_trust_radius)
Expand Down Expand Up @@ -101,7 +101,7 @@ function SciMLBase.__solve(
norm_fx = L2_NORM(fx)

@bb xo = copy(x)
fx_cache = (SciMLBase.isinplace(prob) && !SciMLBase.has_jac(prob.f)) ?
fx_cache = Utils.should_cache_fx(prob, prob.f) ?
NLBUtils.safe_similar(fx) : fx
jac_cache = Utils.prepare_jacobian(prob, autodiff, fx_cache, x)
J = Utils.compute_jacobian!!(nothing, prob, autodiff, fx_cache, x, jac_cache)
Expand Down
29 changes: 21 additions & 8 deletions lib/SimpleNonlinearSolve/src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ using StaticArraysCore: StaticArray, SArray, SMatrix, SVector
const DI = DifferentiationInterface
const NLBUtils = NonlinearSolveBase.Utils

# GPU-compatible helper to extract alias_u0 from NonlinearAliasSpecifier
@inline function get_alias_u0(alias::SciMLBase.NonlinearAliasSpecifier, fallback::Bool)
return something(alias.alias_u0, fallback)
end

# GPU-compatible helper to check if fx should be cached
@inline function should_cache_fx(prob::SciMLBase.AbstractNonlinearProblem, f)
return (prob.f isa SciMLBase.NonlinearFunction{true}) && !SciMLBase.has_jac(f)
end

function identity_jacobian(u::Number, fu::Number, α = true)
return convert(promote_type(eltype(u), eltype(fu)), α)
end
Expand Down Expand Up @@ -84,9 +94,10 @@ function prepare_jacobian(prob, autodiff, _, x::Number)
end
return DINoPreparation()
end

function prepare_jacobian(prob, autodiff, fx, x)
SciMLBase.has_jac(prob.f) && return AnalyticJacobian()
if SciMLBase.isinplace(prob.f)
if prob.f isa SciMLBase.NonlinearFunction{true}
return DIExtras(
DI.prepare_jacobian(
prob.f, fx, autodiff, x, Constant(prob.p), strict = Val(false)
Expand Down Expand Up @@ -120,15 +131,15 @@ end

function compute_jacobian!!(J, prob, autodiff, fx, x, ::AnalyticJacobian)
if J === nothing
if SciMLBase.isinplace(prob.f)
if prob.f isa SciMLBase.NonlinearFunction{true}
J = NLBUtils.safe_similar(fx, length(fx), length(x))
prob.f.jac(J, x, prob.p)
return J
else
return prob.f.jac(x, prob.p)
end
end
if SciMLBase.isinplace(prob.f)
if prob.f isa SciMLBase.NonlinearFunction{true}
prob.f.jac(J, x, prob.p)
return J
else
Expand All @@ -138,13 +149,13 @@ end

function compute_jacobian!!(J, prob, autodiff, fx, x, extras::DIExtras)
if J === nothing
if SciMLBase.isinplace(prob.f)
if prob.f isa SciMLBase.NonlinearFunction{true}
return DI.jacobian(prob.f, fx, extras.prep, autodiff, x, Constant(prob.p))
else
return DI.jacobian(prob.f, extras.prep, autodiff, x, Constant(prob.p))
end
end
if SciMLBase.isinplace(prob.f)
if prob.f isa SciMLBase.NonlinearFunction{true}
DI.jacobian!(prob.f, fx, J, extras.prep, autodiff, x, Constant(prob.p))
else
if ArrayInterface.can_setindex(J)
Expand All @@ -156,7 +167,8 @@ function compute_jacobian!!(J, prob, autodiff, fx, x, extras::DIExtras)
return J
end
function compute_jacobian!!(J, prob, autodiff, fx, x, ::DINoPreparation)
@assert !SciMLBase.isinplace(prob.f) "This shouldn't happen. Open an issue."
# Assertion removed for GPU compatibility - DINoPreparation only used for out-of-place
# @assert !SciMLBase.isinplace(prob.f) "This shouldn't happen. Open an issue."
J === nothing && return DI.jacobian(prob.f, autodiff, x, Constant(prob.p))
if ArrayInterface.can_setindex(J)
DI.jacobian!(prob.f, J, autodiff, x, Constant(prob.p))
Expand All @@ -170,8 +182,9 @@ function compute_hvvp(prob, autodiff, _, x::Number, dir::Number)
H = DI.second_derivative(prob.f, autodiff, x, Constant(prob.p))
return H * dir
end

function compute_hvvp(prob, autodiff, fx, x, dir)
jvp_fn = if SciMLBase.isinplace(prob)
jvp_fn = if prob.f isa SciMLBase.NonlinearFunction{true}
@closure (
u,
p,
Expand All @@ -194,4 +207,4 @@ function nonlinear_solution_new_alg(
)
end

end
end
4 changes: 3 additions & 1 deletion lib/SimpleNonlinearSolve/test/gpu/cuda_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ end
@testset for u0 in (1.0f0, @SVector[1.0f0, 1.0f0])
prob = convert(ImmutableNonlinearProblem, NonlinearProblem{false}(f, u0, 2.0f0))

# Note: SimpleHalley is excluded from kernel tests due to dynamic dispatch issues
# (requires LU factorization, second derivatives, and complex type-dependent operations)
@testset "$(nameof(typeof(alg)))" for alg in (
SimpleNewtonRaphson(; autodiff = AutoForwardDiff()),
SimpleDFSane(),
Expand All @@ -74,7 +76,7 @@ end
SimpleBroyden(),
SimpleLimitedMemoryBroyden(),
SimpleKlement(),
SimpleHalley(; autodiff = AutoForwardDiff()),
# SimpleHalley(; autodiff = AutoForwardDiff()),
SimpleBroyden(; linesearch = Val(true)),
SimpleLimitedMemoryBroyden(; linesearch = Val(true)),
)
Expand Down
Loading