Architecture¶
This page describes how the chebpy source is organised: the two runtime type
hierarchies, the layered module import graph and its direction rule, where the
Chebfun implementation lives, and which files this repository owns versus
those synced from the Rhiza template.
Type hierarchies¶
chebpy has two abstract hierarchies that meet in Classicfun. One
describes a function on the reference interval [-1, 1]; the other places such
a function onto an arbitrary interval [a, b].
Onefun (abstract) a function on the reference interval [-1, 1]
└─ Smoothfun (abstract)
├─ Chebtech Chebyshev series (aperiodic functions)
└─ Trigtech Fourier series (periodic functions)
Fun (abstract)
└─ Classicfun (abstract) maps [-1, 1] ↔ [a, b]; wraps one Onefun
├─ Bndfun bounded interval, affine map
├─ CompactFun (semi-)infinite interval via numerical-support truncation
└─ Singfun endpoint singularities via a non-affine clustering map
A Classicfun holds an Onefun (its representation on [-1, 1]) together
with the interval or map that places it on [a, b]. The choice of Onefun
subclass is orthogonal to the choice of Classicfun subclass: e.g. a Bndfun
usually wraps a Chebtech, but a periodic piece wraps a Trigtech.
The Chebfun container¶
Chebfun is the user-facing class. It is not part of the Fun hierarchy;
it is a container holding an ordered array of Fun pieces over a Domain
(one piece per sub-interval between breakpoints). Piecewise operations
(arithmetic, calculus, root-finding, conv, …) fan out over the pieces.
Chebfun ──holds──▶ [ Fun, Fun, … ] one piece per sub-interval
each Fun ──holds──▶ Onefun on [-1, 1]
Module layering¶
Modules are organised into layers. A module imports only from strictly lower layers (plus, in a few noted cases, siblings in its own layer). Lower layers never import upper layers at module scope — see Import direction for the one audited exception.
L7 High-level API / apps api · quasimatrix · gpr
L6 Container + impl chebfun · _convolution · _pointwise ·
_singular_construction · _ufuncs
L5 Classicfun pieces bndfun · compactfun · singfun
L4 Interval-mapped base classicfun
L3 Reference reps (Onefun) chebtech · trigtech
L2 Numerical kernels algorithms · chebyshev
L1 Primitives utilities · plotting · smoothfun
L0 Foundations settings · exceptions · decorators · maps · fun · onefun
chebpy/__init__.py sits above everything as the package facade, re-exporting
the public surface (chebfun, chebpts, pwc, trigfun, CompactFun,
Singfun, Trigtech, Quasimatrix, gpr, …).
Selected module dependencies (top-level imports):
| Module | Imports |
|---|---|
utilities |
decorators, exceptions, settings |
algorithms |
decorators, settings, utilities |
chebtech / trigtech |
algorithms*/decorators, plotting, settings, smoothfun, utilities |
classicfun |
chebtech, trigtech, fun, decorators, exceptions, plotting, settings, utilities |
bndfun / compactfun / singfun |
classicfun (+ exceptions/settings/utilities/maps) |
chebfun |
bndfun, _ufuncs, decorators, exceptions, plotting, settings, utilities |
api |
algorithms, bndfun, chebfun, settings, utilities |
gpr |
algorithms, chebfun, quasimatrix, settings |
*trigtech does not import algorithms at module scope; it defers that import
(see below).
The Chebfun implementation modules¶
Chebfun is large, so its self-contained algorithms live in dedicated modules.
The public Chebfun methods are thin, documented wrappers that delegate:
| Module | Provides | Fronted by |
|---|---|---|
_convolution.py |
Hale–Townsend / Gauss–Legendre convolution | Chebfun.conv |
_pointwise.py |
root-splitting step ops | Chebfun.abs, sign, ceil, floor, maximum, minimum |
_singular_construction.py |
endpoint-singularity piece construction | the sing= Chebfun constructors |
_ufuncs.py |
registers elementwise NumPy ufunc methods | Chebfun.sin, exp, … |
These modules operate on a Chebfun passed in as an argument (constructing
results via f.__class__(...)) and reference Chebfun only under
TYPE_CHECKING, so importing them never creates a runtime cycle.
Import direction and deferred imports¶
The layering rule is enforced by keeping upward references out of module scope. Function-local (deferred) imports are used for three reasons:
- Typing only —
_convolution,_pointwise,_ufuncsimportChebfununderTYPE_CHECKING. - Breaking sibling cycles — e.g.
singfun/compactfunimportbndfuninsiderestrict;trigtechimportschebtechinsideroots. - Lazy/optional heavy paths —
chebfunimports its implementation modules (_convolution,_pointwise,_singular_construction) inside the relevant methods.
Known exception (tech debt)¶
utilities.generate_funs() contains a function-local
from .compactfun import CompactFun — an L1 module reaching up to L5. This
is the one genuine layering back-edge (a hidden utilities ↔ compactfun
cycle), deferred so it does not manifest at import time. It is tracked in
issue #417; the intended fix is
to inject the compact constructor rather than import it here.
Source ownership: this repo vs Rhiza¶
This project syncs its development infrastructure from the
jebel-quant/rhiza template. The
files: block of .rhiza/template.lock is the authoritative list of
template-owned paths. It is regenerated by every sync, so read it there rather
than trusting a summary kept in prose — a hand-maintained copy of that list goes
stale the first time the template adds or drops a file.
Do not edit those files in place: change them upstream in Rhiza (or via
.rhiza/template.yml) and re-sync.
Everything outside that block — and outside .rhiza/ itself, which is the
template's own config and fixtures — is owned here and may be edited freely:
src/chebpy/**, pyproject.toml, README.md, mkdocs.yml, the project
notebooks, and this repo's own tests and docs. Note that the block does reach
into tests/ and docs/, so check it before assuming a file there is ours.
Two seams exist for repo-specific settings that would otherwise tempt an edit to a template-owned file:
- The root
Makefileis template-owned, but it is only a shim overuv run rhiza-task <task>. Repo-specificmakevariables and one-off targets belong in an uncommittedlocal.mk, which the shim-includes. - Task settings that differ from the CLI defaults (the coverage floor, extra
mkdocs packages, ...) live in
[tool.rhiza-task]inpyproject.toml, which is owned here.
When scoring or reviewing the repo, attribute gaps in Rhiza-managed files to the template (fix upstream), and gaps in the owned files to this project.