| """ |
| 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!") |