""" pl/calculus.py — Calculus as Coherence ======================================= Derivatives and integrals are not separate mathematical objects. They are P / G → Q in the real carrier V = ℝ with specific gradient families. The derivative: the gradient family that linearizes propagation. f'(x) is the load the function generates per unit step. e^x is the fixed point of this gradient family. Fixed point = e^x is its own derivative. This is not magic. It is forced. The integral: accumulation of load across the carrier. ∫ f dx is the total load accumulated from a to b. Fundamental theorem = derivative and integral are inverses in this carrier. Forced by the structure of ℝ. This module implements: 1. Numerical differentiation as P / G → Q 2. Numerical integration as accumulated propagation 3. The fixed point theorem for d/dx 4. Taylor series as recursive load accumulation """ from __future__ import annotations from dataclasses import dataclass, field from typing import Any, Callable, Dict, List, Optional, Tuple import math from pl.core import Pattern, Gradient, Context, seed # ============================================================================= # FUNCTION AS PATTERN # ============================================================================= @dataclass class FunctionPattern(Pattern): """ A pattern whose designation is a function f: ℝ → ℝ. The function is the v_P component. The load accumulates as we apply derivative gradients. f'' has more load than f'. f^(n) has more load than f^(n-1). This matches: higher derivatives are harder to compute. """ func_name: str = "f" def evaluate(self, x: float) -> float: """Evaluate the function at x.""" return self.v(x) def __repr__(self) -> str: return f"FuncPattern({self.func_name}, L={self.L:.2f})" def func_seed(f: Callable, name: str = "f") -> FunctionPattern: """Create a seed function pattern.""" return FunctionPattern(v=f, L=0.0, func_name=name) # ============================================================================= # DERIVATIVE GRADIENT # ============================================================================= def G_derivative(h: float = 1e-7) -> Gradient: """ The derivative gradient: f → f' Numerically: f'(x) ≈ (f(x+h) - f(x-h)) / (2h) This is a gradient field on the carrier of differentiable functions. Applying it once gives the first derivative. Applying it n times gives the nth derivative. The load accumulates: L increases by 1 per application. Key result: e^x is the fixed point of this gradient. G_derivative(e^x) = e^x. This is not assumed. It falls out of the structure of ℝ and G_derivative. """ def differentiate(f: Callable) -> Callable: def df(x: float) -> float: return (f(x + h) - f(x - h)) / (2 * h) return df return Gradient( name="d/dx", transform=differentiate, cost=1.0, description=f"Derivative: f → f' (numerical, h={h})", ) def G_integral(a: float = 0.0, n_steps: int = 1000) -> Gradient: """ The integral gradient: f → ∫_a^x f(t) dt This is accumulation of load across the carrier from a to x. Applying derivative then integral returns to origin (within numerical precision). Applying integral then derivative also returns to origin. Fundamental theorem = these two gradients are inverses. Derived from the structure of ℝ, not assumed. """ def integrate(f: Callable) -> Callable: def F(x: float) -> float: if x == a: return 0.0 n = max(n_steps, int(abs(x - a) * 100)) dx = (x - a) / n total = 0.0 for i in range(n): xi = a + (i + 0.5) * dx total += f(xi) * dx return total return F return Gradient( name=f"∫_[{a}]^x", transform=integrate, cost=1.0, description=f"Integral from {a}: f → ∫_[{a}]^x f(t) dt", ) # ============================================================================= # FIXED POINT THEOREM # ============================================================================= def verify_derivative_fixed_point( func: Callable, func_name: str, test_points: List[float] = None, h: float = 1e-7, tolerance: float = 1e-4, ) -> Dict: """ Verify whether a function is a fixed point of d/dx. Fixed point: G_derivative(f) ≈ f i.e., f'(x) ≈ f(x) for all x in the carrier. The exponential e^x is the unique (up to scaling) fixed point. This is a forced condition of the derivative gradient on ℝ. """ if test_points is None: test_points = [-2.0, -1.0, 0.0, 0.5, 1.0, 2.0] g = G_derivative(h) # Differentiate: f → f' fp = func_seed(func, func_name) fp_prime = FunctionPattern( v=g.transform(func), L=fp.L + g.cost(fp), func_name=f"{func_name}'", ) errors = [] for x in test_points: fx = func(x) fpx = fp_prime.v(x) err = abs(fx - fpx) errors.append((x, fx, fpx, err)) max_error = max(e for _, _, _, e in errors) is_fixed = max_error < tolerance return { "function": func_name, "is_fixed_point": is_fixed, "max_error": max_error, "tolerance": tolerance, "test_points": errors, "interpretation": ( f"{func_name} IS a fixed point of d/dx. " f"f'(x) = f(x) for all x (max error: {max_error:.2e}). " f"This is the exponential fixed point — forced by the ℝ carrier." if is_fixed else f"{func_name} is NOT a fixed point of d/dx. " f"f'(x) ≠ f(x) (max error: {max_error:.2e})." ), } def find_fixed_point_family( seed_func: Callable, n_iterations: int = 5, h: float = 1e-7, ) -> Dict: """ Starting from a seed function, repeatedly apply d/dx. What does the sequence converge to? For e^x: it stays at e^x (fixed point, period 1). For x^n: it degrades to 0 (no cycle, just decreasing degree). For sin(x): it cycles through sin, cos, -sin, -cos (period 4). The cycle structure of d/dx on function space is forced by ℝ. """ g = G_derivative(h) current_func = seed_func chain = [seed_func] for _ in range(n_iterations): current_func = g.transform(current_func) chain.append(current_func) # Sample at x=1.0 to see the value sequence value_chain = [f(1.0) for f in chain] return { "seed_value_at_1": value_chain[0], "value_chain": value_chain, "converging": abs(value_chain[-1]) < 1e-10, "stable": abs(value_chain[-1] - value_chain[-2]) < 1e-6, } # ============================================================================= # TAYLOR SERIES AS RECURSIVE LOAD ACCUMULATION # ============================================================================= def taylor_series( f: Callable, x0: float = 0.0, n_terms: int = 8, h: float = 1e-7, ) -> Dict: """ Taylor series as recursive load accumulation. T_f(x) = Σ_{k=0}^{n} f^(k)(x0) / k! * (x - x0)^k Each term is a propagation step: - Apply d/dx once (load += 1) - Evaluate at x0 (read the designation) - Scale by 1/k! (coherence normalization) The series is not a new object. It is the accumulation pattern of the derivative gradient applied recursively. Observation 2.2 (Gödel connection): The series for f(x) = 1/(1-x) diverges for |x| > 1. The carrier cannot support the load. This is the mechanism's version of incompleteness: some patterns carry more load than the context can support. """ g = G_derivative(h) current_func = f derivatives_at_x0 = [] current_p = func_seed(f, "f") for k in range(n_terms): val = current_func(x0) derivatives_at_x0.append(val) next_func = g.transform(current_func) current_p = g.propagate(current_p) current_func = next_func # Coefficients: f^(k)(x0) / k! factorial = 1 coefficients = [] for k, d in enumerate(derivatives_at_x0): if k > 0: factorial *= k coefficients.append(d / factorial) def taylor_approx(x: float) -> float: result = 0.0 for k, c in enumerate(coefficients): result += c * (x - x0) ** k return result return { "expansion_point": x0, "n_terms": n_terms, "coefficients": coefficients, "derivatives_at_x0": derivatives_at_x0, "approximation": taylor_approx, "load_at_nth_term": float(n_terms), # L_P after n derivative applications "interpretation": ( f"Taylor series = recursive load accumulation. " f"Each term adds 1 unit of load. " f"After {n_terms} terms, pattern has load L={n_terms}. " f"If the carrier (convergence radius) cannot support this load, " f"the series diverges. Load > θ → incoherence." ), } # ============================================================================= # FUNDAMENTAL THEOREM AS INVERSE GRADIENTS # ============================================================================= def demonstrate_fundamental_theorem( f: Callable, func_name: str, a: float = 0.0, b: float = 2.0, h: float = 1e-7, ) -> Dict: """ Fundamental theorem of calculus as inverse gradients. d/dx(∫_a^x f(t)dt) = f(x) [differentiate integral = original] ∫_a^x (df/dt) dt = f(x) - f(a) [integrate derivative = net change] These are not separate theorems. They are one statement: G_derivative and G_integral are inverses on the ℝ carrier. The gradient family {d/dx, ∫} forms a coherent pair. Together they leave the pattern unchanged (up to constant). This is forced by the structure of ℝ — not assumed. """ g_diff = G_derivative(h) g_int = G_integral(a) # Chain 1: integrate then differentiate F = g_int.transform(f) # ∫_a^x f(t) dt dF = g_diff.transform(F) # d/dx[∫_a^x f(t) dt] should = f # Chain 2: differentiate then integrate df = g_diff.transform(f) # f'(x) int_df = g_int.transform(df) # ∫_a^x f'(t) dt should = f(x) - f(a) # Test at several points test_xs = [a + (b - a) * i / 5 for i in range(6)] chain1_errors = [] chain2_errors = [] f_at_a = f(a) for x in test_xs: # Chain 1: dF(x) should ≈ f(x) err1 = abs(dF(x) - f(x)) chain1_errors.append((x, f(x), dF(x), err1)) # Chain 2: int_df(x) should ≈ f(x) - f(a) expected = f(x) - f_at_a err2 = abs(int_df(x) - expected) chain2_errors.append((x, expected, int_df(x), err2)) max_err1 = max(e for _, _, _, e in chain1_errors) max_err2 = max(e for _, _, _, e in chain2_errors) return { "function": func_name, "interval": (a, b), "chain1_result": "d/dx[∫f] = f", "chain1_max_error": max_err1, "chain1_holds": max_err1 < 1e-3, "chain2_result": "∫[df/dx] = f(x) - f(a)", "chain2_max_error": max_err2, "chain2_holds": max_err2 < 1e-3, "interpretation": ( "The fundamental theorem is the statement that " "G_derivative and G_integral are inverses. " "Applying both in sequence returns to origin (± constant). " "This is a FORCED CONDITION of the ℝ carrier. " "The theorem does not need a separate proof — " "it is a boundary condition of propagation on ℝ." ), } if __name__ == "__main__": print("\n" + "="*60) print(" CALCULUS AS COHERENCE IN THE ℝ CARRIER") print("="*60) # 1. e^x is a fixed point of d/dx print("\n1. Fixed point test: e^x") result = verify_derivative_fixed_point(math.exp, "e^x") print(f" Fixed point: {result['is_fixed_point']}") print(f" Max error: {result['max_error']:.2e}") print(f" → {result['interpretation']}") print("\n2. Fixed point test: x^2 (not a fixed point)") result2 = verify_derivative_fixed_point(lambda x: x**2, "x^2") print(f" Fixed point: {result2['is_fixed_point']}") print(f" → {result2['interpretation']}") print("\n3. sin(x) cycle under d/dx") result3 = find_fixed_point_family(math.sin, n_iterations=8) print(f" Values at x=1.0: {[round(v,3) for v in result3['value_chain']]}") print(" → sin → cos → -sin → -cos → sin: 4-cycle, forced by ℝ") print("\n4. Fundamental theorem as inverse gradients") result4 = demonstrate_fundamental_theorem(math.exp, "e^x") print(f" d/dx[∫f] = f holds: {result4['chain1_holds']} (err={result4['chain1_max_error']:.2e})") print(f" ∫[f'] = f-f(a) holds: {result4['chain2_holds']} (err={result4['chain2_max_error']:.2e})")