adv-accept: attempts command (row 90: per-part rejections, cap, last resort, forced-exhaust plant)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-07 18:47:44 +00:00
parent ff81c2dea1
commit 67d40c9b97

View file

@ -328,6 +328,7 @@ fn main() {
"sweep" => sweep(&args[2..]),
"plant" => plant(&args[2..]),
"gap" => gap_cmd(&args[2..]),
"attempts" => attempts_cmd(&args[2..]),
_ => {
eprintln!("unknown command {}", args[1]);
std::process::exit(2);
@ -582,3 +583,127 @@ fn gap_cmd(args: &[String]) {
gap::run(&p, day, threads, zero);
}
}
// --------------------------------------------------------------------------------------------------------------
// Row 90 (owner adv-accept-3, unspawned; claimed by this lane): the attempt loop walked through the real rule,
// attempt by attempt, recording WHICH part rejected each candidate (the first failing test in the module's
// order), the attempt count of the accepted program, any seed reaching the 256 cap, and the last-resort program.
// The draw and every verdict are the library's (candidate_class, accept::check, last_resort_v4).
// Known-failed shape: `--force-exhaust` treats every verdict as a rejection, so the loop MUST hit the cap and
// print the last-resort program's shape (what a rule that rejects everything would hand the chain).
// --------------------------------------------------------------------------------------------------------------
fn attempts_cmd(args: &[String]) {
use igneum_pow::generator::{candidate_class, last_resort_v4, LoadClass, GENERATOR_VERSION_V4, MAX_ATTEMPTS_V4};
let get = |k: &str, d: &str| -> String { args.windows(2).find(|w| w[0] == k).map(|w| w[1].clone()).unwrap_or_else(|| d.to_string()) };
let seed_start: u64 = get("--seed-start", "2").parse().unwrap();
let seeds: u64 = get("--seeds", "1000").parse().unwrap();
let threads: usize = get("--threads", "32").parse().unwrap();
let force_exhaust = args.iter().any(|a| a == "--force-exhaust");
let out = get("--out", "");
println!("adv-accept attempts: seeds {seed_start}..{} ({seeds}), {threads} threads, cap {MAX_ATTEMPTS_V4}, force-exhaust {force_exhaust}", seed_start + seeds);
let t0 = Instant::now();
let next = AtomicUsize::new(0);
// per attempt index (0..=MAX): counts of each reject class; the accepted attempt histogram; exhaustions
const CLASSES: usize = 11;
fn class_of(r: &Reject) -> usize {
match r {
Reject::StaleLoadSource { .. } => 0,
Reject::NoInjectingWrite { .. } => 1,
Reject::UnfreshLoadSource { .. } => 2,
Reject::ConstantBit { .. } => 3,
Reject::LaneConstantSite { .. } => 4,
Reject::Saturated { .. } => 5,
Reject::SaturatedSource { .. } => 6,
Reject::RepeatedSource { .. } => 7,
Reject::LowEntropySite { .. } => 8,
Reject::OutputBias { .. } => 9,
Reject::DistinctAddresses { .. } => 10,
}
}
const NAMES: [&str; CLASSES] = ["(a) stale", "(b) no-inject", "(a') unfresh", "(c) const-bit", "(c) lane-const", "(c) saturated", "(c') sat-source", "(c'') repeated", "(c'') low-entropy", "(c) bias", "(c) distinct"];
let parts: Vec<(Vec<[u64; CLASSES]>, Vec<u64>, u64, Vec<String>, Vec<(u64, u32, u64, f64)>)> = std::thread::scope(|sc| {
let mut hs = Vec::new();
for _ in 0..threads {
let next = &next;
hs.push(sc.spawn(move || {
let mut by_attempt = vec![[0u64; CLASSES]; MAX_ATTEMPTS_V4 as usize + 1];
let mut accepted_at = vec![0u64; MAX_ATTEMPTS_V4 as usize + 1];
let mut exhausted = 0u64;
let mut notes = Vec::new();
let mut rows = Vec::new();
loop {
let i = next.fetch_add(1, Ordering::Relaxed) as u64;
if i >= seeds { break; }
let k = seed_start + i;
let epoch = seed_bytes("epoch", k);
let era = seed_bytes("era", k);
let class = LoadClass::era(V4_CLASS, &era, &V3_ALLOWED);
let label = format!("igneum-attack-f8/{k}");
let mut done = false;
let cap = if force_exhaust { 8 } else { MAX_ATTEMPTS_V4 }; // under the plant a short cap shows the path
for a in 0..cap {
let mut p = candidate_class(&label, &epoch, a, class);
p.generator = GENERATOR_VERSION_V4;
match check(&p) {
Ok(r) if !force_exhaust => {
accepted_at[a as usize] += 1;
rows.push((k, a, p.program_id(), r.distinct_mean()));
done = true;
break;
}
Ok(_) => { by_attempt[a as usize][3] += 0; } // forced: counted as a rejection of no class
Err(r) => { by_attempt[a as usize][class_of(&r)] += 1; }
}
}
if !done {
exhausted += 1;
let mut lr = candidate_class(&label, &epoch, cap, class);
lr.generator = GENERATOR_VERSION_V4;
let lr = last_resort_v4(lr);
let v = check(&lr);
let (lossy_last, n_or, n_mul, n_mulhi, regs) = structure(&lr);
notes.push(format!("seed {k}: EXHAUSTED at cap {cap}; last resort id {:016x} attempt {} op mix {} shadow mix {}; rule verdict on it {:?}; structure lossy_last {lossy_last} or {n_or} mul {n_mul} mulhi {n_mulhi} regs {regs}", lr.program_id(), lr.attempt, lr.op_mix(), lr.shadow_op_mix(), v.as_ref().map(|r| format!("accept distinct_mean {:.3} sat {} bias {}", r.distinct_mean(), r.saturated, r.bias_max)).map_err(|e| e.to_string())));
}
}
(by_attempt, accepted_at, exhausted, notes, rows)
}));
}
hs.into_iter().map(|h| h.join().unwrap()).collect()
});
let mut by_attempt = vec![[0u64; CLASSES]; MAX_ATTEMPTS_V4 as usize + 1];
let mut accepted_at = vec![0u64; MAX_ATTEMPTS_V4 as usize + 1];
let mut exhausted = 0u64;
let mut rows = Vec::new();
for (b, acc, ex, notes, r) in parts {
for a in 0..b.len() { for c in 0..CLASSES { by_attempt[a][c] += b[a][c]; } accepted_at[a] += acc[a]; }
exhausted += ex;
for n in notes { println!("{n}"); }
rows.extend(r);
}
let n = rows.len() as u64 + exhausted;
let total_rej: [u64; CLASSES] = { let mut t = [0u64; CLASSES]; for a in &by_attempt { for c in 0..CLASSES { t[c] += a[c]; } } t };
let rejections: u64 = total_rej.iter().sum();
println!("attempts: {n} seeds in {:.0} s; {rejections} rejected candidates, {} accepted, {exhausted} exhausted (cap {})", t0.elapsed().as_secs_f64(), rows.len(), MAX_ATTEMPTS_V4);
println!("rejection by part over all candidates: {}", NAMES.iter().zip(total_rej.iter()).map(|(nm, c)| format!("{nm}={c} ({:.1}%)", *c as f64 * 100.0 / rejections.max(1) as f64)).collect::<Vec<_>>().join(" "));
let max_a = accepted_at.iter().rposition(|&c| c > 0).unwrap_or(0);
println!("accepted-attempt histogram 0..{max_a}: {:?}", &accepted_at[..=max_a]);
for a in 0..=max_a.min(3) {
let tot: u64 = by_attempt[a].iter().sum::<u64>() + accepted_at[a];
println!("attempt {a}: {tot} candidates, rejected {} ({:.1}%): {}", by_attempt[a].iter().sum::<u64>(), by_attempt[a].iter().sum::<u64>() as f64 * 100.0 / tot.max(1) as f64, NAMES.iter().zip(by_attempt[a].iter()).filter(|(_, c)| **c > 0).map(|(nm, c)| format!("{nm}={c}")).collect::<Vec<_>>().join(" "));
}
let mean_a = accepted_at.iter().enumerate().map(|(a, &c)| a as f64 * c as f64).sum::<f64>() / rows.len().max(1) as f64;
let p_rej = rejections as f64 / (rejections + rows.len() as u64) as f64;
println!("mean accepted attempt {mean_a:.3}; per-candidate rejection probability {p_rej:.4}; P(256 consecutive rejections) if independent = {:.3e}", p_rej.powi(256));
if rows.len() > 0 {
let dm: Vec<f64> = rows.iter().map(|r| r.3).collect();
println!("closed-form distinct-item mean of accepted programs: mean {:.4} min {:.4}", dm.iter().sum::<f64>() / dm.len() as f64, dm.iter().cloned().fold(f64::MAX, f64::min));
}
if !out.is_empty() {
let mut f = std::fs::File::create(&out).unwrap();
writeln!(f, "# seed accepted_attempt program_id distinct_mean").unwrap();
for (k, a, id, d) in &rows { writeln!(f, "{k} {a} {id:016x} {d:.5}").unwrap(); }
println!("wrote {out}");
}
println!("attempts DONE");
}