Files
lucidia-math/lab/unified_geometry_engine.py
Alexa Louise f5f1551964 Initial extraction from blackroad-prism-console
Lucidia Math - Advanced mathematical engines:

forge/ - Mathematical Foundations:
- consciousness.py (650 lines) - Consciousness modeling
- unified_geometry.py (402 lines) - Geometric transformations
- advanced_tools.py (356 lines) - Advanced utilities
- main.py (209 lines) - CLI orchestration
- numbers.py, proofs.py, fractals.py, dimensions.py

lab/ - Experimental Mathematics:
- unified_geometry_engine.py (492 lines) - Geometry engine
- amundson_equations.py (284 lines) - Custom equations
- iterative_math_build.py (198 lines) - Iterative construction
- trinary_logic.py (111 lines) - Three-valued logic
- prime_explorer.py (108 lines) - Prime exploration
- quantum_finance.py (83 lines) - Quantum finance models

3,664 lines of Python.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-30 08:43:07 -06:00

493 lines
17 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Unified Geometry Engine (Package 6).
This module collects analytical machinery described in the Black Road
framework notes. It couples probability, number theory, complex analysis,
quantum field operators, and symmetry analysis into a cohesive toolkit.
Each class encapsulates the equations referenced in the specification.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from fractions import Fraction
import cmath
import math
from typing import Callable
import numpy as np
from numpy.typing import ArrayLike, NDArray
Array1D = NDArray[np.float64]
ComplexArray = NDArray[np.complex128]
@dataclass
class GaussianSignalAnalyzer:
r"""Track a normalized Gaussian in time and frequency domains.
The probability density is
.. math::
f(x) = \frac{1}{\sigma \sqrt{2\pi}} e^{-\frac{(x-\mu)^2}{2\sigma^2}}
The Fourier transform remains Gaussian with variance ``1/σ`` and the
derivative property maps to multiplication by ``i ω`` in frequency space.
"""
mean: float = 0.0
sigma: float = 1.0
def __post_init__(self) -> None:
if self.sigma <= 0:
raise ValueError("sigma must be positive")
@property
def normalization(self) -> float:
return float(1.0 / (self.sigma * math.sqrt(2.0 * math.pi)))
def time_domain(self, x: ArrayLike) -> Array1D:
values = np.asarray(x, dtype=float)
exponent = -0.5 * ((values - self.mean) / self.sigma) ** 2
return np.asarray(self.normalization * np.exp(exponent), dtype=float)
def derivative_time_domain(self, x: ArrayLike) -> Array1D:
values = np.asarray(x, dtype=float)
return -((values - self.mean) / (self.sigma**2)) * self.time_domain(values)
def frequency_domain(self, omega: ArrayLike) -> ComplexArray:
freqs = np.asarray(omega, dtype=float)
envelope = np.exp(-0.5 * (self.sigma**2) * freqs**2)
phase = np.exp(-1j * self.mean * freqs)
return np.asarray(envelope * phase, dtype=np.complex128)
def derivative_frequency_domain(self, omega: ArrayLike) -> ComplexArray:
freqs = np.asarray(omega, dtype=float)
return np.asarray(1j * freqs * self.frequency_domain(freqs), dtype=np.complex128)
def coherence(self) -> dict[str, float]:
sigma_t = float(self.sigma)
sigma_w = float(1.0 / self.sigma)
return {
"time_spread": sigma_t,
"frequency_spread": sigma_w,
"uncertainty_product": sigma_t * sigma_w,
}
@dataclass
class ArithmeticSymmetryEngine:
"""Compute discrete-continuous invariants.
Combines Ramanujan sums, Faulhaber polynomials, and Euler products to
characterise symmetry signatures. The ``fractal_limit`` helper mirrors the
Mandelbrot iteration bound used elsewhere in the Black Road notebooks.
"""
_bernoulli_cache: dict[int, Fraction] = field(default_factory=dict, init=False)
_prime_cache: list[int] = field(default_factory=lambda: [2], init=False)
def ramanujan_sum(self, q: int, n: int) -> complex:
if q <= 0:
raise ValueError("q must be positive")
total = 0.0j
for a in range(1, q + 1):
if math.gcd(a, q) == 1:
total += cmath.exp(2j * math.pi * a * n / q)
return total
def _bernoulli_numbers(self, order: int) -> list[Fraction]:
if order in self._bernoulli_cache:
max_cached = max(self._bernoulli_cache)
else:
max_cached = -1
if max_cached >= order:
return [self._bernoulli_cache[i] for i in range(order + 1)]
bernoullis: list[Fraction] = []
for m in range(order + 1):
a = [Fraction(0) for _ in range(m + 1)]
for j in range(m + 1):
a[j] = Fraction(1, j + 1)
for k in range(j, 0, -1):
a[k - 1] = k * (a[k - 1] - a[k])
bernoullis.append(a[0])
self._bernoulli_cache[m] = a[0]
return bernoullis
def faulhaber_sum(self, power: int, n: int) -> float:
if power < 0:
raise ValueError("power must be non-negative")
if n < 0:
raise ValueError("n must be non-negative")
bernoullis = self._bernoulli_numbers(power)
total = Fraction(0)
for j in range(power + 1):
coeff = Fraction(math.comb(power + 1, j))
total += coeff * bernoullis[j] * Fraction(n ** (power + 1 - j), power + 1)
return float(total)
def _generate_primes(self, count: int) -> list[int]:
if count <= 0:
raise ValueError("count must be positive")
candidate = self._prime_cache[-1]
while len(self._prime_cache) < count:
candidate += 1
is_prime = True
limit = int(math.sqrt(candidate))
for prime in self._prime_cache:
if prime > limit:
break
if candidate % prime == 0:
is_prime = False
break
if is_prime:
self._prime_cache.append(candidate)
return self._prime_cache[:count]
def euler_product(self, s: complex, terms: int = 10) -> complex:
primes = self._generate_primes(terms)
product = 1.0 + 0.0j
for prime in primes:
product *= 1.0 / (1.0 - prime ** (-s))
return product
def fractal_limit(self, c: complex, max_iter: int = 64, threshold: float = 2.0) -> int:
z = 0 + 0j
for iteration in range(1, max_iter + 1):
z = z**2 + c
if abs(z) > threshold:
return iteration
return max_iter
def symmetry_signature(
self,
q: int,
n: int,
power: int,
s: complex,
*,
fractal_seed: complex = 0 + 0j,
terms: int = 10,
max_iter: int = 64,
threshold: float = 2.0,
) -> dict[str, complex | float | int]:
return {
"ramanujan": self.ramanujan_sum(q, n),
"faulhaber": self.faulhaber_sum(power, n),
"euler_product": self.euler_product(s, terms),
"fractal_limit": self.fractal_limit(fractal_seed, max_iter, threshold),
}
class ComplexMatrixMapper:
"""Convert between complex scalars, rotation matrices, and quaternions."""
@staticmethod
def to_matrix(value: complex) -> NDArray[np.float64]:
a = float(value.real)
b = float(value.imag)
return np.array([[a, -b], [b, a]], dtype=float)
@staticmethod
def from_matrix(matrix: ArrayLike) -> complex:
mat = np.asarray(matrix, dtype=float)
if mat.shape != (2, 2):
raise ValueError("matrix must be 2x2")
if not np.allclose(mat[1, 0], -mat[0, 1]):
raise ValueError("matrix does not encode a complex number")
return complex(mat[0, 0], mat[1, 0])
@staticmethod
def to_quaternion(value: complex) -> Array1D:
return np.asarray([value.real, value.imag, 0.0, 0.0], dtype=float)
@staticmethod
def rotate_vector(matrix: ArrayLike, vector: ArrayLike) -> Array1D:
mat = np.asarray(matrix, dtype=float)
vec = np.asarray(vector, dtype=float)
return np.asarray(mat @ vec, dtype=float)
@dataclass
class QuaternionicRotationEngine:
"""Handle quaternion rotations for 3D orientation updates."""
quaternion: Array1D
def __post_init__(self) -> None:
self.quaternion = np.asarray(self.quaternion, dtype=float)
if self.quaternion.shape != (4,):
raise ValueError("quaternion must be length 4")
self.normalize()
@staticmethod
def from_axis_angle(axis: ArrayLike, angle: float) -> QuaternionicRotationEngine:
axis_vec = np.asarray(axis, dtype=float)
if axis_vec.shape != (3,):
raise ValueError("axis must be a 3-vector")
norm = np.linalg.norm(axis_vec)
if norm == 0:
raise ValueError("axis must be non-zero")
unit = axis_vec / norm
half = angle / 2.0
quat = np.concatenate(([math.cos(half)], unit * math.sin(half)))
return QuaternionicRotationEngine(quat)
def normalize(self) -> None:
norm = np.linalg.norm(self.quaternion)
if norm == 0:
raise ValueError("quaternion norm must not be zero")
self.quaternion = self.quaternion / norm
def conjugate(self) -> Array1D:
w, x, y, z = self.quaternion
return np.asarray([w, -x, -y, -z], dtype=float)
@staticmethod
def _multiply(q1: Array1D, q2: Array1D) -> Array1D:
w1, x1, y1, z1 = q1
w2, x2, y2, z2 = q2
return np.asarray(
[
w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2,
w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2,
w1 * y2 - x1 * z2 + y1 * w2 + z1 * x2,
w1 * z2 + x1 * y2 - y1 * x2 + z1 * w2,
],
dtype=float,
)
def rotate(self, vector: ArrayLike) -> Array1D:
vec = np.asarray(vector, dtype=float)
if vec.shape != (3,):
raise ValueError("vector must be length 3")
q_vec = np.concatenate(([0.0], vec))
rotated = self._multiply(self._multiply(self.quaternion, q_vec), self.conjugate())
return rotated[1:]
def compose(self, other: QuaternionicRotationEngine) -> QuaternionicRotationEngine:
combined = self._multiply(self.quaternion, other.quaternion)
return QuaternionicRotationEngine(combined)
def as_matrix(self) -> NDArray[np.float64]:
w, x, y, z = self.quaternion
return np.array(
[
[1 - 2 * (y**2 + z**2), 2 * (x * y - z * w), 2 * (x * z + y * w)],
[2 * (x * y + z * w), 1 - 2 * (x**2 + z**2), 2 * (y * z - x * w)],
[2 * (x * z - y * w), 2 * (y * z + x * w), 1 - 2 * (x**2 + y**2)],
],
dtype=float,
)
def _dirac_gamma_matrices() -> tuple[ComplexArray, ComplexArray, ComplexArray, ComplexArray]:
sigma_x = np.array([[0, 1], [1, 0]], dtype=complex)
sigma_y = np.array([[0, -1j], [1j, 0]], dtype=complex)
sigma_z = np.array([[1, 0], [0, -1]], dtype=complex)
identity = np.eye(2, dtype=complex)
gamma0 = np.block([[identity, np.zeros((2, 2), dtype=complex)], [np.zeros((2, 2), dtype=complex), -identity]])
gamma_i = []
for sigma in (sigma_x, sigma_y, sigma_z):
upper = np.zeros((2, 2), dtype=complex)
lower = np.zeros((2, 2), dtype=complex)
block = np.block([[upper, sigma], [-sigma, lower]])
gamma_i.append(block)
return (gamma0, *gamma_i)
@dataclass
class QuantumFieldManifold:
"""Bridge Schrödinger, Dirac, and Hamiltonian structures."""
hamiltonian: ComplexArray
mass: float = 1.0
hbar: float = 1.0
def __post_init__(self) -> None:
self.hamiltonian = np.asarray(self.hamiltonian, dtype=complex)
if self.hamiltonian.shape[0] != self.hamiltonian.shape[1]:
raise ValueError("hamiltonian must be square")
self._gamma = _dirac_gamma_matrices()
def schrodinger_rhs(self, psi: ArrayLike) -> ComplexArray:
state = np.asarray(psi, dtype=complex)
return np.asarray(-1j / self.hbar * (self.hamiltonian @ state), dtype=complex)
def probability_density(self, psi: ArrayLike) -> Array1D:
state = np.asarray(psi, dtype=complex)
return np.asarray(np.abs(state) ** 2, dtype=float)
def dirac_operator(self, momentum: ArrayLike) -> ComplexArray:
p = np.asarray(momentum, dtype=float)
if p.shape != (3,):
raise ValueError("momentum must be length 3")
energy = math.sqrt(float(np.dot(p, p)) + self.mass**2)
p_mu = np.array([energy, p[0], p[1], p[2]], dtype=float)
operator = sum(gamma * value for gamma, value in zip(self._gamma, p_mu))
operator -= self.mass * np.eye(operator.shape[0], dtype=complex)
return operator
def dirac_residual(self, psi: ArrayLike, momentum: ArrayLike) -> ComplexArray:
state = np.asarray(psi, dtype=complex)
operator = self.dirac_operator(momentum)
return np.asarray(operator @ state, dtype=complex)
def energy_expectation(self, psi: ArrayLike) -> float:
state = np.asarray(psi, dtype=complex)
norm = np.vdot(state, state)
if norm == 0:
raise ValueError("state norm must be non-zero")
value = np.vdot(state, self.hamiltonian @ state) / norm
return float(value.real)
@dataclass
class FractalMobiusCoupler:
"""Iterate Möbius transformations with Mandelbrot-style bounds."""
a: complex
b: complex
c: complex
d: complex
def __post_init__(self) -> None:
determinant = self.a * self.d - self.b * self.c
if abs(determinant) == 0:
raise ValueError("Möbius transformation must have non-zero determinant")
scale = cmath.sqrt(determinant)
self.a /= scale
self.b /= scale
self.c /= scale
self.d /= scale
self._determinant = self.a * self.d - self.b * self.c
def transform(self, z: complex) -> complex:
return (self.a * z + self.b) / (self.c * z + self.d)
def iterate(self, seed: complex, max_iter: int = 64, threshold: float = 2.0) -> list[complex]:
values = [seed]
z = seed
for _ in range(max_iter):
z = self.transform(z)
values.append(z)
if abs(z) > threshold:
break
return values
def bounded(self, seed: complex, max_iter: int = 64, threshold: float = 2.0) -> bool:
for value in self.iterate(seed, max_iter=max_iter, threshold=threshold):
if abs(value) > threshold:
return False
return True
@dataclass
class NoetherAnalyzer:
"""Numerically evaluate conserved quantities from a Lagrangian."""
lagrangian: Callable[[float, Array1D, Array1D], float]
epsilon: float = 1e-6
def conjugate_momentum(self, t: float, q: ArrayLike, qdot: ArrayLike) -> Array1D:
coords = np.asarray(q, dtype=float)
velocities = np.asarray(qdot, dtype=float)
if coords.shape != velocities.shape:
raise ValueError("q and qdot must share shape")
momenta = np.zeros_like(velocities, dtype=float)
for index in range(len(velocities)):
delta = np.zeros_like(velocities)
delta[index] = self.epsilon
plus = self.lagrangian(t, coords, velocities + delta)
minus = self.lagrangian(t, coords, velocities - delta)
momenta[index] = (plus - minus) / (2.0 * self.epsilon)
return momenta
def conserved_quantity(self, t: float, q: ArrayLike, qdot: ArrayLike, delta_q: ArrayLike) -> float:
momentum = self.conjugate_momentum(t, q, qdot)
variation = np.asarray(delta_q, dtype=float)
if variation.shape != momentum.shape:
raise ValueError("delta_q must match configuration shape")
return float(np.dot(momentum, variation))
@dataclass
class EntropyFieldMapper:
"""Translate Gaussian variance into Shannon-style entropy."""
k_B: float = 1.0
def gaussian_entropy(self, sigma: float) -> float:
if sigma <= 0:
raise ValueError("sigma must be positive")
return float(self.k_B * math.log(sigma * math.sqrt(2.0 * math.pi * math.e)))
def coherence_entropy(self, analyzer: GaussianSignalAnalyzer) -> float:
return self.gaussian_entropy(analyzer.sigma)
@dataclass
class HilbertPhaseAnalyzer:
"""Hilbert transform utilities for analytic signal analysis."""
dt: float = 1.0
def _hilbert_transform(self, signal: ArrayLike) -> ComplexArray:
values = np.asarray(signal, dtype=float)
n = values.size
spectrum = np.fft.fft(values)
h = np.zeros(n)
if n % 2 == 0:
h[0] = 1
h[n // 2] = 1
h[1 : n // 2] = 2
else:
h[0] = 1
h[1 : (n + 1) // 2] = 2
analytic = np.fft.ifft(spectrum * h)
return np.asarray(analytic, dtype=np.complex128)
def analytic_signal(self, signal: ArrayLike) -> ComplexArray:
return self._hilbert_transform(signal)
def instantaneous_amplitude(self, signal: ArrayLike) -> Array1D:
analytic = self.analytic_signal(signal)
return np.asarray(np.abs(analytic), dtype=float)
def instantaneous_phase(self, signal: ArrayLike) -> Array1D:
analytic = self.analytic_signal(signal)
phase = np.unwrap(np.angle(analytic))
return np.asarray(phase, dtype=float)
def instantaneous_frequency(self, signal: ArrayLike) -> Array1D:
phase = self.instantaneous_phase(signal)
derivative = np.gradient(phase, self.dt)
return np.asarray(derivative / (2.0 * math.pi), dtype=float)
def frequency_response(self, signal: ArrayLike) -> tuple[Array1D, ComplexArray]:
values = np.asarray(signal, dtype=float)
freq = np.fft.fftfreq(values.size, d=self.dt)
transform = np.fft.fft(values)
return np.asarray(freq, dtype=float), np.asarray(transform, dtype=np.complex128)
def differentiate_via_fourier(self, signal: ArrayLike) -> Array1D:
freq, transform = self.frequency_response(signal)
derivative_freq = 1j * 2.0 * math.pi * freq * transform
derivative_time = np.fft.ifft(derivative_freq)
return np.asarray(derivative_time.real, dtype=float)
__all__ = [
"GaussianSignalAnalyzer",
"ArithmeticSymmetryEngine",
"ComplexMatrixMapper",
"QuaternionicRotationEngine",
"QuantumFieldManifold",
"FractalMobiusCoupler",
"NoetherAnalyzer",
"EntropyFieldMapper",
"HilbertPhaseAnalyzer",
]