diff --git a/lib/BracketingNonlinearSolve/src/BracketingNonlinearSolve.jl b/lib/BracketingNonlinearSolve/src/BracketingNonlinearSolve.jl index b7c16fac3..3198e9b44 100644 --- a/lib/BracketingNonlinearSolve/src/BracketingNonlinearSolve.jl +++ b/lib/BracketingNonlinearSolve/src/BracketingNonlinearSolve.jl @@ -5,7 +5,7 @@ using PrecompileTools: @compile_workload, @setup_workload using Reexport: @reexport using CommonSolve: CommonSolve, solve -using NonlinearSolveBase: NonlinearSolveBase, AbstractNonlinearSolveAlgorithm +using NonlinearSolveBase: NonlinearSolveBase, AbstractNonlinearSolveAlgorithm, NonlinearVerbosity, @SciMLMessage, Verbosity using SciMLBase: SciMLBase, IntervalNonlinearProblem, ReturnCode abstract type AbstractBracketingAlgorithm <: AbstractNonlinearSolveAlgorithm end diff --git a/lib/BracketingNonlinearSolve/src/bisection.jl b/lib/BracketingNonlinearSolve/src/bisection.jl index 5af8f275b..95ed2fe07 100644 --- a/lib/BracketingNonlinearSolve/src/bisection.jl +++ b/lib/BracketingNonlinearSolve/src/bisection.jl @@ -21,7 +21,7 @@ end function SciMLBase.__solve( prob::IntervalNonlinearProblem, alg::Bisection, args...; - maxiters = 1000, abstol = nothing, verbose::Bool = true, kwargs... + maxiters = 1000, abstol = nothing, verbose::NonlinearVerbosity = NonlinearVerbosity(), kwargs... ) @assert !SciMLBase.isinplace(prob) "`Bisection` only supports out-of-place problems." @@ -45,9 +45,9 @@ function SciMLBase.__solve( end if sign(fl) == sign(fr) - verbose && - @warn "The interval is not an enclosing interval, opposite signs at the \ - boundaries are required." + @SciMLMessage("The interval is not an enclosing interval, opposite signs at the \ + boundaries are required.", + verbose, :non_enclosing_interval, :error_control) return SciMLBase.build_solution( prob, alg, left, fl; retcode = ReturnCode.InitialFailure, left, right ) diff --git a/lib/BracketingNonlinearSolve/src/brent.jl b/lib/BracketingNonlinearSolve/src/brent.jl index a1dbb9641..b4936ddb6 100644 --- a/lib/BracketingNonlinearSolve/src/brent.jl +++ b/lib/BracketingNonlinearSolve/src/brent.jl @@ -7,10 +7,20 @@ struct Brent <: AbstractBracketingAlgorithm end function SciMLBase.__solve( prob::IntervalNonlinearProblem, alg::Brent, args...; - maxiters = 1000, abstol = nothing, verbose::Bool = true, kwargs... + maxiters = 1000, abstol = nothing, verbose = NonlinearVerbosity(), kwargs... ) @assert !SciMLBase.isinplace(prob) "`Brent` only supports out-of-place problems." + if verbose isa Bool + if verbose + verbose = NonlinearVerbosity() + else + verbose = NonlinearVerbosity(Verbosity.None()) + end + elseif verbose isa Verbosity.Type + verbose = NonlinearVerbosity(verbose) + end + f = Base.Fix2(prob.f, prob.p) left, right = prob.tspan fl, fr = f(left), f(right) @@ -33,9 +43,9 @@ function SciMLBase.__solve( end if sign(fl) == sign(fr) - verbose && - @warn "The interval is not an enclosing interval, opposite signs at the \ - boundaries are required." + @SciMLMessage("The interval is not an enclosing interval, opposite signs at the \ + boundaries are required.", + verbose, :non_enclosing_interval, :error_control) return SciMLBase.build_solution( prob, alg, left, fl; retcode = ReturnCode.InitialFailure, left, right ) diff --git a/lib/BracketingNonlinearSolve/src/falsi.jl b/lib/BracketingNonlinearSolve/src/falsi.jl index 557fc18fe..e529798ac 100644 --- a/lib/BracketingNonlinearSolve/src/falsi.jl +++ b/lib/BracketingNonlinearSolve/src/falsi.jl @@ -7,10 +7,20 @@ struct Falsi <: AbstractBracketingAlgorithm end function SciMLBase.__solve( prob::IntervalNonlinearProblem, alg::Falsi, args...; - maxiters = 1000, abstol = nothing, verbose::Bool = true, kwargs... + maxiters = 1000, abstol = nothing, verbose = NonlinearVerbosity(), kwargs... ) @assert !SciMLBase.isinplace(prob) "`False` only supports out-of-place problems." + if verbose isa Bool + if verbose + verbose = NonlinearVerbosity() + else + verbose = NonlinearVerbosity(Verbosity.None()) + end + elseif verbose isa Verbosity.Type + verbose = NonlinearVerbosity(verbose) + end + f = Base.Fix2(prob.f, prob.p) l, r = prob.tspan # don't reuse these variables left, right = prob.tspan @@ -32,9 +42,9 @@ function SciMLBase.__solve( end if sign(fl) == sign(fr) - verbose && - @warn "The interval is not an enclosing interval, opposite signs at the \ - boundaries are required." + @SciMLMessage("The interval is not an enclosing interval, opposite signs at the \ + boundaries are required.", + verbose, :non_enclosing_interval, :error_control) return SciMLBase.build_solution( prob, alg, left, fl; retcode = ReturnCode.InitialFailure, left, right ) diff --git a/lib/BracketingNonlinearSolve/src/itp.jl b/lib/BracketingNonlinearSolve/src/itp.jl index c733dc25f..f9791bddf 100644 --- a/lib/BracketingNonlinearSolve/src/itp.jl +++ b/lib/BracketingNonlinearSolve/src/itp.jl @@ -58,7 +58,7 @@ end function SciMLBase.__solve( prob::IntervalNonlinearProblem, alg::ITP, args...; - maxiters = 1000, abstol = nothing, verbose::Bool = true, kwargs... + maxiters = 1000, abstol = nothing, verbose::NonlinearVerbosity = NonlinearVerbosity(), kwargs... ) @assert !SciMLBase.isinplace(prob) "`ITP` only supports out-of-place problems." @@ -83,9 +83,9 @@ function SciMLBase.__solve( end if sign(fl) == sign(fr) - verbose && - @warn "The interval is not an enclosing interval, opposite signs at the \ - boundaries are required." + @SciMLMessage("The interval is not an enclosing interval, opposite signs at the \ + boundaries are required.", + verbose, :non_enclosing_interval, :error_control) return SciMLBase.build_solution( prob, alg, left, fl; retcode = ReturnCode.InitialFailure, left, right ) diff --git a/lib/BracketingNonlinearSolve/src/ridder.jl b/lib/BracketingNonlinearSolve/src/ridder.jl index a647098ad..62207f510 100644 --- a/lib/BracketingNonlinearSolve/src/ridder.jl +++ b/lib/BracketingNonlinearSolve/src/ridder.jl @@ -7,7 +7,7 @@ struct Ridder <: AbstractBracketingAlgorithm end function SciMLBase.__solve( prob::IntervalNonlinearProblem, alg::Ridder, args...; - maxiters = 1000, abstol = nothing, verbose::Bool = true, kwargs... + maxiters = 1000, abstol = nothing, verbose::NonlinearVerbosity = NonlinearVerbosity(), kwargs... ) @assert !SciMLBase.isinplace(prob) "`Ridder` only supports out-of-place problems." @@ -32,9 +32,9 @@ function SciMLBase.__solve( end if sign(fl) == sign(fr) - verbose && - @warn "The interval is not an enclosing interval, opposite signs at the \ - boundaries are required." + @SciMLMessage("The interval is not an enclosing interval, opposite signs at the \ + boundaries are required.", + verbose, :non_enclosing_interval, :error_control) return SciMLBase.build_solution( prob, alg, left, fl; retcode = ReturnCode.InitialFailure, left, right ) diff --git a/lib/NonlinearSolveBase/Project.toml b/lib/NonlinearSolveBase/Project.toml index 1f655cc0d..320b7e4e8 100644 --- a/lib/NonlinearSolveBase/Project.toml +++ b/lib/NonlinearSolveBase/Project.toml @@ -22,6 +22,7 @@ RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" SciMLJacobianOperators = "19f34311-ddf3-4b8b-af20-060888a46c0e" SciMLOperators = "c0aeaf25-5076-4817-a8d5-81caf7dfa961" +SciMLVerbosity = "a05b3ec9-34a1-438a-b0a1-c0adb433119f" StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" @@ -71,6 +72,7 @@ RecursiveArrayTools = "3" SciMLBase = "2.92" SciMLJacobianOperators = "0.1.1" SciMLOperators = "0.4, 1.0" +SciMLVerbosity = "1.2.0" SparseArrays = "1.10" SparseMatrixColorings = "0.4.5" StaticArraysCore = "1.4" diff --git a/lib/NonlinearSolveBase/ext/NonlinearSolveBaseLinearSolveExt.jl b/lib/NonlinearSolveBase/ext/NonlinearSolveBaseLinearSolveExt.jl index c6e88e3ab..6ccc2b1d6 100644 --- a/lib/NonlinearSolveBase/ext/NonlinearSolveBaseLinearSolveExt.jl +++ b/lib/NonlinearSolveBase/ext/NonlinearSolveBaseLinearSolveExt.jl @@ -5,21 +5,22 @@ using ArrayInterface: ArrayInterface using CommonSolve: CommonSolve, init, solve! using LinearSolve: LinearSolve, QRFactorization, SciMLLinearSolveAlgorithm using SciMLBase: ReturnCode, LinearProblem, LinearAliasSpecifier +using SciMLVerbosity: @SciMLMessage using LinearAlgebra: ColumnNorm -using NonlinearSolveBase: NonlinearSolveBase, LinearSolveJLCache, LinearSolveResult, Utils +using NonlinearSolveBase: NonlinearSolveBase, LinearSolveJLCache, LinearSolveResult, Utils, NonlinearVerbosity function (cache::LinearSolveJLCache)(; A = nothing, b = nothing, linu = nothing, - reuse_A_if_factorization = false, verbose = true, kwargs... + reuse_A_if_factorization = false, kwargs... ) cache.stats.nsolve += 1 update_A!(cache, A, reuse_A_if_factorization) b !== nothing && setproperty!(cache.lincache, :b, b) linu !== nothing && NonlinearSolveBase.set_lincache_u!(cache, linu) - + linres = solve!(cache.lincache) if linres.retcode === ReturnCode.Failure return LinearSolveResult(; linres.u, success = false) diff --git a/lib/NonlinearSolveBase/src/NonlinearSolveBase.jl b/lib/NonlinearSolveBase/src/NonlinearSolveBase.jl index e3efc9ef5..a74ff4b7d 100644 --- a/lib/NonlinearSolveBase/src/NonlinearSolveBase.jl +++ b/lib/NonlinearSolveBase/src/NonlinearSolveBase.jl @@ -23,6 +23,7 @@ using SciMLBase: SciMLBase, ReturnCode, AbstractODEIntegrator, AbstractNonlinear LinearAliasSpecifier, ImmutableNonlinearProblem using SciMLJacobianOperators: JacobianOperator, StatefulJacobianOperator using SciMLOperators: AbstractSciMLOperator, IdentityOperator +using SciMLVerbosity: @match, @SciMLMessage, Verbosity, AbstractVerbositySpecifier using SymbolicIndexingInterface: SymbolicIndexingInterface using LinearAlgebra: LinearAlgebra, Diagonal, norm, ldiv!, diagind, mul! @@ -34,6 +35,7 @@ const SII = SymbolicIndexingInterface include("public.jl") include("utils.jl") +include("verbosity.jl") include("abstract_types.jl") include("common_defaults.jl") @@ -47,6 +49,7 @@ include("tracing.jl") include("wrappers.jl") include("polyalg.jl") + include("descent/common.jl") include("descent/newton.jl") include("descent/steepest.jl") @@ -85,6 +88,8 @@ export DescentResult, SteepestDescent, NewtonDescent, DampedNewtonDescent, Dogle export NonlinearSolvePolyAlgorithm +export NonlinearVerbosity, NonlinearPerformanceVerbosity, NonlinearErrorControlVerbosity, NonlinearNumericalVerbosity + export pickchunksize end diff --git a/lib/NonlinearSolveBase/src/abstract_types.jl b/lib/NonlinearSolveBase/src/abstract_types.jl index 252650d03..e67a40fc9 100644 --- a/lib/NonlinearSolveBase/src/abstract_types.jl +++ b/lib/NonlinearSolveBase/src/abstract_types.jl @@ -280,6 +280,7 @@ the cache: - `maxtime`: the maximum time limit for the solver. (Optional) - `timer`: the timer for the solver. (Optional) - `total_time`: the total time taken by the solver. (Optional) + - `verbose`: a verbosity object that contains options determining what log messages are emitted. """ abstract type AbstractNonlinearSolveCache <: AbstractNonlinearSolveBaseAPI end diff --git a/lib/NonlinearSolveBase/src/autodiff.jl b/lib/NonlinearSolveBase/src/autodiff.jl index b09992c1b..5d8b0faa6 100644 --- a/lib/NonlinearSolveBase/src/autodiff.jl +++ b/lib/NonlinearSolveBase/src/autodiff.jl @@ -23,10 +23,13 @@ function select_forward_mode_autodiff( if warn_check_mode && !(ADTypes.mode(ad) isa ADTypes.ForwardMode) && !(ADTypes.mode(ad) isa ADTypes.ForwardOrReverseMode) && !is_finite_differences_backend(ad) - @warn "The chosen AD backend $(ad) is not a forward mode AD. Use with caution." + + @warn "The chosen AD backend $(ad) is not a forward mode AD. Use with caution." + end if incompatible_backend_and_problem(prob, ad) adₙ = select_forward_mode_autodiff(prob, nothing; warn_check_mode) + @warn "The chosen AD backend `$(ad)` does not support the chosen problem. This \ could be because the backend package for the chosen AD isn't loaded. After \ running autodiff selection detected `$(adₙ)` as a potential forward mode \ @@ -50,14 +53,14 @@ function select_reverse_mode_autodiff( if warn_check_mode && !(ADTypes.mode(ad) isa ADTypes.ReverseMode) && !(ADTypes.mode(ad) isa ADTypes.ForwardOrReverseMode) && !is_finite_differences_backend(ad) - @warn "The chosen AD backend $(ad) is not a reverse mode AD. Use with caution." + @warn "The chosen AD backend $(ad) is not a forward mode AD. Use with caution." end if incompatible_backend_and_problem(prob, ad) adₙ = select_reverse_mode_autodiff(prob, nothing; warn_check_mode) @warn "The chosen AD backend `$(ad)` does not support the chosen problem. This \ - could be because the backend package for the chosen AD isn't loaded. After \ - running autodiff selection detected `$(adₙ)` as a potential reverse mode \ - backend." + could be because the backend package for the chosen AD isn't loaded. After \ + running autodiff selection detected `$(adₙ)` as a potential forward mode \ + backend." return adₙ end return ad @@ -76,9 +79,9 @@ function select_jacobian_autodiff(prob::AbstractNonlinearProblem, ad::AbstractAD if incompatible_backend_and_problem(prob, ad) adₙ = select_jacobian_autodiff(prob, nothing) @warn "The chosen AD backend `$(ad)` does not support the chosen problem. This \ - could be because the backend package for the chosen AD isn't loaded. After \ - running autodiff selection detected `$(adₙ)` as a potential jacobian \ - backend." + could be because the backend package for the chosen AD isn't loaded. After \ + running autodiff selection detected `$(adₙ)` as a potential forward mode \ + backend." return adₙ end return ad diff --git a/lib/NonlinearSolveBase/src/descent/newton.jl b/lib/NonlinearSolveBase/src/descent/newton.jl index a0aaa91d3..8a968d58f 100644 --- a/lib/NonlinearSolveBase/src/descent/newton.jl +++ b/lib/NonlinearSolveBase/src/descent/newton.jl @@ -35,9 +35,14 @@ function InternalAPI.init( δus = Utils.unwrap_val(shared) ≤ 1 ? nothing : map(2:Utils.unwrap_val(shared)) do i @bb δu_ = similar(u) end + if Utils.unwrap_val(pre_inverted) lincache = nothing else + if haskey(kwargs, :verbose) + linsolve_kwargs = merge((verbose = kwargs[:verbose].linear_verbosity,), linsolve_kwargs) + end + lincache = construct_linear_solver( alg, alg.linsolve, J, Utils.safe_vec(fu), Utils.safe_vec(u); stats, abstol, reltol, linsolve_kwargs... @@ -61,7 +66,6 @@ function InternalAPI.init( δus = Utils.unwrap_val(shared) ≤ 1 ? nothing : map(2:N) do i @bb δu_ = similar(u) end - normal_form = needs_square_A(alg.linsolve, u) if normal_form JᵀJ = transpose(J) * J @@ -72,6 +76,11 @@ function InternalAPI.init( A, b = J, Utils.safe_vec(fu) end + if haskey(kwargs, :verbose) + linsolve_kwargs = merge( + (verbose = kwargs[:verbose].linear_verbosity,), linsolve_kwargs) + end + lincache = construct_linear_solver( alg, alg.linsolve, A, b, Utils.safe_vec(u); stats, abstol, reltol, linsolve_kwargs... @@ -88,7 +97,6 @@ function InternalAPI.solve!( ) δu = SciMLBase.get_du(cache, idx) skip_solve && return DescentResult(; δu) - if preinverted_jacobian(cache) && !normal_form(cache) @assert J!==nothing "`J` must be provided when `preinverted_jacobian = Val(true)`." @bb δu = J × vec(fu) diff --git a/lib/NonlinearSolveBase/src/descent/steepest.jl b/lib/NonlinearSolveBase/src/descent/steepest.jl index 247490957..8ca7d35f6 100644 --- a/lib/NonlinearSolveBase/src/descent/steepest.jl +++ b/lib/NonlinearSolveBase/src/descent/steepest.jl @@ -37,6 +37,12 @@ function InternalAPI.init( @bb δu_ = similar(u) end if Utils.unwrap_val(pre_inverted) + + if haskey(kwargs, :verbose) + linsolve_kwargs = merge( + (verbose = kwargs[:verbose].linear_verbosity,), linsolve_kwargs) + end + lincache = construct_linear_solver( alg, alg.linsolve, transpose(J), Utils.safe_vec(fu), Utils.safe_vec(u); stats, abstol, reltol, linsolve_kwargs... diff --git a/lib/NonlinearSolveBase/src/linear_solve.jl b/lib/NonlinearSolveBase/src/linear_solve.jl index 592524ec3..af1eb6103 100644 --- a/lib/NonlinearSolveBase/src/linear_solve.jl +++ b/lib/NonlinearSolveBase/src/linear_solve.jl @@ -70,11 +70,10 @@ function construct_linear_solver(alg, linsolve, A, b, u; stats, kwargs...) u_fixed = fix_incompatible_linsolve_arguments(A, b, u) @bb u_cache = copy(u_fixed) - linprob = LinearProblem(A, b; u0 = u_cache, kwargs...) - + linprob = LinearProblem(A, b; u0 = u_cache) # unlias here, we will later use these as caches lincache = init( - linprob, linsolve; alias = LinearAliasSpecifier(alias_A = false, alias_b = false)) + linprob, linsolve; alias = LinearAliasSpecifier(alias_A = false, alias_b = false), kwargs...) return LinearSolveJLCache(lincache, linsolve, stats) end diff --git a/lib/NonlinearSolveBase/src/polyalg.jl b/lib/NonlinearSolveBase/src/polyalg.jl index 1a6e1cfae..276e7b68c 100644 --- a/lib/NonlinearSolveBase/src/polyalg.jl +++ b/lib/NonlinearSolveBase/src/polyalg.jl @@ -61,6 +61,8 @@ end alias_u0::Bool initializealg + + verbose end function update_initial_values!(cache::NonlinearSolvePolyAlgorithmCache, u0, p) @@ -117,15 +119,25 @@ end function SciMLBase.__init( prob::AbstractNonlinearProblem, alg::NonlinearSolvePolyAlgorithm, args...; stats = NLStats(0, 0, 0, 0, 0), maxtime = nothing, maxiters = 1000, - internalnorm = L2_NORM, alias_u0 = false, verbose = true, + internalnorm = L2_NORM, alias_u0 = false, verbose = NonlinearVerbosity(), initializealg = NonlinearSolveDefaultInit(), kwargs... ) if alias_u0 && !ArrayInterface.ismutable(prob.u0) - verbose && @warn "`alias_u0` has been set to `true`, but `u0` is \ - immutable (checked using `ArrayInterface.ismutable`)." + @SciMLMessage("`alias_u0` has been set to `true`, but `u0` is + immutable (checked using `ArrayInterface.ismutable``).", verbose, :alias_u0_immutable, :error_control) alias_u0 = false # If immutable don't care about aliasing end + if verbose isa Bool + if verbose + verbose = NonlinearVerbosity() + else + verbose = NonlinearVerbosity(Verbosity.None()) + end + elseif verbose isa Verbosity.Type + verbose = NonlinearVerbosity(verbose) + end + u0 = prob.u0 u0_aliased = alias_u0 ? copy(u0) : u0 alias_u0 && (prob = SciMLBase.remake(prob; u0 = u0_aliased)) @@ -141,7 +153,7 @@ function SciMLBase.__init( end, alg, -1, alg.start_index, 0, stats, 0.0, maxtime, ReturnCode.Default, false, maxiters, internalnorm, - u0, u0_aliased, alias_u0, initializealg + u0, u0_aliased, alias_u0, initializealg, verbose ) run_initialization!(cache) return cache diff --git a/lib/NonlinearSolveBase/src/solve.jl b/lib/NonlinearSolveBase/src/solve.jl index 91b7a6aa6..f87f3d3b0 100644 --- a/lib/NonlinearSolveBase/src/solve.jl +++ b/lib/NonlinearSolveBase/src/solve.jl @@ -1,9 +1,9 @@ function SciMLBase.__solve( - prob::AbstractNonlinearProblem, alg::AbstractNonlinearSolveAlgorithm, args...; - kwargs... -) + prob::AbstractNonlinearProblem, alg::AbstractNonlinearSolveAlgorithm, args...; kwargs...) cache = SciMLBase.__init(prob, alg, args...; kwargs...) - return CommonSolve.solve!(cache) + sol = CommonSolve.solve!(cache) + + return sol end function CommonSolve.solve!(cache::AbstractNonlinearSolveCache) @@ -129,17 +129,28 @@ end @generated function __generated_polysolve( prob::AbstractNonlinearProblem, alg::NonlinearSolvePolyAlgorithm{Val{N}}, args...; - stats = NLStats(0, 0, 0, 0, 0), alias_u0 = false, verbose = true, + stats = NLStats(0, 0, 0, 0, 0), alias_u0 = false, verbose = NonlinearVerbosity(), initializealg = NonlinearSolveDefaultInit(), kwargs... ) where {N} + + if verbose isa Bool + if verbose + verbose = NonlinearVerbosity() + else + verbose = NonlinearVerbosity(Verbosity.None()) + end + elseif verbose isa Verbosity.Type + verbose = NonlinearVerbosity(verbose) + end + sol_syms = [gensym("sol") for _ in 1:N] prob_syms = [gensym("prob") for _ in 1:N] u_result_syms = [gensym("u_result") for _ in 1:N] calls = [quote current = alg.start_index if alias_u0 && !ArrayInterface.ismutable(prob.u0) - verbose && @warn "`alias_u0` has been set to `true`, but `u0` is \ - immutable (checked using `ArrayInterface.ismutable`)." + @SciMLMessage("`alias_u0` has been set to `true`, but `u0` is + immutable (checked using `ArrayInterface.ismutable``).", verbose, :alias_u0_immutable, :error_control) alias_u0 = false # If immutable don't care about aliasing end end] @@ -283,6 +294,8 @@ end initializealg retcode::ReturnCode.T + + verbose end function get_abstol(cache::NonlinearSolveNoInitCache) @@ -311,11 +324,11 @@ end function SciMLBase.__init( prob::AbstractNonlinearProblem, alg::AbstractNonlinearSolveAlgorithm, args...; - initializealg = NonlinearSolveDefaultInit(), + initializealg = NonlinearSolveDefaultInit(), verbose = NonlinearVerbosity(), kwargs... ) cache = NonlinearSolveNoInitCache( - prob, alg, args, kwargs, initializealg, ReturnCode.Default) + prob, alg, args, kwargs, initializealg, ReturnCode.Default, verbose) run_initialization!(cache) return cache end diff --git a/lib/NonlinearSolveBase/src/verbosity.jl b/lib/NonlinearSolveBase/src/verbosity.jl new file mode 100644 index 000000000..1dfa012b9 --- /dev/null +++ b/lib/NonlinearSolveBase/src/verbosity.jl @@ -0,0 +1,221 @@ +nonlinear_verbosity_defaults = Dict( + :immutable_u0 => Verbosity.Warn(), + :non_enclosing_interval => Verbosity.Warn(), + :non_forward_mode => Verbosity.Warn(), + :fd_ad_caution => Verbosity.Warn(), + :ad_backend_incompatible => Verbosity.Warn(), + :colorvec_non_sparse => Verbosity.Warn(), + :colorvec_no_prototype => Verbosity.Warn(), + :sparsity_using_jac_prototype => Verbosity.Warn(), + :sparse_matrixcolorings_not_loaded => Verbosity.Warn(), + :alias_u0_immutable => Verbosity.Warn(), + :linsolve_failed_noncurrent => Verbosity.Warn(), + :jacobian_free => Verbosity.Warn(), + :termination_condition => Verbosity.Warn(), + :threshold_state => Verbosity.Warn(), + :pinv_undefined => Verbosity.Warn() +) + + +mutable struct NonlinearErrorControlVerbosity + immutable_u0::Verbosity.Type + non_enclosing_interval::Verbosity.Type + non_forward_mode::Verbosity.Type + fd_ad_caution::Verbosity.Type + ad_backend_incompatible::Verbosity.Type + alias_u0_immutable::Verbosity.Type + linsolve_failed_noncurrent::Verbosity.Type + jacobian_free::Verbosity.Type + termination_condition::Verbosity.Type + + function NonlinearErrorControlVerbosity(;immutable_u0 = nonlinear_verbosity_defaults[:immutable_u0], + non_enclosing_interval = nonlinear_verbosity_defaults[:non_enclosing_interval], + non_forward_mode = nonlinear_verbosity_defaults[:non_forward_mode], + fd_ad_caution = nonlinear_verbosity_defaults[:fd_ad_caution], + ad_backend_incompatible = nonlinear_verbosity_defaults[:ad_backend_incompatible], + alias_u0_immutable = nonlinear_verbosity_defaults[:alias_u0_immutable], + linsolve_failed_noncurrent = nonlinear_verbosity_defaults[:linsolve_failed_noncurrent], + jacobian_free = nonlinear_verbosity_defaults[:jacobian_free], + termination_condition = nonlinear_verbosity_defaults[:termination_condition]) + new(immutable_u0, non_enclosing_interval, non_forward_mode, fd_ad_caution, ad_backend_incompatible, + alias_u0_immutable, linsolve_failed_noncurrent, jacobian_free, termination_condition) + end + +end + +function NonlinearErrorControlVerbosity(verbose::Verbosity.Type) + @match verbose begin + Verbosity.None() => NonlinearErrorControlVerbosity(;NamedTuple{fieldnames(NonlinearErrorControlVerbosity)}(fill( + Verbosity.None(), + length(fieldnames(NonlinearErrorControlVerbosity))))...) + + Verbosity.Info() => NonlinearErrorControlVerbosity(;NamedTuple{fieldnames(NonlinearErrorControlVerbosity)}(fill( + Verbosity.Info(), + length(fieldnames(NonlinearErrorControlVerbosity))))...) + + Verbosity.Warn() => NonlinearErrorControlVerbosity(;NamedTuple{fieldnames(NonlinearErrorControlVerbosity)}(fill( + Verbosity.Warn(), + length(fieldnames(NonlinearErrorControlVerbosity))))...) + + Verbosity.Error() => NonlinearErrorControlVerbosity(;NamedTuple{fieldnames(NonlinearErrorControlVerbosity)}(fill( + Verbosity.Error(), + length(fieldnames(NonlinearErrorControlVerbosity))))...) + + Verbosity.Default() => NonlinearErrorControlVerbosity() + + Verbosity.Edge() => NonlinearErrorControlVerbosity() + + _ => @error "Not a valid choice for verbosity." + end +end + +mutable struct NonlinearPerformanceVerbosity + colorvec_non_sparse::Verbosity.Type + colorvec_no_prototype::Verbosity.Type + sparsity_using_jac_prototype::Verbosity.Type + sparse_matrixcolorings_not_loaded::Verbosity.Type + + function NonlinearPerformanceVerbosity(; colorvec_non_sparse=nonlinear_verbosity_defaults[:colorvec_non_sparse], + colorvec_no_prototype=nonlinear_verbosity_defaults[:colorvec_no_prototype], + sparsity_using_jac_prototype=nonlinear_verbosity_defaults[:sparsity_using_jac_prototype], + sparse_matrixcolorings_not_loaded=nonlinear_verbosity_defaults[:sparse_matrixcolorings_not_loaded]) + new(colorvec_non_sparse, colorvec_no_prototype, sparsity_using_jac_prototype, sparse_matrixcolorings_not_loaded) + end +end + +function NonlinearPerformanceVerbosity(verbose::Verbosity.Type) + @match verbose begin + Verbosity.None() => NonlinearPerformanceVerbosity(;NamedTuple{fieldnames(NonlinearPerformanceVerbosity)}(fill( + Verbosity.None(), + length(fieldnames(NonlinearPerformanceVerbosity))))...) + + Verbosity.Info() => NonlinearPerformanceVerbosity(;NamedTuple{fieldnames(NonlinearPerformanceVerbosity)}(fill( + Verbosity.Info(), + length(fieldnames(NonlinearPerformanceVerbosity))))...) + + Verbosity.Warn() => NonlinearPerformanceVerbosity(;NamedTuple{fieldnames(NonlinearPerformanceVerbosity)}(fill( + Verbosity.Warn(), + length(fieldnames(NonlinearPerformanceVerbosity))))...) + + Verbosity.Error() => NonlinearPerformanceVerbosity(;NamedTuple{fieldnames(NonlinearPerformanceVerbosity)}(fill( + Verbosity.Error(), + length(fieldnames(NonlinearPerformanceVerbosity))))...) + + Verbosity.Default() => NonlinearPerformanceVerbosity() + + Verbosity.Edge() => NonlinearPerformanceVerbosity() + + _ => @error "Not a valid choice for verbosity." + end +end + +mutable struct NonlinearNumericalVerbosity + threshold_state::Verbosity.Type + pinv_undefined::Verbosity.Type + function NonlinearNumericalVerbosity(; + threshold_state = nonlinear_verbosity_defaults[:threshold_state], + pinv_undefined = nonlinear_verbosity_defaults[:pinv_undefined]) + new(threshold_state, pinv_undefined) + end +end + +function NonlinearNumericalVerbosity(verbose::Verbosity.Type) + @match verbose begin + Verbosity.None() => NonlinearNumericalVerbosity(;NamedTuple{fieldnames(NonlinearNumericalVerbosity)}(fill(Verbosity.None(), + length(fieldnames(NonlinearNumericalVerbosity))))...) + + + Verbosity.Info() => NonlinearNumericalVerbosity(;NamedTuple{fieldnames(NonlinearNumericalVerbosity)}(fill( + Verbosity.Info(), + length(fieldnames(NonlinearNumericalVerbosity))))...) + + Verbosity.Warn() => NonlinearNumericalVerbosity(;NamedTuple{fieldnames(NonlinearNumericalVerbosity)}(fill( + Verbosity.Warn(), + length(fieldnames(NonlinearNumericalVerbosity))))...) + + Verbosity.Error() => NonlinearNumericalVerbosity(;NamedTuple{fieldnames(NonlinearNumericalVerbosity)}(fill( + Verbosity.Error(), + length(fieldnames(NonlinearNumericalVerbosity))))...) + + Verbosity.Default() => NonlinearNumericalVerbosity() + + Verbosity.Edge() => NonlinearNumericalVerbosity() + + _ => @error "Not a valid choice for verbosity." + end +end + +struct NonlinearVerbosity{T} <: AbstractVerbositySpecifier{T} + linear_verbosity + + error_control::NonlinearErrorControlVerbosity + performance::NonlinearPerformanceVerbosity + numerical::NonlinearNumericalVerbosity +end + +function NonlinearVerbosity(verbose::Verbosity.Type) + @match verbose begin + Verbosity.Default() => NonlinearVerbosity{true}( + Verbosity.Default(), + NonlinearErrorControlVerbosity(Verbosity.Default()), + NonlinearPerformanceVerbosity(Verbosity.Default()), + NonlinearNumericalVerbosity(Verbosity.Default()) + ) + + Verbosity.None() => NonlinearVerbosity{false}( + Verbosity.None(), + NonlinearErrorControlVerbosity(Verbosity.None()), + NonlinearPerformanceVerbosity(Verbosity.None()), + NonlinearNumericalVerbosity(Verbosity.None())) + + Verbosity.All() => NonlinearVerbosity{true}( + Verbosity.All(), + NonlinearErrorControlVerbosity(Verbosity.Info()), + NonlinearPerformanceVerbosity(Verbosity.Info()), + NonlinearNumericalVerbosity(Verbosity.Info()) + ) + + _ => @error "Not a valid choice for verbosity." + end +end + +function NonlinearVerbosity(; + error_control=Verbosity.Default(), performance=Verbosity.Default(), + numerical=Verbosity.Default(), linear_verbosity = Verbosity.Default(), kwargs...) + + + if error_control isa Verbosity.Type + error_control_verbosity = NonlinearErrorControlVerbosity(error_control) + else + error_control_verbosity = error_control + end + + if performance isa Verbosity.Type + performance_verbosity = NonlinearPerformanceVerbosity(performance) + else + performance_verbosity = performance + end + + if numerical isa Verbosity.Type + numerical_verbosity = NonlinearNumericalVerbosity(numerical) + else + numerical_verbosity = numerical + end + + if !isempty(kwargs) + for (key, value) in pairs(kwargs) + if hasfield(NonlinearErrorControlVerbosity, key) + setproperty!(error_control_verbosity, key, value) + elseif hasfield(NonlinearPerformanceVerbosity, key) + setproperty!(performance_verbosity, key, value) + elseif hasfield(NonlinearNumericalVerbosity, key) + setproperty!(numerical_verbosity, key, value) + else + error("$key is not a recognized verbosity toggle.") + end + end + end + + NonlinearVerbosity{true}(linear_verbosity, error_control_verbosity, + performance_verbosity, numerical_verbosity) +end \ No newline at end of file diff --git a/lib/NonlinearSolveFirstOrder/src/NonlinearSolveFirstOrder.jl b/lib/NonlinearSolveFirstOrder/src/NonlinearSolveFirstOrder.jl index 57a1f0105..8b6f78c48 100644 --- a/lib/NonlinearSolveFirstOrder/src/NonlinearSolveFirstOrder.jl +++ b/lib/NonlinearSolveFirstOrder/src/NonlinearSolveFirstOrder.jl @@ -22,7 +22,8 @@ using NonlinearSolveBase: NonlinearSolveBase, AbstractNonlinearSolveAlgorithm, Utils, InternalAPI, get_timer_output, @static_timeit, update_trace!, L2_NORM, NonlinearSolvePolyAlgorithm, NewtonDescent, DampedNewtonDescent, GeodesicAcceleration, - Dogleg, NonlinearSolveForwardDiffCache + Dogleg, NonlinearSolveForwardDiffCache, NonlinearVerbosity, + @SciMLMessage, Verbosity using SciMLBase: SciMLBase, AbstractNonlinearProblem, NLStats, ReturnCode, NonlinearFunction, NonlinearLeastSquaresProblem, NonlinearProblem, NoSpecialize diff --git a/lib/NonlinearSolveFirstOrder/src/solve.jl b/lib/NonlinearSolveFirstOrder/src/solve.jl index e9fc30e2e..5ec1d021f 100644 --- a/lib/NonlinearSolveFirstOrder/src/solve.jl +++ b/lib/NonlinearSolveFirstOrder/src/solve.jl @@ -89,6 +89,8 @@ end kwargs initializealg + + verbose end function InternalAPI.reinit_self!( @@ -122,7 +124,7 @@ function SciMLBase.__init( prob::AbstractNonlinearProblem, alg::GeneralizedFirstOrderAlgorithm, args...; stats = NLStats(0, 0, 0, 0, 0), alias_u0 = false, maxiters = 1000, abstol = nothing, reltol = nothing, maxtime = nothing, - termination_condition = nothing, internalnorm = L2_NORM, + termination_condition = nothing, internalnorm = L2_NORM, verbose = NonlinearVerbosity(), linsolve_kwargs = (;), initializealg = NonlinearSolveBase.NonlinearSolveDefaultInit(), kwargs... ) @set! alg.autodiff = NonlinearSolveBase.select_jacobian_autodiff(prob, alg.autodiff) @@ -145,6 +147,16 @@ function SciMLBase.__init( NonlinearSolveBase.select_reverse_mode_autodiff(prob, alg.vjp_autodiff) end + if verbose isa Bool + if verbose + verbose = NonlinearVerbosity() + else + verbose = NonlinearVerbosity(Verbosity.None()) + end + elseif verbose isa Verbosity.Type + verbose = NonlinearVerbosity(verbose) + end + timer = get_timer_output() @static_timeit timer "cache construction" begin u = Utils.maybe_unaliased(prob.u0, alias_u0) @@ -157,7 +169,7 @@ function SciMLBase.__init( termination_cache = NonlinearSolveBase.init_termination_cache( prob, abstol, reltol, fu, u, termination_condition, Val(:regular) ) - linsolve_kwargs = merge((; abstol, reltol), linsolve_kwargs) + linsolve_kwargs = merge((; verbose = verbose.linear_verbosity, abstol, reltol), linsolve_kwargs) jac_cache = NonlinearSolveBase.construct_jacobian_cache( prob, alg, prob.f, fu, u, prob.p; @@ -214,7 +226,7 @@ function SciMLBase.__init( jac_cache, descent_cache, linesearch_cache, trustregion_cache, stats, 0, maxiters, maxtime, alg.max_shrink_times, timer, 0.0, true, termination_cache, trace, ReturnCode.Default, false, kwargs, - initializealg + initializealg, verbose ) NonlinearSolveBase.run_initialization!(cache) end @@ -258,10 +270,8 @@ function InternalAPI.step!( return else # Jacobian Information is not current and linear solve failed, recompute it - if !haskey(cache.kwargs, :verbose) || cache.kwargs[:verbose] - @warn "Linear Solve Failed but Jacobian Information is not current. \ - Retrying with updated Jacobian." - end + @SciMLMessage("Linear Solve Failed but Jacobian information is not current. Retrying with updated Jacobian. \ + Retrying with updated Jacobian.", cache.verbose, :linsolve_failed_noncurrent, :error_control) # In the 2nd call the `new_jacobian` is guaranteed to be `true`. cache.make_new_jacobian = true InternalAPI.step!(cache; recompute_jacobian = true, kwargs...) diff --git a/lib/NonlinearSolveQuasiNewton/src/NonlinearSolveQuasiNewton.jl b/lib/NonlinearSolveQuasiNewton/src/NonlinearSolveQuasiNewton.jl index 167f1fa85..1e6608265 100644 --- a/lib/NonlinearSolveQuasiNewton/src/NonlinearSolveQuasiNewton.jl +++ b/lib/NonlinearSolveQuasiNewton/src/NonlinearSolveQuasiNewton.jl @@ -19,7 +19,8 @@ using NonlinearSolveBase: NonlinearSolveBase, AbstractNonlinearSolveAlgorithm, AbstractApproximateJacobianUpdateRule, AbstractDescentDirection, AbstractApproximateJacobianUpdateRuleCache, Utils, InternalAPI, get_timer_output, @static_timeit, - update_trace!, L2_NORM, NewtonDescent + update_trace!, L2_NORM, NewtonDescent, NonlinearVerbosity, + @SciMLMessage, Verbosity using SciMLBase: SciMLBase, AbstractNonlinearProblem, NLStats, ReturnCode, NonlinearProblem, NonlinearFunction, NoSpecialize using SciMLOperators: AbstractSciMLOperator diff --git a/lib/NonlinearSolveQuasiNewton/src/initialization.jl b/lib/NonlinearSolveQuasiNewton/src/initialization.jl index f730b541b..a0d56fd44 100644 --- a/lib/NonlinearSolveQuasiNewton/src/initialization.jl +++ b/lib/NonlinearSolveQuasiNewton/src/initialization.jl @@ -153,7 +153,7 @@ NonlinearSolveBase.jacobian_initialized_preinverted(::BroydenLowRankInitializati function InternalAPI.init( prob::AbstractNonlinearProblem, alg::BroydenLowRankInitialization, solver, f::F, fu, u, p; - internalnorm::IN = L2_NORM, maxiters = 1000, kwargs... + internalnorm::IN = L2_NORM, maxiters = 1000, verbose = NonlinearVerbosity(), kwargs... ) where {F, IN} if u isa Number # Use the standard broyden return InternalAPI.init( @@ -161,6 +161,17 @@ function InternalAPI.init( solver, f, fu, u, p; internalnorm, maxiters, kwargs... ) end + + if verbose isa Bool + if verbose + verbose = NonlinearVerbosity() + else + verbose = NonlinearVerbosity(Verbosity.None()) + end + elseif verbose isa Verbosity.Type + verbose = NonlinearVerbosity(verbose) + end + # Pay to cost of slightly more allocations to prevent type-instability for StaticArrays α = inv(Utils.initial_jacobian_scaling_alpha(alg.alpha, u, fu, internalnorm)) if u isa StaticArray @@ -168,8 +179,8 @@ function InternalAPI.init( else threshold = min(Utils.unwrap_val(alg.threshold), maxiters) if threshold > length(u) - @warn "`threshold` is larger than the size of the state, which may cause \ - numerical instability. Consider reducing `threshold`." + @SciMLMessage("`threshold` is larger than the size of the state, which may cause \ + numerical instability. Consider reducing `threshold`.", verbose, :threshold_state, :numerical) end J = BroydenLowRankJacobian(fu, u; threshold, alpha = α) end diff --git a/lib/NonlinearSolveQuasiNewton/src/solve.jl b/lib/NonlinearSolveQuasiNewton/src/solve.jl index 24e740d3f..b31851c21 100644 --- a/lib/NonlinearSolveQuasiNewton/src/solve.jl +++ b/lib/NonlinearSolveQuasiNewton/src/solve.jl @@ -96,6 +96,8 @@ end # Initialization initializealg + + verbose end function NonlinearSolveBase.get_abstol(cache::QuasiNewtonCache) @@ -141,10 +143,22 @@ function SciMLBase.__init( maxiters = 1000, abstol = nothing, reltol = nothing, linsolve_kwargs = (;), termination_condition = nothing, internalnorm::F = L2_NORM, initializealg = NonlinearSolveBase.NonlinearSolveDefaultInit(), + verbose = NonlinearVerbosity(), kwargs... ) where {F} timer = get_timer_output() @static_timeit timer "cache construction" begin + + if verbose isa Bool + if verbose + verbose = NonlinearVerbosity() + else + verbose = NonlinearVerbosity(Verbosity.None()) + end + elseif verbose isa Verbosity.Type + verbose = NonlinearVerbosity(verbose) + end + u = Utils.maybe_unaliased(prob.u0, alias_u0) fu = Utils.evaluate_f(prob, u) @bb u_cache = copy(u) @@ -162,7 +176,7 @@ function SciMLBase.__init( termination_cache = NonlinearSolveBase.init_termination_cache( prob, abstol, reltol, fu, u, termination_condition, Val(:regular) ) - linsolve_kwargs = merge((; abstol, reltol), linsolve_kwargs) + linsolve_kwargs = merge((;verbose = verbose.linear_verbosity, abstol, reltol), linsolve_kwargs) J = initialization_cache(nothing) @@ -223,7 +237,7 @@ function SciMLBase.__init( trustregion_cache, update_rule_cache, reinit_rule_cache, inv_workspace, stats, 0, 0, alg.max_resets, maxiters, maxtime, alg.max_shrink_times, 0, timer, 0.0, termination_cache, trace, - ReturnCode.Default, false, false, kwargs, initializealg + ReturnCode.Default, false, false, kwargs, initializealg, verbose ) NonlinearSolveBase.run_initialization!(cache) end @@ -325,10 +339,10 @@ function InternalAPI.step!( return else # Force a reinit because the problem is currently un-solvable - if !haskey(cache.kwargs, :verbose) || cache.kwargs[:verbose] - @warn "Linear Solve Failed but Jacobian Information is not current. \ - Retrying with reinitialized Approximate Jacobian." - end + + @SciMLMessage("Linear Solve Failed but Jacobian information is not current. Retrying with updated Jacobian. \ + Retrying with updated Jacobian.", cache.verbose, :linsolve_failed_noncurrent, :error_control) + cache.force_reinit = true InternalAPI.step!(cache; recompute_jacobian = true) return diff --git a/lib/NonlinearSolveSpectralMethods/src/NonlinearSolveSpectralMethods.jl b/lib/NonlinearSolveSpectralMethods/src/NonlinearSolveSpectralMethods.jl index 93a620761..5e68c7414 100644 --- a/lib/NonlinearSolveSpectralMethods/src/NonlinearSolveSpectralMethods.jl +++ b/lib/NonlinearSolveSpectralMethods/src/NonlinearSolveSpectralMethods.jl @@ -10,7 +10,7 @@ using LineSearch: RobustNonMonotoneLineSearch using MaybeInplace: @bb using NonlinearSolveBase: NonlinearSolveBase, AbstractNonlinearSolveAlgorithm, AbstractNonlinearSolveCache, Utils, InternalAPI, get_timer_output, - @static_timeit, update_trace! + @static_timeit, update_trace!, NonlinearVerbosity, @SciMLMessage, Verbosity using SciMLBase: SciMLBase, AbstractNonlinearProblem, NLStats, ReturnCode, NonlinearProblem, NonlinearFunction, NoSpecialize diff --git a/lib/NonlinearSolveSpectralMethods/src/solve.jl b/lib/NonlinearSolveSpectralMethods/src/solve.jl index ebb8d13a9..1de2c6bd2 100644 --- a/lib/NonlinearSolveSpectralMethods/src/solve.jl +++ b/lib/NonlinearSolveSpectralMethods/src/solve.jl @@ -70,6 +70,8 @@ end kwargs initializealg + + verbose end function InternalAPI.reinit_self!( @@ -115,7 +117,8 @@ function SciMLBase.__init( prob::AbstractNonlinearProblem, alg::GeneralizedDFSane, args...; stats = NLStats(0, 0, 0, 0, 0), alias_u0 = false, maxiters = 1000, abstol = nothing, reltol = nothing, termination_condition = nothing, - maxtime = nothing, initializealg = NonlinearSolveBase.NonlinearSolveDefaultInit(), kwargs... + maxtime = nothing, verbose = NonlinearVerbosity(), + initializealg = NonlinearSolveBase.NonlinearSolveDefaultInit(), kwargs... ) timer = get_timer_output() @@ -149,11 +152,21 @@ function SciMLBase.__init( σ_n = T(alg.σ_1) end + if verbose isa Bool + if verbose + verbose = NonlinearVerbosity() + else + verbose = NonlinearVerbosity(Verbosity.None()) + end + elseif verbose isa Verbosity.Type + verbose = NonlinearVerbosity(verbose) + end + cache = GeneralizedDFSaneCache( fu, fu_cache, u, u_cache, prob.p, du, alg, prob, σ_n, T(alg.σ_min), T(alg.σ_max), linesearch_cache, stats, 0, maxiters, maxtime, timer, 0.0, - tc_cache, trace, ReturnCode.Default, false, kwargs, initializealg + tc_cache, trace, ReturnCode.Default, false, kwargs, initializealg, verbose ) NonlinearSolveBase.run_initialization!(cache) end @@ -166,8 +179,8 @@ function InternalAPI.step!( kwargs... ) if recompute_jacobian !== nothing - @warn "GeneralizedDFSane is a Jacobian-Free Algorithm. Ignoring \ - `recompute_jacobian`" maxlog=1 + @SciMLMessage("GeneralizedDFSane is a Jacobian-Free Algorithm. Ignoring \ + `recompute_jacobian`", cache.verbose, :jacobian_free, :error_control) end @static_timeit cache.timer "descent" begin diff --git a/lib/SimpleNonlinearSolve/src/SimpleNonlinearSolve.jl b/lib/SimpleNonlinearSolve/src/SimpleNonlinearSolve.jl index 1908a1131..4f7c4d916 100644 --- a/lib/SimpleNonlinearSolve/src/SimpleNonlinearSolve.jl +++ b/lib/SimpleNonlinearSolve/src/SimpleNonlinearSolve.jl @@ -12,7 +12,7 @@ using LineSearch: LiFukushimaLineSearch using MaybeInplace: @bb using NonlinearSolveBase: NonlinearSolveBase, ImmutableNonlinearProblem, L2_NORM, nonlinearsolve_forwarddiff_solve, nonlinearsolve_dual_solution, - AbstractNonlinearSolveAlgorithm + AbstractNonlinearSolveAlgorithm, NonlinearVerbosity, @SciMLMessage, Verbosity using SciMLBase: SciMLBase, NonlinearFunction, NonlinearProblem, NonlinearLeastSquaresProblem, ReturnCode, remake diff --git a/lib/SimpleNonlinearSolve/src/lbroyden.jl b/lib/SimpleNonlinearSolve/src/lbroyden.jl index 979b244f8..d5a2ea425 100644 --- a/lib/SimpleNonlinearSolve/src/lbroyden.jl +++ b/lib/SimpleNonlinearSolve/src/lbroyden.jl @@ -36,7 +36,7 @@ end function SciMLBase.__solve( prob::ImmutableNonlinearProblem, alg::SimpleLimitedMemoryBroyden, - args...; termination_condition = nothing, kwargs...) + args...; termination_condition = nothing, verbose = NonlinearVerbosity(), kwargs...) if prob.u0 isa SArray if termination_condition === nothing || termination_condition isa NonlinearSolveBase.AbsNormTerminationMode @@ -44,10 +44,21 @@ function SciMLBase.__solve( prob, alg, args...; termination_condition, kwargs... ) end - @warn "Specifying `termination_condition = $(termination_condition)` for \ + + if verbose isa Bool + if verbose + verbose = NonlinearVerbosity() + else + verbose = NonlinearVerbosity(Verbosity.None()) + end + elseif verbose isa Verbosity.Type + verbose = NonlinearVerbosity(verbose) + end + + @SciMLMessage("Specifying `termination_condition = $(termination_condition)` for \ `SimpleLimitedMemoryBroyden` with `SArray` is not non-allocating. Use \ either `termination_condition = AbsNormTerminationMode(Base.Fix2(norm, Inf))` \ - or `termination_condition = nothing`." maxlog=1 + or `termination_condition = nothing`.", verbose, :termination_condition, :error_control) end return internal_generic_solve(prob, alg, args...; termination_condition, kwargs...) end diff --git a/src/NonlinearSolve.jl b/src/NonlinearSolve.jl index b765d9573..3a2345beb 100644 --- a/src/NonlinearSolve.jl +++ b/src/NonlinearSolve.jl @@ -12,7 +12,7 @@ using DiffEqBase: DiffEqBase # Needed for `init` / `solve` dispatches using LinearAlgebra: LinearAlgebra using LineSearch: BackTracking using NonlinearSolveBase: NonlinearSolveBase, AbstractNonlinearSolveAlgorithm, - NonlinearSolvePolyAlgorithm, pickchunksize + NonlinearSolvePolyAlgorithm, pickchunksize, NonlinearVerbosity using SciMLBase: SciMLBase, ReturnCode, AbstractNonlinearProblem, NonlinearFunction, @@ -90,18 +90,17 @@ include("forward_diff.jl") push!(nlls_problems, NonlinearLeastSquaresProblem(fn, u0, 2.0)) end + nlp_algs = [NewtonRaphson(), TrustRegion(), LevenbergMarquardt()] + nlls_algs = [GaussNewton(), TrustRegion(), LevenbergMarquardt()] + @compile_workload begin @sync begin - for prob in nonlinear_problems - Threads.@spawn CommonSolve.solve( - prob, nothing; abstol = 1e-2, verbose = false - ) + for prob in nonlinear_problems, alg in nlp_algs + Threads.@spawn CommonSolve.solve(prob, alg; abstol = 1e-2, verbose = NonlinearVerbosity()) end - for prob in nlls_problems - Threads.@spawn CommonSolve.solve( - prob, nothing; abstol = 1e-2, verbose = false - ) + for prob in nlls_problems, alg in nlls_algs + Threads.@spawn CommonSolve.solve(prob, alg; abstol = 1e-2, verbose = NonlinearVerbosity()) end end end diff --git a/test/core_tests.jl b/test/core_tests.jl index fc0bb2026..33990aec1 100644 --- a/test/core_tests.jl +++ b/test/core_tests.jl @@ -216,7 +216,8 @@ end function objective_function!(resid, u0, p) odeprob = ODEProblem{true}(ode_func!, u0, (0.0, 100.0), p) - sol = solve(odeprob, Tsit5(), abstol = 1e-9, reltol = 1e-9, verbose = false) + sol = solve( + odeprob, Tsit5(), abstol = 1e-9, reltol = 1e-9, verbose = true) resid[1] = sol(0.0)[1] resid[2] = sol(100.0)[1] - 1.0 return nothing diff --git a/test/verbosity.jl b/test/verbosity.jl new file mode 100644 index 000000000..062d3baae --- /dev/null +++ b/test/verbosity.jl @@ -0,0 +1,53 @@ +@testitem "Nonlinear Verbosity" tags=[:misc] begin + using IntervalNonlinearSolve + using NonlinearSolve + using NonlinearSolve: NonlinearVerbosity + using LinearSolve: LinearVerbosity + using SciMLVerbosity: SciMLVerbosity, Verbosity + using Test + using Logging + + g(u, p) = u^2 - 4 + + int_prob = IntervalNonlinearProblem(g, (3.0, 5.0)) + + @test_logs (:info, + "The interval is not an enclosing interval, opposite signs at the boundaries are required.") solve( + int_prob, + ITP(), verbose = NonlinearVerbosity(non_enclosing_interval = Verbosity.Info())) + + @test_logs (:error, + "The interval is not an enclosing interval, opposite signs at the boundaries are required.") solve( + int_prob, + ITP(), verbose = NonlinearVerbosity(non_enclosing_interval = Verbosity.Error())) + + # Test that the linear verbosity is passed to the linear solve + f(u, p) = [u[1]^2 - 2u[1] + 1, sum(u)] + prob = NonlinearProblem(f, [1.0, 1.0]) + + @test_logs (:warn, + "LU factorization failed, falling back to QR factorization. `A` is potentially rank-deficient.") match_mode=:any solve( + prob, + verbose = NonlinearVerbosity()) + + @test_logs (:info, + "LU factorization failed, falling back to QR factorization. `A` is potentially rank-deficient.") match_mode=:any solve( + prob, + verbose = NonlinearVerbosity(linear_verbosity = LinearVerbosity(default_lu_fallback = Verbosity.Info()))) + + @test_logs (:warn, + "LU factorization failed, falling back to QR factorization. `A` is potentially rank-deficient.") match_mode=:any solve( + prob, + verbose = true) + + @test_logs min_level=Logging.Info solve(prob, + verbose = NonlinearVerbosity(Verbosity.None())) + + @test_logs min_level=Logging.Info solve(prob, + verbose = false) + + # Test that caches get correct verbosities + cache = init(prob, verbose = NonlinearVerbosity(threshold_state = Verbosity.Info())) + + @test cache.verbose.numerical.threshold_state == Verbosity.Info() +end