On this page

NumericsLib

Numerical methods domain for Free Pascal — root finding, quadrature, ODE solvers, and interpolation.

For the 1.7 adaptive, vector, fitting, derivative, and reusable-interpolant APIs, see the numerical modelling guide. The APIs on this page remain the concise compatibility and teaching path.

Depends on: MathBase

Learning routes

Beginner route

Copy and run the double-real numerical quick start. It prints checked root, integral, ODE, and spline values using plain callbacks and allocating result records. Start with Brent for a bracketed scalar root.

Common tasks and algorithm choice

Task Start with Contract or failure guidance
Bracketed scalar root Brent Root finding
Smooth finite integral SimpsonRule for teaching, IntegrateAdaptive for diagnostics Modelling selection
Introductory scalar ODE RK4Solve ODE solvers
Interpolation between knots CubicSplineBuild Interpolation
Fitting/vector equations/adaptive ODE TModellingKit Numerical modelling

Advanced route

Run example 17 for reusable interpolants, fitting, bounded options, event-aware adaptive ODEs, and explicit statuses. It retains the double-real array path; coefficient/result arrays are allocated explicitly and no private dense conversion is needed.

Units

Unit File Class
NumericsLib.Numerics NumericsLib.Numerics.pas TNumericsKit

All methods are class-static — no instantiation required:


Root := TNumericsKit.Brent(@F, 1.0, 2.0);

---

Function Types


TScalarFunc = function(X: Double): Double;        // f(x) — used by root finders and integrators

TODEFunc    = function(T, Y: Double): Double;     // dy/dt = f(t,y) — used by ODE solvers

Result Records


TODESolution = record

  T: TDoubleArray;   // independent variable values (length N+1)

  Y: TDoubleArray;   // solution values             (length N+1)

end;



TCubicSpline = record

  X: TDoubleArray;   // knot x-values (sorted ascending)

  A, B, C, D: TDoubleArray;  // polynomial coefficients per interval

end;

---

Root Finding

All methods find x such that f(x) ≈ 0. Tolerance parameters control convergence. Each scalar-returning method has a corresponding ...Result overload returning:


TRootResult = record

  Root: Double;

  Residual: Double;

  Iterations: Integer;

  Converged: Boolean;

  Status: TIterationStatus;

end;

Use the detailed result when iteration counts or explicit convergence reporting matter. Scalar methods raise ENumericsConvergenceError when their iteration limit is exhausted. Status distinguishes convergence, numerical breakdown, and iteration exhaustion; Converged remains for source compatibility.

Bisection


class function Bisection(F: TScalarFunc; A, B: Double;

  Tol: Double = 1E-10; MaxIter: Integer = 100): Double;

class function BisectionResult(F: TScalarFunc; A, B: Double;

  Tol: Double = 1E-10; MaxIter: Integer = 100): TRootResult;

Guaranteed convergence for bracketed roots. Requires opposite endpoint signs, unless either endpoint is itself a root, in which case it is returned immediately. Raises EInvalidArgument if the bracket is invalid.

Best for: when robustness matters more than speed; verifying root existence.

NewtonRaphson


class function NewtonRaphson(F, DF: TScalarFunc; X0: Double;

  Tol: Double = 1E-10; MaxIter: Integer = 100): Double;

class function NewtonRaphsonResult(F, DF: TScalarFunc; X0: Double;

  Tol: Double = 1E-10; MaxIter: Integer = 100): TRootResult;

Quadratic convergence near the root using f and its derivative df/dx. Raises EInvalidArgument if df/dx is near zero (flat region).

Best for: smooth functions where the derivative is cheap to evaluate.

Brent


class function Brent(F: TScalarFunc; A, B: Double;

  Tol: Double = 1E-10; MaxIter: Integer = 100): Double;

class function BrentResult(F: TScalarFunc; A, B: Double;

  Tol: Double = 1E-10; MaxIter: Integer = 100): TRootResult;

Hybrid method combining bisection, secant, and inverse-quadratic interpolation. Superlinear convergence in practice; falls back to bisection when necessary. Raises EInvalidArgument if f(A) · f(B) > 0.

Best for: general use — the recommended default solver.

Secant


class function Secant(F: TScalarFunc; X0, X1: Double;

  Tol: Double = 1E-10; MaxIter: Integer = 100): Double;

class function SecantResult(F: TScalarFunc; X0, X1: Double;

  Tol: Double = 1E-10; MaxIter: Integer = 100): TRootResult;

Derivative-free quasi-Newton using two initial guesses. Superlinear convergence but may diverge without a good bracket.

Best for: when a derivative is unavailable and you have a good initial estimate.

---

Numerical Integration

TrapezoidalRule


class function TrapezoidalRule(F: TScalarFunc; A, B: Double;

  N: Integer = 1000): Double;

Composite trapezoidal rule with N sub-intervals. Error O(h²). Exact for linear functions.

SimpsonRule


class function SimpsonRule(F: TScalarFunc; A, B: Double;

  N: Integer = 1000): Double;

Composite Simpson's 1/3 rule with N sub-intervals. Values below 2 are rejected, and an odd N is incremented to the next even value. Error O(h⁴). Exact for polynomials of degree ≤ 3.

GaussLegendre5


class function GaussLegendre5(F: TScalarFunc; A, B: Double): Double;

5-point Gauss-Legendre quadrature on [A, B]. Exact for polynomials of degree ≤ 9. Very accurate for smooth functions with only 5 function evaluations.

Comparison:

Method Order Evaluations per call Notes
TrapezoidalRule O(h²) N+1 Robust, slow
SimpsonRule O(h⁴) N+1 Good default
GaussLegendre5 exact deg 9 5 Best for smooth f

---

ODE Solvers

Solve dy/dt = f(t, y), y(t₀) = y₀ over the interval [T0, T1].

Single Steps


class function EulerStep(F: TODEFunc; T0, Y0, H: Double): Double;

class function RK4Step(F: TODEFunc; T0, Y0, H: Double): Double;

Return the solution at T0 + H given the current state (T0, Y0).

Full Solvers


class function EulerSolve(F: TODEFunc; T0, Y0, T1: Double; N: Integer): TODESolution;

class function RK4Solve(F: TODEFunc; T0, Y0, T1: Double; N: Integer): TODESolution;

Integrate from T0 to T1 using N uniform steps of size h = (T1−T0)/N. Return a TODESolution with T and Y arrays of length N+1.

Accuracy comparison for dy/dt = y, y(0) = 1 over [0, 1] (exact: e ≈ 2.71828):

Method Steps Typical error
EulerSolve 10 000 ~1×10⁻⁴
RK4Solve 100 ~1×10⁻⁷
RK4Solve 1 000 ~1×10⁻¹¹

---

Interpolation

LinearInterp


class function LinearInterp(const XKnots, YKnots: TDoubleArray; X: Double): Double;

Piecewise linear interpolation between sorted knots using binary search. Clamps to endpoint values outside the knot range.

XKnots must be strictly increasing and the two arrays must have equal, non-zero length. Array lengths, finite values, ordering, and duplicate knots are validated.

LagrangeInterp


class function LagrangeInterp(const XKnots, YKnots: TDoubleArray; X: Double): Double;

Global Lagrange polynomial interpolation through all N knots. Exact at every knot.

The arrays must have equal, non-zero length and finite values, and X knots must be distinct. These conditions are validated before interpolation.

> Warning: ill-conditioned for N > ~10 (Runge phenomenon). Prefer CubicSplineBuild for larger datasets.

CubicSplineBuild / CubicSplineEval


class function CubicSplineBuild(const XKnots, YKnots: TDoubleArray): TCubicSpline;

class function CubicSplineEval(const S: TCubicSpline; X: Double): Double;

Natural cubic spline (zero second-derivative boundary conditions) solved via the Thomas tridiagonal algorithm. Exact at every knot; smooth C² between knots. Clamps to endpoint values outside the knot range.

Spline construction requires equal-length arrays with at least two strictly increasing finite knots. Dimensions, ordering, duplicates, and finite values are validated during construction; evaluation also validates spline dimensions and its finite query value.

---

Quick Start


uses MathBase.SharedTypes, NumericsLib.Numerics;



{ --- functions for root finding and integration --- }

function F(X: Double): Double; begin Result := X*X - 2; end;

function DF(X: Double): Double; begin Result := 2*X; end;

function G(X: Double): Double; begin Result := X*X; end;



{ --- ODE: dy/dt = y  →  exact solution y(t) = e^t --- }

function DYDT(T, Y: Double): Double; begin Result := Y; end;



var

  Root, Integral: Double;

  Sol: TODESolution;

  XK, YK: TDoubleArray;

  Spline: TCubicSpline;

begin

  { Root: Brent's method — recommended default }

  Root := TNumericsKit.Brent(@F, 1.0, 2.0);

  Writeln('sqrt(2) = ', Root:0:10);      // 1.4142135624



  { Root: Newton-Raphson — fast with derivative }

  Root := TNumericsKit.NewtonRaphson(@F, @DF, 1.5);

  Writeln('sqrt(2) = ', Root:0:10);



  { Integration: ∫₀¹ x² dx = 0.3333... }

  Integral := TNumericsKit.SimpsonRule(@G, 0, 1, 1000);

  Writeln('Integral = ', Integral:0:6);  // 0.333333



  { ODE: RK4 is accurate with far fewer steps than Euler }

  Sol := TNumericsKit.RK4Solve(@DYDT, 0, 1.0, 1.0, 100);

  Writeln('y(1) = e = ', Sol.Y[100]:0:8);  // 2.71828183



  { Spline interpolation through y = x² at integer knots }

  XK := TDoubleArray.Create(0, 1, 2, 3, 4);

  YK := TDoubleArray.Create(0, 1, 4, 9, 16);

  Spline := TNumericsKit.CubicSplineBuild(XK, YK);

  Writeln('Spline(1.5) = ', TNumericsKit.CubicSplineEval(Spline, 1.5):0:4);  // ≈ 2.2321

end.

Expected output:


sqrt(2) = 1.4142135624

sqrt(2) = 1.4142135624

Integral = 0.333333

y(1) = e = 2.71828183

Spline(1.5) = 2.2321

---

Error Handling

Condition Exception
Bisection/Brent: f(A) and f(B) same sign EInvalidArgument
NewtonRaphson: derivative near zero EInvalidArgument
Secant: f(x₁) ≈ f(x₀) (division by near-zero) EInvalidArgument
TrapezoidalRule: N < 1 EInvalidArgument
SimpsonRule: N < 2 EInvalidArgument
EulerSolve / RK4Solve: N < 1 EInvalidArgument
LinearInterp / LagrangeInterp: empty arrays EInvalidArgument
Interpolation: X/Y array lengths differ EInvalidArgument
CubicSplineBuild: fewer than 2 knots EInvalidArgument
CubicSplineEval: empty spline EInvalidArgument
Scalar root wrapper exhausts MaxIter ENumericsConvergenceError
Nil callbacks, non-finite values/results, or non-positive controls EInvalidArgument

---

Design Notes

  • All functions are class-static — pass function pointers, not method pointers. In FPC, unit-level function declarations are compatible with TScalarFunc / TODEFunc.
  • The cubic spline uses natural boundary conditions (S''(x₀) = S''(xₙ) = 0). This is the standard choice when no derivative information is available at the boundaries.
  • GaussLegendre5 performs only 5 function evaluations regardless of the smoothness of f. For oscillatory functions, use SimpsonRule with a large N instead.
  • TODESolution arrays are zero-indexed: Sol.T[0] = T0, Sol.T[N] = T1.
  • Root ...Result methods expose root, residual, iteration count, and a convergence flag. Scalar wrappers raise ENumericsConvergenceError on exhaustion.
  • Root, integration, ODE, and interpolation entry points validate callbacks, finite inputs and callback outputs, positive controls, array dimensions, and strictly increasing/distinct interpolation knots as applicable.
  • Bracketed solvers return endpoint roots immediately and reject brackets that contain no sign change.