[ANN] CompactBases.jl

Introduction

I’m happy to announce the first release of CompactBases.jl, a library for easily approximating functions and matrix representations of linear operators in various bases where the basis functions have compact support, which leads to highly structured matrices which are non-zero only close to the diagonal (for some definition of close). At the moment, these bases are offered:

  • various finite-differences (only three-point stencils as of now ⇒ tridiagonal matrices, but more will come; local operators diagonal),

  • finite-elements, discrete-variable representation (FE-DVR, almost-block-diagonal matrices, local operators diagonal),

  • B-splines (non-orthogonal basis functions ⇒ all operators banded)

A while back, I approached @dlfivefifty with the question as to how one would structure a code such that one could easily swap out the underlying basis with minimal effort. It turned out that he had been thinking along those lines as well, and this led to QuasiArrays.jl and ContinuumArrays.jl, which consider the basis as a quasi-matrix, where the first axis is continuous on an interval, and the second axis is discrete, corresponding to the number of basis functions. A companion package for global bases, i.e. orthogonal polynomials, is OrthogonalPolynomialsQuasi.jl.

Simple usage

Install and load as usual:

julia> ]add CompactBases

julia> using CompactBases

We start out by constructing the basis. In principle, this is the only step that differs between different kinds of bases, ideally, the rest should be exactly the same.

For instance, we can easily construct simple finite-differences, FE-DVR, and B-spline bases like so:

julia> fd = FiniteDifferences(9, 0.1)
Finite differences basis {Float64} on 0.0..1.0 with 9 points spaced by Δx = 0.1

julia> fedvr = FEDVR(range(0, stop=1, length=3), 6)
FEDVR{Float64} basis with 2 elements on 0.0..1.0

julia> bsplines = BSpline(LinearKnotSet(4, 0.0, 1.0, 5))
BSpline{Float64} basis with LinearKnotSet(Float64) of order k = 4 (cubic) on 0.0..1.0 (5 intervals)

We can investigate the axes of the different bases and see that they are indeed continuous along the first dimension, and discrete along the second:

julia> axes(fd)
(Inclusion(0.0..1.0), Base.OneTo(9))

julia> axes(fedvr)
(Inclusion(0.0..1.0), Base.OneTo(11))

julia> axes(bsplines)
(Inclusion(0.0..1.0), Base.OneTo(8))

If we wish to construct the matrix representation of the second derivative in the different bases, we do this quite naturally using the language of linear operators:

julia> x = axes(fd, 1)
Inclusion(0.0..1.0)

julia> D = Derivative(x)
Derivative{Float64,IntervalSets.Interval{:closed,:closed,Float64}}(Inclusion(0.0..1.0))

(where D acts along the continuous dimension, which happens to be the same for all bases in this example)

julia> fd' * D' * D * fd
9×9 LinearAlgebra.SymTridiagonal{Float64,Array{Float64,1}}:
 -200.0   100.0      ⋅       ⋅       ⋅       ⋅       ⋅       ⋅       ⋅
  100.0  -200.0   100.0      ⋅       ⋅       ⋅       ⋅       ⋅       ⋅
     ⋅    100.0  -200.0   100.0      ⋅       ⋅       ⋅       ⋅       ⋅
     ⋅       ⋅    100.0  -200.0   100.0      ⋅       ⋅       ⋅       ⋅
     ⋅       ⋅       ⋅    100.0  -200.0   100.0      ⋅       ⋅       ⋅
     ⋅       ⋅       ⋅       ⋅    100.0  -200.0   100.0      ⋅       ⋅
     ⋅       ⋅       ⋅       ⋅       ⋅    100.0  -200.0   100.0      ⋅
     ⋅       ⋅       ⋅       ⋅       ⋅       ⋅    100.0  -200.0   100.0
     ⋅       ⋅       ⋅       ⋅       ⋅       ⋅       ⋅    100.0  -200.0
julia> fedvr' * D' * D * fedvr
3×3-blocked 11×11 BlockBandedMatrices.BlockSkylineMatrix{Float64,Array{Float64,1},BlockBandedMatrices.BlockSkylineSizes{Tuple{BlockArrays.BlockedUnitRange{Array{Int64,1}},BlockArrays.BlockedUnitRange{Array{Int64,1}}},Array{Int64,1},Array{Int64,1},BandedMatrices.BandedMatrix{Int64,Array{Int64,2},Base.OneTo{Int64}},Array{Int64,1}}}:
 -1240.0       579.721     -62.6353    19.3726   -10.2715  │      5.65685  │      ⋅          ⋅          ⋅          ⋅            ⋅
   579.721    -385.83      138.991    -29.0091    13.668   │     -7.26304  │      ⋅          ⋅          ⋅          ⋅            ⋅
   -62.6353    138.991    -174.17      98.332    -29.0091  │     13.6985   │      ⋅          ⋅          ⋅          ⋅            ⋅
    19.3726    -29.0091     98.332   -174.17     138.991   │    -44.2898   │      ⋅          ⋅          ⋅          ⋅            ⋅
   -10.2715     13.668     -29.0091   138.991   -385.83    │    409.924    │      ⋅          ⋅          ⋅          ⋅            ⋅
 ──────────────────────────────────────────────────────────┼───────────────┼──────────────────────────────────────────────────────────
     5.65685    -7.26304    13.6985   -44.2898   409.924   │  -1240.0      │   409.924    -44.2898    13.6985    -7.26304      5.65685
 ──────────────────────────────────────────────────────────┼───────────────┼──────────────────────────────────────────────────────────
      ⋅           ⋅           ⋅          ⋅          ⋅      │    409.924    │  -385.83     138.991    -29.0091    13.668      -10.2715
      ⋅           ⋅           ⋅          ⋅          ⋅      │    -44.2898   │   138.991   -174.17      98.332    -29.0091      19.3726
      ⋅           ⋅           ⋅          ⋅          ⋅      │     13.6985   │   -29.0091    98.332   -174.17     138.991      -62.6353
      ⋅           ⋅           ⋅          ⋅          ⋅      │     -7.26304  │    13.668    -29.0091   138.991   -385.83       579.721
      ⋅           ⋅           ⋅          ⋅          ⋅      │      5.65685  │   -10.2715    19.3726   -62.6353   579.721    -1240.0
julia> bsplines' * D' * D * bsplines
8×8 BandedMatrices.BandedMatrix{Float64,Array{Float64,2},Base.OneTo{Int64}}:
 -9.0     6.375    2.375       0.25        ⋅          ⋅           ⋅        ⋅
  6.375  -7.5     -0.1875      1.25       0.0625      ⋅           ⋅        ⋅
  2.375  -0.1875  -3.375       0.166667   0.979167   0.0416667    ⋅        ⋅
  0.25    1.25     0.166667   -3.33333    0.625      0.979167    0.0625    ⋅
   ⋅      0.0625   0.979167    0.625     -3.33333    0.166667    1.25     0.25
   ⋅       ⋅       0.0416667   0.979167   0.166667  -3.375      -0.1875   2.375
   ⋅       ⋅        ⋅          0.0625     1.25      -0.1875     -7.5      6.375
   ⋅       ⋅        ⋅           ⋅         0.25       2.375       6.375   -9.0

Finally, it is very easy to evaluate the basis functions at arbitrary points, which is useful for function reconstruction (or simply plotting):

julia> xx = range(0, stop=1, length=1000)
0.0:0.001001001001001001:1.0

julia> χfd = fd[xx, :];

julia> χfedvr = fedvr[xx, :];

julia> χbsplines = bsplines[xx, :];

Many more features are available, most of which are described in the documentation.

Known limitations

  • Anything but Dirichlet-0 boundary conditions are awkward at the moment, since we have not yet figured out what the interface should look like. Being initially geared towards atoms, i.e. spherical symmetry, there are some hacks for particular situations arising therein.

  • Slow compile time.

  • Documentation in a bit of disarray, since the finite-differences, FE-DVR, and B-splines started out as separate packages, but nothing should be incorrect, per se. If you find anything, please let me know.

21 Likes