File size: 13,950 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
"""
Group Theory-based Algebraic Neural Network

This example demonstrates neural networks that use group theory operations
for data transformations, particularly focusing on symmetry groups.
"""

import numpy as np
from typing import List, Tuple, Dict
import math


class GroupTheoryNetwork:
    """
    Neural network based on group theory operations and symmetries.
    """
    
    def __init__(self, input_dim: int, group_types: List[str], output_dim: int):
        self.input_dim = input_dim
        self.group_types = group_types
        self.output_dim = output_dim
        
        # Initialize group operations
        self.groups = self._initialize_groups()
        
    def _initialize_groups(self) -> Dict[str, List[np.ndarray]]:
        """Initialize various group operations."""
        groups = {}
        
        for group_type in self.group_types:
            if group_type.startswith("cyclic_"):
                n = int(group_type.split("_")[1])
                groups[group_type] = self._generate_cyclic_group(n)
            elif group_type.startswith("dihedral_"):
                n = int(group_type.split("_")[1])
                groups[group_type] = self._generate_dihedral_group(n)
            elif group_type == "symmetric_3":
                groups[group_type] = self._generate_symmetric_group_3()
            elif group_type == "reflection":
                groups[group_type] = self._generate_reflection_group()
                
        return groups
    
    def _generate_cyclic_group(self, n: int) -> List[np.ndarray]:
        """Generate cyclic group Cn as rotation matrices."""
        group_elements = []
        
        for k in range(n):
            angle = 2 * math.pi * k / n
            
            if self.input_dim == 2:
                # 2D rotation matrix
                rotation = np.array([
                    [math.cos(angle), -math.sin(angle)],
                    [math.sin(angle), math.cos(angle)]
                ])
            elif self.input_dim >= 3:
                # 3D rotation around z-axis, identity for higher dimensions
                rotation = np.eye(self.input_dim)
                if self.input_dim >= 2:
                    rotation[0, 0] = math.cos(angle)
                    rotation[0, 1] = -math.sin(angle)
                    rotation[1, 0] = math.sin(angle)
                    rotation[1, 1] = math.cos(angle)
            else:
                # 1D case - just scaling
                rotation = np.array([[(-1) ** k]])
                
            group_elements.append(rotation)
            
        return group_elements
    
    def _generate_dihedral_group(self, n: int) -> List[np.ndarray]:
        """Generate dihedral group Dn (rotations + reflections)."""
        group_elements = []
        
        # Add rotations (same as cyclic group)
        rotations = self._generate_cyclic_group(n)
        group_elements.extend(rotations)
        
        # Add reflections
        for k in range(n):
            angle = 2 * math.pi * k / n
            
            if self.input_dim == 2:
                # Reflection across line through origin at angle/2
                reflection_angle = angle / 2
                cos_2theta = math.cos(2 * reflection_angle)
                sin_2theta = math.sin(2 * reflection_angle)
                
                reflection = np.array([
                    [cos_2theta, sin_2theta],
                    [sin_2theta, -cos_2theta]
                ])
            else:
                # For higher dimensions, reflect across first coordinate
                reflection = np.eye(self.input_dim)
                reflection[0, 0] = -1
                
            group_elements.append(reflection)
            
        return group_elements
    
    def _generate_symmetric_group_3(self) -> List[np.ndarray]:
        """Generate symmetric group S3 (permutations of 3 elements)."""
        if self.input_dim < 3:
            # For lower dimensions, use reduced representation
            return self._generate_cyclic_group(3)
        
        # All permutations of 3 elements as permutation matrices
        permutations = [
            [0, 1, 2],  # identity
            [1, 2, 0],  # (0 1 2)
            [2, 0, 1],  # (0 2 1)
            [1, 0, 2],  # (0 1)
            [0, 2, 1],  # (0 2)
            [2, 1, 0],  # (1 2)
        ]
        
        group_elements = []
        for perm in permutations:
            matrix = np.eye(self.input_dim)
            for i in range(3):
                if i < self.input_dim and perm[i] < self.input_dim:
                    matrix[i, i] = 0
                    matrix[i, perm[i]] = 1
            group_elements.append(matrix)
            
        return group_elements
    
    def _generate_reflection_group(self) -> List[np.ndarray]:
        """Generate group of reflections across coordinate axes."""
        group_elements = []
        
        # Identity
        group_elements.append(np.eye(self.input_dim))
        
        # Reflections across each coordinate axis
        for i in range(self.input_dim):
            reflection = np.eye(self.input_dim)
            reflection[i, i] = -1
            group_elements.append(reflection)
            
        # Reflections across diagonal planes (for 2D and 3D)
        if self.input_dim == 2:
            # Reflection across y = x
            diag_reflection = np.array([[0, 1], [1, 0]])
            group_elements.append(diag_reflection)
            
            # Reflection across y = -x
            anti_diag_reflection = np.array([[0, -1], [-1, 0]])
            group_elements.append(anti_diag_reflection)
            
        return group_elements
    
    def apply_group_actions(self, x: np.ndarray, group_name: str) -> np.ndarray:
        """Apply all group actions to input data."""
        if x.ndim == 1:
            x = x.reshape(1, -1)
            
        group_elements = self.groups[group_name]
        results = []
        
        for element in group_elements:
            if x.shape[1] == element.shape[0]:
                transformed = x @ element.T
            else:
                # Handle dimension mismatch by padding or truncating
                min_dim = min(x.shape[1], element.shape[0])
                transformed = x[:, :min_dim] @ element[:min_dim, :min_dim].T
                
            # Compute invariant features
            norm = np.linalg.norm(transformed, axis=1, keepdims=True)
            mean = np.mean(transformed, axis=1, keepdims=True)
            std = np.std(transformed, axis=1, keepdims=True) + 1e-8
            
            # Combine features
            features = np.concatenate([norm, mean, std], axis=1)
            results.append(features)
            
        return np.concatenate(results, axis=1)
    
    def forward(self, x: np.ndarray) -> np.ndarray:
        """Forward pass through group theory network."""
        all_features = []
        
        # Apply each group type
        for group_name in self.group_types:
            group_features = self.apply_group_actions(x, group_name)
            all_features.append(group_features)
            
        # Concatenate all features
        combined_features = np.concatenate(all_features, axis=1)
        
        # Linear combination to get desired output dimension
        feature_dim = combined_features.shape[1]
        if feature_dim >= self.output_dim:
            # Take first output_dim features
            output = combined_features[:, :self.output_dim]
        else:
            # Repeat features to reach output_dim
            repeats = (self.output_dim + feature_dim - 1) // feature_dim
            repeated = np.tile(combined_features, (1, repeats))
            output = repeated[:, :self.output_dim]
            
        return output
    
    def predict(self, x: np.ndarray) -> np.ndarray:
        """Prediction method."""
        return self.forward(x)


def test_rotation_invariance():
    """Test rotation invariance of the group theory network."""
    print("=== Group Theory Network: Rotation Invariance Test ===\n")
    
    # Create network with cyclic group
    network = GroupTheoryNetwork(
        input_dim=2,
        group_types=["cyclic_8"],
        output_dim=4
    )
    
    # Create test patterns
    original_pattern = np.array([[1, 0], [0, 1], [1, 1]])
    
    # Manual rotations for comparison
    rotation_angles = [0, np.pi/4, np.pi/2, np.pi, 3*np.pi/2]
    
    print("Testing rotation invariance:")
    outputs = []
    
    for angle in rotation_angles:
        # Manually rotate the pattern
        cos_a, sin_a = np.cos(angle), np.sin(angle)
        rotation_matrix = np.array([[cos_a, -sin_a], [sin_a, cos_a]])
        rotated_pattern = original_pattern @ rotation_matrix.T
        
        # Get network output
        output = network.predict(rotated_pattern)
        outputs.append(output)
        
        print(f"  Rotation {angle:.2f} rad: mean output = {np.mean(output):.4f}")
    
    # Check invariance (outputs should be similar)
    output_array = np.array(outputs)
    variance_across_rotations = np.var(output_array, axis=0)
    mean_variance = np.mean(variance_across_rotations)
    
    print(f"\nMean variance across rotations: {mean_variance:.6f}")
    print("(Lower values indicate better rotation invariance)\n")
    
    return outputs


def test_symmetry_detection():
    """Test the network's ability to detect different symmetries."""
    print("=== Group Theory Network: Symmetry Detection ===\n")
    
    # Create network with multiple group types
    network = GroupTheoryNetwork(
        input_dim=2,
        group_types=["cyclic_4", "dihedral_4", "reflection"],
        output_dim=6
    )
    
    # Define test patterns with different symmetries
    patterns = {
        "Square": np.array([[1, 1], [1, -1], [-1, -1], [-1, 1]]),
        "Triangle": np.array([[1, 0], [-0.5, np.sqrt(3)/2], [-0.5, -np.sqrt(3)/2]]),
        "Line": np.array([[1, 0], [0.5, 0], [0, 0], [-0.5, 0], [-1, 0]]),
        "Circle": np.array([[np.cos(θ), np.sin(θ)] for θ in np.linspace(0, 2*np.pi, 8)]),
        "Asymmetric": np.array([[1, 0], [0, 1], [0.3, 0.7], [0.8, 0.2]])
    }
    
    print("Symmetry detection results:")
    pattern_outputs = {}
    
    for pattern_name, pattern_points in patterns.items():
        output = network.predict(pattern_points)
        pattern_outputs[pattern_name] = output
        
        print(f"\n{pattern_name}:")
        print(f"  Mean output: {np.mean(output, axis=0)}")
        print(f"  Output std: {np.std(output, axis=0)}")
        
    return pattern_outputs


def test_group_composition():
    """Test composition of group operations."""
    print("=== Group Theory Network: Group Composition ===\n")
    
    network = GroupTheoryNetwork(
        input_dim=3,
        group_types=["symmetric_3"],
        output_dim=3
    )
    
    # Test group properties
    test_input = np.array([[1, 2, 3]])
    
    print("Testing group composition properties:")
    
    # Get all group elements
    group_elements = network.groups["symmetric_3"]
    
    # Test identity element (should be first)
    identity_result = test_input @ group_elements[0].T
    print(f"Identity transformation: {test_input[0]}{identity_result[0]}")
    
    # Test composition of operations
    for i, g1 in enumerate(group_elements[:3]):
        for j, g2 in enumerate(group_elements[:3]):
            # Apply g1 then g2
            intermediate = test_input @ g1.T
            final = intermediate @ g2.T
            
            # Apply composition g2∘g1
            composition = g2 @ g1
            direct = test_input @ composition.T
            
            # Check if they're the same (within numerical precision)
            difference = np.linalg.norm(final - direct)
            print(f"  g{j}∘g{i}: composition error = {difference:.8f}")


def test_invariant_features():
    """Test extraction of invariant features."""
    print("=== Group Theory Network: Invariant Feature Extraction ===\n")
    
    # Network for extracting rotation-invariant features
    network = GroupTheoryNetwork(
        input_dim=2,
        group_types=["cyclic_16"],  # Fine rotation sampling
        output_dim=8
    )
    
    # Test patterns with known geometric properties
    test_cases = [
        ("Unit Circle", np.array([[np.cos(θ), np.sin(θ)] for θ in np.linspace(0, 2*np.pi, 10)])),
        ("Ellipse", np.array([[2*np.cos(θ), np.sin(θ)] for θ in np.linspace(0, 2*np.pi, 10)])),
        ("Square", np.array([[1, 1], [1, -1], [-1, -1], [-1, 1]])),
        ("Random", np.random.randn(8, 2))
    ]
    
    print("Invariant feature analysis:")
    
    for case_name, points in test_cases:
        # Original features
        original_features = network.predict(points)
        
        # Rotated version
        rotation_45 = np.array([[np.cos(np.pi/4), -np.sin(np.pi/4)],
                               [np.sin(np.pi/4), np.cos(np.pi/4)]])
        rotated_points = points @ rotation_45.T
        rotated_features = network.predict(rotated_points)
        
        # Measure feature consistency
        feature_difference = np.linalg.norm(original_features - rotated_features)
        relative_difference = feature_difference / (np.linalg.norm(original_features) + 1e-8)
        
        print(f"\n{case_name}:")
        print(f"  Feature difference: {feature_difference:.6f}")
        print(f"  Relative difference: {relative_difference:.6f}")
        print(f"  Original features mean: {np.mean(original_features):.4f}")
        print(f"  Rotated features mean: {np.mean(rotated_features):.4f}")


if __name__ == "__main__":
    print("Group Theory Algebraic Neural Network Demo\n")
    print("="*60)
    
    # Run tests
    test_rotation_invariance()
    test_symmetry_detection()
    test_group_composition()
    test_invariant_features()
    
    print("\n" + "="*60)
    print("Group theory demo completed successfully!")