core/algebra/cga.py

70 lines
2.2 KiB
Python

"""
Conformal Geometric Algebra geometry on Cl(4,1).
Key identity: for null vectors X, Y on the horosphere,
cga_inner(X, Y) = -d(X, Y)^2 / 2
where d is Euclidean distance.
This is the ONLY distance metric in CORE-AI.
No cosine similarity. No L2 norm. No approximate indexing.
"""
import numpy as np
from .cl41 import geometric_product, reverse, scalar_part, basis_vector, N_COMPONENTS
def cga_inner(X: np.ndarray, Y: np.ndarray) -> float:
"""
Symmetric inner product: 0.5 * scalar_part(X*Y + Y*X).
For null vectors representing conformal points: equals -d^2 / 2.
"""
XY = geometric_product(X, Y)
YX = geometric_product(Y, X)
return 0.5 * scalar_part(XY + YX)
def outer_product(X: np.ndarray, Y: np.ndarray) -> np.ndarray:
"""
Outer (wedge) product: X ^ Y.
For a prompt versor X_p and response versor X_r,
X_p ^ X_r is a grade-2 object encoding their geometric relationship.
A real (non-imaginary) result means the dialogue is coherent.
"""
XY = geometric_product(X, Y)
YX = geometric_product(Y, X)
return 0.5 * (XY - YX)
def is_null(X: np.ndarray, tol: float = 1e-6) -> bool:
"""Check if X lies on the null cone: X*X = 0."""
return abs(cga_inner(X, X)) < tol
def null_project(X: np.ndarray) -> np.ndarray:
"""
Re-project X onto the null cone.
Call on vault entries periodically to correct floating-point null-cone drift.
This is numerical maintenance, not a heat shield.
Method: extract Euclidean part, re-embed via standard CGA point map.
"""
euclidean = X[1:4].copy().astype(np.float32)
x_sq = float(np.dot(euclidean, euclidean))
result = np.zeros(N_COMPONENTS, dtype=np.float32)
result[1:4] = euclidean
result[4] = 0.5 * x_sq # e+ coefficient
result[5] = 1.0 # e- coefficient
return result
def embed_point(x: np.ndarray) -> np.ndarray:
"""
Embed a Euclidean point x in R^3 into the CGA null cone.
Standard map: X = x + (1/2)|x|^2 * e+ + e-
"""
x = np.asarray(x, dtype=np.float32)
assert len(x) == 3, "embed_point expects a 3D vector"
result = np.zeros(N_COMPONENTS, dtype=np.float32)
result[1:4] = x
result[4] = 0.5 * float(np.dot(x, x))
result[5] = 1.0
return result