Skip to content

Commit 996fa8c

Browse files
Add NonlinearKeywordArgError to accept lb and ub in NonlinearSolveBase
Created a custom keyword argument handler NonlinearKeywordArgError that extends the standard SciMLBase keywords to include bounds (lb, ub) for NonlinearLeastSquaresProblem. - Added NonlinearKeywordArgError struct and checkkwargs method in NonlinearSolveBase.jl - Added specific solve_call and init_call methods for NonlinearLeastSquaresProblem that use NonlinearKeywordArgError as the default kwargshandle - This allows bounds to be passed as problem kwargs without triggering "Unrecognized keyword arguments" errors 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent fd13f1f commit 996fa8c

File tree

2 files changed

+84
-0
lines changed

2 files changed

+84
-0
lines changed

lib/NonlinearSolveBase/src/NonlinearSolveBase.jl

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,35 @@ using Printf: @printf
4242
const DI = DifferentiationInterface
4343
const SII = SymbolicIndexingInterface
4444

45+
# Custom keyword argument handler that extends the standard SciMLBase keywords
46+
# to include bounds (lb, ub) for NonlinearLeastSquaresProblem
47+
struct NonlinearKeywordArgError end
48+
49+
function SciMLBase.checkkwargs(::Type{NonlinearKeywordArgError}; kwargs...)
50+
keywords = keys(kwargs)
51+
allowed_keywords = (:dense, :saveat, :save_idxs, :save_discretes, :tstops, :tspan,
52+
:d_discontinuities, :save_everystep, :save_on, :save_start, :save_end,
53+
:initialize_save, :adaptive, :abstol, :reltol, :dt, :dtmax, :dtmin,
54+
:force_dtmin, :internalnorm, :controller, :gamma, :beta1, :beta2,
55+
:qmax, :qmin, :qsteady_min, :qsteady_max, :qoldinit, :failfactor,
56+
:calck, :alias_u0, :maxiters, :maxtime, :callback, :isoutofdomain,
57+
:unstable_check, :verbose, :merge_callbacks, :progress, :progress_steps,
58+
:progress_name, :progress_message, :progress_id, :timeseries_errors,
59+
:dense_errors, :weak_timeseries_errors, :weak_dense_errors, :wrap,
60+
:calculate_error, :initializealg, :alg, :save_noise, :delta, :seed,
61+
:alg_hints, :kwargshandle, :trajectories, :batch_size, :sensealg,
62+
:advance_to_tstop, :stop_at_next_tstop, :u0, :p, :default_set,
63+
:second_time, :prob_choice, :alias_jump, :alias_noise, :batch,
64+
:nlsolve_kwargs, :odesolve_kwargs, :linsolve_kwargs, :ensemblealg,
65+
:show_trace, :trace_level, :store_trace, :termination_condition,
66+
:alias, :fit_parameters, :lb, :ub) # Added lb and ub
67+
for kw in keywords
68+
if kw allowed_keywords
69+
throw(SciMLBase.KeywordArgumentError(kw))
70+
end
71+
end
72+
end
73+
4574
include("public.jl")
4675
include("utils.jl")
4776
include("verbosity.jl")

lib/NonlinearSolveBase/src/solve.jl

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,42 @@ function solve_call(prob::SteadyStateProblem,
171171
kwargs...)
172172
end
173173

174+
function solve_call(prob::NonlinearLeastSquaresProblem, args...;
175+
merge_callbacks = true, kwargshandle = nothing, kwargs...)
176+
# Use NonlinearKeywordArgError which accepts lb and ub
177+
kwargshandle = kwargshandle === nothing ? NonlinearKeywordArgError : kwargshandle
178+
kwargshandle = has_kwargs(prob) && haskey(prob.kwargs, :kwargshandle) ?
179+
prob.kwargs[:kwargshandle] : kwargshandle
180+
181+
if has_kwargs(prob)
182+
kwargs = isempty(prob.kwargs) ? kwargs : merge(values(prob.kwargs), kwargs)
183+
end
184+
185+
checkkwargs(kwargshandle; kwargs...)
186+
if isdefined(prob, :u0)
187+
if prob.u0 isa Array
188+
if !isconcretetype(RecursiveArrayTools.recursive_unitless_eltype(prob.u0))
189+
throw(NonConcreteEltypeError(RecursiveArrayTools.recursive_unitless_eltype(prob.u0)))
190+
end
191+
192+
if !(eltype(prob.u0) <: Number) && !(eltype(prob.u0) <: Enum)
193+
throw(NonNumberEltypeError(eltype(prob.u0)))
194+
end
195+
end
196+
197+
if prob.u0 === nothing
198+
return build_null_solution(prob, args...; kwargs...)
199+
end
200+
end
201+
202+
if hasfield(typeof(prob), :f) && hasfield(typeof(prob.f), :f) &&
203+
prob.f.f isa EvalFunc
204+
Base.invokelatest(__solve, prob, args...; kwargs...)
205+
else
206+
__solve(prob, args...; kwargs...)
207+
end
208+
end
209+
174210
function init(
175211
prob::AbstractNonlinearProblem, args...; sensealg = nothing,
176212
u0 = nothing, p = nothing, verbose = NonlinearVerbosity(), kwargs...)
@@ -258,6 +294,25 @@ function init_call(_prob, args...; merge_callbacks=true, kwargshandle=nothing,
258294
end
259295
end
260296

297+
function init_call(prob::NonlinearLeastSquaresProblem, args...;
298+
merge_callbacks = true, kwargshandle = nothing, kwargs...)
299+
# Use NonlinearKeywordArgError which accepts lb and ub
300+
kwargshandle = kwargshandle === nothing ? NonlinearKeywordArgError : kwargshandle
301+
kwargshandle = has_kwargs(prob) && haskey(prob.kwargs, :kwargshandle) ?
302+
prob.kwargs[:kwargshandle] : kwargshandle
303+
if has_kwargs(prob)
304+
kwargs = isempty(prob.kwargs) ? kwargs : merge(values(prob.kwargs), kwargs)
305+
end
306+
307+
checkkwargs(kwargshandle; kwargs...)
308+
if hasfield(typeof(prob), :f) && hasfield(typeof(prob.f), :f) &&
309+
prob.f.f isa EvalFunc
310+
Base.invokelatest(__init, prob, args...; kwargs...)
311+
else
312+
__init(prob, args...; kwargs...)
313+
end
314+
end
315+
261316
function SciMLBase.__solve(
262317
prob::AbstractNonlinearProblem, alg::AbstractNonlinearSolveAlgorithm, args...; kwargs...)
263318
cache = SciMLBase.__init(prob, alg, args...; kwargs...)

0 commit comments

Comments
 (0)