22
33This tutorial is for getting into the extra features of using NonlinearSolve.jl. Solving
44ill-conditioned nonlinear systems requires specializing the linear solver on properties of
5- the Jacobian in order to cut down on the `` \mathcal{O}(n^3) ` ` linear solve and the
6- `` \mathcal{O}(n^2) ` ` back-solves. This tutorial is designed to explain the advanced usage of
5+ the Jacobian in order to cut down on the ` \mathcal{O}(n^3) ` linear solve and the
6+ ` \mathcal{O}(n^2) ` back-solves. This tutorial is designed to explain the advanced usage of
77NonlinearSolve.jl by solving the steady state stiff Brusselator partial differential
88equation (BRUSS) using NonlinearSolve.jl.
99
1010## Definition of the Brusselator Equation
1111
1212!!! note
13-
13+
1414 Feel free to skip this section: it simply defines the example problem.
1515
1616The Brusselator PDE is defined as follows:
@@ -60,7 +60,7 @@ broadcast). Use `dx=dy=1/32`.
6060The resulting ` NonlinearProblem ` definition is:
6161
6262``` @example ill_conditioned_nlprob
63- using NonlinearSolve, LinearAlgebra, SparseArrays, LinearSolve, SparseDiffTools
63+ using NonlinearSolve, LinearAlgebra, SparseArrays, LinearSolve
6464
6565const N = 32
6666const xyd_brusselator = range(0, stop = 1, length = N)
@@ -117,31 +117,37 @@ However, if you know the sparsity of your problem, then you can pass a different
117117type. For example, a ` SparseMatrixCSC ` will give a sparse matrix. Other sparse matrix types
118118include:
119119
120- - Bidiagonal
121- - Tridiagonal
122- - SymTridiagonal
123- - BandedMatrix ([ BandedMatrices.jl] ( https://github.com/JuliaLinearAlgebra/BandedMatrices.jl ) )
124- - BlockBandedMatrix ([ BlockBandedMatrices.jl] ( https://github.com/JuliaLinearAlgebra/BlockBandedMatrices.jl ) )
120+ - Bidiagonal
121+ - Tridiagonal
122+ - SymTridiagonal
123+ - BandedMatrix ([ BandedMatrices.jl] ( https://github.com/JuliaLinearAlgebra/BandedMatrices.jl ) )
124+ - BlockBandedMatrix ([ BlockBandedMatrices.jl] ( https://github.com/JuliaLinearAlgebra/BlockBandedMatrices.jl ) )
125125
126126## Approximate Sparsity Detection & Sparse Jacobians
127127
128- In the next section, we will discuss how to declare a sparse Jacobian and how to use
129- [ Symbolics.jl] ( https://github.com/JuliaSymbolics/Symbolics.jl ) , to compute exact sparse
130- jacobians. This is triggered if you pass in a sparse autodiff type such as
131- ` AutoSparse(AutoForwardDiff()) ` . If ` Symbolics.jl ` is loaded, then the default changes to
132- Symbolic Sparsity Detection. See the manual entry on
133- [ Sparsity Detection] (@ref sparsity-detection) for more details on the default.
128+ In the next section, we will show how to specify ` sparsity ` to trigger automatic sparsity
129+ detection.
134130
135131``` @example ill_conditioned_nlprob
136132using BenchmarkTools # for @btime
137133
138134@btime solve(prob_brusselator_2d, NewtonRaphson());
139- @btime solve(prob_brusselator_2d,
140- NewtonRaphson(; autodiff = AutoSparse(AutoForwardDiff(; chunksize = 32))));
141- @btime solve(prob_brusselator_2d,
142- NewtonRaphson(; autodiff = AutoSparse(AutoForwardDiff(; chunksize = 32)),
135+ nothing # hide
136+ ```
137+
138+ ``` @example ill_conditioned_nlprob
139+ using SparseConnectivityTracer
140+
141+ prob_brusselator_2d_autosparse = NonlinearProblem(
142+ NonlinearFunction(brusselator_2d_loop; sparsity = TracerSparsityDetector()),
143+ u0, p; abstol = 1e-10, reltol = 1e-10)
144+
145+ @btime solve(prob_brusselator_2d_autosparse,
146+ NewtonRaphson(; autodiff = AutoForwardDiff(; chunksize = 32)));
147+ @btime solve(prob_brusselator_2d_autosparse,
148+ NewtonRaphson(; autodiff = AutoForwardDiff(; chunksize = 32),
143149 linsolve = KLUFactorization()));
144- @btime solve(prob_brusselator_2d ,
150+ @btime solve(prob_brusselator_2d_autosparse ,
145151 NewtonRaphson(; autodiff = AutoForwardDiff(; chunksize = 32),
146152 linsolve = KrylovJL_GMRES()));
147153nothing # hide
@@ -160,8 +166,14 @@ declaration of Jacobian sparsity types. To see this in action, we can give an ex
160166and ` u ` and call ` jacobian_sparsity ` on our function with the example arguments, and it will
161167kick out a sparse matrix with our pattern, that we can turn into our ` jac_prototype ` .
162168
169+ !!! tip
170+
171+ Alternatively you can use the `SparseConnectivityTracer.jl` package to automatically
172+ generate a sparse Jacobian.
173+
163174``` @example ill_conditioned_nlprob
164175using Symbolics
176+
165177du0 = copy(u0)
166178jac_sparsity = Symbolics.jacobian_sparsity(
167179 (du, u) -> brusselator_2d_loop(du, u, p), du0, u0)
@@ -171,7 +183,7 @@ Notice that Julia gives a nice print out of the sparsity pattern. That's neat, a
171183tedious to build by hand! Now we just pass it to the ` NonlinearFunction ` like as before:
172184
173185``` @example ill_conditioned_nlprob
174- ff = NonlinearFunction(brusselator_2d_loop; sparsity = jac_sparsity)
186+ ff = NonlinearFunction(brusselator_2d_loop; jac_prototype = jac_sparsity)
175187```
176188
177189Build the ` NonlinearProblem ` :
@@ -212,7 +224,7 @@ choices, see the
212224` linsolve ` choices are any valid [ LinearSolve.jl] ( https://linearsolve.sciml.ai/dev/ ) solver.
213225
214226!!! note
215-
227+
216228 Switching to a Krylov linear solver will automatically change the nonlinear problem
217229 solver into Jacobian-free mode, dramatically reducing the memory required. This can be
218230 overridden by adding `concrete_jac=true` to the algorithm.
@@ -275,8 +287,8 @@ function algebraicmultigrid(W, du, u, p, t, newW, Plprev, Prprev, solverdata)
275287end
276288
277289@btime solve(prob_brusselator_2d_sparse,
278- NewtonRaphson(
279- linsolve = KrylovJL_GMRES(), precs = algebraicmultigrid, concrete_jac = true));
290+ NewtonRaphson(linsolve = KrylovJL_GMRES(), precs = algebraicmultigrid,
291+ concrete_jac = true));
280292nothing # hide
281293```
282294
@@ -296,8 +308,8 @@ function algebraicmultigrid2(W, du, u, p, t, newW, Plprev, Prprev, solverdata)
296308end
297309
298310@btime solve(prob_brusselator_2d_sparse,
299- NewtonRaphson(
300- linsolve = KrylovJL_GMRES(), precs = algebraicmultigrid2, concrete_jac = true));
311+ NewtonRaphson(linsolve = KrylovJL_GMRES(), precs = algebraicmultigrid2,
312+ concrete_jac = true));
301313nothing # hide
302314```
303315
@@ -308,15 +320,22 @@ for the exact sparsity detection case, we left out the time it takes to perform
308320sparsity detection. Let's compare the two by setting the sparsity detection algorithms.
309321
310322``` @example ill_conditioned_nlprob
311- prob_brusselator_2d_exact = NonlinearProblem(
312- NonlinearFunction(brusselator_2d_loop; sparsity = SymbolicsSparsityDetection()),
323+ using DifferentiationInterface, SparseConnectivityTracer
324+
325+ prob_brusselator_2d_exact_symbolics = NonlinearProblem(
326+ NonlinearFunction(brusselator_2d_loop; sparsity = SymbolicsSparsityDetector()),
327+ u0, p; abstol = 1e-10, reltol = 1e-10)
328+ prob_brusselator_2d_exact_tracer = NonlinearProblem(
329+ NonlinearFunction(brusselator_2d_loop; sparsity = TracerSparsityDetector()),
313330 u0, p; abstol = 1e-10, reltol = 1e-10)
314- prob_brusselator_2d_approx = NonlinearProblem(
315- NonlinearFunction(brusselator_2d_loop; sparsity = ApproximateJacobianSparsity()),
331+ prob_brusselator_2d_approx_di = NonlinearProblem(
332+ NonlinearFunction(brusselator_2d_loop;
333+ sparsity = DenseSparsityDetector(AutoForwardDiff(); atol=1e-4)),
316334 u0, p; abstol = 1e-10, reltol = 1e-10)
317335
318- @btime solve(prob_brusselator_2d_exact, NewtonRaphson());
319- @btime solve(prob_brusselator_2d_approx, NewtonRaphson());
336+ @btime solve(prob_brusselator_2d_exact_symbolics, NewtonRaphson());
337+ @btime solve(prob_brusselator_2d_exact_tracer, NewtonRaphson());
338+ @btime solve(prob_brusselator_2d_approx_di, NewtonRaphson());
320339nothing # hide
321340```
322341
0 commit comments