Not-trained-Neural-Networks / examples /group_theory_network.py
ewdlop's picture
Upload folder using huggingface_hub
461349d verified
"""
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!")