igneum/docs/analysis/class-v6/logs/fg-summary.py

43 lines
3.1 KiB
Python

#!/usr/bin/env python3
"""family-gate lane: per-stratum summary of family_gate_era_census TSV rows (and the gate-record JSON per era)."""
import sys, csv, json, math, os, statistics
def load(path):
with open(path) as f:
r = csv.DictReader(f, delimiter='\t')
return [row for row in r]
def num(x, d=float('nan')):
try: return float(x)
except: return d
def summarise(name, rows):
n = len(rows)
if n == 0: return f"| {name} | 0 | | | | | | | | |"
acc = [r for r in rows if r['attempt'] not in ('', '-1')]
cands = sum(int(r['candidates']) for r in rows); rej = cands - len(acc)
r_per = rej / cands if cands else float('nan')
atts = [int(r['attempt']) for r in acc]
exh = n - len(acc)
ratios = [(num(r['min_ratio']), r) for r in acc]
worst = min(ratios, key=lambda x: x[0])
under995 = sum(1 for x, _ in ratios if x < 0.995)
c3 = sum(int(r['c3_hot_item']) for r in rows); c2 = sum(int(r['c2_low_entropy']) for r in rows)
exh_eras = [r['k'] for r in rows if r['attempt'] in ('', '-1')]
bz = [(num(r.get('bucket_z_max', 'nan')), r) for r in acc]
bzw = max(bz, key=lambda x: x[0]) if bz and not math.isnan(bz[0][0]) else (float('nan'), {})
bitz = [(abs(num(r['bit_z_max'])), r) for r in acc]
bitw = max(bitz, key=lambda x: x[0])
over6 = sum(1 for z, _ in bitz if z > 6); over100 = sum(1 for z, _ in bitz if z > 100)
parts = {k: sum(int(r[k]) for r in rows) for k in ['a_prime','a','b','c_const','c_lane','c_sat','c_bias','c_distinct','c1_sat_source','c2_low_entropy','c3_hot_item']}
pshare = ' '.join(f"{k} {100*v/rej:.1f}" for k, v in parts.items() if rej and v)
def era(r): return f"era {r['k']} (R {r['R']}, shape {r['shape']}, W {r['width']})"
return (f"| {name} | {n} | {r_per:.3f} | {statistics.mean(atts):.2f}, {max(atts)} | {exh}{(' (eras ' + ','.join(exh_eras[:8]) + ')') if exh else ''} | {worst[0]:.4f} at site {worst[1]['min_site']} of {era(worst[1])} | (c''') refused {c3} of {cands} candidates ({100*c3/cands:.2f} percent), (c'') {c2} ({100*c2/cands:.2f}) "
f"| +{bzw[0]:.1f} sigma at site {bzw[1].get('bucket_site','')} (win {bzw[1].get('bucket_win','')}) of {era(bzw[1]) if bzw[1] else ''} | {bitw[1]['bit_z_max']} at site {bitw[1]['bit_site']} bit {bitw[1]['bit']} of {era(bitw[1])}; over 6 sigma {over6} ({100*over6/len(acc):.1f} percent), over 100 sigma {over100} ({100*over100/len(acc):.1f} percent) |\n"
f"| rejections by part (percent of {rej}) | {pshare} | | | | | | | |")
if __name__ == '__main__':
print("| Stratum | Eras | r per candidate | Mean attempt, max | Exhausted at the 256 cap | min (c'') ratio of the accepted draw, worst era | The floors' refuse rates over all candidates | Largest bucket excess (sigma), worst era | Largest index-bit bias (sigma), worst era; the share over 6 and over 100 sigma |")
print("|---|---|---|---|---|---|---|---|---|")
for arg in sys.argv[1:]:
name, path = arg.split('::', 1)
rows = []
for p in path.split(','):
if os.path.exists(p): rows += load(p)
print(summarise(name, rows))