Diagnostics API
Detailed API Documentation is provided at Diagnostics API Reference.
Logging the Solve Process
All NonlinearSolve.jl native solvers allow storing and displaying the trace of the nonlinear solve process. This is controlled by 3 keyword arguments to solve
:
show_trace
: Must beVal(true)
orVal(false)
. This controls whether the trace is displayed to the console. (Defaults toVal(false)
)trace_level
: Needs to be one of Trace Objects:TraceMinimal
,TraceWithJacobianConditionNumber
, orTraceAll
. This controls the level of detail of the trace. (Defaults toTraceMinimal()
)store_trace
: Must beVal(true)
orVal(false)
. This controls whether the trace is stored in the solution object. (Defaults toVal(false)
)
Detailed Internal Timings
All the native NonlinearSolve.jl algorithms come with in-built TimerOutputs.jl support. However, this is disabled by default and can be enabled via NonlinearSolveBase.enable_timer_outputs
.
Note that you will have to restart Julia to disable the timer outputs once enabled.
Example Usage
using NonlinearSolve
function nlfunc(resid, u0, p)
resid[1] = u0[1] * u0[1] - p
resid[2] = u0[2] * u0[2] - p
resid[3] = u0[3] * u0[3] - p
nothing
end
prob = NonlinearProblem(nlfunc, [1.0, 3.0, 5.0], 2.0)
solve(prob)
retcode: Success
u: 3-element Vector{Float64}:
1.414213562373095
1.414213562373095
1.4142135623730951
This produced the output, but it is hard to diagnose what is going on. We can turn on the trace to see what is happening:
solve(prob; show_trace = Val(true), trace_level = TraceAll(10))
Algorithm: NewtonRaphson(
linesearch = BackTracking(
c_1 = 0.0001,
ρ_hi = 0.5,
ρ_lo = 0.1,
order = Val{3}(),
maxstep = Inf,
initial_alpha = true,
maxiters = 1000
),
descent = NewtonDescent(),
autodiff = AutoForwardDiff(),
vjp_autodiff = AutoFiniteDiff(
fdtype = Val{:forward}(),
fdjtype = Val{:forward}(),
fdhtype = Val{:hcentral}(),
dir = true
),
jvp_autodiff = AutoForwardDiff(),
concrete_jac = Val{false}()
)
---- ------------- ----------- -------
Iter f(u) inf-norm Step 2-norm cond(J)
---- ------------- ----------- -------
0 2.30000000e+01 0.00000000e+00 5.00000000e+00
1 5.29000000e+00 2.62699660e+00 5.00000000e+00
Final 4.44089210e-16
----------------------
You can also store the trace in the solution object:
sol = solve(prob; trace_level = TraceAll(), store_trace = Val(true));
sol.trace
---- ------------- ----------- -------
Iter f(u) inf-norm Step 2-norm cond(J)
---- ------------- ----------- -------
0 2.30000000e+01 0.00000000e+00 5.00000000e+00
1 5.29000000e+00 2.62699660e+00 5.00000000e+00
2 9.59674211e-01 1.05091251e+00 1.80000000e+00
3 7.77935784e-02 2.82878317e-01 1.21437908e+00
4 7.28157132e-04 2.69957925e-02 1.01926133e+00
5 6.62524795e-08 2.57395663e-04 1.00018202e+00
6 4.44089210e-16 2.34237884e-08 1.00000002e+00
Now, let's try to investigate the time it took for individual internal steps. We will have to use the init
and solve!
API for this. The TimerOutput
will be present in cache.timer
. However, note that for poly-algorithms this is currently not implemented.
cache = init(prob, NewtonRaphson(); show_trace = Val(true));
solve!(cache)
cache.timer
Algorithm: NewtonRaphson(
descent = NewtonDescent(),
autodiff = AutoForwardDiff(),
vjp_autodiff = AutoFiniteDiff(
fdtype = Val{:forward}(),
fdjtype = Val{:forward}(),
fdhtype = Val{:hcentral}(),
dir = true
),
jvp_autodiff = AutoForwardDiff(),
concrete_jac = Val{false}()
)
---- ------------- -----------
Iter f(u) inf-norm Step 2-norm
---- ------------- -----------
0 2.30000000e+01 0.00000000e+00
1 5.29000000e+00 2.62699660e+00
2 9.59674211e-01 1.05091251e+00
3 7.77935784e-02 2.82878317e-01
4 7.28157132e-04 2.69957925e-02
5 6.62524795e-08 2.57395663e-04
6 4.44089210e-16 2.34237884e-08
Final 4.44089210e-16
----------------------
Let's try for some other solver:
cache = init(prob, DFSane(); show_trace = Val(true), trace_level = TraceMinimal(50));
solve!(cache)
cache.timer
Algorithm: DFSane(
linesearch = RobustNonMonotoneLineSearch(
gamma = 1//10000,
sigma_1 = 1,
M = 10,
tau_min = 1//10,
tau_max = 1//2,
n_exp = 2,
maxiters = 100,
η_strategy = #2#4()
),
σ_min = 1//10000000000,
σ_max = 1.0e10
)
---- ------------- -----------
Iter f(u) inf-norm Step 2-norm
---- ------------- -----------
0 2.30000000e+01 0.00000000e+00
1 1.07270233e+00 6.23840488e+00
Final 4.44089210e-16
----------------------
For iteration == 0
only the norm(fu, Inf)
is guaranteed to be meaningful. The other values being meaningful are solver dependent.