The internals of Chebpy have been designed to resemble the design structure of Matlab Chebfun. The Chebfun v5 class diagram thus provides a useful map for understanding how the various pieces of Chebpy fit together (diagram courtesy of the Chebfun team, via https://github.com/chebfun/chebfun/wiki/Class-diagram):
At this stage, only a relatively small subset of Matlab Chebfun has been
implemented in Chebpy. In the class diagram above, this consists of all
the classes traced by the path going up from from Chebtech2
(green box
in the bottom right), to Chebfun
(blue box near the top-left). More
explicitly, the following classes currently exist in Chebpy:
Chebfun
(core/chebfun.py)Fun
(core/fun.py)Classicfun
(core/classicfun.py)Bndfun
(core/bndfun.py)Onefun
(core/onefun.py)Smoothfun
(core/smoothfun.py)Chebtech
(core/chebtech.py)Chebtech2
(core/chebtech.py)
Chebpy additionally provides the following classes which do not appear in their present form in Matlab Chebfun:
Interval
(core/utilities.py)Domain
(core/utilities.py)DefaultPrefs
(core/settings.py)
The general rule is that each Chebpy class lives in its own python file.
One can explore the organisation of the library in practice as follows. We describe the three core components with reference to the colours in the above class diagram.
from numpy import *
from chebpy import chebfun
We'll start by creating an identity chebfun on an arbitrary non-unit interval:
x = chebfun('x', [-2, 3])
x
chebfun column (1 smooth piece) interval length endpoint values [ -2, 3] 2 -2 3 vertical scale = 3
This variable is an object of class Chebfun
:
type(x)
chebpy.core.chebfun.Chebfun
Conceptually, Chebfun
objects are defined as a collection (numpy
array) of Fun objects. One can access these via the .funs
attribute, and in this example, since our function is globally smooth,
our chebfun is composed of single Fun:
x.funs
array([Bndfun([-2.0, 3.0], 2)], dtype=object)
Fun is an abstract class, so we don't actually see Fun objects per-se,
but rather objects further down the inheritance tree. Specifically, we
see objects of type BndFun
, denoting a function defined on
a bounded interval.
Here's a more interesting function, this time composed of two Fun
objects and created by inducing a discontinuity via use of the
maximum operator:
f = sin(3*x).maximum(-sin(x))
f
chebfun column (2 smooth pieces) interval length endpoint values [ -2,-2.2e-16] 20 0.91 -5.6e-17 [-2.2e-16, 3] 31 -4.4e-16 0.41 vertical scale = 1 total length = 51
f.funs
array([Bndfun([-2.0, -2.22044604925e-16], 20), Bndfun([-2.22044604925e-16, 3.0], 31)], dtype=object)
The core Chebfun
class constructor accepts iterable collections of
Fun
objects, and the above chebfun can be recreated as follows:
from chebpy.core.chebfun import Chebfun
Chebfun(f.funs)
chebfun column (2 smooth pieces) interval length endpoint values [ -2,-2.2e-16] 20 0.91 -5.6e-17 [-2.2e-16, 3] 31 -4.4e-16 0.41 vertical scale = 1 total length = 51
The Fun objects defining a chebfun must provide a non-overlapping and complete partition of the global approximation interval. Chebpy-specific exceptions will be raised if the user/developer does not properly account for this.
To see this, let's break the second Fun into two sub-pieces (using
fun.restrict
) and attempt various reconstruction
permutations.
from chebpy.core.utilities import Interval
_, a, c = f.breakpoints
b = 1
fun0 = f.funs[0]
fun1 = f.funs[1].restrict(Interval(a,b))
fun2 = f.funs[1].restrict(Interval(b,c))
fun0.plot(linewidth=3); fun1.plot(linewidth=3); fun2.plot(linewidth=3);
So, the following works:
Chebfun([fun0,fun1,fun2])
chebfun column (3 smooth pieces) interval length endpoint values [ -2,-2.2e-16] 20 0.91 -5.6e-17 [-2.2e-16, 1] 31 -1.7e-16 0.14 [ 1, 3] 31 0.14 0.41 vertical scale = 1 total length = 82
But on the other other hand, the following raises an exception:
Chebfun([fun0,fun2])
/chebpy/chebpy/core/utilities.pyc in _sortindex(intervals) 198 raise IntervalOverlap 199 if (d>0).any(): --> 200 raise IntervalGap 201 202 return idx IntervalGap: The supplied Interval objects do not form a complete partition of the approximation interval
So also does this:
Chebfun([fun0,f.funs[1],fun1])
/chebpy/chebpy/core/utilities.pyc in _sortindex(intervals) 196 d = x[1::2] - x[::2] 197 if (d<0).any(): --> 198 raise IntervalOverlap 199 if (d>0).any(): 200 raise IntervalGap IntervalOverlap: The supplied Interval objects overlap
A Fun object is defined by the composition of Onefun
object and an
Interval object. A Onefun
object in Chebpy, as with Matlab Chebfun,
define a set of core approximation behaviour on the unit interval
[-1,1]. The computational mechanics of mapping these operations
to arbitrary intervals [a,b] is managed, in part, by the a
corresponding Interval object.
To illustrate, let's take the first component Fun
from earlier
(which was specifically a Bndfun
):
f.funs[0]
Bndfun([-2.0, -2.22044604925e-16], 20)
The Onefun and Interval object components are accessed as follows:
print f.funs[0].onefun
<Chebtech2{20}>
print f.funs[0].interval
[ -2.00000000e+00 -2.22044605e-16]
Onefun
is an abstract class, so what we actually see instantiated is an
object of type Chebtech2. To see that the Onefun
object is what is
claims to be (a representation defined on the unit-interval), we can
plot it (users will rarely do this in practice, but this can
nevertheless be a useful feature for developers):
f.funs[0].onefun.plot(linewidth=3);
So while the Onefun
attribute determines approximation behaviour, the
interval attribute manages the mapping to and from the approximation
interval to [-1,1]. So for instance, one can evaluate the
interval object at some set of points in [-1,1] and obtain these
values mapped to [a,b]:
f.funs[0].interval(linspace(-1,1,11))
array([ -2.00000000e+00, -1.80000000e+00, -1.60000000e+00, -1.40000000e+00, -1.20000000e+00, -1.00000000e+00, -8.00000000e-01, -6.00000000e-01, -4.00000000e-01, -2.00000000e-01, -2.22044605e-16])