A pluggable grid-transcription toolkit in Julia. Turns differentiation and integration into matrix
operations — differentiation matrices D/D2 and quadrature weights ws on a grid of nodes taus —
so that a continuous problem (a BVP, a calculus-of-variations problem) can be discretized into a linear
system, a nonlinear system, or an NLP. Built around a single parametric type, TranscriptionSolver{R,N,SK},
with four pluggable transcription kinds. Defines no problem types of its own — see VariationalProblems.jl
for a consumer.
- Four transcription kinds (
solver_kindsymbol)::FiniteDiff(uniform grid, 2nd/4th-order central differences + composite-trapezoidal quadrature),:Chebyshev(Clenshaw-Curtis pseudospectral),:LGL(Legendre-Gauss-Lobatto pseudospectral),:LGR(Legendre-Gauss-Radau pseudospectral) - Domain-agnostic operators: nodes/
D/D2/wsare built once on the reference domain[-1,1]; rescale to any physical[x0,xf]via 3-arg accessor methods, no reconstruction needed - Runtime factory:
new_transcription_solver(Nx; solver_kind, kwargs...)resolvessolver_kindto its concrete type by naming convention — adding a new kind never touches the factory - Pseudospectral kinds (
:Chebyshev,:LGL,:LGR) are exact for polynomials up to degreeNx-1
Requires Julia 1.12.6. Direct dependencies: LegendreTools, StaticArrays, SparseArrays,
PrecompileTools (see Dependencies).
using Pkg
Pkg.add(url="https://github.com/raholanda/TranscriptionSolvers.jl")using TranscriptionSolvers
Nx = 8
solver = new_transcription_solver(Nx; solver_kind=:Chebyshev)
# also available: :FiniteDiff (kwarg order=2 or 4, default 4), :LGL, :LGRusing LinearAlgebra
taus = grid_points(solver) # SVector{Nx}, nodes on [-1,1]
D, D2 = differential_matrix(solver) # SparseMatrixCSC, N×N
ws = quadrature_weights(solver) # Vector, length N
y = taus .^ 2
D * y ≈ 2taus # first derivative
D2 * y ≈ fill(2.0, Nx) # second derivative (exact: pseudospectral, degree 2 ≤ Nx-1)
dot(ws, y) # ∫_{-1}^{1} τ² dτx0, xf = 0.0, 10.0
xs = grid_points(solver, x0, xf) # nodes rescaled onto [x0,xf]
D_phys, D2_phys = differential_matrix(solver, x0, xf) # scaled by 2/Tspan, 4/Tspan^2
ws_phys = quadrature_weights(solver, x0, xf) # scaled by Tspan/2ys, yps, ypps = transcribe(sin, x0, xf, solver) # y, y', y'' at the physical-domain nodesThree scripts under examples/ explore D/D2 accuracy for progressively harder test functions.
Plotting uses CairoMakie, kept out of the main package's dependencies via a separate
examples/Project.toml:
derivative_comparison.jl— analytical vs. numericalD/D2across all four kinds on smooth test functions (polynomial,sin,exp, Runge), with a tiled overlay plot and a convergence-with-Nxstudy showing pseudospectral (spectral) vs.:FiniteDiff(algebraic) error decay.wavenumber_resolution.jl— differentiation accuracy vs. wavenumber forf_k(x) = cos(kπx/(2N)),k = 0:N-1,N = Nx: sweeps from fully-resolved down to the grid's own Nyquist-like resolution limit.nonsmooth_functions.jl— differentiation accuracy as smoothness degrades: aC¹kink (x|x|), aC⁰kink (|x|), and a genuine jump (sign(x), visualized via the Gibbs phenomenon inD*yrather than an error table, since no classical derivative exists there).
# one-time setup
using Pkg
Pkg.activate("examples")
Pkg.develop(path=".")
Pkg.add("CairoMakie")julia --project=examples examples/derivative_comparison.jlbash scripts/validate.shExpected output:
=== TranscriptionSolvers: precompile ===
OK
=== TranscriptionSolvers: tests ===
Test Summary: | Pass Total Time
FiniteDiff | 36 36 1.0s
Test Summary: | Pass Total Time
Chebyshev | 32 32 0.1s
Test Summary: | Pass Total Time
LGL | 32 32 1.4s
Test Summary: | Pass Total Time
LGR | 33 33 0.1s
scripts/validate.sh skips the JET lint pass (see Commands); run
julia --project=. -e 'using Pkg; Pkg.test()' for the full suite including JET.
| Kind | Method | D/D2 from |
Notes |
|---|---|---|---|
:FiniteDiff |
Uniform grid | Central differences (order=2|4) |
Ascending taus; composite-trapezoidal ws |
:Chebyshev |
Clenshaw-Curtis pseudospectral | Lagrange-polynomial differentiation | Descending taus (native, not canonicalized) |
:LGL |
Legendre-Gauss-Lobatto pseudospectral | Legendre-polynomial differentiation | Ascending taus |
:LGR |
Legendre-Gauss-Radau pseudospectral | Legendre-polynomial differentiation | Ascending taus; fixed at -1 only (taus[end] < 1) |
solver_kind is resolved to its tag type (:FiniteDiff → FiniteDiffKind) by naming convention, not a
hardcoded dispatch table — see Module Structure.
TranscriptionSolvers
├── core.jl — TranscriptionSolver{R,N,SK} struct, accessors, new_transcription_solver factory
├── FiniteDiff.jl — FiniteDiffKind, central-difference D/D2, trapezoidal ws
├── Chebyshev.jl — ChebyshevKind, Clenshaw-Curtis nodes/quadrature
├── LGL.jl — LGLKind, Legendre-Gauss-Lobatto nodes/quadrature
├── LGR.jl — LGRKind, Legendre-Gauss-Radau nodes/quadrature
└── precompile.jl — @compile_workload warmup for all kinds
Each kind lives in its own file/module and exports its ...Kind tag type plus a
new_solver(::Type{<:SolverKind}, Nx; kwargs...) constructor. core.jl never imports the kind modules
or hardcodes an if/elseif over them — to add a kind, add one new file/module; core.jl itself never
changes. Only the tag types are re-exported at the top level (TranscriptionSolvers.jl), so every kind
module can reuse the identical internal constructor name new_solver without collision.
LegendreTools.jl— Legendre polynomial evaluation and Gauss-type node/weight generation, used by:LGL/:LGRStaticArrays.jl—SVector{N}fortausSparseArrays.jl—SparseMatrixCSCforD/D2PrecompileTools.jl— workload precompilation
:Chebyshev's native node ordering is descending, unlike:FiniteDiff/:LGL's ascending order; this is not canonicalized to a uniform convention across kinds.
MIT © 2026 raholanda
Preferred citation:
raholanda (2026). TranscriptionSolvers.jl (v1.0.0-DEV). GitHub. https://github.com/raholanda/TranscriptionSolvers.jl
Rodrigo Holanda Role: maintainer / developer Email: rodrigo.holanda.21@gmail.com
- Huntington, Geoffrey & Rao, Anil. (2008). Comparison of Global and Local Collocation Methods for Optimal Control. Journal of Guidance, Control, and Dynamics, 31, 432-436. doi:10.2514/1.30915
- Trefethen, Lloyd N. (2000). Spectral Methods in MATLAB. SIAM. doi:10.1137/1.9780898719598
- Davis, P. J., & Rabinowitz, P. (1984). Methods of Numerical Integration (2nd ed.). Academic Press.