code wiki / (root) / nx_dr_elo.nx

nx_dr_elo.nx source

↩ module page · 94 lines · 3800 B

1// nx_dr_elo.nx -- SOVEREIGN deterministic integer Elo TOURNAMENT ranker (DR-1). 2// The Co-Scientist "Tournament of Ideas": pairwise-critique match outcomes -> Elo 3// ratings -> a ranking that decides which research vectors deserve deep compute. 4// Determinism/reproducibility = the sovereign EXCEED axis vs single-agent tools: 5// bit-exact, NO float, a fixed 41-point expected-score table 6// (E(d)=1000/(1+10^(d/400)) in permille, d=rating gap) with integer linear 7// interpolation. No hardware writes (Rule 26). Imports ONLY nx_syscalls = drift-immune. 8// 9// module: nishi-core.research.dr_elo 10// depends: nx_syscalls.nx 11// capability: CORE_COMPUTE 12// wired_status: LIBRARY (conductor wiring = DR-4; critic match-outcomes = DR-2) 13// genealogy_id: elo_1978_rating + coscientist_2026_tournament_of_ideas 14import "nx_syscalls.nx" 15 16const EL_START: i64 = 1500 17const EL_K: i64 = 32 18 19// Build the 41-entry expected-score table E(d) permille, d=-800..800 step 40. 20// E(d)=1000/(1+10^(d/400)); symmetric: E(d)+E(-d)=1000. 21func el_make_etab() -> *i64 { 22 let t: *i64 = sys_mmap(41 * 8) as *i64 23 t[0]=990; t[1]=988; t[2]=984; t[3]=980; t[4]=975; t[5]=969; t[6]=962; t[7]=952 24 t[8]=941; t[9]=926; t[10]=909; t[11]=888; t[12]=863; t[13]=834; t[14]=799 25 t[15]=760; t[16]=715; t[17]=666; t[18]=613; t[19]=557; t[20]=500; t[21]=443 26 t[22]=387; t[23]=334; t[24]=285; t[25]=240; t[26]=201; t[27]=166; t[28]=137 27 t[29]=112; t[30]=91; t[31]=74; t[32]=59; t[33]=48; t[34]=38; t[35]=31; t[36]=25 28 t[37]=20; t[38]=16; t[39]=12; t[40]=10 29 return t 30} 31 32// Expected score (permille 0..1000) for `rself` vs `ropp`. d=ropp-rself clamped 33// to [-800,800], linear-interpolated over the 41-point table. 34func el_expected(rself: i64, ropp: i64, tab: *i64) -> i64 { 35 var d: i64 = ropp - rself 36 if d < (0 - 800) { d = 0 - 800 } 37 if d > 800 { d = 800 } 38 let x: i64 = d + 800 39 let idx: i64 = x / 40 40 if idx >= 40 { return tab[40] } 41 let frac: i64 = x - idx * 40 42 let a: i64 = tab[idx] 43 let b: i64 = tab[idx + 1] 44 return a + (b - a) * frac / 40 45} 46 47// One rating update. score/expected in permille (1000 win / 500 draw / 0 loss); 48// K in points. delta = K*(score-expected)/1000. 49func el_update(rating: i64, expected: i64, score: i64, k: i64) -> i64 { 50 return rating + (k * (score - expected)) / 1000 51} 52 53// Run M pairwise matches sequentially over N players. ma/mb = player indices, 54// mo = player-A actual score in permille. Mutates ratings[] in place. Sequential 55// = deterministic (same matches -> same bits, any machine). 56func el_run_tournament(ratings: *i64, n: i64, ma: *i64, mb: *i64, mo: *i64, m: i64, k: i64, tab: *i64) -> i64 { 57 var t: i64 = 0 58 while t < m { 59 let a: i64 = ma[t] 60 let b: i64 = mb[t] 61 let sa: i64 = mo[t] 62 let sb: i64 = 1000 - sa 63 let ea: i64 = el_expected(ratings[a], ratings[b], tab) 64 let eb: i64 = el_expected(ratings[b], ratings[a], tab) 65 ratings[a] = el_update(ratings[a], ea, sa, k) 66 ratings[b] = el_update(ratings[b], eb, sb, k) 67 t = t + 1 68 } 69 return 0 70} 71 72// Rank player ids by rating DESC (ties -> lower id). out_order[n]. Selection sort 73// (post-tournament fields are small: the shortlist of research vectors). 74func el_rank(ratings: *i64, n: i64, out_order: *i64) -> i64 { 75 let used: *u8 = sys_mmap(n) 76 var i: i64 = 0 77 while i < n { used[i] = 0 as u8; i = i + 1 } 78 var r: i64 = 0 79 while r < n { 80 var best: i64 = 0 - 1 81 var j: i64 = 0 82 while j < n { 83 if used[j] == (0 as u8) { 84 if best < 0 { best = j } 85 else { if ratings[j] > ratings[best] { best = j } } 86 } 87 j = j + 1 88 } 89 out_order[r] = best 90 used[best] = 1 as u8 91 r = r + 1 92 } 93 return 0 94}