File size: 17,564 Bytes
461349d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
"""
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
        
        # Initialize geometric algebra basis
        self.basis_elements = self._generate_basis_elements()
        self.metric = self._generate_metric()
        
        # Generate transformation coefficients
        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  # Time component with opposite signature
            return metric
        elif self.signature == "conformal":
            # Conformal geometric algebra Cl(n+1,1)
            metric = np.eye(self.input_dim + 2)
            metric[-1, -1] = -1  # One negative signature
            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 (grade 0)
        scalar_basis = np.zeros(2**self.input_dim)
        scalar_basis[0] = 1.0
        basis.append(("scalar", scalar_basis))
        
        # Vector basis elements (grade 1)
        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))
        
        # Bivector basis elements (grade 2)
        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))
        
        # Higher grade elements for small dimensions
        if self.input_dim <= 4:
            # Trivectors (grade 3)
            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))
            
            # Pseudoscalar (highest grade)
            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 = {}
        
        # Constants from geometric algebra theory
        sqrt2 = math.sqrt(2)
        sqrt3 = math.sqrt(3)
        phi = (1 + math.sqrt(5)) / 2  # Golden ratio
        
        constants = [1.0, 1/sqrt2, 1/sqrt3, 1/phi, phi/3, sqrt2/3, sqrt3/5]
        
        # Input to hidden transformation
        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]
        
        # Hidden to output transformation
        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."""
        # Simplified geometric product implementation
        # In full implementation, this would use the basis multiplication table
        
        if len(a) != len(b):
            min_len = min(len(a), len(b))
            a, b = a[:min_len], b[:min_len]
        
        # For this implementation, approximate with:
        # ab = a·b + a∧b (dot + wedge products)
        
        # Dot product component (grade reduction)
        dot_product = np.dot(a, b)
        
        # Wedge product component (grade increase) - simplified
        wedge_magnitude = np.linalg.norm(np.outer(a, b) - np.outer(b, a))
        
        # Combine into multivector representation
        result = np.zeros(max(len(a), 2**self.input_dim))
        result[0] = dot_product  # Scalar part
        
        if len(result) > 1:
            result[1] = wedge_magnitude  # Vector part approximation
        
        # Additional components based on input structure
        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:
            # 2D outer product as determinant
            return abs(a[0] * b[1] - a[1] * b[0])
        else:
            # Higher dimensional approximation
            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)."""
        # For bivectors and higher grades, reverse changes sign
        reversed_mv = mv.copy()
        
        # Approximate reversal by alternating signs for higher components
        for i in range(1, len(reversed_mv)):
            grade = bin(i).count('1')  # Grade based on binary representation
            if grade % 4 in [2, 3]:  # Bivectors and trivectors change sign
                reversed_mv[i] *= -1
                
        return reversed_mv
    
    def magnitude(self, mv: np.ndarray) -> float:
        """Compute magnitude of multivector."""
        # Magnitude is sqrt(mv * reverse(mv))
        reversed_mv = self.reverse(mv)
        product = self.geometric_product(mv, reversed_mv)
        return math.sqrt(abs(product[0]))  # Scalar part should be positive
    
    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):
                # Construct multivector from input
                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]
                        
                        # Ensure basis_vector has correct length
                        basis_component = basis_vector[:num_basis] if len(basis_vector) >= num_basis else np.pad(basis_vector, (0, num_basis - len(basis_vector)))
                        
                        # Weight by input value and coefficient
                        multivector += x[batch_idx, in_idx] * coeff * basis_component
                
                # Apply geometric algebra operations
                # 1. Geometric product with basis elements
                transformed_mv = multivector.copy()
                
                for basis_idx in range(min(3, num_basis)):  # Use first few basis elements
                    _, basis_vector = self.basis_elements[basis_idx]
                    transformed_mv = self.geometric_product(transformed_mv, basis_vector[:num_basis])
                
                # 2. Extract scalar and vector parts
                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))])
                
                # 3. Combine into output
                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."""
        # Input to hidden layer
        hidden = self.apply_ga_transformation(x, self.coefficients['input_hidden'])
        
        # Apply nonlinearity (preserve geometric structure)
        hidden = np.tanh(hidden)
        
        # Hidden to output layer
        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)
    
    # Test vectors
    a = np.array([1, 0, 0])  # e1
    b = np.array([0, 1, 0])  # e2
    c = np.array([1, 1, 0])  # e1 + e2
    
    print("Testing geometric algebra operations:")
    
    # Inner products
    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 products
    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)")
    
    # Geometric products
    geom_ab = network.geometric_product(a, b)
    print(f"Geometric product a*b = {geom_ab[:3]} (first 3 components)")
    
    # Magnitudes
    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)
    
    # Generate 3D rotation data (axis-angle representation)
    rotation_axes = [
        [1, 0, 0],  # X-axis rotation
        [0, 1, 0],  # Y-axis rotation
        [0, 0, 1],  # Z-axis rotation
        [1, 1, 1],  # Diagonal rotation
        [1, -1, 0], # Mixed rotation
    ]
    
    print("Processing 3D rotation data:")
    
    for i, axis in enumerate(rotation_axes):
        axis = np.array(axis, dtype=float)
        axis = axis / np.linalg.norm(axis)  # Normalize
        
        # Different rotation angles
        angles = [0, np.pi/4, np.pi/2, np.pi, 3*np.pi/2]
        
        outputs = []
        for angle in angles:
            # Rotation vector (axis * angle)
            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)}")
        
        # Check for periodic behavior (rotations should have 2π periodicity)
        first_output = outputs[0]  # 0 radians
        last_output = outputs[-1]   # 3π/2 radians, should be similar to π/2
        
        periodicity_error = np.linalg.norm(first_output - outputs[2])  # Compare 0 and π
        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")
    
    # Use conformal signature for 2D conformal GA
    network = GeometricAlgebraNetwork(input_dim=2, hidden_dim=8, output_dim=4, signature="conformal")
    
    # Test geometric primitives
    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():
        # Convert to conformal representation
        # In conformal GA: P = point + 0.5*|point|²*e∞ + e₀
        point_squared = np.dot(point, point)
        conformal_point = np.concatenate([point, [0.5 * point_squared, 1]])
        
        # Process through network
        output = network.predict(conformal_point.reshape(1, -1))
        object_features[obj_name] = output[0]
        
        print(f"{obj_name:>10}: {point} → output: {output[0]}")
    
    # Analyze relationships between objects
    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)
    
    # Create bivectors representing oriented areas
    bivectors = [
        [1, 0, 1, 0],  # e1∧e3
        [0, 1, 0, 1],  # e2∧e4
        [1, 1, 0, 0],  # e1∧e2
        [0, 0, 1, 1],  # e3∧e4
        [1, 0, 0, 1],  # e1∧e4
    ]
    
    print("Processing bivector data:")
    
    for i, bivector in enumerate(bivectors):
        bv = np.array(bivector, dtype=float)
        output = network.predict(bv.reshape(1, -1))
        
        # Calculate bivector magnitude
        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)
    
    # Create multivectors with different grade components
    multivectors = [
        [1, 0, 0],     # Pure vector e1
        [0, 1, 0],     # Pure vector e2
        [0, 0, 1],     # Pure vector e3
        [1, 1, 0],     # e1 + e2
        [1, 1, 1],     # e1 + e2 + e3
        [2, -1, 0.5],  # 2*e1 - e2 + 0.5*e3
    ]
    
    print("Multivector algebra processing:")
    
    for i, mv in enumerate(multivectors):
        mv_array = np.array(mv, dtype=float)
        
        # Test reverse operation
        reversed_mv = network.reverse(mv_array)
        
        # Test magnitude
        magnitude = network.magnitude(mv_array)
        
        # Test normalization
        normalized_mv = network.normalize(mv_array)
        
        # Network processing
        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)
    
    # Run tests
    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!")