| Crates.io | amari-holographic |
| lib.rs | amari-holographic |
| version | 0.17.0 |
| created_at | 2025-12-22 22:54:27.677757+00 |
| updated_at | 2026-01-11 22:36:04.517882+00 |
| description | Holographic reduced representations and vector symbolic architectures |
| homepage | https://github.com/justinelliottcobb/Amari |
| repository | https://github.com/justinelliottcobb/Amari |
| max_upload_size | |
| id | 2000419 |
| size | 286,829 |
Holographic Reduced Representations and Vector Symbolic Architectures for the Amari mathematical computing library.
amari-holographic provides implementations of various binding algebras for holographic memory and vector symbolic architectures (VSA). These high-dimensional distributed representations enable storing and retrieving associations through algebraic operations.
VSA systems use high-dimensional vectors (typically 1000+ dimensions) with three core operations:
| Operation | Symbol | Purpose | Property |
|---|---|---|---|
| Binding | ⊛ |
Create associations | Result dissimilar to inputs |
| Bundling | ⊕ |
Superposition | Result similar to all inputs |
| Similarity | sim(a,b) |
Compare representations | Cosine similarity |
HRR stores key-value associations in superposition:
memory = (key₁ ⊛ value₁) ⊕ (key₂ ⊛ value₂) ⊕ ... ⊕ (keyₙ ⊛ valueₙ)
Retrieval uses unbinding:
retrieved_value ≈ key⁻¹ ⊛ memory
Product of K copies of Cl(3,0,0). Provides O(64K) compute complexity with dimension 8K.
use amari_holographic::{ProductCliffordAlgebra, BindingAlgebra};
type ProductCl3x32 = ProductCliffordAlgebra<32>; // 256-dimensional
let key = ProductCl3x32::random_versor(2);
let value = ProductCl3x32::random_versor(2);
let bound = key.bind(&value);
// Retrieve
let retrieved = key.unbind(&bound)?;
assert!(retrieved.similarity(&value) > 0.9);
Advantages:
General Clifford algebras Cl(p,q,r) wrapping amari-core::Multivector.
use amari_holographic::{CliffordAlgebra, BindingAlgebra};
type Cl8 = CliffordAlgebra<8, 0, 0>; // 256-dimensional
let a = Cl8::random_versor(2);
let b = Cl8::random_versor(2);
let product = a.bind(&b);
Properties:
Optimized 3D Clifford algebra with fully unrolled operations.
use amari_holographic::{Cl3, BindingAlgebra};
let a = Cl3::random_versor(2);
let b = Cl3::random_versor(2);
let product = a.bind(&b); // Unrolled, no loops
Performance:
[scalar, e1, e2, e3, e12, e13, e23, e123]Fourier Holographic Reduced Representation using frequency-domain operations.
use amari_holographic::{FHRRAlgebra, BindingAlgebra};
type FHRR256 = FHRRAlgebra<256>;
let key = FHRR256::random_unitary();
let value = FHRR256::random_unitary();
let bound = key.bind(&value); // Element-wise complex multiplication
Properties:
Multiply-Add-Permute bipolar algebra with self-inverse property.
use amari_holographic::{MAPAlgebra, BindingAlgebra};
type MAP256 = MAPAlgebra<256>;
let key = MAP256::random_bipolar();
let value = MAP256::random_bipolar();
// Self-inverse: key.bind(key) ≈ identity
let bound = key.bind(&value);
let retrieved = key.bind(&bound); // Same as unbind!
Properties:
The HolographicMemory<A> type provides key-value storage in superposition:
use amari_holographic::{HolographicMemory, AlgebraConfig, ProductCliffordAlgebra, BindingAlgebra};
type ProductCl3x32 = ProductCliffordAlgebra<32>;
// Create memory
let mut memory = HolographicMemory::<ProductCl3x32>::new(AlgebraConfig::default());
// Store associations
let key1 = ProductCl3x32::random_versor(2);
let value1 = ProductCl3x32::random_versor(2);
memory.store(&key1, &value1);
let key2 = ProductCl3x32::random_versor(2);
let value2 = ProductCl3x32::random_versor(2);
memory.store(&key2, &value2);
// Retrieve
let result = memory.retrieve(&key1);
println!("Confidence: {}", result.confidence);
println!("Similarity to original: {}", result.value.similarity(&value1));
// Check capacity
let info = memory.capacity_info();
println!("Items: {} / {}", info.item_count, info.theoretical_capacity);
with_key_tracking() for attributionstore_batch(&pairs) for efficient bulk insertionmerge()Resonator networks clean up noisy retrievals by iteratively projecting toward valid codebook items:
use amari_holographic::{Resonator, ResonatorConfig, ProductCliffordAlgebra, BindingAlgebra};
type ProductCl3x32 = ProductCliffordAlgebra<32>;
// Create codebook of valid states
let codebook: Vec<ProductCl3x32> = (0..10)
.map(|_| ProductCl3x32::random_versor(2))
.collect();
// Create resonator
let config = ResonatorConfig {
max_iterations: 50,
convergence_threshold: 0.999,
initial_beta: 1.0,
final_beta: 100.0,
};
let resonator = Resonator::new(codebook, config)?;
// Clean up noisy input
let noisy = ProductCl3x32::random_versor(2); // Some noisy retrieval
let result = resonator.cleanup(&noisy);
println!("Converged: {}", result.converged);
println!("Best match index: {}", result.best_match_index);
println!("Final similarity: {}", result.final_similarity);
Resonators can factorize bound pairs to recover original factors:
let result = resonator_a.factorize(&bound_pair, &resonator_b);
println!("Factor A similarity: {}", result.factor_a.similarity(&original_a));
println!("Factor B similarity: {}", result.factor_b.similarity(&original_b));
The optical module provides GA-native optical field operations for Lee hologram encoding, enabling seamless integration with DMD-based optical systems.
Complex numbers are isomorphic to the even subalgebra of Cl(2,0):
Complex: z = a + bi where i² = -1
GA: R = a + b·e₁₂ where (e₁₂)² = -1
Isomorphism:
i ↔ e₁₂ (unit bivector)
z* ↔ R† (reverse/conjugate)
|z|² ↔ R·R† (norm squared)
An optical field at position (x,y) is represented as a rotor field:
E(x,y) = A(x,y) · exp(φ(x,y) · e₁₂)
= A(x,y) · (cos(φ) + sin(φ)·e₁₂)
Grade-separated rotor field for SIMD-optimized operations:
use amari_holographic::optical::{OpticalRotorField, OpticalFieldAlgebra};
// Create from phase array
let phases = vec![0.0, std::f32::consts::FRAC_PI_4, std::f32::consts::FRAC_PI_2];
let field = OpticalRotorField::from_phase(phases, (3, 1));
// Random field with deterministic seed
let random_field = OpticalRotorField::random((64, 64), 42);
// Access components
let phase = field.phase_at(1, 0); // π/4
let amplitude = field.amplitude_at(1, 0); // 1.0
VSA operations on optical rotor fields:
use amari_holographic::optical::{OpticalFieldAlgebra, OpticalRotorField};
let algebra = OpticalFieldAlgebra::new((64, 64));
let a = OpticalRotorField::random((64, 64), 1);
let b = OpticalRotorField::random((64, 64), 2);
// Binding = phase addition (rotor product)
let bound = algebra.bind(&a, &b);
// Inverse = phase negation (rotor reverse)
let inv = algebra.inverse(&a);
// bind(a, inverse(a)) ≈ identity
let identity = algebra.bind(&a, &inv);
// Similarity (normalized inner product)
let sim = algebra.similarity(&a, &a); // 1.0
// Bundling (weighted superposition)
let bundled = algebra.bundle(&[a.clone(), b.clone()], &[0.5, 0.5]);
| VSA Operation | Complex Form | GA Form |
|---|---|---|
| Binding | z₁ · z₂ | R₁ · R₂ (rotor product) |
| Unbinding | z₁ · z₂* | R₁ · R₂† (product with reverse) |
| Bundling | Σ wᵢzᵢ | Σ wᵢRᵢ (weighted sum) |
| Similarity | Re(z₁*z₂)/|z₁||z₂| | ⟨R₁†R₂⟩₀ / (|R₁||R₂|) |
Lee hologram encoding for DMD display:
use amari_holographic::optical::{GeometricLeeEncoder, OpticalRotorField};
// Create encoder with carrier frequency
let encoder = GeometricLeeEncoder::with_frequency((256, 256), 0.25);
// Create optical field (use amplitude 0.5 for optimal encoding)
let mut field = OpticalRotorField::random((256, 256), 42);
for i in 0..field.len() {
field.amplitudes_mut()[i] = 0.5;
}
// Encode to binary hologram
let hologram = encoder.encode(&field);
// Check encoding quality
println!("Fill factor: {}", hologram.fill_factor());
println!("Theoretical efficiency: {}", encoder.theoretical_efficiency(&field));
The Lee encoding process:
B(x,y) = 1 if ⟨Modulated⟩₀ > cos(π·A)Bit-packed binary pattern for hardware interface:
use amari_holographic::optical::BinaryHologram;
// Create from boolean pattern
let pattern = vec![true, false, true, false, true, true, false, false];
let hologram = BinaryHologram::from_bools(&pattern, (8, 1));
// Access pixels
assert!(hologram.get(0, 0));
assert!(!hologram.get(1, 0));
// Statistics
println!("Fill factor: {}", hologram.fill_factor());
println!("Popcount: {}", hologram.popcount());
// Hamming distance between holograms
let other = BinaryHologram::from_bools(&vec![true; 8], (8, 1));
println!("Hamming distance: {}", hologram.hamming_distance(&other));
Seed-based symbol mapping for compact serialization:
use amari_holographic::optical::{OpticalCodebook, CodebookConfig, SymbolId};
let config = CodebookConfig::new((64, 64), 12345);
let mut codebook = OpticalCodebook::new(config);
// Register symbols (deterministic from seed)
codebook.register("AGENT".into());
codebook.register("ACTION".into());
codebook.register("OBJECT".into());
// Get field for symbol (generated on first access, cached)
let agent_field = codebook.get(&"AGENT".into()).unwrap();
// Export seeds for persistence (compact!)
let seeds = codebook.export_seeds();
// Restore from seeds
let mut restored = OpticalCodebook::new(CodebookConfig::new((64, 64), 12345));
restored.import_seeds(seeds);
Tropical semiring operations for attractor dynamics:
use amari_holographic::optical::{TropicalOpticalAlgebra, OpticalRotorField};
let tropical = TropicalOpticalAlgebra::new((64, 64));
let a = OpticalRotorField::random((64, 64), 1);
let b = OpticalRotorField::random((64, 64), 2);
// Tropical add: point-wise minimum phase magnitude
let min_phase = tropical.tropical_add(&a, &b);
// Soft tropical add with temperature
let soft = tropical.soft_tropical_add(&a, &b, 10.0);
// Attractor convergence
let attractors = vec![
OpticalRotorField::uniform(0.0, 1.0, (64, 64)),
OpticalRotorField::uniform(std::f32::consts::FRAC_PI_4, 1.0, (64, 64)),
];
let initial = OpticalRotorField::random((64, 64), 42);
let (final_state, iterations) = tropical.attractor_converge(&initial, &attractors, 100, 1e-6);
BindingAlgebrapub trait BindingAlgebra: Clone + Send + Sync {
// Required methods
fn zero() -> Self;
fn identity() -> Self;
fn bind(&self, other: &Self) -> Self;
fn inverse(&self) -> AlgebraResult<Self>;
fn unbind(&self, other: &Self) -> AlgebraResult<Self>;
fn bundle(&self, other: &Self, beta: f64) -> AlgebraResult<Self>;
fn similarity(&self, other: &Self) -> f64;
fn norm(&self) -> f64;
fn normalize(&self) -> AlgebraResult<Self>;
fn permute(&self, shift: usize) -> Self;
fn dimension(&self) -> usize;
fn from_coefficients(coeffs: &[f64]) -> AlgebraResult<Self>;
fn to_coefficients(&self) -> Vec<f64>;
fn random_vector() -> Self;
fn random_versor(num_factors: usize) -> Self;
// Provided methods
fn bundle_all(items: &[Self], beta: f64) -> AlgebraResult<Self>;
fn estimate_snr(&self, num_items: usize) -> f64;
fn theoretical_capacity(&self) -> usize;
}
pub struct AlgebraConfig {
pub bundle_beta: f64, // Temperature for bundling (default: 1.0)
pub retrieval_beta: f64, // Temperature for retrieval (default: f64::INFINITY)
pub similarity_threshold: f64, // Threshold for "probably contains" (default: 0.5)
}
impl<A: BindingAlgebra> HolographicMemory<A> {
pub fn new(config: AlgebraConfig) -> Self;
pub fn with_key_tracking(config: AlgebraConfig) -> Self;
pub fn store(&mut self, key: &A, value: &A);
pub fn store_batch(&mut self, pairs: &[(A, A)]);
pub fn retrieve(&self, key: &A) -> RetrievalResult<A>;
pub fn retrieve_at_temperature(&self, key: &A, beta: f64) -> RetrievalResult<A>;
pub fn probably_contains(&self, key: &A) -> bool;
pub fn capacity_info(&self) -> CapacityInfo;
pub fn clear(&mut self);
pub fn merge(&mut self, other: &Self);
pub fn item_count(&self) -> usize;
pub fn trace(&self) -> &A;
}
pub struct RetrievalResult<A: BindingAlgebra> {
pub value: A, // Retrieved value (after cleanup)
pub raw_value: A, // Raw retrieved value
pub confidence: f64, // Estimated confidence [0, 1]
pub attribution: Vec<(usize, f64)>, // Which keys contributed
pub query_similarity: f64, // Similarity to query key
}
impl<A: BindingAlgebra> Resonator<A> {
pub fn new(codebook: Vec<A>, config: ResonatorConfig) -> HolographicResult<Self>;
pub fn cleanup(&self, noisy: &A) -> CleanupResult<A>;
pub fn factorize(&self, bound: &A, other: &Resonator<A>) -> FactorizationResult<A>;
pub fn codebook_size(&self) -> usize;
pub fn get_codebook_item(&self, index: usize) -> Option<&A>;
}
All algebras provide theoretical capacity of O(D / ln D) where D is dimension:
| Algebra | Dimension | Capacity (~items) |
|---|---|---|
| ProductCl3x32 | 256 | ~46 |
| ProductCl3x128 | 1024 | ~147 |
| FHRR1024 | 1024 | ~147 |
| MAP2048 | 2048 | ~280 |
[dependencies]
amari-holographic = { version = "0.15", features = ["parallel"] }
| Feature | Description |
|---|---|
std |
Standard library (default) |
parallel |
Parallel operations via rayon |
serialize |
Serde serialization support |
The optical module is always available and uses rand and rand_chacha for deterministic random generation.
// TropicalDualClifford has built-in binding operations
use amari_fusion::TropicalDualClifford;
let tdc1 = TropicalDualClifford::<f64, 8>::random_with_scale(1.0);
let tdc2 = TropicalDualClifford::<f64, 8>::random_with_scale(1.0);
let bound = tdc1.bind(&tdc2);
let similarity = tdc1.similarity(&tdc2);
use amari_gpu::{GpuHolographic, GpuHolographicMemory};
// GPU-accelerated batch operations
let gpu = GpuHolographic::new_product_cl3x32().await?;
let results = gpu.batch_bind(&keys_flat, &values_flat).await?;
let similarities = gpu.batch_similarity(&a_flat, &b_flat).await?;
// Store word associations
let mut memory = HolographicMemory::<ProductCl3x32>::new(AlgebraConfig::default());
let dog = ProductCl3x32::random_versor(2);
let animal = ProductCl3x32::random_versor(2);
let bark = ProductCl3x32::random_versor(2);
// dog IS-A animal
memory.store(&dog, &animal);
// dog CAN bark
let can = ProductCl3x32::random_versor(2);
memory.store(&dog.bind(&can), &bark);
// Query: what can dog do?
let query = dog.bind(&can);
let result = memory.retrieve(&query);
println!("Dog can: similarity to bark = {}", result.value.similarity(&bark));
// Represent structured knowledge: "John loves Mary"
let john = ProductCl3x32::random_versor(2);
let mary = ProductCl3x32::random_versor(2);
let agent_role = ProductCl3x32::random_versor(2);
let patient_role = ProductCl3x32::random_versor(2);
let loves_frame = ProductCl3x32::random_versor(2);
// Create frame
let sentence = loves_frame
.bind(&agent_role.bind(&john))
.bind(&patient_role.bind(&mary));
// Query: who is the agent?
let agent_query = loves_frame.bind(&agent_role);
let agent = agent_query.unbind(&sentence)?;
println!("Agent similarity to John: {}", agent.similarity(&john));
MIT OR Apache-2.0