Skip to content

Commit b300050

Browse files
Merge pull request #257 from avik-pal/ap/more_alg
Porting over some low cost Algorithms
2 parents 0dac21a + e1940d4 commit b300050

24 files changed

+1240
-92
lines changed

.github/workflows/CI.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ jobs:
1616
strategy:
1717
matrix:
1818
group:
19-
- All
19+
- Core
20+
- 23TestProblems
2021
version:
2122
- '1'
2223
steps:

docs/pages.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22

33
pages = ["index.md",
44
"Getting Started with Nonlinear Rootfinding in Julia" => "tutorials/getting_started.md",
5-
"Tutorials" => Any[
6-
"Code Optimization for Small Nonlinear Systems" => "tutorials/code_optimization.md",
5+
"Tutorials" => Any["Code Optimization for Small Nonlinear Systems" => "tutorials/code_optimization.md",
76
"Handling Large Ill-Conditioned and Sparse Systems" => "tutorials/large_systems.md",
87
"Symbolic System Definition and Acceleration via ModelingToolkit" => "tutorials/modelingtoolkit.md",
98
"tutorials/small_compile.md",
@@ -18,7 +17,8 @@ pages = ["index.md",
1817
"Solver Summaries and Recommendations" => Any["solvers/NonlinearSystemSolvers.md",
1918
"solvers/BracketingSolvers.md",
2019
"solvers/SteadyStateSolvers.md",
21-
"solvers/NonlinearLeastSquaresSolvers.md"],
20+
"solvers/NonlinearLeastSquaresSolvers.md",
21+
"solvers/LineSearch.md"],
2222
"Detailed Solver APIs" => Any["api/nonlinearsolve.md",
2323
"api/simplenonlinearsolve.md",
2424
"api/minpack.md",

docs/src/api/nonlinearsolve.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ These are the native solvers of NonlinearSolve.jl.
88
NewtonRaphson
99
TrustRegion
1010
PseudoTransient
11+
DFSane
12+
GeneralBroyden
13+
GeneralKlement
1114
```
1215

1316
## Polyalgorithms

docs/src/solvers/LineSearch.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# [Line Search](@id linesearch)
2+
3+
A convenience wrapper over `LineSearches.jl` and some native Line Search methods, powered
4+
internally with fast automatic differentiation.
5+
6+
```@docs
7+
LineSearch
8+
```
9+
10+
## Native Line Search Methods
11+
12+
```@docs
13+
LiFukushimaLineSearch
14+
```

docs/src/solvers/NonlinearSystemSolvers.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ features, but have a bit of overhead on very small problems.
6565
- `FastShortcutNonlinearPolyalg()`: The default method. A polyalgorithm that mixes fast methods
6666
with fallbacks to robust methods to allow for solving easy problems quickly without sacrificing
6767
robustnes on the hard problems.
68+
- `GeneralBroyden()`: Generalization of Broyden's Quasi-Newton Method with Line Search and
69+
Automatic Jacobian Resetting. This is a fast method but unstable when the condition number of
70+
the Jacobian matrix is sufficiently large.
71+
- `GeneralKlement()`: Generalization of Klement's Quasi-Newton Method with Line Search and
72+
Automatic Jacobian Resetting. This is a fast method but unstable when the condition number of
73+
the Jacobian matrix is sufficiently large.
6874

6975
### SimpleNonlinearSolve.jl
7076

docs/src/tutorials/code_optimization.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Take for example a prototypical small nonlinear solver code in its out-of-place
3434
```@example small_opt
3535
using NonlinearSolve
3636
37-
function f(u, p)
37+
function f(u, p)
3838
u .* u .- p
3939
end
4040
u0 = [1.0, 1.0]
@@ -54,7 +54,7 @@ using BenchmarkTools
5454
Note that this way of writing the function is a shorthand for:
5555

5656
```@example small_opt
57-
function f(u, p)
57+
function f(u, p)
5858
[u[1] * u[1] - p, u[2] * u[2] - p]
5959
end
6060
```
@@ -69,25 +69,25 @@ In order to avoid this issue, we can use a non-allocating "in-place" approach. W
6969
this looks like:
7070

7171
```@example small_opt
72-
function f(du, u, p)
72+
function f(du, u, p)
7373
du[1] = u[1] * u[1] - p
7474
du[2] = u[2] * u[2] - p
7575
nothing
7676
end
7777
7878
prob = NonlinearProblem(f, u0, p)
79-
@btime sol = solve(prob, NewtonRaphson())
79+
@btime sol = solve(prob, NewtonRaphson())
8080
```
8181

8282
Notice how much faster this already runs! We can make this code even simpler by using
8383
the `.=` in-place broadcasting.
8484

8585
```@example small_opt
86-
function f(du, u, p)
86+
function f(du, u, p)
8787
du .= u .* u .- p
8888
end
8989
90-
@btime sol = solve(prob, NewtonRaphson())
90+
@btime sol = solve(prob, NewtonRaphson())
9191
```
9292

9393
## Further Optimizations for Small Nonlinear Solves with Static Arrays and SimpleNonlinearSolve
@@ -140,7 +140,7 @@ want to use the out-of-place allocating form, but this time we want to output
140140
a static array. Doing it with broadcasting looks like:
141141

142142
```@example small_opt
143-
function f_SA(u, p)
143+
function f_SA(u, p)
144144
u .* u .- p
145145
end
146146
u0 = SA[1.0, 1.0]
@@ -153,7 +153,7 @@ Note that only change here is that `u0` is made into a StaticArray! If we needed
153153
for a more complex nonlinear case, then we'd simply do the following:
154154

155155
```@example small_opt
156-
function f_SA(u, p)
156+
function f_SA(u, p)
157157
SA[u[1] * u[1] - p, u[2] * u[2] - p]
158158
end
159159
@@ -170,4 +170,4 @@ which are designed for these small-scale static examples. Let's now use `SimpleN
170170
@btime solve(prob, SimpleNewtonRaphson())
171171
```
172172

173-
And there we go, around 100ns from our starting point of almost 6μs!
173+
And there we go, around 100ns from our starting point of almost 6μs!

docs/src/tutorials/large_systems.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# [Efficiently Solving Large Sparse Ill-Conditioned Nonlinear Systems in Julia](@id large_systems)
22

3-
This tutorial is for getting into the extra features of using NonlinearSolve.jl. Solving ill-conditioned nonlinear systems
4-
requires specializing the linear solver on properties of the Jacobian in order to cut down on the ``\mathcal{O}(n^3)``
5-
linear solve and the ``\mathcal{O}(n^2)`` back-solves. This tutorial is designed to explain the advanced usage of
3+
This tutorial is for getting into the extra features of using NonlinearSolve.jl. Solving ill-conditioned nonlinear systems
4+
requires specializing the linear solver on properties of the Jacobian in order to cut down on the ``\mathcal{O}(n^3)``
5+
linear solve and the ``\mathcal{O}(n^2)`` back-solves. This tutorial is designed to explain the advanced usage of
66
NonlinearSolve.jl by solving the steady state stiff Brusselator partial differential equation (BRUSS) using NonlinearSolve.jl.
77

88
## Definition of the Brusselator Equation
@@ -182,8 +182,8 @@ nothing # hide
182182

183183
Notice that this acceleration does not require the definition of a sparsity
184184
pattern, and can thus be an easier way to scale for large problems. For more
185-
information on linear solver choices, see the
186-
[linear solver documentation](https://docs.sciml.ai/DiffEqDocs/stable/features/linear_nonlinear/#linear_nonlinear).
185+
information on linear solver choices, see the
186+
[linear solver documentation](https://docs.sciml.ai/DiffEqDocs/stable/features/linear_nonlinear/#linear_nonlinear).
187187
`linsolve` choices are any valid [LinearSolve.jl](https://linearsolve.sciml.ai/dev/) solver.
188188

189189
!!! note

docs/src/tutorials/modelingtoolkit.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,4 @@ sol[u5]
120120

121121
If you're interested in building models in a component or block based form, such as seen in systems like Simulink or Modelica,
122122
take a deeper look at [ModelingToolkit.jl's documentation](https://docs.sciml.ai/ModelingToolkit/stable/) which goes into
123-
detail on such features.
123+
detail on such features.

docs/src/tutorials/small_compile.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,18 @@ sol = solve(prob, SimpleNewtonRaphson())
1919

2020
However, there are a few downsides to SimpleNonlinearSolve's `SimpleX` style algorithms to note:
2121

22-
1. SimpleNonlinearSolve.jl's methods are not hooked into the LinearSolve.jl system, and thus do not have
23-
the ability to specify linear solvers, use sparse matrices, preconditioners, and all of the other features
24-
which are required to scale for very large systems of equations.
25-
2. SimpleNonlinearSolve.jl's methods have less robust error handling and termination conditions, and thus
26-
these methods are missing some flexibility and give worse hints for debugging.
27-
3. SimpleNonlinearSolve.jl's methods are focused on out-of-place support. There is some in-place support,
28-
but it's designed for simple smaller systems and so some optimizations are missing.
22+
1. SimpleNonlinearSolve.jl's methods are not hooked into the LinearSolve.jl system, and thus do not have
23+
the ability to specify linear solvers, use sparse matrices, preconditioners, and all of the other features
24+
which are required to scale for very large systems of equations.
25+
2. SimpleNonlinearSolve.jl's methods have less robust error handling and termination conditions, and thus
26+
these methods are missing some flexibility and give worse hints for debugging.
27+
3. SimpleNonlinearSolve.jl's methods are focused on out-of-place support. There is some in-place support,
28+
but it's designed for simple smaller systems and so some optimizations are missing.
2929

3030
However, the major upsides of SimpleNonlinearSolve.jl are:
3131

32-
1. The methods are optimized and non-allocating on StaticArrays
33-
2. The methods are minimal in compilation
32+
1. The methods are optimized and non-allocating on StaticArrays
33+
2. The methods are minimal in compilation
3434

3535
As such, you can use the code as shown above to have very low startup with good methods, but for more scaling and debuggability
3636
we recommend the full NonlinearSolve.jl. But that said,
@@ -51,4 +51,4 @@ is not only sufficient but optimal.
5151

5252
Julia has tools for building small binaries via static compilation with [StaticCompiler.jl](https://github.com/tshort/StaticCompiler.jl).
5353
However, these tools are currently limited to type-stable non-allocating functions. That said, SimpleNonlinearSolve.jl's solvers are
54-
precisely the subset of NonlinearSolve.jl which are compatible with static compilation.
54+
precisely the subset of NonlinearSolve.jl which are compatible with static compilation.

src/NonlinearSolve.jl

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import ArrayInterface: restructure
99
import ForwardDiff
1010

1111
import ADTypes: AbstractFiniteDifferencesMode
12-
import ArrayInterface: undefmatrix, matrix_colors, parameterless_type, ismutable
12+
import ArrayInterface: undefmatrix, matrix_colors, parameterless_type, ismutable, issingular
1313
import ConcreteStructs: @concrete
1414
import EnumX: @enumx
1515
import ForwardDiff: Dual
@@ -26,6 +26,8 @@ import UnPack: @unpack
2626
const AbstractSparseADType = Union{ADTypes.AbstractSparseFiniteDifferences,
2727
ADTypes.AbstractSparseForwardMode, ADTypes.AbstractSparseReverseMode}
2828

29+
abstract type AbstractNonlinearSolveLineSearchAlgorithm end
30+
2931
abstract type AbstractNonlinearSolveAlgorithm <: AbstractNonlinearAlgorithm end
3032
abstract type AbstractNewtonAlgorithm{CJ, AD} <: AbstractNonlinearSolveAlgorithm end
3133

@@ -50,10 +52,13 @@ function SciMLBase.solve!(cache::AbstractNonlinearSolveCache)
5052
cache.stats.nsteps += 1
5153
end
5254

53-
if cache.stats.nsteps == cache.maxiters
54-
cache.retcode = ReturnCode.MaxIters
55-
else
56-
cache.retcode = ReturnCode.Success
55+
# The solver might have set a different `retcode`
56+
if cache.retcode == ReturnCode.Default
57+
if cache.stats.nsteps == cache.maxiters
58+
cache.retcode = ReturnCode.MaxIters
59+
else
60+
cache.retcode = ReturnCode.Success
61+
end
5762
end
5863

5964
return SciMLBase.build_solution(cache.prob, cache.alg, cache.u, get_fu(cache);
@@ -69,18 +74,22 @@ include("levenberg.jl")
6974
include("gaussnewton.jl")
7075
include("dfsane.jl")
7176
include("pseudotransient.jl")
77+
include("broyden.jl")
78+
include("klement.jl")
79+
include("lbroyden.jl")
7280
include("jacobian.jl")
7381
include("ad.jl")
7482
include("default.jl")
7583

7684
import PrecompileTools
7785

78-
@static if VERSION >= v"1.10"
86+
@static if VERSION v"1.10-"
7987
PrecompileTools.@compile_workload begin
8088
for T in (Float32, Float64)
8189
prob = NonlinearProblem{false}((u, p) -> u .* u .- p, T(0.1), T(2))
8290

83-
precompile_algs = (NewtonRaphson(), TrustRegion(), LevenbergMarquardt())
91+
precompile_algs = (NewtonRaphson(), TrustRegion(), LevenbergMarquardt(),
92+
PseudoTransient(), GeneralBroyden(), GeneralKlement(), nothing)
8493

8594
for alg in precompile_algs
8695
solve(prob, alg, abstol = T(1e-2))
@@ -97,10 +106,11 @@ end
97106

98107
export RadiusUpdateSchemes
99108

100-
export NewtonRaphson, TrustRegion, LevenbergMarquardt, DFSane, GaussNewton, PseudoTransient
109+
export NewtonRaphson, TrustRegion, LevenbergMarquardt, DFSane, GaussNewton, PseudoTransient,
110+
GeneralBroyden, GeneralKlement, LimitedMemoryBroyden
101111
export LeastSquaresOptimJL, FastLevenbergMarquardtJL
102112
export RobustMultiNewton, FastShortcutNonlinearPolyalg
103113

104-
export LineSearch
114+
export LineSearch, LiFukushimaLineSearch
105115

106116
end # module

0 commit comments

Comments
 (0)