diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 2bdc0de..e147acc 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.6.7","generation_timestamp":"2024-01-31T21:47:13","documenter_version":"1.2.1"}} \ No newline at end of file +{"documenter":{"julia_version":"1.6.7","generation_timestamp":"2024-02-02T08:30:54","documenter_version":"1.2.1"}} \ No newline at end of file diff --git a/dev/docstrings/index.html b/dev/docstrings/index.html index 3c447f2..443fc28 100644 --- a/dev/docstrings/index.html +++ b/dev/docstrings/index.html @@ -1,5 +1,14 @@ -Docstrings · PseudostableRecurrences

Docstrings

PseudostableRecurrences.AbstractLinearRecurrenceType
AbstractLinearRecurrence{T} <: AbstractRecurrence{T}

A recurrence where the results are linear w.r.t. the initial conditions. To support pseudostablization, the following methods are needed:

  • rdiv!(::AbstractLinearRecurrence, x::Number): divides the system by x. It is used to prevent overflow.
  • LinearAlgebra.norm(step!(::AbstractLinearRecurrence), Inf): computes the ∞-norm of a step. It is used to get the amplification of the system.
source
PseudostableRecurrences.AbstractRecurrenceType
AbstractRecurrence{T}

The abstract type where a recurrence instance runs. It needs to support the following methods:

  • step!(::AbstractRecurrence): step forward the recurrence. Returns nothing if the recurrence terminates, otherwise returns a view of the stepping result.
source
PseudostableRecurrences.AbstractRecurrencePlanType
AbstractRecurrencePlan

The abstract type of recurrence plans. A recurrence plan should include all the information that can perform a recurrence with a precision. It needs to support the following methods:

  • init(::AbstractRecurrencePlan; T=Float64, init=:default): generates an AbstractRecurrence object where the actual recurrence runs on. Returns a vector of the view of the initial steps as well.
    • T is the underlying type where the precision is implied.
    • init tells how the initial values are generated. Must support :default for the forward recurrence to run. It is also suggested to support :rand for the pseudostablization algorithm.
source
PseudostableRecurrences.StencilRecurrenceType
StencilRecurrence{N,T,S,
+Docstrings · PseudostableRecurrences

Docstrings

PseudostableRecurrences.AbstractLinearRecurrenceType
AbstractLinearRecurrence{T} <: AbstractRecurrence{T}

A recurrence where the results are linear w.r.t. the initial conditions. To support pseudostablization, the following methods are needed:

  • rdiv!(::AbstractLinearRecurrence, x::Number): divides the system by x. It is used to prevent overflow.
  • LinearAlgebra.norm(step!(::AbstractLinearRecurrence), Inf): computes the ∞-norm of a step. It is used to get the amplification of the system.
source
PseudostableRecurrences.AbstractRecurrenceType
AbstractRecurrence{T}

The abstract type where a recurrence instance runs. It needs to support the following methods:

  • step!(::AbstractRecurrence): step forward the recurrence. Returns nothing if the recurrence terminates, otherwise returns a view of the stepping result.
source
PseudostableRecurrences.AbstractRecurrencePlanType
AbstractRecurrencePlan

The abstract type of recurrence plans. A recurrence plan should include all the information that can perform a recurrence with a precision. It needs to support the following methods:

  • init(::AbstractRecurrencePlan; T=Float64, init=:default): generates an AbstractRecurrence object where the actual recurrence runs on. Returns a vector of the view of the initial steps as well.
    • T is the underlying type where the precision is implied.
    • init tells how the initial values are generated. Must support :default for the forward recurrence to run. It is also suggested to support :rand for the pseudostablization algorithm.
source
PseudostableRecurrences.StencilRecurrenceType
StencilRecurrence{N,T,S,
     COEF<:NTuple{S,AbstractArray{T,N}},
     TB<:AbstractArray{T,N},}
-    (stencil, coef, buffer, slicestart, sliceend, lastind)

Properties

For coef and slicesupport, tt's suggested to use lazy arrays for performance.

  • stencil::NTuple{S, CartesianIndex{N}}: The relative index of the stencil. Can contain (0,0) (see coef)
  • coef::COEF<:NTuple{S,AbstractArray{T,N}}: The coefficient associated with each relative index. The one associated with CartesianIndex(0,0) refers to a constant added to that entry. It's suggested to use lazy arrays for performance.
  • buffer::CircularArray{T,N,TB<:AbstractArray{T,N}}: a buffer to store temp results.
  • slicestart::MVector{N, Int} and sliceend::MVector{N, Int}: marks the current range of entries to be determined. Technically NTuple{N-1, Int} should work, but Julia doesn't support computed type parameters.
  • lastslice::Int: marks the index of the slice where the recurrence terminates.
source
PseudostableRecurrences.StencilRecurrencePlanType
StencilRecurrencePlan{N, S, COEF<:NTuple{S,Function}, INIT<:Function} <: AbstractLinearRecurrencePlan

Properties

  • stencil::SVector{S, CartesianIndex{N}}: The relative index of the stencil. Can contain (0,0) (see coef)
  • coef::COEF<:NTuple{S,Function}: The coefficient associated with each relative index. The one associated with CartesianIndex(0,0) refers to a constant added to that entry. The functions should be in the form f(I..., T) where I is the index of the stencil and T is the suggested return type. Coefficients should be at least as accurate as T. Exact-value types such as Irrational, Rational or Integer would do the job, and if that's not possible, BigFloat would work as well.
  • init::INIT<:Function: the function used for initial values. The functions should be in the form f(I..., T) where I is the size of the array and T is the eltype.
  • size::Dims{N}: the size of the whole array.
  • offset::NTuple{N,Int}: the very first index where the recurrence starts at.
source
+ (stencil, coef, buffer, slicestart, sliceend, lastind)

Properties

For coef and slicesupport, tt's suggested to use lazy arrays for performance.

  • stencil::NTuple{S, CartesianIndex{N}}: The relative index of the stencil. Can contain (0,0) (see coef)
  • coef::COEF<:NTuple{S,AbstractArray{T,N}}: The coefficient associated with each relative index. The one associated with CartesianIndex(0,0) refers to a constant added to that entry. It's suggested to use lazy arrays for performance.
  • buffer::CircularArray{T,N,TB<:AbstractArray{T,N}}: a buffer to store temp results.
  • slicestart::MVector{N, Int} and sliceend::MVector{N, Int}: marks the current range of entries to be determined. Technically NTuple{N-1, Int} should work, but Julia doesn't support computed type parameters.
  • lastslice::Int: marks the index of the slice where the recurrence terminates.
source
PseudostableRecurrences.StencilRecurrencePlanType
StencilRecurrencePlan{N, S, COEF<:NTuple{S,Function}, INIT<:Function} <: AbstractLinearRecurrencePlan

Properties

  • stencil::SVector{S, CartesianIndex{N}}: The relative index of the stencil. Can contain (0,0) (see coef)
  • coef::COEF<:NTuple{S,Function}: The coefficient associated with each relative index. The one associated with CartesianIndex(0,0) refers to a constant added to that entry. The functions should be in the form f(I..., T) where I is the index of the stencil and T is the suggested return type. Coefficients should be at least as accurate as T. Exact-value types such as Irrational, Rational or Integer would do the job, and if that's not possible, BigFloat would work as well.
  • init::INIT<:Function: the function used for initial values. The functions should be in the form f(I..., T) where I is the size of the array and T is the eltype.
  • size::Dims{N}: the size of the whole array.
  • offset::NTuple{N,Int}: the very first index where the recurrence starts at.
source
PseudostableRecurrences.slicetypeMethod
slicetype(T)

Get the type of a slice of T

Examples

julia> using PseudostableRecurrences: slicetype
+
+julia> slicetype(Matrix{Float64})
+Vector{Float64} (alias for Array{Float64, 1})
+
+julia> slicetype(UnitRange{Int})
+Int64
+
+julia> slicetype(BitArray{3})
+BitMatrix (alias for BitArray{2})
source
diff --git a/dev/examples/151bbf28.svg b/dev/examples/151bbf28.svg deleted file mode 100644 index 095e5d6..0000000 --- a/dev/examples/151bbf28.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dev/examples/1a71ce91.svg b/dev/examples/1a71ce91.svg new file mode 100644 index 0000000..1dc9127 --- /dev/null +++ b/dev/examples/1a71ce91.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/index.html b/dev/examples/index.html index 1e4226d..06e2390 100644 --- a/dev/examples/index.html +++ b/dev/examples/index.html @@ -8,7 +8,7 @@ for k in 1:N-3 x[k+3] = 10/3*x[k+2] - 3*x[k+1] + 2/3*x[k] end -plot(4:N, abs.(x.-sqrt(2))[4:N], label="numerical error", yaxis=:log, background_color=:transparent, foreground_color=:gray)Example block output

The backward recurrence, on the other hand, will be dominated by the eigenvalue $\frac{1}{3}$ instead. While there may be other algorithms that are stable in this case, they are not trivial and vary across different problems.

Pseudo-stablisation methods

The idea of pseudo-stablisation is to use a high precision to achieve a given error tolerance despite the instability. The precision choice is smart such that no extra care is taken by the end user, resulting in a "stable" behavior. Needless to say, pseudo-stablisation only works on problems where the initial values and the recurrence coefficients can be computed to arbitrary precision.

The current iteration of the strategy focuses on linear problems, where the end error scales linearly with the initial error which is considered as machine epsilon. To get the scaling factor, a test recurrence with random initial values is run. The random initial values ensure that the initial error is significant and the leading eigenvalue immediately takes over. The estimated scaling factor is then the ratio between the end norm of the test run and the initial norm.

The test recurrence may sound like a performance trade-off, but since it is run under a low precision, the cost is negligible.

Linear-recursive Sequence

First, we shall recognise that the linear recursive sequence is basically a 1D stencil recurrence with the stencil

stencil = (CartesianIndex(-3), CartesianIndex(-2), CartesianIndex(-1));
(CartesianIndex(-3,), CartesianIndex(-2,), CartesianIndex(-1,))

with the coefficients

coef0(m) = 2//3
+plot(4:N, abs.(x.-sqrt(2))[4:N], label="numerical error", yaxis=:log, background_color=:transparent, foreground_color=:gray)
Example block output

The backward recurrence, on the other hand, will be dominated by the eigenvalue $\frac{1}{3}$ instead. While there may be other algorithms that are stable in this case, they are not trivial and vary across different problems.

Pseudo-stablisation methods

The idea of pseudo-stablisation is to use a high precision to achieve a given error tolerance despite the instability. The precision choice is smart such that no extra care is taken by the end user, resulting in a "stable" behavior. Needless to say, pseudo-stablisation only works on problems where the initial values and the recurrence coefficients can be computed to arbitrary precision.

The current iteration of the strategy focuses on linear problems, where the end error scales linearly with the initial error which is considered as machine epsilon. To get the scaling factor, a test recurrence with random initial values is run. The random initial values ensure that the initial error is significant and the leading eigenvalue immediately takes over. The estimated scaling factor is then the ratio between the end norm of the test run and the initial norm.

The test recurrence may sound like a performance trade-off, but since it is run under a low precision, the cost is negligible.

Linear-recursive Sequence

First, we shall recognise that the linear recursive sequence is basically a 1D stencil recurrence with the stencil

stencil = (CartesianIndex(-3), CartesianIndex(-2), CartesianIndex(-1));
(CartesianIndex(-3,), CartesianIndex(-2,), CartesianIndex(-1,))

with the coefficients

coef0(m) = 2//3
 coef1(m) = -3
 coef2(m) = 10//3
 coefs = (coef0, coef1, coef2)
(Main.coef0, Main.coef1, Main.coef2)

They will work together to define the recurrence

# calculate x[m]
@@ -36,6 +36,6 @@
  1.4142135623730951
  1.4142135623730951
  1.4142135623730951
- 1.414213562373095
- 1.414213562373095
- 1.414213562373095
+ 1.4142135623730951 + 1.4142135623730951 + 1.4142135623730951 diff --git a/dev/index.html b/dev/index.html index 88b91ce..e309e8c 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1,2 +1,2 @@ -PseudostableRecurrences.jl · PseudostableRecurrences
+PseudostableRecurrences.jl · PseudostableRecurrences
diff --git a/dev/search_index.js b/dev/search_index.js index e71a629..996c692 100644 --- a/dev/search_index.js +++ b/dev/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"examples/#Examples","page":"Examples","title":"Examples","text":"","category":"section"},{"location":"examples/#Linear-recursive-Sequence","page":"Examples","title":"Linear-recursive Sequence","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"Consider the recurrence","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"x_1=x_2=x_3=sqrt2 x_n+3-frac103x_n+2+3x_n+1-frac23x_n=0","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Its eigenvalues are 2, frac13 and 1, so the solution is constant - x_n=sqrt2. However, the forward recurrence is numerically unstable since an eigenvalue is greater than 1. The following experiment confirms that.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Plots\nN = 100\nx = zeros(N)\nx[1] = sqrt(2)\nx[2] = sqrt(2)\nx[3] = sqrt(2)\nfor k in 1:N-3\n x[k+3] = 10/3*x[k+2] - 3*x[k+1] + 2/3*x[k]\nend\nplot(4:N, abs.(x.-sqrt(2))[4:N], label=\"numerical error\", yaxis=:log, background_color=:transparent, foreground_color=:gray)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"The backward recurrence, on the other hand, will be dominated by the eigenvalue frac13 instead. While there may be other algorithms that are stable in this case, they are not trivial and vary across different problems.","category":"page"},{"location":"examples/#Pseudo-stablisation-methods","page":"Examples","title":"Pseudo-stablisation methods","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"The idea of pseudo-stablisation is to use a high precision to achieve a given error tolerance despite the instability. The precision choice is smart such that no extra care is taken by the end user, resulting in a \"stable\" behavior. Needless to say, pseudo-stablisation only works on problems where the initial values and the recurrence coefficients can be computed to arbitrary precision.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"The current iteration of the strategy focuses on linear problems, where the end error scales linearly with the initial error which is considered as machine epsilon. To get the scaling factor, a test recurrence with random initial values is run. The random initial values ensure that the initial error is significant and the leading eigenvalue immediately takes over. The estimated scaling factor is then the ratio between the end norm of the test run and the initial norm.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"The test recurrence may sound like a performance trade-off, but since it is run under a low precision, the cost is negligible.","category":"page"},{"location":"examples/#Linear-recursive-Sequence-2","page":"Examples","title":"Linear-recursive Sequence","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"First, we shall recognise that the linear recursive sequence is basically a 1D stencil recurrence with the stencil","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"stencil = (CartesianIndex(-3), CartesianIndex(-2), CartesianIndex(-1));","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"with the coefficients","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"coef0(m) = 2//3\ncoef1(m) = -3\ncoef2(m) = 10//3\ncoefs = (coef0, coef1, coef2)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"They will work together to define the recurrence","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"# calculate x[m]\nx[m] = 0\nfor i in 1:3\n x[m] += coef[i](m)*x[m+stencil[i]]\nend","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"note: Inhomogenious case\nTo define a linear inhomogeneous recurrence, the coefficient associated with the zero cartesian index is used. For example, the recurrencex_n+3-frac103x_n+2+3x_n+1-frac23x_n=frac1nshould be defined bystencil = (CartesianIndex(-3), CartesianIndex(-2), CartesianIndex(-1), CartesianIndex(0))\ncoefs = (coef0, coef1, coef2, m -> 1//m)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"We then need to define the initial values","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"f_init(T) = [sqrt(T(2)), sqrt(T(2)), sqrt(T(2)), T(0)]","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"where the place for the next step should be reserved. This is a function that can generate values based on type T.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Now we only need to further provide the size of the recurrence and we are ready to go.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using PseudostableRecurrences\nP = StencilRecurrencePlan(stencil, coefs, f_init, (100,))\nstable_recurrence(P) # defaults to stable_recurrence(P, Float64)","category":"page"},{"location":"#PseudostableRecurrences.jl","page":"PseudostableRecurrences.jl","title":"PseudostableRecurrences.jl","text":"","category":"section"},{"location":"","page":"PseudostableRecurrences.jl","title":"PseudostableRecurrences.jl","text":"Documentation for PseudostableRecurrences.jl","category":"page"},{"location":"docstrings/#Docstrings","page":"Docstrings","title":"Docstrings","text":"","category":"section"},{"location":"docstrings/","page":"Docstrings","title":"Docstrings","text":"Modules = [PseudostableRecurrences]","category":"page"},{"location":"docstrings/#PseudostableRecurrences.AbstractLinearRecurrence","page":"Docstrings","title":"PseudostableRecurrences.AbstractLinearRecurrence","text":"AbstractLinearRecurrence{T} <: AbstractRecurrence{T}\n\nA recurrence where the results are linear w.r.t. the initial conditions. To support pseudostablization, the following methods are needed:\n\nrdiv!(::AbstractLinearRecurrence, x::Number): divides the system by x. It is used to prevent overflow.\nLinearAlgebra.norm(step!(::AbstractLinearRecurrence), Inf): computes the ∞-norm of a step. It is used to get the amplification of the system.\n\n\n\n\n\n","category":"type"},{"location":"docstrings/#PseudostableRecurrences.AbstractLinearRecurrencePlan","page":"Docstrings","title":"PseudostableRecurrences.AbstractLinearRecurrencePlan","text":"AbstractLinearRecurrencePlan <: AbstractRecurrencePlan\n\nA recurrence plan where the results are linear w.r.t. the initial conditions. Pseudostablization algorithm on linear recurrences is implemented.\n\n\n\n\n\n","category":"type"},{"location":"docstrings/#PseudostableRecurrences.AbstractRecurrence","page":"Docstrings","title":"PseudostableRecurrences.AbstractRecurrence","text":"AbstractRecurrence{T}\n\nThe abstract type where a recurrence instance runs. It needs to support the following methods:\n\nstep!(::AbstractRecurrence): step forward the recurrence. Returns nothing if the recurrence terminates, otherwise returns a view of the stepping result.\n\n\n\n\n\n","category":"type"},{"location":"docstrings/#PseudostableRecurrences.AbstractRecurrencePlan","page":"Docstrings","title":"PseudostableRecurrences.AbstractRecurrencePlan","text":"AbstractRecurrencePlan\n\nThe abstract type of recurrence plans. A recurrence plan should include all the information that can perform a recurrence with a precision. It needs to support the following methods:\n\ninit(::AbstractRecurrencePlan; T=Float64, init=:default): generates an AbstractRecurrence object where the actual recurrence runs on. Returns a vector of the view of the initial steps as well.\nT is the underlying type where the precision is implied. \ninit tells how the initial values are generated. Must support :default for the forward recurrence to run. It is also suggested to support :rand for the pseudostablization algorithm.\n\n\n\n\n\n","category":"type"},{"location":"docstrings/#PseudostableRecurrences.StencilRecurrence","page":"Docstrings","title":"PseudostableRecurrences.StencilRecurrence","text":"StencilRecurrence{N,T,S,\n COEF<:NTuple{S,AbstractArray{T,N}},\n TB<:AbstractArray{T,N},}\n (stencil, coef, buffer, slicestart, sliceend, lastind)\n\nProperties\n\nFor coef and slicesupport, tt's suggested to use lazy arrays for performance.\n\nstencil::NTuple{S, CartesianIndex{N}}: The relative index of the stencil. Can contain (0,0) (see coef)\ncoef::COEF<:NTuple{S,AbstractArray{T,N}}: The coefficient associated with each relative index. The one associated with CartesianIndex(0,0) refers to a constant added to that entry. It's suggested to use lazy arrays for performance.\nbuffer::CircularArray{T,N,TB<:AbstractArray{T,N}}: a buffer to store temp results. \nslicestart::MVector{N, Int} and sliceend::MVector{N, Int}: marks the current range of entries to be determined. Technically NTuple{N-1, Int} should work, but Julia doesn't support computed type parameters.\nlastslice::Int: marks the index of the slice where the recurrence terminates.\n\n\n\n\n\n","category":"type"},{"location":"docstrings/#PseudostableRecurrences.StencilRecurrencePlan","page":"Docstrings","title":"PseudostableRecurrences.StencilRecurrencePlan","text":"StencilRecurrencePlan{N, S, COEF<:NTuple{S,Function}, INIT<:Function} <: AbstractLinearRecurrencePlan\n\nProperties\n\nstencil::SVector{S, CartesianIndex{N}}: The relative index of the stencil. Can contain (0,0) (see coef)\ncoef::COEF<:NTuple{S,Function}: The coefficient associated with each relative index. The one associated with CartesianIndex(0,0) refers to a constant added to that entry. The functions should be in the form f(I..., T) where I is the index of the stencil and T is the suggested return type. Coefficients should be at least as accurate as T. Exact-value types such as Irrational, Rational or Integer would do the job, and if that's not possible, BigFloat would work as well.\ninit::INIT<:Function: the function used for initial values. The functions should be in the form f(I..., T) where I is the size of the array and T is the eltype.\nsize::Dims{N}: the size of the whole array.\noffset::NTuple{N,Int}: the very first index where the recurrence starts at.\n\n\n\n\n\n","category":"type"},{"location":"docstrings/#PseudostableRecurrences.precision_shift-Tuple{PseudostableRecurrences.AbstractLinearRecurrencePlan}","page":"Docstrings","title":"PseudostableRecurrences.precision_shift","text":"precision_shift(P::AbstractLinearRecurrencePlan)\n\nEstimates log2 of the amplification of P by performing a full recurrence based on random initial conditions.\n\n\n\n\n\n","category":"method"}] +[{"location":"examples/#Examples","page":"Examples","title":"Examples","text":"","category":"section"},{"location":"examples/#Linear-recursive-Sequence","page":"Examples","title":"Linear-recursive Sequence","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"Consider the recurrence","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"x_1=x_2=x_3=sqrt2 x_n+3-frac103x_n+2+3x_n+1-frac23x_n=0","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Its eigenvalues are 2, frac13 and 1, so the solution is constant - x_n=sqrt2. However, the forward recurrence is numerically unstable since an eigenvalue is greater than 1. The following experiment confirms that.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Plots\nN = 100\nx = zeros(N)\nx[1] = sqrt(2)\nx[2] = sqrt(2)\nx[3] = sqrt(2)\nfor k in 1:N-3\n x[k+3] = 10/3*x[k+2] - 3*x[k+1] + 2/3*x[k]\nend\nplot(4:N, abs.(x.-sqrt(2))[4:N], label=\"numerical error\", yaxis=:log, background_color=:transparent, foreground_color=:gray)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"The backward recurrence, on the other hand, will be dominated by the eigenvalue frac13 instead. While there may be other algorithms that are stable in this case, they are not trivial and vary across different problems.","category":"page"},{"location":"examples/#Pseudo-stablisation-methods","page":"Examples","title":"Pseudo-stablisation methods","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"The idea of pseudo-stablisation is to use a high precision to achieve a given error tolerance despite the instability. The precision choice is smart such that no extra care is taken by the end user, resulting in a \"stable\" behavior. Needless to say, pseudo-stablisation only works on problems where the initial values and the recurrence coefficients can be computed to arbitrary precision.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"The current iteration of the strategy focuses on linear problems, where the end error scales linearly with the initial error which is considered as machine epsilon. To get the scaling factor, a test recurrence with random initial values is run. The random initial values ensure that the initial error is significant and the leading eigenvalue immediately takes over. The estimated scaling factor is then the ratio between the end norm of the test run and the initial norm.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"The test recurrence may sound like a performance trade-off, but since it is run under a low precision, the cost is negligible.","category":"page"},{"location":"examples/#Linear-recursive-Sequence-2","page":"Examples","title":"Linear-recursive Sequence","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"First, we shall recognise that the linear recursive sequence is basically a 1D stencil recurrence with the stencil","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"stencil = (CartesianIndex(-3), CartesianIndex(-2), CartesianIndex(-1));","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"with the coefficients","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"coef0(m) = 2//3\ncoef1(m) = -3\ncoef2(m) = 10//3\ncoefs = (coef0, coef1, coef2)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"They will work together to define the recurrence","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"# calculate x[m]\nx[m] = 0\nfor i in 1:3\n x[m] += coef[i](m)*x[m+stencil[i]]\nend","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"note: Inhomogenious case\nTo define a linear inhomogeneous recurrence, the coefficient associated with the zero cartesian index is used. For example, the recurrencex_n+3-frac103x_n+2+3x_n+1-frac23x_n=frac1nshould be defined bystencil = (CartesianIndex(-3), CartesianIndex(-2), CartesianIndex(-1), CartesianIndex(0))\ncoefs = (coef0, coef1, coef2, m -> 1//m)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"We then need to define the initial values","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"f_init(T) = [sqrt(T(2)), sqrt(T(2)), sqrt(T(2)), T(0)]","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"where the place for the next step should be reserved. This is a function that can generate values based on type T.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Now we only need to further provide the size of the recurrence and we are ready to go.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using PseudostableRecurrences\nP = StencilRecurrencePlan(stencil, coefs, f_init, (100,))\nstable_recurrence(P) # defaults to stable_recurrence(P, Float64)","category":"page"},{"location":"#PseudostableRecurrences.jl","page":"PseudostableRecurrences.jl","title":"PseudostableRecurrences.jl","text":"","category":"section"},{"location":"","page":"PseudostableRecurrences.jl","title":"PseudostableRecurrences.jl","text":"Documentation for PseudostableRecurrences.jl","category":"page"},{"location":"docstrings/#Docstrings","page":"Docstrings","title":"Docstrings","text":"","category":"section"},{"location":"docstrings/","page":"Docstrings","title":"Docstrings","text":"Modules = [PseudostableRecurrences]","category":"page"},{"location":"docstrings/#PseudostableRecurrences.AbstractLinearRecurrence","page":"Docstrings","title":"PseudostableRecurrences.AbstractLinearRecurrence","text":"AbstractLinearRecurrence{T} <: AbstractRecurrence{T}\n\nA recurrence where the results are linear w.r.t. the initial conditions. To support pseudostablization, the following methods are needed:\n\nrdiv!(::AbstractLinearRecurrence, x::Number): divides the system by x. It is used to prevent overflow.\nLinearAlgebra.norm(step!(::AbstractLinearRecurrence), Inf): computes the ∞-norm of a step. It is used to get the amplification of the system.\n\n\n\n\n\n","category":"type"},{"location":"docstrings/#PseudostableRecurrences.AbstractLinearRecurrencePlan","page":"Docstrings","title":"PseudostableRecurrences.AbstractLinearRecurrencePlan","text":"AbstractLinearRecurrencePlan <: AbstractRecurrencePlan\n\nA recurrence plan where the results are linear w.r.t. the initial conditions. Pseudostablization algorithm on linear recurrences is implemented.\n\n\n\n\n\n","category":"type"},{"location":"docstrings/#PseudostableRecurrences.AbstractRecurrence","page":"Docstrings","title":"PseudostableRecurrences.AbstractRecurrence","text":"AbstractRecurrence{T}\n\nThe abstract type where a recurrence instance runs. It needs to support the following methods:\n\nstep!(::AbstractRecurrence): step forward the recurrence. Returns nothing if the recurrence terminates, otherwise returns a view of the stepping result.\n\n\n\n\n\n","category":"type"},{"location":"docstrings/#PseudostableRecurrences.AbstractRecurrencePlan","page":"Docstrings","title":"PseudostableRecurrences.AbstractRecurrencePlan","text":"AbstractRecurrencePlan\n\nThe abstract type of recurrence plans. A recurrence plan should include all the information that can perform a recurrence with a precision. It needs to support the following methods:\n\ninit(::AbstractRecurrencePlan; T=Float64, init=:default): generates an AbstractRecurrence object where the actual recurrence runs on. Returns a vector of the view of the initial steps as well.\nT is the underlying type where the precision is implied. \ninit tells how the initial values are generated. Must support :default for the forward recurrence to run. It is also suggested to support :rand for the pseudostablization algorithm.\n\n\n\n\n\n","category":"type"},{"location":"docstrings/#PseudostableRecurrences.StencilRecurrence","page":"Docstrings","title":"PseudostableRecurrences.StencilRecurrence","text":"StencilRecurrence{N,T,S,\n COEF<:NTuple{S,AbstractArray{T,N}},\n TB<:AbstractArray{T,N},}\n (stencil, coef, buffer, slicestart, sliceend, lastind)\n\nProperties\n\nFor coef and slicesupport, tt's suggested to use lazy arrays for performance.\n\nstencil::NTuple{S, CartesianIndex{N}}: The relative index of the stencil. Can contain (0,0) (see coef)\ncoef::COEF<:NTuple{S,AbstractArray{T,N}}: The coefficient associated with each relative index. The one associated with CartesianIndex(0,0) refers to a constant added to that entry. It's suggested to use lazy arrays for performance.\nbuffer::CircularArray{T,N,TB<:AbstractArray{T,N}}: a buffer to store temp results. \nslicestart::MVector{N, Int} and sliceend::MVector{N, Int}: marks the current range of entries to be determined. Technically NTuple{N-1, Int} should work, but Julia doesn't support computed type parameters.\nlastslice::Int: marks the index of the slice where the recurrence terminates.\n\n\n\n\n\n","category":"type"},{"location":"docstrings/#PseudostableRecurrences.StencilRecurrencePlan","page":"Docstrings","title":"PseudostableRecurrences.StencilRecurrencePlan","text":"StencilRecurrencePlan{N, S, COEF<:NTuple{S,Function}, INIT<:Function} <: AbstractLinearRecurrencePlan\n\nProperties\n\nstencil::SVector{S, CartesianIndex{N}}: The relative index of the stencil. Can contain (0,0) (see coef)\ncoef::COEF<:NTuple{S,Function}: The coefficient associated with each relative index. The one associated with CartesianIndex(0,0) refers to a constant added to that entry. The functions should be in the form f(I..., T) where I is the index of the stencil and T is the suggested return type. Coefficients should be at least as accurate as T. Exact-value types such as Irrational, Rational or Integer would do the job, and if that's not possible, BigFloat would work as well.\ninit::INIT<:Function: the function used for initial values. The functions should be in the form f(I..., T) where I is the size of the array and T is the eltype.\nsize::Dims{N}: the size of the whole array.\noffset::NTuple{N,Int}: the very first index where the recurrence starts at.\n\n\n\n\n\n","category":"type"},{"location":"docstrings/#PseudostableRecurrences.precision_shift-Tuple{PseudostableRecurrences.AbstractLinearRecurrencePlan}","page":"Docstrings","title":"PseudostableRecurrences.precision_shift","text":"precision_shift(P::AbstractLinearRecurrencePlan)\n\nEstimates log2 of the amplification of P by performing a full recurrence based on random initial conditions.\n\n\n\n\n\n","category":"method"},{"location":"docstrings/#PseudostableRecurrences.slicetype-Tuple{Any}","page":"Docstrings","title":"PseudostableRecurrences.slicetype","text":"slicetype(T)\n\nGet the type of a slice of T\n\nExamples\n\njulia> using PseudostableRecurrences: slicetype\n\njulia> slicetype(Matrix{Float64})\nVector{Float64} (alias for Array{Float64, 1})\n\njulia> slicetype(UnitRange{Int})\nInt64\n\njulia> slicetype(BitArray{3})\nBitMatrix (alias for BitArray{2})\n\n\n\n\n\n","category":"method"}] }