Class v6 census: the era-aware F8-form uniformity tool (v6census-uniform)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Documents-only replay of 14c550e38 (cfd0b1db936c45275e33697c5b283eb12b25a424) for the box mirror master
This commit is contained in:
parent
082042e6e1
commit
2df3aedafe
2 changed files with 132 additions and 0 deletions
15
tools/attack/v6-census/uniform/Cargo.toml
Normal file
15
tools/attack/v6-census/uniform/Cargo.toml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "v6census-uniform"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[[bin]]
|
||||
name = "v6census-uniform"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
igneum-pow = { path = "../../../../igneum-pow" }
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
117
tools/attack/v6-census/uniform/src/main.rs
Normal file
117
tools/attack/v6-census/uniform/src/main.rs
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
//! v6census-uniform (class v6 census lane, 8 October 2026): the F8-form uniformity read of the invention lane's tool
|
||||
//! (`tools/attack/v6-invention/uniform`) with the era draw added, for the read-width and op-mix candidates. Per seed:
|
||||
//! the first accepted candidate of the class under the harness (`IGNEUM_FAMILY_GATE`, the class's own attempt cap),
|
||||
//! then over `nonces` nonces every load's word index through the library's own `trace_load_indices`; per site the
|
||||
//! distinct-index ratio `d_s / E_s` with `E_s = N - N^2 / (2 W_s)` at the site's window over the width (the (c'')
|
||||
//! form), and the cross-hash ITEM histogram's top 0.1 percent share against a uniform SplitMix64 control of the same
|
||||
//! size (the F8 harness's null; under an era the narrow-window sites raise the share by design, so the line to read
|
||||
//! is the candidate against the control class at the same era, not the no-era 1.2x alone). One TSV row per seed.
|
||||
//!
|
||||
//! v6census-uniform --class w64m8g+sh256x27 --seeds 16 --nonces 1048576 --threads 16 [--era igneum-era-test/3 --era-widths 64]
|
||||
use igneum_pow::generator::{attempts_class_to, max_attempts_for, EraParams, LoadClass, ITERATIONS};
|
||||
use igneum_pow::seed::{seed_words_from_bytes, SplitMix64};
|
||||
use igneum_pow::verify::{trace_load_indices, window, DatasetMode, DatasetSource};
|
||||
use std::collections::HashSet;
|
||||
use std::io::Write;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Mutex;
|
||||
|
||||
fn arg(args: &[String], k: &str, d: &str) -> String {
|
||||
args.iter().position(|a| a == k).and_then(|i| args.get(i + 1).cloned()).unwrap_or_else(|| d.to_string())
|
||||
}
|
||||
|
||||
fn top_share(counts: &mut Vec<u32>, frac: f64) -> (f64, u32) {
|
||||
let total: u64 = counts.iter().map(|&c| c as u64).sum();
|
||||
counts.sort_unstable_by(|a, b| b.cmp(a));
|
||||
let k = ((counts.len() as f64) * frac) as usize;
|
||||
let top: u64 = counts[..k].iter().map(|&c| c as u64).sum();
|
||||
(top as f64 / total as f64, counts[0])
|
||||
}
|
||||
|
||||
fn parse_widths(s: &str) -> Vec<u8> {
|
||||
let mut v: Vec<u8> = s.split(',').map(|x| match x.trim() { "4" => 1u8, "16" => 4, "64" => 16, _ => panic!("width") }).collect();
|
||||
v.sort_unstable();
|
||||
v
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let class_name = arg(&args, "--class", "mx8+sh256x27");
|
||||
let seeds: u32 = arg(&args, "--seeds", "16").parse().unwrap();
|
||||
let nonces: u32 = arg(&args, "--nonces", "1048576").parse().unwrap();
|
||||
let threads: usize = arg(&args, "--threads", "16").parse().unwrap();
|
||||
let log2: u32 = arg(&args, "--log2", "28").parse().unwrap();
|
||||
let day = arg(&args, "--day", "2026-10-03");
|
||||
let prefix = arg(&args, "--prefix", "igneum-v6c");
|
||||
let era = arg(&args, "--era", "none");
|
||||
let widths = parse_widths(&arg(&args, "--era-widths", "4"));
|
||||
let mut class = LoadClass::parse(&class_name).expect("class");
|
||||
if era != "none" {
|
||||
assert!(era.starts_with("igneum-era-test/"), "era: igneum-era-test/<n>");
|
||||
class = LoadClass::era(class, &EraParams::test_era_bytes(&era), &widths);
|
||||
}
|
||||
let cap = max_attempts_for(&class);
|
||||
let ds = DatasetSource::new(&day, DatasetMode::ClosedForm, log2);
|
||||
let items = 1usize << (log2 - 4);
|
||||
let mask = (1u32 << log2) - 1;
|
||||
let n = (nonces as f64) * (ITERATIONS as f64); // evaluations per site
|
||||
let next = AtomicUsize::new(0);
|
||||
let out = Mutex::new(std::io::stdout());
|
||||
println!("class\tera\tseed\tattempt\tprogram_id\tmin_site_ratio\tmin_site\ttop0.1_share\tcontrol_share\tratio_to_control\tmax_item_reads\tcontrol_max\treads");
|
||||
std::thread::scope(|s| {
|
||||
for _ in 0..threads {
|
||||
s.spawn(|| loop {
|
||||
let i = next.fetch_add(1, Ordering::Relaxed) as u32;
|
||||
if i >= seeds {
|
||||
break;
|
||||
}
|
||||
let label = format!("{prefix}/{i}");
|
||||
let tries = attempts_class_to(&label, label.as_bytes(), class, cap);
|
||||
let Some((p, _)) = tries.into_iter().find(|(_, r)| r.is_ok()) else {
|
||||
let mut o = out.lock().unwrap();
|
||||
writeln!(o, "{class_name}\t{era}\t{i}\texhausted").unwrap();
|
||||
continue;
|
||||
};
|
||||
let seed = seed_words_from_bytes(label.as_bytes());
|
||||
let loads: Vec<_> = p.instrs.iter().filter(|x| x.op.is_load()).cloned().collect();
|
||||
let sites = loads.len();
|
||||
let mut distinct: Vec<HashSet<u32>> = (0..sites).map(|_| HashSet::with_capacity(nonces as usize)).collect();
|
||||
let mut counts = vec![0u32; items];
|
||||
let mut reads: u64 = 0;
|
||||
let mut base = 0u32;
|
||||
while base < nonces {
|
||||
let rows = trace_load_indices(&p, &seed, base, &ds);
|
||||
for (k, row) in rows.iter().enumerate() {
|
||||
let site = k % sites;
|
||||
for &idx in row.iter() {
|
||||
distinct[site].insert(idx);
|
||||
counts[(idx >> 4) as usize] += 1;
|
||||
reads += 1;
|
||||
}
|
||||
}
|
||||
base += 32;
|
||||
}
|
||||
let (min_ratio, min_site) = distinct
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(s, d)| {
|
||||
let ins = &loads[s];
|
||||
let w = if p.class.era.is_some() { (window(ins, mask, log2).0 as u64 + 1) as f64 } else { (1u64 << log2) as f64 } / (ins.width.max(1) as f64);
|
||||
let e_s = n - n * n / (2.0 * w);
|
||||
(d.len() as f64 / e_s, s)
|
||||
})
|
||||
.fold((f64::MAX, 0usize), |a, b| if b.0 < a.0 { b } else { a });
|
||||
let (share, max_reads) = top_share(&mut counts, 0.001);
|
||||
let mut ctrl = vec![0u32; items];
|
||||
let mut rng = SplitMix64::new(0x9e37_79b9_7f4a_7c15 ^ i as u64);
|
||||
for _ in 0..reads {
|
||||
let x = rng.next() as usize & (items - 1);
|
||||
ctrl[x] += 1;
|
||||
}
|
||||
let (cshare, cmax) = top_share(&mut ctrl, 0.001);
|
||||
let mut o = out.lock().unwrap();
|
||||
writeln!(o, "{class_name}\t{era}\t{i}\t{}\t{:016x}\t{min_ratio:.5}\t{min_site}\t{share:.6}\t{cshare:.6}\t{:.4}\t{max_reads}\t{cmax}\t{reads}", p.attempt, p.program_id(), share / cshare).unwrap();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
Loading…
Reference in a new issue