use holographic_memory::EntangledHVec;
const DIM: usize = 16_384;
const DENSITY_DENOM: usize = 256; const N_SYMBOLS: usize = 2048;
const ROLE1_XOR_SEED: u64 = 0x1111_1111;
const ROLE2_XOR_SEED: u64 = 0x2222_2222;
const ROLE1_PERM_SEED: u64 = 0xA5A5_A5A5;
const ROLE2_PERM_SEED: u64 = 0x5A5A_5A5A;
const ROLE1_INV_SEED: u64 = 0xC0DE_1111;
const ROLE2_INV_SEED: u64 = 0xC0DE_2222;
const SEEDS: u64 = 8;
const LOADS: &[usize] = &[40, 80, 160, 320, 640, 1280];
struct Fact {
a: usize,
b: usize,
}
fn build_facts(n: usize, seed: u64) -> Vec<Fact> {
(0..n)
.map(|i| {
let a = (mix(i as u64, seed ^ 0xF11E) % N_SYMBOLS as u64) as usize;
let mut b = (mix(i as u64, seed ^ 0xB0B0) % N_SYMBOLS as u64) as usize;
if b == a {
b = (b + 1) % N_SYMBOLS;
}
Fact { a, b }
})
.collect()
}
fn mix(x: u64, k: u64) -> u64 {
let mut h = x.wrapping_mul(0x9E37_79B9_7F4A_7C15) ^ k.wrapping_mul(0xC2B2_AE3D_27D4_EB4F);
h ^= h >> 29;
h = h.wrapping_mul(0xBF58_476D_1CE4_E5B9);
h ^= h >> 32;
h
}
fn thin(hv: &EntangledHVec, k: usize) -> EntangledHVec {
let mut idx: Vec<u32> = hv.indices().to_vec();
if idx.len() <= k {
return hv.clone();
}
idx.sort_by_key(|&i| mix(i as u64, 0x7417_5A17));
idx.truncate(k);
idx.sort_unstable();
EntangledHVec::from_indices(idx, DIM)
}
fn dprime(correct: &[f64], misbound: &[f64]) -> f64 {
let mean = |v: &[f64]| v.iter().sum::<f64>() / v.len() as f64;
let var = |v: &[f64], m: f64| v.iter().map(|x| (x - m).powi(2)).sum::<f64>() / v.len() as f64;
let mc = mean(correct);
let mm = mean(misbound);
let pooled = 0.5 * (var(correct, mc) + var(misbound, mm));
let sd = pooled.sqrt().max(1e-3);
((mc - mm) / sd).clamp(0.0, 50.0)
}
fn run_b0(n: usize, seed: u64, codebook: &[EntangledHVec]) -> f64 {
let role1 = EntangledHVec::new_with_density(DIM, DENSITY_DENOM, ROLE1_XOR_SEED ^ seed);
let role2 = EntangledHVec::new_with_density(DIM, DENSITY_DENOM, ROLE2_XOR_SEED ^ seed);
let facts = build_facts(n, seed);
let mut pairs = Vec::with_capacity(2 * n);
for f in &facts {
pairs.push(role1.bind(&codebook[f.a]));
pairs.push(role2.bind(&codebook[f.b]));
}
let bundle = EntangledHVec::bundle(&pairs);
let mut correct = Vec::with_capacity(n);
let mut misbound = Vec::with_capacity(n);
for f in &facts {
let residue = bundle.bind(&role1);
correct.push(residue.similarity(&codebook[f.a]));
misbound.push(residue.similarity(&codebook[f.b]));
}
dprime(&correct, &misbound)
}
fn run_b0_thin(n: usize, seed: u64, codebook: &[EntangledHVec]) -> f64 {
let role1 = EntangledHVec::new_with_density(DIM, DENSITY_DENOM, ROLE1_XOR_SEED ^ seed);
let role2 = EntangledHVec::new_with_density(DIM, DENSITY_DENOM, ROLE2_XOR_SEED ^ seed);
let facts = build_facts(n, seed);
let k = DIM / DENSITY_DENOM;
let mut pairs = Vec::with_capacity(2 * n);
for f in &facts {
pairs.push(thin(&role1.bind(&codebook[f.a]), k));
pairs.push(thin(&role2.bind(&codebook[f.b]), k));
}
let bundle = EntangledHVec::bundle_bloom(&pairs);
let mut correct = Vec::with_capacity(n);
let mut misbound = Vec::with_capacity(n);
for f in &facts {
correct.push(thin(&role1.bind(&codebook[f.a]), k).corrected_containment(&bundle));
misbound.push(thin(&role1.bind(&codebook[f.b]), k).corrected_containment(&bundle));
}
dprime(&correct, &misbound)
}
fn run_inv(n: usize, seed: u64, codebook: &[EntangledHVec]) -> f64 {
let mask1 = ((mix(seed, ROLE1_INV_SEED) % DIM as u64) as u32).max(1);
let mask2 = ((mix(seed, ROLE2_INV_SEED) % DIM as u64) as u32).max(1);
let facts = build_facts(n, seed);
let inv = |hv: &EntangledHVec, mask: u32| -> EntangledHVec {
let mut idx: Vec<u32> = hv.indices().iter().map(|&i| i ^ mask).collect();
idx.sort_unstable();
EntangledHVec::from_indices(idx, DIM)
};
let mut pairs = Vec::with_capacity(2 * n);
for f in &facts {
pairs.push(inv(&codebook[f.a], mask1));
pairs.push(inv(&codebook[f.b], mask2));
}
let bundle = EntangledHVec::bundle_bloom(&pairs);
let mut correct = Vec::with_capacity(n);
let mut misbound = Vec::with_capacity(n);
for f in &facts {
correct.push(inv(&codebook[f.a], mask1).corrected_containment(&bundle));
misbound.push(inv(&codebook[f.b], mask1).corrected_containment(&bundle));
}
dprime(&correct, &misbound)
}
fn run_x(n: usize, seed: u64, codebook: &[EntangledHVec]) -> f64 {
let r1 = ROLE1_PERM_SEED ^ seed;
let r2 = ROLE2_PERM_SEED ^ seed;
let facts = build_facts(n, seed);
let mut pairs = Vec::with_capacity(2 * n);
for f in &facts {
pairs.push(codebook[f.a].hash_permute(r1));
pairs.push(codebook[f.b].hash_permute(r2));
}
let bundle = EntangledHVec::bundle_bloom(&pairs);
let mut correct = Vec::with_capacity(n);
let mut misbound = Vec::with_capacity(n);
for f in &facts {
correct.push(
codebook[f.a]
.hash_permute(r1)
.corrected_containment(&bundle),
);
misbound.push(
codebook[f.b]
.hash_permute(r1)
.corrected_containment(&bundle),
);
}
dprime(&correct, &misbound)
}
fn mean_sd(v: &[f64]) -> (f64, f64) {
let m = v.iter().sum::<f64>() / v.len() as f64;
let sd = (v.iter().map(|x| (x - m).powi(2)).sum::<f64>() / v.len() as f64).sqrt();
(m, sd)
}
fn main() {
println!(
"binding-discriminator | D={DIM} density=1/{DENSITY_DENOM} symbols={N_SYMBOLS} seeds={SEEDS}"
);
println!("d' = discrimination of correct-binding vs mis-binding (chance floor 0).");
println!("B0nat = XOR native stack (majority+unbind). B0thin = XOR thinned to k through");
println!("X's bloom+containment stack (density- and readout-matched to X).\n");
println!("Inv = self-inverse involution permutation (idx^mask) through X's stack; same as");
println!("X but self-inverse. Inv vs X isolates non-self-inverse-ness; B0thin vs Inv");
println!("isolates index-union vs role-relocation.\n");
println!(
"{:>5} {:>15} {:>15} {:>15} {:>15}",
"N", "B0nat XOR d'", "B0thin XOR d'", "Inv perm d'", "X perm d'"
);
for &n in LOADS {
let mut b0_d = Vec::with_capacity(SEEDS as usize);
let mut b0t_d = Vec::with_capacity(SEEDS as usize);
let mut inv_d = Vec::with_capacity(SEEDS as usize);
let mut x_d = Vec::with_capacity(SEEDS as usize);
for seed in 0..SEEDS {
let codebook: Vec<EntangledHVec> = (0..N_SYMBOLS)
.map(|i| EntangledHVec::new_with_density(DIM, DENSITY_DENOM, mix(i as u64, seed)))
.collect();
b0_d.push(run_b0(n, seed, &codebook));
b0t_d.push(run_b0_thin(n, seed, &codebook));
inv_d.push(run_inv(n, seed, &codebook));
x_d.push(run_x(n, seed, &codebook));
}
let (b0m, b0s) = mean_sd(&b0_d);
let (b0tm, b0ts) = mean_sd(&b0t_d);
let (invm, invs) = mean_sd(&inv_d);
let (xm, xs) = mean_sd(&x_d);
println!(
"{n:>5} {:>8.2} ({:>4.2}) {:>8.2} ({:>4.2}) {:>8.2} ({:>4.2}) {:>8.2} ({:>4.2})",
b0m, b0s, b0tm, b0ts, invm, invs, xm, xs
);
}
println!("\nB0thin = XOR at X's density+readout (isolates bind vs density/readout).");
println!("Inv = self-inverse relocation; X = non-self-inverse relocation. Inv~X would mean");
println!("the driver is role-relocation, NOT non-self-inverse-ness (reframes hypothesis).");
}