adv-mixer-3: sac0 (t-bit avalanche on the round-0 input) and queue 09

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-07 18:50:03 +00:00
parent c969914afe
commit 8c830edaea
2 changed files with 116 additions and 1 deletions

View file

@ -0,0 +1,17 @@
#!/usr/bin/env bash
# adv-mixer-3 queue file (owner: adv-mixer-3). Self-contained: binary, args, logs. Runs at nice 10 on cores 8-95 via run-box.sh.
set -uo pipefail
X=/srv/builds/_adv-mixer-3/bin/adv-mixer-3-v2; L=/srv/builds/_adv-mixer-3/logs; mkdir -p "$L"
echo "start $(hostname) $(date -u +%FT%TZ) $(sha256sum $X)"
for d in 20729 20733; do
echo "== sac0 plant k=0 (the init alone) day $d"; $X sac0 --day $d --apps 0 --states 65536 --threads 64 > "$L/sac0-$d-k0.log" 2>&1; grep -E 'holes=|verdict' "$L/sac0-$d-k0.log"
for k in 1 2 3 4 5 6 7 8; do
echo "== sac0 day $d k=$k states 2^24 $(date -u +%FT%TZ)"
$X sac0 --day $d --apps $k --states 2^24 --threads 64 > "$L/sac0-$d-k$k.log" 2>&1; echo "rc=$?"; grep -E 'holes=|line index|verdict' "$L/sac0-$d-k$k.log"
done
for k in 2 3 4; do
echo "== sac0 day $d k=$k states 2^28 $(date -u +%FT%TZ)"
$X sac0 --day $d --apps $k --states 2^28 --threads 64 > "$L/sac0-$d-k$k-n28.log" 2>&1; echo "rc=$?"; grep -E 'holes=|line index|verdict' "$L/sac0-$d-k$k-n28.log"
done
done
echo "end $(date -u +%FT%TZ)"

View file

@ -10,6 +10,7 @@
//!
//! adv-mixer-3 index --day D --apps k --threads T exhaustive line index census over all 2^32 t (round 0)
//! adv-mixer-3 sac --day D --apps k --states N --threads T single-bit avalanche matrix on random states
//! adv-mixer-3 sac0 --day D --apps k --states N --threads T t-bit avalanche on the round-0 input (32 bits in)
//! adv-mixer-3 diff --day D --apps k --samples N --threads T differential multiplicity for low-weight input differences
//! adv-mixer-3 lin --day D --apps k --samples N --threads T single-bit linear correlations on random states
//! adv-mixer-3 lin0 --day D --apps k --samples N --threads T t-bit to state-bit correlations on the round-0 input
@ -398,6 +399,102 @@ fn cmd_sac(a: &Args) {
println!("verdict: {verdict}");
}
// ------------------------------------------------------------------------------------------------------------------
// sac0: t-bit avalanche on the round-0 input (32 input bits, the item's whole entropy before the first read)
// ------------------------------------------------------------------------------------------------------------------
fn cmd_sac0(a: &Args) {
let day = a.u64("day", 20729);
let plant = Plant::parse(&a.str("plant", "none"));
let k = a.usize("apps", 8);
let start = a.usize("start", 0);
let threads = a.usize("threads", 32);
let n = a.u64("states", 1 << 20);
let mp = params_of_day(day, plant);
header(a, &mp, plant, start, k);
let ks = keys(start, k, plant);
let t0 = Instant::now();
let per = n / threads as u64;
let mats: Vec<Vec<u32>> = std::thread::scope(|sc| {
let hs: Vec<_> = (0..threads)
.map(|th| {
let mp = &mp;
let ks = &ks;
sc.spawn(move || {
let mut m = vec![0u32; 32 * 512];
let mut rng = seed_thread(day, 6, th as u64);
let cnt = if th + 1 == threads { n - per * (threads as u64 - 1) } else { per };
for _ in 0..cnt {
let t = rng.next() as u32;
let mut y0 = init(t, mp);
apply(&mut y0, ks, mp, plant);
for i in 0..32 {
let mut yi = init(t ^ (1u32 << i), mp);
apply(&mut yi, ks, mp, plant);
let row = &mut m[i * 512..(i + 1) * 512];
for w in 0..16 {
let mut d = yi[w] ^ y0[w];
while d != 0 {
let b = d.trailing_zeros() as usize;
row[w * 32 + b] += 1;
d &= d - 1;
}
}
}
}
m
})
})
.collect();
hs.into_iter().map(|h| h.join().unwrap()).collect()
});
let mut m = vec![0u64; 32 * 512];
for mm in &mats {
for i in 0..m.len() {
m[i] += mm[i] as u64;
}
}
let nf = n as f64;
let sq = nf.sqrt();
let mut holes = 0usize;
let mut beyond6 = 0usize;
let mut worst = (0usize, 0usize, 0f64);
let mut idx_worst = 0f64;
let mut idx_beyond6 = 0usize;
let mut sum = 0f64;
for i in 0..32 {
for j in 0..512 {
let c = m[i * 512 + j];
if c == 0 || c == n {
holes += 1;
}
let p = c as f64 / nf;
sum += p;
let z = (2.0 * p - 1.0) * sq;
if z.abs() > 6.0 {
beyond6 += 1;
}
if z.abs() > worst.2.abs() {
worst = (i, j, z);
}
if j < 22 {
if z.abs() > idx_worst.abs() {
idx_worst = z;
}
if z.abs() > 6.0 {
idx_beyond6 += 1;
}
}
}
}
let worst_p = m[worst.0 * 512 + worst.1] as f64 / nf;
println!("sac0: states={n} wall={:.1}s band(6 sigma)={:.5} inputs=32 t bits", t0.elapsed().as_secs_f64(), 6.0 / (2.0 * sq));
println!("holes={holes} beyond6={beyond6} mean_flip={:.5} worst t-bit={} out={} (word {}) p={worst_p:.5} z={:.2}", sum / (32.0 * 512.0), worst.0, worst.1, worst.1 / 32, worst.2);
println!("line index cells (out bits 0..21): worst z={idx_worst:.2} beyond6={idx_beyond6} of {}", 32 * 22);
let verdict = if holes > 0 || beyond6 > 0 { "DISTINGUISHED" } else { "no cell beyond 6 sigma" };
println!("verdict: {verdict}");
}
// ------------------------------------------------------------------------------------------------------------------
// diff: differential multiplicity
// ------------------------------------------------------------------------------------------------------------------
@ -1005,6 +1102,7 @@ fn main() {
match a.cmd.as_str() {
"index" => cmd_index(&a),
"sac" => cmd_sac(&a),
"sac0" => cmd_sac0(&a),
"diff" => cmd_diff(&a),
"lin" => lin_core(&a, false),
"lin0" => lin_core(&a, true),
@ -1014,7 +1112,7 @@ fn main() {
"model" => cmd_model(&a),
"params" => cmd_params(&a),
_ => {
eprintln!("commands: index sac diff lin lin0 rx cnf verify model params (see the header of src/main.rs)");
eprintln!("commands: index sac sac0 diff lin lin0 rx cnf verify model params (see the header of src/main.rs)");
std::process::exit(2);
}
}