This guide explains how composite calculus works internally and how to implement it yourself.
- Core Data Structure
- Dimensional System
- Arithmetic Operations
- How Derivatives Emerge
- Transcendental Functions
- Integration
- Optimization Strategies
Composite numbers are represented as a sparse dictionary mapping dimensions to coefficients:
class Composite:
def __init__(self, coefficients=None):
if coefficients is None:
self.c = {}
elif isinstance(coefficients, (int, float)):
# Scalar: store at dimension 0
self.c = {0: float(coefficients)} if coefficients != 0 else {}
elif isinstance(coefficients, dict):
# Filter out near-zero coefficients
self.c = {k: v for k, v in coefficients.items() if abs(v) > 1e-15}Why sparse? Most composite numbers have only a few non-zero dimensions. A polynomial x³ evaluated at x = 2 + h produces only 4 dimensions: {0: 8, -1: 12, -2: 6, -3: 1}.
Memory: O(k) where k = number of non-zero dimensions, typically 3-10.
| Dimension | Meaning | Example |
|---|---|---|
| 0 | Real value | {0: 5} = 5 |
| -1 | First infinitesimal | {-1: 1} = h |
| -2 | Second infinitesimal | {-2: 1} = h² |
| -3 | Third infinitesimal | {-3: 1} = h³ |
| +1 | Infinity | {1: 1} = ∞ |
Derivatives naturally appear at negative dimensions because:
- f(x + h) = f(x) + f'(x)·h + f''(x)·h²/2! + ...
- h → dimension -1
- h² → dimension -2
- Coefficient at -1 is the derivative (after factorial rescaling)
ZERO = Composite({-1: 1.0}) # |1|₋₁This is the structural zero that enables reversible multiplication:
- 5 × ZERO =
{-1: 5}(not 0!) - The coefficient 5 is preserved at dimension -1
Coefficients at matching dimensions are added:
def __add__(self, other):
result = dict(self.c) # Copy self
for dim, coeff in other.c.items():
result[dim] = result.get(dim, 0) + coeff
return Composite(result)Example:
{0: 3, -1: 2} (3 + 2h)
+ {0: 1, -1: 4} (1 + 4h)
= {0: 4, -1: 6} (4 + 6h)
Dimensions add, coefficients multiply (convolution):
def __mul__(self, other):
result = {}
for d1, c1 in self.c.items():
for d2, c2 in other.c.items():
dim = d1 + d2 # Dimensions add!
result[dim] = result.get(dim, 0) + c1 * c2
return Composite(result)Why this works:
(a + bh)(c + dh) = ac + (ad + bc)h + bdh²
Dimension view:
a at dim 0, b at dim -1
c at dim 0, d at dim -1
Products:
a×c → dim 0+0=0, coeff a*c
a×d → dim 0+(-1)=-1, coeff a*d
b×c → dim (-1)+0=-1, coeff b*c
b×d → dim (-1)+(-1)=-2, coeff b*d
This IS the Leibniz product rule! The convolution formula for polynomial multiplication is identical to the formula for derivatives of products.
For single-term divisors (fast path):
def __truediv__(self, other):
if len(other.c) == 1:
div_dim, div_coeff = list(other.c.items())[0]
result = {}
for dim, coeff in self.c.items():
result[dim - div_dim] = coeff / div_coeff # Dimensions subtract
return Composite(result)For multi-term divisors, use polynomial long division.
Why it works:
(5h) / h = 5 (dimensions: -1 - (-1) = 0)
(12h²) / (2h) = 6h (dimensions: -2 - (-1) = -1)
When you evaluate f(x + h):
x = R(3) + ZERO # 3 + h
result = x**2 # (3 + h)²You're computing the Taylor series:
(3 + h)² = 9 + 6h + h²
In composite representation:
{0: 9, -1: 6, -2: 1}
The coefficient at dimension -n gives f⁽ⁿ⁾(a) / n!:
def d(self, n=1):
"""Extract nth derivative"""
return self.c.get(-n, 0.0) * math.factorial(n)Why factorial? Taylor series has factorials:
f(a+h) = f(a) + f'(a)h/1! + f''(a)h²/2! + f'''(a)h³/3! + ...
So:
- Coefficient at -1 = f'(a) / 1! → multiply by 1! to get f'(a)
- Coefficient at -2 = f''(a) / 2! → multiply by 2! to get f''(a)
Since we can't "multiply sin(x) by h" directly, we expand sin as a Taylor series:
def sin(x, terms=12):
if isinstance(x, (int, float)):
return math.sin(x) # Fast path for scalars
result = Composite({})
for n in range(terms):
sign = (-1) ** n
coeff = sign / math.factorial(2*n + 1)
result = result + coeff * (x ** (2*n + 1))
return resultWhat this does:
sin(x) = x - x³/3! + x⁵/5! - x⁷/7! + ...
Each power of x is computed via composite arithmetic, so:
- x = 3 + h
- x³ = (3 + h)³ = 27 + 27h + 9h² + h³
- The convolution automatically handles all derivatives!
More terms = more accuracy:
terms=12: Good for most functions, derivatives up to ~10th orderterms=20: High-order derivatives or difficult functionsterms=30: Extreme precision needs
Tradeoff: More terms = slower, but more accurate derivatives.
For functions like ln(x), we need the standard part first:
def ln(x, terms=15):
a = x.st() # Standard part
if a <= 0:
raise ValueError("ln requires positive standard part")
h_part = x - R(a) # Infinitesimal part
ratio = h_part / R(a) # h/a
# Mercator series: ln(1 + u) = u - u²/2 + u³/3 - ...
result = Composite({0: math.log(a)})
power = Composite({0: 1})
for n in range(1, terms):
power = power * ratio
sign = (-1) ** (n + 1)
result = result + sign * power / n
return resultKey insight: ln(a + h) = ln(a) + ln(1 + h/a), then expand ln(1 + u) as a series.
Integration is the inverse of differentiation, so:
- Differentiation shifts dimensions down (0 → -1 → -2)
- Integration shifts dimensions up (-2 → -1 → 0)
def antiderivative(f_composite, constant=0):
result = {0: constant}
for dim, coeff in f_composite.c.items():
new_dim = dim - 1 # Shift up (more negative becomes less negative)
divisor = abs(new_dim)
if divisor > 0:
result[new_dim] = coeff / divisor
return Composite(result)Example:
f = {0: 6, -1: 12, -2: 6} (polynomial with derivatives)
F = antiderivative(f)
= {0: constant, -1: 6/1, -2: 12/2, -3: 6/3}
= {0: constant, -1: 6, -2: 6, -3: 2}
Use stepped integration:
- Evaluate f at point x₀ as composite → get all derivatives
- Shift dimensions to get antiderivative
- Evaluate F(x₀ + Δx) - F(x₀) using Taylor coefficients
- Repeat for next step
def integrate_adaptive(f, a, b, tol=1e-10):
total = 0.0
x0 = a
while x0 < b:
# ONE evaluation → all derivatives
fx = f(R(x0) + ZERO)
# ONE dimensional shift → antiderivative
Fx = antiderivative(fx)
# Evaluate contribution from this step
dx = min(step_size, b - x0)
contribution = sum(
coeff * dx ** abs(dim)
for dim, coeff in Fx.c.items() if dim < 0
)
total += contribution
x0 += dx
return totalEfficiency: One function evaluation per step gives you the antiderivative for free!
Only store non-zero coefficients:
# Good: {0: 5, -2: 3} (2 entries)
# Bad: {0: 5, -1: 0, -2: 3, -3: 0} (4 entries, 2 useless)Filter on creation:
self.c = {k: v for k, v in coefficients.items() if abs(v) > 1e-15}Check for scalar operations first:
def __mul__(self, other):
if isinstance(other, (int, float)):
# Fast scalar multiply
return Composite({k: v * other for k, v in self.c.items()})
# ... full convolution for composite × compositeFor most applications, you don't need infinite dimensions:
MAX_DIMENSION = 10 # Track up to 10th derivative
def prune(self):
self.c = {k: v for k, v in self.c.items() if abs(k) <= MAX_DIMENSION}Replace dictionary with dense array for fixed dimension range:
# Instead of: {0: 5, -1: 3, -2: 1}
# Use array: [5, 3, 1, 0, 0, ...] with known dimension mappingBenefit: SIMD operations, much faster convolution.
For high-order derivatives (20+ dimensions), use FFT:
import numpy.fft as fft
def mul_fft(a, b):
n = len(a) + len(b) - 1
fa = fft.fft(a, n)
fb = fft.fft(b, n)
return fft.ifft(fa * fb).realSpeedup: O(n log n) instead of O(n²).
Composite object:
c: dict (Python dict, ~200 bytes overhead + 24 bytes per entry)
Typical size for x³ at x=2:
4 dimensions × 24 bytes = 96 bytes
+ 200 bytes overhead
= ~300 bytes per number
class CompositeOptimized:
__slots__ = ['coeffs', 'dim_offset']
def __init__(self, coeffs, dim_offset):
self.coeffs = np.array(coeffs, dtype=np.float64) # Dense array
self.dim_offset = dim_offset # Lowest dimension indexSize: 8 bytes per coefficient + 16 bytes overhead = 88 bytes for x³.
Bottlenecks:
- Dictionary operations - Hash lookups instead of array indexing
- Python loops - No vectorization
- Memory allocation - New dict for every operation
- No JIT compilation - Interpreted Python
Expected speedup with optimization:
- Vectorized arrays: 10×
- FFT convolution: 20× (high-order)
- JIT compilation (Numba): 50×
- GPU implementation: 100×
Total potential: 500-1000× faster → competitive with PyTorch autograd!
| System | How it works | Complexity |
|---|---|---|
| Dual numbers | ε² = 0 truncation | O(1) per op |
| Taylor AD | Fixed-order tape | O(k) per op |
| This system | Sparse convolution | O(k²) per op |
Tradeoff: This system is slower per operation but gives all orders of derivatives simultaneously. Taylor AD requires specifying order upfront.
The fundamental insight: polynomial multiplication is the Leibniz product rule. You don't need to "implement" the product rule - it emerges from convolution.
Integration isn't a complex algorithm - it's just shifting dimensions up and dividing by the new index.
Transcendental functions are "encoded" as their Taylor series, then composite arithmetic handles all derivatives automatically.
Unlike standard calculus where you need N evaluations for N derivatives, composite arithmetic gets all N derivatives from a single evaluation.
Template:
def my_function(x, terms=15):
if isinstance(x, (int, float)):
return standard_implementation(x)
a = x.st() # Get standard part
# ... compute f(a) using standard math
# Taylor expand around a
result = Composite({0: f_at_a})
# Add terms: use known Taylor series for your function
# ...
return resultChange coefficient type:
self.c = {k: complex(v) for k, v in coefficients.items()}All arithmetic operations work unchanged!
- 10-Minute Tutorial - Get started quickly
- API Reference - Complete function docs
- Implementation Guide - How it works internally
- Examples - Code snippets for common tasks
- Roadmap (DRAFT) - What's next
© Toni Milovan. Documentation licensed under CC BY-SA 4.0. Code licensed under AGPL-3.0.