Hypervector Representations¶
VSAX provides four hypervector representations, each designed for a specific VSA algebra. All representations inherit from AbstractHypervector and provide a consistent interface.
Overview¶
| Representation | Values | Use Case | Operations |
|---|---|---|---|
ComplexHypervector |
Complex unit-magnitude | FHRR (Fourier) | Circular convolution |
RealHypervector |
Real continuous | MAP | Element-wise multiply |
BinaryHypervector |
Bipolar {-1,+1} or Binary {0,1} | Binary VSA | XOR, majority vote |
QuaternionHypervector |
Unit quaternions | Quaternion VSA | Hamilton product (non-commutative) |
ComplexHypervector¶
Phase-based representation using complex numbers for FHRR (Fourier Holographic Reduced Representation).
Features¶
- Unit magnitude: All elements have magnitude 1.0
- Phase encoding: Information stored in phase (angle)
- Exact unbinding: Circular convolution is invertible via conjugate
- GPU-friendly: Leverages JAX's complex number support
Example¶
import jax
import jax.numpy as jnp
from vsax import ComplexHypervector, sample_complex_random
# Sample a complex vector
key = jax.random.PRNGKey(42)
vec = sample_complex_random(dim=512, n=1, key=key)[0]
# Create hypervector
hv = ComplexHypervector(vec)
# Normalize to unit magnitude (phase-only)
normalized = hv.normalize()
# Access properties
print(f"Phase: {hv.phase}") # Angles in [-π, π]
print(f"Magnitude: {hv.magnitude}") # All should be ~1.0
print(f"Shape: {hv.shape}") # (512,)
Properties¶
phase: Extract phase component (angles)magnitude: Extract magnitude componentvec: Underlying JAX arrayshape: Vector shapedtype: Data type (complex64 or complex128)
Methods¶
normalize(): Normalize to unit magnitude (phase-only representation)to_numpy(): Convert to NumPy array
RealHypervector¶
Continuous real-valued representation for MAP (Multiply-Add-Permute) operations.
Features¶
- L2 normalization: Vectors normalized to unit length
- Continuous values: Real-valued elements
- Approximate unbinding: MAP unbinding is approximate, not exact
- Simple operations: Element-wise multiplication and mean
Example¶
from vsax import RealHypervector, sample_random
# Sample a real vector
key = jax.random.PRNGKey(42)
vec = sample_random(dim=512, n=1, key=key)[0]
# Create hypervector
hv = RealHypervector(vec)
# L2 normalize
normalized = hv.normalize()
# Properties
print(f"L2 norm: {jnp.linalg.norm(normalized.vec)}") # Should be 1.0
print(f"Is complex: {jnp.iscomplexobj(hv.vec)}") # False
Methods¶
normalize(): L2 normalization to unit lengthto_numpy(): Convert to NumPy array
BinaryHypervector¶
Discrete binary representation for Binary VSA with XOR binding.
Features¶
- Exact unbinding: XOR is self-inverse
- Two modes: Bipolar {-1, +1} or Binary {0, 1}
- Hardware-friendly: Efficient for digital hardware
- Majority voting: Robust bundling via majority vote
Example¶
from vsax import BinaryHypervector, sample_binary_random
# Sample bipolar vectors
key = jax.random.PRNGKey(42)
vec = sample_binary_random(dim=512, n=1, key=key, bipolar=True)[0]
# Create bipolar hypervector
hv = BinaryHypervector(vec, bipolar=True)
# Check mode
print(f"Is bipolar: {hv.bipolar}") # True
# Convert between representations
binary_hv = hv.to_binary() # Convert to {0, 1}
bipolar_hv = binary_hv.to_bipolar() # Convert back to {-1, +1}
# Verify values
print(f"Values: {jnp.unique(hv.vec)}") # Array([-1, 1])
Conversion¶
# Bipolar {-1, +1} to Binary {0, 1}
# Formula: (x + 1) / 2
# Example: -1 → 0, +1 → 1
# Binary {0, 1} to Bipolar {-1, +1}
# Formula: 2*x - 1
# Example: 0 → -1, 1 → +1
Properties¶
bipolar: Check if using bipolar encodingvec: Underlying JAX arrayshape: Vector shapedtype: Data type (typically int32)
Methods¶
normalize(): No-op for binary (already normalized)to_bipolar(): Convert to {-1, +1} representationto_binary(): Convert to {0, 1} representationto_numpy(): Convert to NumPy array
Common Interface¶
All representations share a common interface via AbstractHypervector:
class AbstractHypervector:
@property
def vec(self) -> jnp.ndarray:
"""Access underlying JAX array"""
@property
def shape(self) -> tuple[int, ...]:
"""Vector shape"""
@property
def dtype(self):
"""Data type"""
def normalize(self) -> "AbstractHypervector":
"""Normalize the hypervector"""
def to_numpy(self) -> np.ndarray:
"""Convert to NumPy array"""
Choosing a Representation¶
Use ComplexHypervector when: - You need exact unbinding - Working with sequences or structured data - GPU acceleration is available - Circular convolution is suitable for your task
Use RealHypervector when: - You have continuous-valued data - Approximate unbinding is acceptable - Simple operations are preferred - Working with embeddings or features
Use BinaryHypervector when: - Deploying to hardware (FPGA, ASIC) - Memory constraints are tight - You need exact unbinding - Working with symbolic/discrete data
Use QuaternionHypervector when:
- Order matters in bindings (role/filler, subject/object)
- You need non-commutative binding: bind(x, y) ≠ bind(y, x)
- Exact unbinding from both directions (left and right) is needed
- Working with asymmetric relationships
Performance Considerations¶
| Representation | Memory | Computation | Unbinding |
|---|---|---|---|
| Complex | 2x (real+imag) | FFT overhead | Exact |
| Real | 1x | Fast multiply/add | Approximate |
| Binary | 1/32x (int vs float) | Fastest | Exact |
| Quaternion | 4x (4 components) | Hamilton product | Exact (left/right) |
Next Steps¶
- Learn about Operations for each representation
- See Examples for complete workflows
- Check API Reference for detailed docs