|
|
""" |
|
|
Geometric Algebra (Clifford Algebra) Neural Network |
|
|
|
|
|
This example demonstrates neural networks using geometric algebra operations |
|
|
for processing geometric and spatial data. |
|
|
""" |
|
|
|
|
|
import numpy as np |
|
|
from typing import List, Tuple, Dict, Union |
|
|
import math |
|
|
|
|
|
|
|
|
class GeometricAlgebraNetwork: |
|
|
""" |
|
|
Neural network based on geometric algebra (Clifford algebra) operations. |
|
|
""" |
|
|
|
|
|
def __init__(self, input_dim: int, hidden_dim: int, output_dim: int, signature: str = "euclidean"): |
|
|
self.input_dim = input_dim |
|
|
self.hidden_dim = hidden_dim |
|
|
self.output_dim = output_dim |
|
|
self.signature = signature |
|
|
|
|
|
|
|
|
self.basis_elements = self._generate_basis_elements() |
|
|
self.metric = self._generate_metric() |
|
|
|
|
|
|
|
|
self.coefficients = self._generate_ga_coefficients() |
|
|
|
|
|
def _generate_metric(self) -> np.ndarray: |
|
|
"""Generate metric tensor for the geometric algebra.""" |
|
|
if self.signature == "euclidean": |
|
|
return np.eye(self.input_dim) |
|
|
elif self.signature == "minkowski": |
|
|
metric = np.eye(self.input_dim) |
|
|
metric[0, 0] = -1 |
|
|
return metric |
|
|
elif self.signature == "conformal": |
|
|
|
|
|
metric = np.eye(self.input_dim + 2) |
|
|
metric[-1, -1] = -1 |
|
|
return metric |
|
|
else: |
|
|
return np.eye(self.input_dim) |
|
|
|
|
|
def _generate_basis_elements(self) -> List[Tuple[str, np.ndarray]]: |
|
|
"""Generate basis elements for geometric algebra.""" |
|
|
basis = [] |
|
|
|
|
|
|
|
|
scalar_basis = np.zeros(2**self.input_dim) |
|
|
scalar_basis[0] = 1.0 |
|
|
basis.append(("scalar", scalar_basis)) |
|
|
|
|
|
|
|
|
for i in range(self.input_dim): |
|
|
vector_basis = np.zeros(2**self.input_dim) |
|
|
vector_basis[2**i] = 1.0 |
|
|
basis.append((f"e{i+1}", vector_basis)) |
|
|
|
|
|
|
|
|
for i in range(self.input_dim): |
|
|
for j in range(i+1, self.input_dim): |
|
|
bivector_basis = np.zeros(2**self.input_dim) |
|
|
bivector_basis[2**i + 2**j] = 1.0 |
|
|
basis.append((f"e{i+1}e{j+1}", bivector_basis)) |
|
|
|
|
|
|
|
|
if self.input_dim <= 4: |
|
|
|
|
|
for i in range(self.input_dim): |
|
|
for j in range(i+1, self.input_dim): |
|
|
for k in range(j+1, self.input_dim): |
|
|
trivector_basis = np.zeros(2**self.input_dim) |
|
|
trivector_basis[2**i + 2**j + 2**k] = 1.0 |
|
|
basis.append((f"e{i+1}e{j+1}e{k+1}", trivector_basis)) |
|
|
|
|
|
|
|
|
if self.input_dim >= 2: |
|
|
pseudo_basis = np.zeros(2**self.input_dim) |
|
|
pseudo_basis[-1] = 1.0 |
|
|
basis.append(("pseudoscalar", pseudo_basis)) |
|
|
|
|
|
return basis |
|
|
|
|
|
def _generate_ga_coefficients(self) -> Dict[str, np.ndarray]: |
|
|
"""Generate coefficients for geometric algebra transformations.""" |
|
|
coeffs = {} |
|
|
|
|
|
|
|
|
sqrt2 = math.sqrt(2) |
|
|
sqrt3 = math.sqrt(3) |
|
|
phi = (1 + math.sqrt(5)) / 2 |
|
|
|
|
|
constants = [1.0, 1/sqrt2, 1/sqrt3, 1/phi, phi/3, sqrt2/3, sqrt3/5] |
|
|
|
|
|
|
|
|
num_basis = len(self.basis_elements) |
|
|
coeffs['input_hidden'] = np.zeros((self.hidden_dim, self.input_dim, num_basis)) |
|
|
|
|
|
for i in range(self.hidden_dim): |
|
|
for j in range(self.input_dim): |
|
|
for k in range(num_basis): |
|
|
const_idx = (i + j + k) % len(constants) |
|
|
coeffs['input_hidden'][i, j, k] = constants[const_idx] |
|
|
|
|
|
|
|
|
coeffs['hidden_output'] = np.zeros((self.output_dim, self.hidden_dim, num_basis)) |
|
|
|
|
|
for i in range(self.output_dim): |
|
|
for j in range(self.hidden_dim): |
|
|
for k in range(num_basis): |
|
|
const_idx = (i + j + k + 1) % len(constants) |
|
|
coeffs['hidden_output'][i, j, k] = constants[const_idx] |
|
|
|
|
|
return coeffs |
|
|
|
|
|
def geometric_product(self, a: np.ndarray, b: np.ndarray) -> np.ndarray: |
|
|
"""Compute geometric product of two multivectors.""" |
|
|
|
|
|
|
|
|
|
|
|
if len(a) != len(b): |
|
|
min_len = min(len(a), len(b)) |
|
|
a, b = a[:min_len], b[:min_len] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dot_product = np.dot(a, b) |
|
|
|
|
|
|
|
|
wedge_magnitude = np.linalg.norm(np.outer(a, b) - np.outer(b, a)) |
|
|
|
|
|
|
|
|
result = np.zeros(max(len(a), 2**self.input_dim)) |
|
|
result[0] = dot_product |
|
|
|
|
|
if len(result) > 1: |
|
|
result[1] = wedge_magnitude |
|
|
|
|
|
|
|
|
for i in range(2, min(len(result), len(a) + len(b) - 1)): |
|
|
result[i] = (a[i % len(a)] * b[i % len(b)] + |
|
|
b[i % len(b)] * a[i % len(a)]) / 2 |
|
|
|
|
|
return result[:len(a)] |
|
|
|
|
|
def outer_product(self, a: np.ndarray, b: np.ndarray) -> float: |
|
|
"""Compute outer (wedge) product magnitude.""" |
|
|
if len(a) >= 2 and len(b) >= 2: |
|
|
|
|
|
return abs(a[0] * b[1] - a[1] * b[0]) |
|
|
else: |
|
|
|
|
|
return np.linalg.norm(np.outer(a, b) - np.outer(b, a)) |
|
|
|
|
|
def inner_product(self, a: np.ndarray, b: np.ndarray) -> float: |
|
|
"""Compute inner (dot) product.""" |
|
|
return np.dot(a, b) |
|
|
|
|
|
def reverse(self, mv: np.ndarray) -> np.ndarray: |
|
|
"""Compute reverse of multivector (reverse order of basis elements).""" |
|
|
|
|
|
reversed_mv = mv.copy() |
|
|
|
|
|
|
|
|
for i in range(1, len(reversed_mv)): |
|
|
grade = bin(i).count('1') |
|
|
if grade % 4 in [2, 3]: |
|
|
reversed_mv[i] *= -1 |
|
|
|
|
|
return reversed_mv |
|
|
|
|
|
def magnitude(self, mv: np.ndarray) -> float: |
|
|
"""Compute magnitude of multivector.""" |
|
|
|
|
|
reversed_mv = self.reverse(mv) |
|
|
product = self.geometric_product(mv, reversed_mv) |
|
|
return math.sqrt(abs(product[0])) |
|
|
|
|
|
def normalize(self, mv: np.ndarray) -> np.ndarray: |
|
|
"""Normalize multivector.""" |
|
|
mag = self.magnitude(mv) |
|
|
if mag > 1e-10: |
|
|
return mv / mag |
|
|
else: |
|
|
return mv |
|
|
|
|
|
def apply_ga_transformation(self, x: np.ndarray, coeffs: np.ndarray) -> np.ndarray: |
|
|
"""Apply geometric algebra transformation.""" |
|
|
if x.ndim == 1: |
|
|
x = x.reshape(1, -1) |
|
|
|
|
|
batch_size, input_size = x.shape |
|
|
output_size = coeffs.shape[0] |
|
|
num_basis = len(self.basis_elements) |
|
|
|
|
|
result = np.zeros((batch_size, output_size)) |
|
|
|
|
|
for batch_idx in range(batch_size): |
|
|
for out_idx in range(output_size): |
|
|
|
|
|
multivector = np.zeros(num_basis) |
|
|
|
|
|
for in_idx in range(min(input_size, self.input_dim)): |
|
|
for basis_idx in range(num_basis): |
|
|
basis_name, basis_vector = self.basis_elements[basis_idx] |
|
|
coeff = coeffs[out_idx, in_idx, basis_idx] |
|
|
|
|
|
|
|
|
basis_component = basis_vector[:num_basis] if len(basis_vector) >= num_basis else np.pad(basis_vector, (0, num_basis - len(basis_vector))) |
|
|
|
|
|
|
|
|
multivector += x[batch_idx, in_idx] * coeff * basis_component |
|
|
|
|
|
|
|
|
|
|
|
transformed_mv = multivector.copy() |
|
|
|
|
|
for basis_idx in range(min(3, num_basis)): |
|
|
_, basis_vector = self.basis_elements[basis_idx] |
|
|
transformed_mv = self.geometric_product(transformed_mv, basis_vector[:num_basis]) |
|
|
|
|
|
|
|
|
scalar_part = transformed_mv[0] if len(transformed_mv) > 0 else 0 |
|
|
vector_magnitude = np.linalg.norm(transformed_mv[1:min(4, len(transformed_mv))]) |
|
|
|
|
|
|
|
|
result[batch_idx, out_idx] = scalar_part + vector_magnitude |
|
|
|
|
|
return result |
|
|
|
|
|
def forward(self, x: np.ndarray) -> np.ndarray: |
|
|
"""Forward pass through geometric algebra network.""" |
|
|
|
|
|
hidden = self.apply_ga_transformation(x, self.coefficients['input_hidden']) |
|
|
|
|
|
|
|
|
hidden = np.tanh(hidden) |
|
|
|
|
|
|
|
|
output = self.apply_ga_transformation(hidden, self.coefficients['hidden_output']) |
|
|
|
|
|
return output |
|
|
|
|
|
def predict(self, x: np.ndarray) -> np.ndarray: |
|
|
"""Prediction method.""" |
|
|
return self.forward(x) |
|
|
|
|
|
|
|
|
def test_geometric_operations(): |
|
|
"""Test basic geometric algebra operations.""" |
|
|
print("=== Geometric Algebra: Basic Operations Test ===\n") |
|
|
|
|
|
network = GeometricAlgebraNetwork(input_dim=3, hidden_dim=4, output_dim=2) |
|
|
|
|
|
|
|
|
a = np.array([1, 0, 0]) |
|
|
b = np.array([0, 1, 0]) |
|
|
c = np.array([1, 1, 0]) |
|
|
|
|
|
print("Testing geometric algebra operations:") |
|
|
|
|
|
|
|
|
inner_ab = network.inner_product(a, b) |
|
|
inner_aa = network.inner_product(a, a) |
|
|
print(f"Inner product a·b = {inner_ab:.3f} (should be 0)") |
|
|
print(f"Inner product a·a = {inner_aa:.3f} (should be 1)") |
|
|
|
|
|
|
|
|
outer_ab = network.outer_product(a, b) |
|
|
outer_aa = network.outer_product(a, a) |
|
|
print(f"Outer product a∧b magnitude = {outer_ab:.3f} (should be 1)") |
|
|
print(f"Outer product a∧a magnitude = {outer_aa:.3f} (should be 0)") |
|
|
|
|
|
|
|
|
geom_ab = network.geometric_product(a, b) |
|
|
print(f"Geometric product a*b = {geom_ab[:3]} (first 3 components)") |
|
|
|
|
|
|
|
|
mag_a = network.magnitude(a) |
|
|
mag_c = network.magnitude(c) |
|
|
print(f"Magnitude |a| = {mag_a:.3f}") |
|
|
print(f"Magnitude |c| = {mag_c:.3f}") |
|
|
|
|
|
print() |
|
|
|
|
|
|
|
|
def test_3d_rotation_processing(): |
|
|
"""Test processing of 3D rotational data.""" |
|
|
print("=== Geometric Algebra: 3D Rotation Processing ===\n") |
|
|
|
|
|
network = GeometricAlgebraNetwork(input_dim=3, hidden_dim=6, output_dim=4) |
|
|
|
|
|
|
|
|
rotation_axes = [ |
|
|
[1, 0, 0], |
|
|
[0, 1, 0], |
|
|
[0, 0, 1], |
|
|
[1, 1, 1], |
|
|
[1, -1, 0], |
|
|
] |
|
|
|
|
|
print("Processing 3D rotation data:") |
|
|
|
|
|
for i, axis in enumerate(rotation_axes): |
|
|
axis = np.array(axis, dtype=float) |
|
|
axis = axis / np.linalg.norm(axis) |
|
|
|
|
|
|
|
|
angles = [0, np.pi/4, np.pi/2, np.pi, 3*np.pi/2] |
|
|
|
|
|
outputs = [] |
|
|
for angle in angles: |
|
|
|
|
|
rotation_vector = axis * angle |
|
|
output = network.predict(rotation_vector.reshape(1, -1)) |
|
|
outputs.append(output[0]) |
|
|
|
|
|
outputs = np.array(outputs) |
|
|
|
|
|
print(f"\nRotation axis {i+1}: {axis}") |
|
|
print(f" Output range: [{np.min(outputs):.3f}, {np.max(outputs):.3f}]") |
|
|
print(f" Output variance: {np.var(outputs, axis=0)}") |
|
|
|
|
|
|
|
|
first_output = outputs[0] |
|
|
last_output = outputs[-1] |
|
|
|
|
|
periodicity_error = np.linalg.norm(first_output - outputs[2]) |
|
|
print(f" Periodicity error (0 vs π): {periodicity_error:.6f}") |
|
|
|
|
|
|
|
|
def test_conformal_geometry(): |
|
|
"""Test conformal geometric algebra for 2D points.""" |
|
|
print("=== Geometric Algebra: Conformal Geometry ===\n") |
|
|
|
|
|
|
|
|
network = GeometricAlgebraNetwork(input_dim=2, hidden_dim=8, output_dim=4, signature="conformal") |
|
|
|
|
|
|
|
|
geometric_objects = { |
|
|
"Point": np.array([1, 1]), |
|
|
"Origin": np.array([0, 0]), |
|
|
"Unit_X": np.array([1, 0]), |
|
|
"Unit_Y": np.array([0, 1]), |
|
|
"Diagonal": np.array([1, 1]) / np.sqrt(2) |
|
|
} |
|
|
|
|
|
print("Processing geometric objects in conformal space:") |
|
|
|
|
|
object_features = {} |
|
|
for obj_name, point in geometric_objects.items(): |
|
|
|
|
|
|
|
|
point_squared = np.dot(point, point) |
|
|
conformal_point = np.concatenate([point, [0.5 * point_squared, 1]]) |
|
|
|
|
|
|
|
|
output = network.predict(conformal_point.reshape(1, -1)) |
|
|
object_features[obj_name] = output[0] |
|
|
|
|
|
print(f"{obj_name:>10}: {point} → output: {output[0]}") |
|
|
|
|
|
|
|
|
print("\nAnalyzing geometric relationships:") |
|
|
|
|
|
origin_features = object_features["Origin"] |
|
|
for obj_name, features in object_features.items(): |
|
|
if obj_name != "Origin": |
|
|
distance = np.linalg.norm(features - origin_features) |
|
|
print(f" Feature distance from origin to {obj_name}: {distance:.4f}") |
|
|
|
|
|
|
|
|
def test_bivector_operations(): |
|
|
"""Test bivector operations for oriented areas.""" |
|
|
print("=== Geometric Algebra: Bivector Operations ===\n") |
|
|
|
|
|
network = GeometricAlgebraNetwork(input_dim=4, hidden_dim=6, output_dim=3) |
|
|
|
|
|
|
|
|
bivectors = [ |
|
|
[1, 0, 1, 0], |
|
|
[0, 1, 0, 1], |
|
|
[1, 1, 0, 0], |
|
|
[0, 0, 1, 1], |
|
|
[1, 0, 0, 1], |
|
|
] |
|
|
|
|
|
print("Processing bivector data:") |
|
|
|
|
|
for i, bivector in enumerate(bivectors): |
|
|
bv = np.array(bivector, dtype=float) |
|
|
output = network.predict(bv.reshape(1, -1)) |
|
|
|
|
|
|
|
|
bv_magnitude = np.linalg.norm(bv) |
|
|
|
|
|
print(f"Bivector {i+1}: {bv}") |
|
|
print(f" Magnitude: {bv_magnitude:.3f}") |
|
|
print(f" Network output: {output[0]}") |
|
|
print(f" Output magnitude: {np.linalg.norm(output[0]):.3f}") |
|
|
print() |
|
|
|
|
|
|
|
|
def test_multivector_algebra(): |
|
|
"""Test general multivector operations.""" |
|
|
print("=== Geometric Algebra: Multivector Operations ===\n") |
|
|
|
|
|
network = GeometricAlgebraNetwork(input_dim=3, hidden_dim=5, output_dim=2) |
|
|
|
|
|
|
|
|
multivectors = [ |
|
|
[1, 0, 0], |
|
|
[0, 1, 0], |
|
|
[0, 0, 1], |
|
|
[1, 1, 0], |
|
|
[1, 1, 1], |
|
|
[2, -1, 0.5], |
|
|
] |
|
|
|
|
|
print("Multivector algebra processing:") |
|
|
|
|
|
for i, mv in enumerate(multivectors): |
|
|
mv_array = np.array(mv, dtype=float) |
|
|
|
|
|
|
|
|
reversed_mv = network.reverse(mv_array) |
|
|
|
|
|
|
|
|
magnitude = network.magnitude(mv_array) |
|
|
|
|
|
|
|
|
normalized_mv = network.normalize(mv_array) |
|
|
|
|
|
|
|
|
output = network.predict(mv_array.reshape(1, -1)) |
|
|
|
|
|
print(f"\nMultivector {i+1}: {mv}") |
|
|
print(f" Reversed: {reversed_mv}") |
|
|
print(f" Magnitude: {magnitude:.4f}") |
|
|
print(f" Normalized: {normalized_mv}") |
|
|
print(f" Network output: {output[0]}") |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
print("Geometric Algebra Neural Network Demo\n") |
|
|
print("="*60) |
|
|
|
|
|
|
|
|
test_geometric_operations() |
|
|
test_3d_rotation_processing() |
|
|
test_conformal_geometry() |
|
|
test_bivector_operations() |
|
|
test_multivector_algebra() |
|
|
|
|
|
print("\n" + "="*60) |
|
|
print("Geometric algebra demo completed successfully!") |