Complete API documentation for Constraint Theory Python bindings.
- Schema Alignment
- Installation
- Module Overview
- Classes
- Functions
- Type Aliases
- Protocol Classes
- Error Handling
- Thread Safety
- Performance Characteristics
- Type Hints
This Python API is designed to match the Rust core (constraint-theory-core) exactly.
| Python | Rust Core | Notes |
|---|---|---|
PythagoreanManifold(density) |
PythagoreanManifold::new(density) |
Constructor |
manifold.snap(x, y) |
manifold.snap([x, y]) |
Python uses separate args |
manifold.snap_batch(vectors) |
manifold.snap_batch_simd(vectors) |
SIMD batch processing |
manifold.state_count |
manifold.state_count() |
Python property vs Rust method |
manifold.density |
self.density |
Python property |
| Rust Type | Python Type | Notes |
|---|---|---|
usize |
int |
Density parameter |
f32 |
float |
Rust uses 32-bit, Python uses 64-bit floats |
[f32; 2] |
Tuple[float, float] |
Fixed array to tuple |
Vec<([f32; 2], f32)> |
List[Tuple[float, float, float]] |
Batch results |
i32 |
int |
Triple components |
| Aspect | WASM Bindings | PyO3 Bindings |
|---|---|---|
| Vector Input | Float32Array |
Python list/tuple or NumPy array |
| Vector Output | Float32Array |
Python tuple |
| GIL | N/A | Released for batch operations |
| Memory | Shared ArrayBuffer | Copied at FFI boundary |
| Async | Native Promise support | Use run_in_executor |
pip install constraint-theoryFor development installation:
git clone https://github.com/SuperInstance/constraint-theory-python
cd constraint-theory-python
pip install maturin
maturin develop --releasefrom constraint_theory import (
PythagoreanManifold, # Main manifold class
snap, # Convenience snapping function
generate_triples, # Pythagorean triple generator
__version__, # Package version string
)The main class for creating and using a Pythagorean manifold for deterministic vector snapping.
PythagoreanManifold(density: int)Creates a new Pythagorean manifold with the specified density parameter.
Parameters:
| Parameter | Type | Description |
|---|---|---|
density |
int |
Maximum value of m in Euclid's formula. Controls the resolution and number of exact states. Higher values = more states = finer resolution. |
Returns:
| Type | Description |
|---|---|
PythagoreanManifold |
A new manifold instance with pre-computed valid states |
Raises:
| Exception | Condition |
|---|---|
ValueError |
If density is 0 or negative (via Rust panic) |
Example:
from constraint_theory import PythagoreanManifold
# Create a manifold with moderate density
manifold = PythagoreanManifold(density=200)
# Check the number of pre-computed states
print(f"States: {manifold.state_count}")
# Output: States: 1013Density 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 |
| 2000 | ~10000 | 0.0005 | High-precision CAD |
manifold.state_count -> intReturns the number of valid Pythagorean states in the manifold.
Type: int (read-only)
Description:
Each state represents a unique point on the unit circle that corresponds to a normalized Pythagorean triple (a/c, b/c) where a² + b² = c². The count includes points in all quadrants.
Example:
manifold = PythagoreanManifold(100)
count = manifold.state_count
print(f"Manifold contains {count} exact states")manifold.snap(x: float, y: float) -> tuple[float, float, float]Snap a 2D vector to the nearest Pythagorean triple state.
Parameters:
| Parameter | Type | Description |
|---|---|---|
x |
float |
X coordinate of the input vector |
y |
float |
Y coordinate of the input vector |
Returns:
| Type | Description |
|---|---|
tuple[float, float, float] |
A tuple of (snapped_x, snapped_y, noise) |
Return Values:
| Value | Type | Description |
|---|---|---|
snapped_x |
float |
X coordinate of the snapped point on the unit circle |
snapped_y |
float |
Y coordinate of the snapped point on the unit circle |
noise |
float |
Distance from input to snapped point (snapping error) |
Notes:
- Input vectors are NOT normalized internally; the snap distance is computed from the raw input
- The snapped point always satisfies
snapped_x² + snapped_y² = 1.0exactly - The noise value indicates how far your input was from an exact Pythagorean state
- Noise of 0.0 means the input was already an exact state
Example:
manifold = PythagoreanManifold(200)
# Snap an approximate vector
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
# Snap an exact Pythagorean triple
x, y, noise = manifold.snap(0.6, 0.8)
print(f"Snapped: ({x:.4f}, {y:.4f}), noise: {noise:.6f}")
# Output: Snapped: (0.6000, 0.8000), noise: 0.0000Performance: O(log n) where n is the number of states, typically ~100ns per call.
manifold.snap_batch(vectors: list[list[float]] | numpy.ndarray) -> list[tuple[float, float, float]]Snap multiple vectors at once with optimized batch processing.
Parameters:
| Parameter | Type | Description |
|---|---|---|
vectors |
list[list[float]] or numpy.ndarray |
Collection of 2D vectors. Either a list of [x, y] pairs or an Nx2 NumPy array. |
Returns:
| Type | Description |
|---|---|
list[tuple[float, float, float]] |
List of (snapped_x, snapped_y, noise) tuples, one per input vector |
Example:
import numpy as np
from constraint_theory import PythagoreanManifold
manifold = PythagoreanManifold(200)
# Using a list of pairs
vectors = [[0.6, 0.8], [0.8, 0.6], [0.1, 0.99]]
results = manifold.snap_batch(vectors)
for i, (sx, sy, noise) in enumerate(results):
print(f"[{i}] ({vectors[i][0]}, {vectors[i][1]}) -> ({sx:.4f}, {sy:.4f})")
# Using NumPy array
np_vectors = np.array([[0.6, 0.8], [0.707, 0.707]])
np_results = manifold.snap_batch(np_vectors)Performance: Approximately 2-5x faster than individual snap() calls due to reduced Python-Rust boundary crossings.
Memory: For very large datasets, consider processing in chunks to avoid memory pressure.
repr(manifold) -> str
str(manifold) -> strReturns a string representation of the manifold.
Returns:
| Type | Description |
|---|---|
str |
String like "PythagoreanManifold(density=200, states=1013)" |
snap(manifold: PythagoreanManifold, x: float, y: float) -> tuple[float, float, float]Convenience function for one-off snapping operations.
Parameters:
| Parameter | Type | Description |
|---|---|---|
manifold |
PythagoreanManifold |
The manifold to use for snapping |
x |
float |
X coordinate of the input vector |
y |
float |
Y coordinate of the input vector |
Returns:
| Type | Description |
|---|---|
tuple[float, float, float] |
Same as manifold.snap(x, y) |
Example:
from constraint_theory import PythagoreanManifold, snap
manifold = PythagoreanManifold(200)
result = snap(manifold, 0.577, 0.816)
# Equivalent to: result = manifold.snap(0.577, 0.816)Note: For multiple snaps, prefer calling manifold.snap() directly or using manifold.snap_batch() for better performance.
generate_triples(max_c: int) -> list[tuple[int, int, int]]Generate all primitive Pythagorean triples where the hypotenuse c is less than or equal to max_c.
Parameters:
| Parameter | Type | Description |
|---|---|---|
max_c |
int |
Maximum value of the hypotenuse c |
Returns:
| Type | Description |
|---|---|
list[tuple[int, int, int]] |
List of (a, b, c) tuples where a² + b² = c² |
Notes:
- Only primitive triples are generated (where gcd(a, b, c) = 1)
- In each tuple,
a < b(smaller leg first) - Triples are generated using Euclid's formula with coprime m, n where m > n
Example:
from constraint_theory import generate_triples
# Generate triples with hypotenuse <= 50
triples = generate_triples(50)
for a, b, c in triples[:5]:
print(f"{a}² + {b}² = {c}²")
# Output:
# 3² + 4² = 5²
# 5² + 12² = 13²
# 8² + 15² = 17²
# 7² + 24² = 25²
# 20² + 21² = 29²
print(f"\nTotal triples: {len(triples)}")Mathematical Background:
Pythagorean triples are generated using Euclid's formula:
a = m² - n²
b = 2mn
c = m² + n²
where m > n > 0, gcd(m, n) = 1, and exactly one of m, n is even.
The library provides type aliases for clarity in type hints:
from constraint_theory import VectorLike
# Type: Union[List[Tuple[float, float]], numpy.ndarray]
# Represents input that can be a list of (x, y) tuples or NumPy arrayUsage:
from typing import List
from constraint_theory import PythagoreanManifold, VectorLike
def process_vectors(vectors: VectorLike) -> List[tuple]:
manifold = PythagoreanManifold(200)
return manifold.snap_batch(vectors)from constraint_theory import SnapResultTuple
# Type: Tuple[float, float, float]
# Represents a snap result: (snapped_x, snapped_y, noise)Usage:
from constraint_theory import PythagoreanManifold, SnapResultTuple
def snap_and_validate(manifold: PythagoreanManifold, x: float, y: float, max_noise: float = 0.1) -> SnapResultTuple:
result = manifold.snap(x, y)
if result[2] > max_noise:
raise ValueError(f"Noise {result[2]} exceeds threshold {max_noise}")
return resultfrom constraint_theory import PythagoreanTripleTuple
# Type: Tuple[int, int, int]
# Represents a Pythagorean triple: (a, b, c) where a² + b² = c²Protocol classes enable structural subtyping for type checking:
from constraint_theory import ManifoldProtocol
# Protocol defining the manifold interface
# Matches the Rust trait implicitlyAttributes:
| Attribute | Type | Description |
|---|---|---|
state_count |
int |
Number of valid Pythagorean states |
density |
int |
Density parameter used in construction |
Methods:
| Method | Signature | Description |
|---|---|---|
snap |
(x: float, y: float) -> Tuple[float, float, float] |
Snap single vector |
snap_batch |
(vectors: List[Tuple[float, float]]) -> List[Tuple[float, float, float]] |
Batch snap |
Usage:
from typing import Protocol
from constraint_theory import ManifoldProtocol
def process_with_manifold(manifold: ManifoldProtocol, x: float, y: float) -> float:
"""Works with any manifold-like object."""
_, _, noise = manifold.snap(x, y)
return noisefrom constraint_theory import SnapResult
# Protocol for snap result - supports indexing and len()
# Enables duck typing with tuplesfrom constraint_theory import Vector2D
# Protocol for 2D vector input - supports indexing
# Works with tuples, lists, NumPy arraysfrom constraint_theory import __version__
print(__version__) # e.g., "0.1.0"The package version string, following semantic versioning.
from constraint_theory import CORE_MIN_VERSION, CORE_MAX_VERSION
print(f"Compatible with core {CORE_MIN_VERSION} to {CORE_MAX_VERSION}")
# Output: Compatible with core (1, 0, 0) to (2, 0, 0)Version bounds for the Rust core library compatibility.
| Exception | Cause | Resolution |
|---|---|---|
ImportError |
Package not installed | Run pip install constraint-theory |
TypeError |
Wrong argument types | Check parameter types in API |
ValueError |
Invalid parameter values | Ensure density > 0 |
RuntimeError |
Rust panic | Report as bug with reproduction case |
The Python bindings perform type checking at the Python-Rust boundary:
manifold = PythagoreanManifold(200)
# Valid
manifold.snap(0.5, 0.8) # Two floats
manifold.snap_batch([[0.5, 0.8]]) # List of pairs
# Invalid - raises TypeError
manifold.snap("0.5", 0.8) # String instead of float
manifold.snap_batch([0.5, 0.8]) # Single list, not list of listsThe PythagoreanManifold class is fully thread-safe for read operations:
from concurrent.futures import ThreadPoolExecutor
from constraint_theory import PythagoreanManifold
manifold = PythagoreanManifold(200)
def snap_vectors(vectors):
"""Safe for parallel execution."""
return [manifold.snap(x, y) for x, y in vectors]
# Multiple threads can safely share the manifold
with ThreadPoolExecutor(max_workers=8) as executor:
results = list(executor.map(snap_vectors, data_chunks))- The Rust core uses immutable data structures after construction
- All KD-tree lookups are read-only
- No internal state is modified during
snap()orsnap_batch()calls - The GIL is released during expensive computations in Rust
| Operation | Complexity | Typical Time |
|---|---|---|
PythagoreanManifold(density) |
O(density² log density) | 1-50ms |
snap() |
O(log n) | ~100ns |
snap_batch() |
O(m log n) | ~30ns per vector |
generate_triples(max_c) |
O(max_c) | ~1μs per 1000 |
Where n is the number of states and m is the batch size.
| Density | States | Memory |
|---|---|---|
| 100 | ~500 | ~40 KB |
| 200 | ~1000 | ~80 KB |
| 500 | ~2500 | ~200 KB |
| 1000 | ~5000 | ~400 KB |
| 2000 | ~10000 | ~800 KB |
- Reuse manifold instances: Construction is expensive; reuse across calls
- Use batch operations:
snap_batch()is 2-5x faster than individualsnap()calls - Choose appropriate density: Higher density = more memory + slower construction
- Process in chunks: For datasets > 100K vectors, use chunked processing
- Release GIL: Long-running batch operations release the GIL for parallelism
import time
from constraint_theory import PythagoreanManifold
manifold = PythagoreanManifold(200)
# Single snaps
start = time.perf_counter()
for _ in range(10000):
manifold.snap(0.577, 0.816)
single_time = time.perf_counter() - start
# Batch snapping
vectors = [[0.577, 0.816] for _ in range(10000)]
start = time.perf_counter()
manifold.snap_batch(vectors)
batch_time = time.perf_counter() - start
print(f"Single: {single_time*1000:.2f}ms")
print(f"Batch: {batch_time*1000:.2f}ms")
print(f"Speedup: {single_time/batch_time:.1f}x")The library provides comprehensive type hints for static analysis:
from typing import List, Tuple, Union
from numpy.typing import NDArray
import numpy as np
# Type aliases for clarity
Vector2D = Tuple[float, float]
SnapResult = Tuple[float, float, float] # (x, y, noise)
VectorLike = Union[List[Vector2D], NDArray[np.floating]]
class PythagoreanManifold:
def __init__(self, density: int) -> None: ...
def snap(self, x: float, y: float) -> SnapResult: ...
def snap_batch(self, vectors: VectorLike) -> List[SnapResult]: ...
@property
def state_count(self) -> int: ...
def snap(manifold: PythagoreanManifold, x: float, y: float) -> SnapResult: ...
def generate_triples(max_c: int) -> List[Tuple[int, int, int]]: ...# Install mypy
pip install mypy
# Type check your code
mypy your_script.pyfrom constraint_theory import PythagoreanManifold, snap, generate_triples
from typing import List, Tuple
def process_vectors(vectors: List[Tuple[float, float]]) -> List[Tuple[float, float, float]]:
"""Process vectors with full type safety."""
manifold = PythagoreanManifold(density=200)
return manifold.snap_batch(vectors)
# mypy will verify type correctness
results = process_vectors([(0.6, 0.8), (0.707, 0.707)])| Version | Changes |
|---|---|
| 0.1.0 | Initial release with core snapping functionality |
| 0.2.0 | Added batch processing with NumPy support (planned) |
| 0.3.0 | Performance optimizations and expanded documentation (planned) |
- Migration Guide - For users coming from other libraries
- Examples - Practical code examples
- Constraint Theory Core - Rust implementation
- Web Demos - Interactive visualizations
- Research Papers - Mathematical foundations