Skip to content

Latest commit

 

History

History
executable file
·
618 lines (443 loc) · 14.8 KB

File metadata and controls

executable file
·
618 lines (443 loc) · 14.8 KB

Onboarding Guide: constraint-theory-python

Repository: https://github.com/SuperInstance/constraint-theory-python Language: Python (PyO3 bindings to Rust core) Version: 0.1.0 Last Updated: 2025-01-27


Welcome to Constraint Theory Python

This repository provides Python bindings for Constraint Theory, enabling exact constraint satisfaction in Python applications including NumPy workflows.

What You'll Learn

  1. Installation and setup
  2. Core API usage
  3. NumPy integration
  4. Practical applications
  5. Performance optimization

Prerequisites

Required

  • Python 3.8+
  • pip or conda

Optional (for advanced use cases)

  • NumPy 1.20+ (for batch processing)
  • PyTorch 2.0+ or TensorFlow 2.10+ (for ML integration patterns)

Installation

From PyPI (Recommended)

pip install constraint-theory

From Source

# Clone repository
git clone https://github.com/SuperInstance/constraint-theory-python.git
cd constraint-theory-python

# Install with maturin (for development)
pip install maturin
maturin develop --release

Verify Installation

import constraint_theory
print(f"Version: {constraint_theory.__version__}")

# Quick test
from constraint_theory import PythagoreanManifold
manifold = PythagoreanManifold(density=200)
print(f"Manifold has {manifold.state_count} states")
# Output: Manifold has 1013 states

Quick Start (5 Minutes)

1. Basic Pythagorean Snapping

from constraint_theory import PythagoreanManifold

# Create a manifold - the density parameter controls resolution
# Higher density = more exact states = finer resolution
manifold = PythagoreanManifold(density=200)

# Snap a point to the nearest Pythagorean triple
# Input: (0.577, 0.816) - approximate direction
# Output: (0.6, 0.8) - exact 3-4-5 triangle normalized
x, y, noise = manifold.snap(0.577, 0.816)

print(f"Snapped: ({x:.4f}, {y:.4f}), noise: {noise:.6f}")
# Output: Snapped: (0.6000, 0.8000), noise: 0.0236

2. Understanding the Noise Metric

from constraint_theory import PythagoreanManifold

manifold = PythagoreanManifold(density=200)

# Test vectors with different characteristics
test_cases = [
    ("Exact 3-4-5", (0.6, 0.8)),       # Should snap exactly
    ("Exact 5-12-13", (0.384615, 0.923077)),
    ("Near 45°", (0.707, 0.707)),      # Approximate
    ("Arbitrary", (0.543, 0.839)),     # Random direction
]

for name, (x, y) in test_cases:
    sx, sy, noise = manifold.snap(x, y)
    print(f"{name}: ({x:.3f}, {y:.3f}) -> ({sx:.4f}, {sy:.4f}), noise={noise:.6f}")

3. NumPy Integration

import numpy as np
from constraint_theory import PythagoreanManifold

# Create manifold
manifold = PythagoreanManifold(density=200)

# Generate random unit vectors
np.random.seed(42)
angles = np.random.uniform(0, 2 * np.pi, 1000)
vectors = np.column_stack([np.cos(angles), np.sin(angles)])

# Snap all vectors efficiently
results = manifold.snap_batch(vectors)

# Analyze results
snapped = np.array([[sx, sy] for sx, sy, _ in results])
noises = np.array([noise for _, _, noise in results])

print(f"Mean noise: {noises.mean():.6f}")
print(f"Max noise: {noises.max():.6f}")

Core Concepts

1. The Pythagorean Manifold

The PythagoreanManifold contains pre-computed normalized Pythagorean triples (a/c, b/c) where a² + b² = c². Each state represents an exact point on the unit circle.

from constraint_theory import PythagoreanManifold

# Density controls the maximum hypotenuse in Euclid's formula
# Higher density = more states = better resolution
manifold = PythagoreanManifold(density=200)

print(f"Total states: {manifold.state_count}")
# Each state is a point (x, y) where x² + y² = 1 EXACTLY

Density Guidelines:

Density Approximate States Resolution Use Case
50 ~250 0.02 Quick prototypes
100 ~500 0.01 Game physics
200 ~1000 0.005 General purpose
500 ~2500 0.002 ML augmentation
1000 ~5000 0.001 Scientific computing

2. Snapping Process

The snapping algorithm uses a KD-tree for O(log n) lookup:

Input: (x, y) - any 2D vector
         ↓
Normalize: (x/|v|, y/|v|) - project to unit circle
         ↓
KD-Tree: O(log n) nearest neighbor search
         ↓
Output: (sx, sy, noise) - exact Pythagorean state + distance

3. Deterministic Results

from constraint_theory import PythagoreanManifold

manifold = PythagoreanManifold(density=200)

# Same input ALWAYS produces same output
for _ in range(5):
    x, y, noise = manifold.snap(0.577, 0.816)
    print(f"Result: ({x:.6f}, {y:.6f})")
# All outputs are IDENTICAL - deterministic!

NumPy Integration

Basic Batch Processing

import numpy as np
from constraint_theory import PythagoreanManifold

manifold = PythagoreanManifold(density=200)

# Create vectors as NumPy array
vectors = np.array([
    [0.6, 0.8],
    [0.707, 0.707],
    [0.1, 0.995],
], dtype=np.float32)

# snap_batch accepts NumPy arrays directly
results = manifold.snap_batch(vectors)

for i, (sx, sy, noise) in enumerate(results):
    print(f"[{i}] ({vectors[i,0]:.3f}, {vectors[i,1]:.3f}) -> ({sx:.4f}, {sy:.4f})")

Large-Scale Processing

import numpy as np
import time
from constraint_theory import PythagoreanManifold

manifold = PythagoreanManifold(density=500)

# Generate 100,000 random unit vectors
n = 100000
angles = np.random.uniform(0, 2 * np.pi, n)
vectors = np.column_stack([np.cos(angles), np.sin(angles)]).astype(np.float32)

# Process with timing
start = time.time()
results = manifold.snap_batch(vectors)
elapsed = time.time() - start

print(f"Processed {n:,} vectors in {elapsed*1000:.2f}ms")
print(f"Throughput: {n/elapsed:,.0f} vectors/second")

# Analyze noise distribution
noises = np.array([noise for _, _, noise in results])
print(f"Mean noise: {noises.mean():.6f}")
print(f"Max noise: {noises.max():.6f}")

Replacing Normalization

import numpy as np
from constraint_theory import PythagoreanManifold

manifold = PythagoreanManifold(density=200)

# Old way: floating-point normalization
def old_normalize(v):
    return v / np.linalg.norm(v)

# New way: exact Pythagorean snapping
def exact_direction(x, y, manifold):
    sx, sy, noise = manifold.snap(x, y)
    return sx, sy, noise

# Compare
v = np.array([3, 4])
normalized = old_normalize(v)
print(f"Float normalization: ({normalized[0]:.10f}, {normalized[1]:.10f})")
# Note: 0.6 and 0.8 may have floating-point representation errors

sx, sy, noise = exact_direction(3, 4, manifold)
print(f"Exact snapping: ({sx:.10f}, {sy:.10f})")
# These are EXACT Pythagorean values

Machine Learning Integration Patterns

While the core library provides geometric snapping, here are patterns for ML integration:

Data Augmentation with Exact Directions

import numpy as np
from constraint_theory import PythagoreanManifold

manifold = PythagoreanManifold(density=500)

def augment_gradient(dx, dy):
    """Deterministic gradient augmentation for training."""
    sx, sy, noise = manifold.snap(dx, dy)
    # Return exact direction - reproducible across runs
    return sx, sy

# Use in training loop for reproducible augmentation
gradients = np.random.randn(1000, 2)
augmented = [augment_gradient(g[0], g[1]) for g in gradients]

Weight Quantization Helper

import numpy as np
from constraint_theory import PythagoreanManifold

def quantize_directions(weights_2d, manifold):
    """Quantize 2D weight directions to exact Pythagorean states."""
    # Normalize weights
    norms = np.linalg.norm(weights_2d, axis=1, keepdims=True)
    normalized = weights_2d / norms
    
    # Snap to exact
    results = manifold.snap_batch(normalized)
    
    # Reconstruct with original magnitudes
    quantized = np.array([[sx, sy] for sx, sy, _ in results])
    return quantized * norms

# Example usage
manifold = PythagoreanManifold(density=200)
weights = np.random.randn(100, 2)
quantized = quantize_directions(weights, manifold)

PyTorch Integration Pattern

import torch
import numpy as np
from constraint_theory import PythagoreanManifold

class ExactDirectionLayer(torch.nn.Module):
    """Custom layer that snaps outputs to exact Pythagorean directions."""
    
    def __init__(self, density=200):
        super().__init__()
        self.manifold = PythagoreanManifold(density)
    
    def forward(self, x):
        # x: (batch, 2) tensor
        with torch.no_grad():
            # Convert to numpy, snap, convert back
            np_x = x.detach().cpu().numpy()
            results = self.manifold.snap_batch(np_x)
            snapped = np.array([[sx, sy] for sx, sy, _ in results])
            return torch.from_numpy(snapped).to(x.device)

# Usage
layer = ExactDirectionLayer(density=200)
output = layer(torch.randn(32, 2))

Practical Applications

Game Development - Deterministic Physics

from constraint_theory import PythagoreanManifold

manifold = PythagoreanManifold(density=150)

def process_player_input(vx, vy):
    """Convert player input to exact direction for networked physics."""
    dx, dy, noise = manifold.snap(vx, vy)
    return dx, dy  # Same on ALL clients - no reconciliation needed

# All clients see identical physics
direction = process_player_input(0.7, 0.7)
print(f"Exact direction: {direction}")

Scientific Computing - Monte Carlo

import numpy as np
from constraint_theory import PythagoreanManifold

manifold = PythagoreanManifold(density=300)

def monte_carlo_directions(n_samples, seed=42):
    """Generate reproducible random directions for Monte Carlo."""
    np.random.seed(seed)
    angles = np.random.uniform(0, 2 * np.pi, n_samples)
    raw_directions = np.column_stack([np.cos(angles), np.sin(angles)])
    
    # Snap to exact - reproducible on any platform
    results = manifold.snap_batch(raw_directions)
    return [(sx, sy) for sx, sy, _ in results]

# Same results on laptop, server, cluster
directions = monte_carlo_directions(10000)

Robotics - Navigation

import math
from constraint_theory import PythagoreanManifold

manifold = PythagoreanManifold(density=200)

def navigate_toward(current, target):
    """Calculate exact heading toward target."""
    dx = target[0] - current[0]
    dy = target[1] - current[1]
    
    # Get exact direction
    sx, sy, noise = manifold.snap(dx, dy)
    heading = math.degrees(math.atan2(sy, sx))
    
    return heading, (sx, sy)

# Robot navigation with exact headings
heading, direction = navigate_toward((0, 0), (3, 4))
print(f"Heading: {heading:.2f}°, Direction: {direction}")

API Reference

Core Classes

class PythagoreanManifold:
    """Constraint manifold with Pythagorean lattice snapping."""
    
    def __init__(self, density: int):
        """
        Initialize manifold with specified density.
        
        Args:
            density: Maximum value of m in Euclid's formula. 
                     Higher = more states = finer resolution.
        """
    
    def snap(self, x: float, y: float) -> tuple[float, float, float]:
        """
        Snap single vector to nearest Pythagorean triple.
        
        Returns:
            (snapped_x, snapped_y, noise) where noise is distance 
            from input to snapped point.
        """
    
    def snap_batch(self, vectors) -> list[tuple[float, float, float]]:
        """
        Snap multiple vectors efficiently.
        
        Args:
            vectors: List of [x, y] pairs or Nx2 NumPy array
        
        Returns:
            List of (snapped_x, snapped_y, noise) tuples
        """
    
    @property
    def state_count(self) -> int:
        """Number of exact Pythagorean states in the manifold."""

Functions

def snap(manifold: PythagoreanManifold, x: float, y: float) -> tuple[float, float, float]:
    """Convenience function for one-off snapping."""

def generate_triples(max_c: int) -> list[tuple[int, int, int]]:
    """
    Generate primitive Pythagorean triples with hypotenuse <= max_c.
    
    Returns:
        List of (a, b, c) tuples where a² + b² = c²
    """

Performance

Benchmarks (Apple M1 Max)

Operation Time Throughput
Single snap ~100 ns 10M/sec
Batch 1,000 ~74 μs 13M/sec
Batch 10,000 ~740 μs 13M/sec
Batch 100,000 ~7.4 ms 13M/sec

Memory Usage

Density States Memory
100 ~500 ~40 KB
200 ~1000 ~80 KB
500 ~2500 ~200 KB
1000 ~5000 ~400 KB

Optimization Tips

  1. Reuse manifold instances - Construction is expensive
  2. Use batch operations - 2-5x faster than individual snaps
  3. Choose appropriate density - Balance resolution vs. memory
  4. Process in chunks - For very large datasets (>100K)

Troubleshooting

Common Issues

1. Import Error

ImportError: cannot import name 'PythagoreanManifold'

Solution: Ensure proper installation

pip install --upgrade constraint-theory

2. Construction Error

RuntimeError: density must be positive

Solution: Use positive density values

manifold = PythagoreanManifold(density=200)  # Correct

3. Wrong API Usage

# WRONG - dimensions parameter doesn't exist
manifold = PythagoreanManifold(dimensions=2)

# CORRECT - use density parameter
manifold = PythagoreanManifold(density=200)

4. NumPy Array Shape

# WRONG - 1D array
vectors = np.array([0.6, 0.8, 0.707, 0.707])

# CORRECT - Nx2 array
vectors = np.array([[0.6, 0.8], [0.707, 0.707]])

Resources

Documentation

Related Repositories


Examples

Run the included examples:

# Clone repository
git clone https://github.com/SuperInstance/constraint-theory-python.git
cd constraint-theory-python

# Run examples
python examples/quickstart.py
python examples/basic_usage.py
python examples/numpy_integration.py
python examples/batch_processing.py
python examples/game_dev.py
python examples/scientific.py
python examples/robotics.py

Contributing

# Install development dependencies
pip install -e ".[dev]"

# Run tests
pytest tests/

# Build from source
maturin develop --release

License

MIT License - See LICENSE for details.


Next Steps

  1. ✅ Install the package
  2. ✅ Run the quickstart example
  3. 📖 Explore the examples
  4. 🚀 Integrate into your project!

Happy coding with exact constraints! 🎉