2
2
3
3
This tutorial is for getting into the extra features of using NonlinearSolve.jl. Solving
4
4
ill-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
7
7
NonlinearSolve.jl by solving the steady state stiff Brusselator partial differential
8
8
equation (BRUSS) using NonlinearSolve.jl.
9
9
10
10
## Definition of the Brusselator Equation
11
11
12
12
!!! note
13
-
13
+
14
14
Feel free to skip this section: it simply defines the example problem.
15
15
16
16
The Brusselator PDE is defined as follows:
@@ -60,7 +60,7 @@ broadcast). Use `dx=dy=1/32`.
60
60
The resulting ` NonlinearProblem ` definition is:
61
61
62
62
``` @example ill_conditioned_nlprob
63
- using NonlinearSolve, LinearAlgebra, SparseArrays, LinearSolve, SparseDiffTools
63
+ using NonlinearSolve, LinearAlgebra, SparseArrays, LinearSolve
64
64
65
65
const N = 32
66
66
const 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
117
117
type. For example, a ` SparseMatrixCSC ` will give a sparse matrix. Other sparse matrix types
118
118
include:
119
119
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 ) )
125
125
126
126
## Approximate Sparsity Detection & Sparse Jacobians
127
127
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.
134
130
135
131
``` @example ill_conditioned_nlprob
136
132
using BenchmarkTools # for @btime
137
133
138
134
@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),
143
149
linsolve = KLUFactorization()));
144
- @btime solve(prob_brusselator_2d ,
150
+ @btime solve(prob_brusselator_2d_autosparse ,
145
151
NewtonRaphson(; autodiff = AutoForwardDiff(; chunksize = 32),
146
152
linsolve = KrylovJL_GMRES()));
147
153
nothing # hide
@@ -160,8 +166,14 @@ declaration of Jacobian sparsity types. To see this in action, we can give an ex
160
166
and ` u ` and call ` jacobian_sparsity ` on our function with the example arguments, and it will
161
167
kick out a sparse matrix with our pattern, that we can turn into our ` jac_prototype ` .
162
168
169
+ !!! tip
170
+
171
+ Alternatively you can use the `SparseConnectivityTracer.jl` package to automatically
172
+ generate a sparse Jacobian.
173
+
163
174
``` @example ill_conditioned_nlprob
164
175
using Symbolics
176
+
165
177
du0 = copy(u0)
166
178
jac_sparsity = Symbolics.jacobian_sparsity(
167
179
(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
171
183
tedious to build by hand! Now we just pass it to the ` NonlinearFunction ` like as before:
172
184
173
185
``` @example ill_conditioned_nlprob
174
- ff = NonlinearFunction(brusselator_2d_loop; sparsity = jac_sparsity)
186
+ ff = NonlinearFunction(brusselator_2d_loop; jac_prototype = jac_sparsity)
175
187
```
176
188
177
189
Build the ` NonlinearProblem ` :
@@ -212,7 +224,7 @@ choices, see the
212
224
` linsolve ` choices are any valid [ LinearSolve.jl] ( https://linearsolve.sciml.ai/dev/ ) solver.
213
225
214
226
!!! note
215
-
227
+
216
228
Switching to a Krylov linear solver will automatically change the nonlinear problem
217
229
solver into Jacobian-free mode, dramatically reducing the memory required. This can be
218
230
overridden by adding `concrete_jac=true` to the algorithm.
@@ -275,8 +287,8 @@ function algebraicmultigrid(W, du, u, p, t, newW, Plprev, Prprev, solverdata)
275
287
end
276
288
277
289
@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));
280
292
nothing # hide
281
293
```
282
294
@@ -296,8 +308,8 @@ function algebraicmultigrid2(W, du, u, p, t, newW, Plprev, Prprev, solverdata)
296
308
end
297
309
298
310
@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));
301
313
nothing # hide
302
314
```
303
315
@@ -308,15 +320,22 @@ for the exact sparsity detection case, we left out the time it takes to perform
308
320
sparsity detection. Let's compare the two by setting the sparsity detection algorithms.
309
321
310
322
``` @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()),
313
330
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)),
316
334
u0, p; abstol = 1e-10, reltol = 1e-10)
317
335
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());
320
339
nothing # hide
321
340
```
322
341
0 commit comments