code wiki / _hdl_build / nx_softtissue_rot_gate.nx

nx_softtissue_rot_gate.nx source

↩ module page · 336 lines · 15607 B

1// nx_softtissue_rot_gate.nx -- THE INVARIANCE SEAT FOR THE VOLUMETRIC TIER. 2// 3// WHY THIS EXISTS. nx_softtissue's 19 teeth are all BEHAVIOURAL (does it droop, does it flatten, 4// does it dimple). Not one of them is an INVARIANCE, and the two council seats built 2026-08-03 5// target the 3-point shipping solver and the 2D ring -- neither had ever run against this, the best 6// tier. The modelling workstream named the gap and the claim in one line: XPBD driven by a gravity 7// UNIT VECTOR should be EXACTLY rotation-equivariant, and nothing has ever tested it. Debt 1785786068. 8// 9// V2 (this file): the first cut tested ONE element of the cube group (180 about z). This version 10// enumerates ALL 24 proper rotations (signed permutation matrices, det=+1) and asserts bit-exactness 11// for every one, plus exact translation equivariance. The enumeration itself is a tooth (T0): a 12// partition is a claim, so the generator must produce exactly 24 or the sweep is measuring a subset 13// while claiming the group. The identity element is deliberately IN the sweep -- it doubles as a 14// determinism tooth (two independent builds+runs of the same world must agree bit-for-bit). 15// 16// WHY THE CLAIM SURVIVES THE RIBCAGE. It looks false at first: st_wall_drop uses st_abs(xm) and the 17// header says the wall curves in x and NOT in y, so the model is deliberately anisotropic. But the 18// wall is applied ONCE, at build, into the initial z of each particle (nx_softtissue.nx:500) -- it is 19// GEOMETRY, not a runtime constraint. Every runtime constraint here is distance or signed volume, 20// both rotation- and translation-invariant, plus gravity, which is a vector we rotate with the cage. 21// SCOPE: the touch-sphere constraint (ST_W_TON, absolute center TCX/TCY/TCZ) is a genuine absolute 22// anchor, but st_new zeroes it and no run here enables it -- equivariance WITH touch enabled would 23// need the touch center rotated too, and is deliberately out of scope of this gate. 24// 25// WHY SIGNED PERMUTATIONS SURVIVE THE ARITHMETIC. Axis permutations relabel identical code paths 26// (constraint order is index-based, never coordinate-based), and negations survive st_div_r because 27// it rounds half AWAY FROM ZERO (sign-symmetric by construction). Mirrors (det=-1) are excluded: a 28// mirror flips the sign of every tet's 6V and fights the inversion barrier -- a RED that would say 29// nothing about equivariance. 30// 31// MEMORY: 27 worlds x ~3.6MB (no st_free exists; a gate process releases everything at exit) -- 32// ~100MB transient, declared here so nobody reads the mmap total as a leak. 33// license_tier: ORIGINAL No hw writes (Rule 26). 34import "nx_syscalls.nx" 35import "nx_gate_verdict.nx" 36import "nx_softtissue.nx" 37 38const RG_DT_US: i64 = 4167 39const RG_ITERS: i64 = 2 40const RG_STEPS: i64 = 300 41const RG_G: i64 = 4096 42const RG_NROT: i64 = 24 43// translation offsets, cmm: non-round, sign-varied (TY is applied negated), all nonzero so T4's 44// expectation is exactly 3n. Magnitudes only here -- nx_cc const-expression folding is unproven. 45const RG_TX: i64 = 7000 46const RG_TY: i64 = 3000 47const RG_TZ: i64 = 11000 48 49// pick component k of (a,b,c) 50func rg_pick(a: i64, b: i64, c: i64, k: i64) -> i64 { 51 if k == 0 { return a } 52 if k == 1 { return b } 53 return c 54} 55 56// Build the 24 proper rotations into RT as flat rows of 6 i64: [p0,p1,p2,s0,s1,s2], 57// meaning new[axis a] = s_a * old[p_a]. det = parity(perm) * s0*s1*s2; keep det=+1 only. 58// Returns the row count -- the caller asserts it equals 24 (T0). 59func rg_build_rots(RT: *i64) -> i64 { 60 var cnt: i64 = 0 61 var p0: i64 = 0 62 while p0 < 3 { 63 var p1: i64 = 0 64 while p1 < 3 { 65 var p2: i64 = 0 66 while p2 < 3 { 67 var distinct: i64 = 1 68 if p0 == p1 { distinct = 0 } 69 if p0 == p2 { distinct = 0 } 70 if p1 == p2 { distinct = 0 } 71 if distinct == 1 { 72 var inv: i64 = 0 73 if p0 > p1 { inv = inv + 1 } 74 if p0 > p2 { inv = inv + 1 } 75 if p1 > p2 { inv = inv + 1 } 76 var par: i64 = 1 77 if inv == 1 { par = 0 - 1 } 78 if inv == 3 { par = 0 - 1 } 79 var i0: i64 = 0 80 while i0 < 2 { 81 var i1: i64 = 0 82 while i1 < 2 { 83 var i2: i64 = 0 84 while i2 < 2 { 85 let s0: i64 = 1 - i0 * 2 86 let s1: i64 = 1 - i1 * 2 87 let s2: i64 = 1 - i2 * 2 88 if par * s0 * s1 * s2 == 1 { 89 RT[cnt * 6 + 0] = p0 90 RT[cnt * 6 + 1] = p1 91 RT[cnt * 6 + 2] = p2 92 RT[cnt * 6 + 3] = s0 93 RT[cnt * 6 + 4] = s1 94 RT[cnt * 6 + 5] = s2 95 cnt = cnt + 1 96 } 97 i2 = i2 + 1 98 } 99 i1 = i1 + 1 100 } 101 i0 = i0 + 1 102 } 103 } 104 p2 = p2 + 1 105 } 106 p1 = p1 + 1 107 } 108 p0 = p0 + 1 109 } 110 return cnt 111} 112 113// apply rotation row r of RT to the whole cage state (positions, prev-positions, velocities) 114func rg_apply(W: *i64, RT: *i64, r: i64) -> i64 { 115 let p0: i64 = RT[r * 6 + 0] 116 let p1: i64 = RT[r * 6 + 1] 117 let p2: i64 = RT[r * 6 + 2] 118 let s0: i64 = RT[r * 6 + 3] 119 let s1: i64 = RT[r * 6 + 4] 120 let s2: i64 = RT[r * 6 + 5] 121 let n: i64 = W[ST_W_NP] 122 let px: *i64 = W[ST_W_PX] as *i64 123 let py: *i64 = W[ST_W_PY] as *i64 124 let pz: *i64 = W[ST_W_PZ] as *i64 125 let qx: *i64 = W[ST_W_QX] as *i64 126 let qy: *i64 = W[ST_W_QY] as *i64 127 let qz: *i64 = W[ST_W_QZ] as *i64 128 let vx: *i64 = W[ST_W_VX] as *i64 129 let vy: *i64 = W[ST_W_VY] as *i64 130 let vz: *i64 = W[ST_W_VZ] as *i64 131 var i: i64 = 0 132 while i < n { 133 var a: i64 = px[i]; var b: i64 = py[i]; var c: i64 = pz[i] 134 px[i] = s0 * rg_pick(a, b, c, p0) 135 py[i] = s1 * rg_pick(a, b, c, p1) 136 pz[i] = s2 * rg_pick(a, b, c, p2) 137 a = qx[i]; b = qy[i]; c = qz[i] 138 qx[i] = s0 * rg_pick(a, b, c, p0) 139 qy[i] = s1 * rg_pick(a, b, c, p1) 140 qz[i] = s2 * rg_pick(a, b, c, p2) 141 a = vx[i]; b = vy[i]; c = vz[i] 142 vx[i] = s0 * rg_pick(a, b, c, p0) 143 vy[i] = s1 * rg_pick(a, b, c, p1) 144 vz[i] = s2 * rg_pick(a, b, c, p2) 145 i = i + 1 146 } 147 return 0 148} 149 150// count position components where WB differs from rotation-r image of WA; report worst gap in cmm 151func rg_diff_r(WA: *i64, WB: *i64, RT: *i64, r: i64, worst: *i64) -> i64 { 152 let p0: i64 = RT[r * 6 + 0] 153 let p1: i64 = RT[r * 6 + 1] 154 let p2: i64 = RT[r * 6 + 2] 155 let s0: i64 = RT[r * 6 + 3] 156 let s1: i64 = RT[r * 6 + 4] 157 let s2: i64 = RT[r * 6 + 5] 158 let n: i64 = WA[ST_W_NP] 159 let ax: *i64 = WA[ST_W_PX] as *i64 160 let ay: *i64 = WA[ST_W_PY] as *i64 161 let az: *i64 = WA[ST_W_PZ] as *i64 162 let bx: *i64 = WB[ST_W_PX] as *i64 163 let by: *i64 = WB[ST_W_PY] as *i64 164 let bz: *i64 = WB[ST_W_PZ] as *i64 165 var bad: i64 = 0 166 worst[0] = 0 167 var i: i64 = 0 168 while i < n { 169 let a: i64 = ax[i] 170 let b: i64 = ay[i] 171 let c: i64 = az[i] 172 var d: i64 = bx[i] - s0 * rg_pick(a, b, c, p0) 173 if d < 0 { d = 0 - d } 174 if d != 0 { bad = bad + 1; if d > worst[0] { worst[0] = d } } 175 var e: i64 = by[i] - s1 * rg_pick(a, b, c, p1) 176 if e < 0 { e = 0 - e } 177 if e != 0 { bad = bad + 1; if e > worst[0] { worst[0] = e } } 178 var f: i64 = bz[i] - s2 * rg_pick(a, b, c, p2) 179 if f < 0 { f = 0 - f } 180 if f != 0 { bad = bad + 1; if f > worst[0] { worst[0] = f } } 181 i = i + 1 182 } 183 return bad 184} 185 186// shift the whole cage (positions and prev-positions; velocities are translation-invariant) 187func rg_shift(W: *i64, tx: i64, ty: i64, tz: i64) -> i64 { 188 let n: i64 = W[ST_W_NP] 189 let px: *i64 = W[ST_W_PX] as *i64 190 let py: *i64 = W[ST_W_PY] as *i64 191 let pz: *i64 = W[ST_W_PZ] as *i64 192 let qx: *i64 = W[ST_W_QX] as *i64 193 let qy: *i64 = W[ST_W_QY] as *i64 194 let qz: *i64 = W[ST_W_QZ] as *i64 195 var i: i64 = 0 196 while i < n { 197 px[i] = px[i] + tx; py[i] = py[i] + ty; pz[i] = pz[i] + tz 198 qx[i] = qx[i] + tx; qy[i] = qy[i] + ty; qz[i] = qz[i] + tz 199 i = i + 1 200 } 201 return 0 202} 203 204// count position components where WB differs from WA + (tx,ty,tz); report worst gap in cmm 205func rg_diff_t(WA: *i64, WB: *i64, tx: i64, ty: i64, tz: i64, worst: *i64) -> i64 { 206 let n: i64 = WA[ST_W_NP] 207 let ax: *i64 = WA[ST_W_PX] as *i64 208 let ay: *i64 = WA[ST_W_PY] as *i64 209 let az: *i64 = WA[ST_W_PZ] as *i64 210 let bx: *i64 = WB[ST_W_PX] as *i64 211 let by: *i64 = WB[ST_W_PY] as *i64 212 let bz: *i64 = WB[ST_W_PZ] as *i64 213 var bad: i64 = 0 214 worst[0] = 0 215 var i: i64 = 0 216 while i < n { 217 var d: i64 = bx[i] - ax[i] - tx 218 if d < 0 { d = 0 - d } 219 if d != 0 { bad = bad + 1; if d > worst[0] { worst[0] = d } } 220 var e: i64 = by[i] - ay[i] - ty 221 if e < 0 { e = 0 - e } 222 if e != 0 { bad = bad + 1; if e > worst[0] { worst[0] = e } } 223 var f: i64 = bz[i] - az[i] - tz 224 if f < 0 { f = 0 - f } 225 if f != 0 { bad = bad + 1; if f > worst[0] { worst[0] = f } } 226 i = i + 1 227 } 228 return bad 229} 230 231func main(argc: i64, argv: *i64) -> i64 { 232 let ctr: *i64 = gv_ctr() 233 gv_head("nx_softtissue_rot_gate -- the volumetric tier must be EXACTLY equivariant under the full proper cube group and translation" as *u8) 234 let worst: *i64 = sys_mmap(16) as *i64 235 let RT: *i64 = sys_mmap(2048) as *i64 236 237 // T0: the enumeration is complete. 6 permutations x 4 admissible sign patterns = 24, det=+1 each. 238 let cnt: i64 = rg_build_rots(RT) 239 gv_puts(" proper rotations generated=" as *u8); gv_num(cnt); gv_puts("\n" as *u8) 240 var t0: i64 = 0 241 if cnt == RG_NROT { t0 = 1 } 242 gv_check("T0 THE GROUP IS ALL THERE: the signed-permutation generator emits exactly 24 proper rotations -- a partition is a claim, and a sweep over a silently short table would claim the group while measuring a subset" as *u8, t0, ctr) 243 244 // REFERENCE: standing gravity (0,-G,0) 245 let WA: *i64 = st_new(ST_PROF_LARGE_SOFT, 10) 246 st_run(WA, 0, 0 - RG_G, 0, RG_STEPS, RG_DT_US, RG_ITERS) 247 248 // T1: every element of the proper cube group, bit-for-bit. Gravity G0=(0,-G,0) is rotated by the 249 // SAME matrix: g_a = s_a * G0[p_a]. The identity row is in the sweep and is the determinism tooth. 250 var failrot: i64 = 0 251 var worstall: i64 = 0 252 var r: i64 = 0 253 while r < cnt { 254 let WB: *i64 = st_new(ST_PROF_LARGE_SOFT, 10) 255 rg_apply(WB, RT, r) 256 let gx: i64 = RT[r * 6 + 3] * rg_pick(0, 0 - RG_G, 0, RT[r * 6 + 0]) 257 let gy: i64 = RT[r * 6 + 4] * rg_pick(0, 0 - RG_G, 0, RT[r * 6 + 1]) 258 let gz: i64 = RT[r * 6 + 5] * rg_pick(0, 0 - RG_G, 0, RT[r * 6 + 2]) 259 st_run(WB, gx, gy, gz, RG_STEPS, RG_DT_US, RG_ITERS) 260 let bad: i64 = rg_diff_r(WA, WB, RT, r, worst) 261 gv_puts(" r=" as *u8); gv_num(r) 262 gv_puts(" perm=" as *u8); gv_num(RT[r * 6 + 0]); gv_num(RT[r * 6 + 1]); gv_num(RT[r * 6 + 2]) 263 gv_puts(" sgn=" as *u8); gv_num(RT[r * 6 + 3]); gv_num(RT[r * 6 + 4]); gv_num(RT[r * 6 + 5]) 264 gv_puts(" bad=" as *u8); gv_num(bad) 265 gv_puts(" worst-cmm=" as *u8); gv_num(worst[0]) 266 gv_puts("\n" as *u8) 267 if bad != 0 { failrot = failrot + 1 } 268 if worst[0] > worstall { worstall = worst[0] } 269 r = r + 1 270 } 271 gv_puts(" particles=" as *u8); gv_num(WA[ST_W_NP]) 272 gv_puts(" rotations-failed=" as *u8); gv_num(failrot) 273 gv_puts(" worst-cmm-anywhere=" as *u8); gv_num(worstall) 274 gv_puts("\n" as *u8) 275 var t1: i64 = 0 276 if failrot == 0 { if cnt == RG_NROT { t1 = 1 } } 277 gv_check("T1 ROTATION EQUIVARIANCE IS EXACT FOR THE WHOLE PROPER CUBE GROUP: all 24 rotations of cage+gravity reproduce the rotated result BIT-FOR-BIT -- no oracle, no band, no tolerance; the identity element makes the sweep also a determinism proof" as *u8, t1, ctr) 278 279 // T2 NON-VACUITY, IN THE SAME RUN. Rotate the cage 180 about z but LEAVE GRAVITY ALONE: genuinely 280 // different physics, MUST diverge. Locate that row (perm identity, signs -,-,+) in the table so 281 // the control exercises the same apply/diff machinery the sweep used. 282 let neg1: i64 = 0 - 1 283 var cidx: i64 = 0 - 1 284 var r2: i64 = 0 285 while r2 < cnt { 286 if RT[r2 * 6 + 0] == 0 { 287 if RT[r2 * 6 + 1] == 1 { 288 if RT[r2 * 6 + 2] == 2 { 289 if RT[r2 * 6 + 3] == neg1 { 290 if RT[r2 * 6 + 4] == neg1 { 291 if RT[r2 * 6 + 5] == 1 { 292 cidx = r2 293 } } } } } } 294 r2 = r2 + 1 295 } 296 var t2: i64 = 0 297 if cidx >= 0 { 298 let WC: *i64 = st_new(ST_PROF_LARGE_SOFT, 10) 299 rg_apply(WC, RT, cidx) 300 st_run(WC, 0, 0 - RG_G, 0, RG_STEPS, RG_DT_US, RG_ITERS) 301 let bad2: i64 = rg_diff_r(WA, WC, RT, cidx, worst) 302 gv_puts(" control (cage rotated, gravity NOT): mismatched-components=" as *u8); gv_num(bad2) 303 gv_puts(" worst-cmm=" as *u8); gv_num(worst[0]) 304 gv_puts("\n" as *u8) 305 if bad2 > 0 { t2 = 1 } 306 } 307 gv_check("T2 THE RELATION CAN FAIL: rotating the cage while leaving gravity in the old frame DIVERGES, so T1 is a measurement and not a tautology -- the bite rides in the gate instead of waiting for a mutation run" as *u8, t2, ctr) 308 309 // T3: exact translation equivariance. Every runtime constraint is relative (distance, signed 310 // volume); the wall is baked geometry and moves with the shifted initial state. 311 let WT: *i64 = st_new(ST_PROF_LARGE_SOFT, 10) 312 rg_shift(WT, RG_TX, 0 - RG_TY, RG_TZ) 313 st_run(WT, 0, 0 - RG_G, 0, RG_STEPS, RG_DT_US, RG_ITERS) 314 let bad3: i64 = rg_diff_t(WA, WT, RG_TX, 0 - RG_TY, RG_TZ, worst) 315 gv_puts(" translation: mismatched-components=" as *u8); gv_num(bad3) 316 gv_puts(" worst-cmm=" as *u8); gv_num(worst[0]) 317 gv_puts("\n" as *u8) 318 var t3: i64 = 0 319 if bad3 == 0 { t3 = 1 } 320 gv_check("T3 TRANSLATION EQUIVARIANCE IS EXACT: shifting the cage by (7000,-3000,11000) cmm shifts the result by exactly that and nothing else, BIT-FOR-BIT" as *u8, t3, ctr) 321 322 // T4 CONTROL for the translation diff: against the UNSHIFTED reference the same run must be off 323 // by the offset in EVERY component -- given T3, exactly 3n mismatches, worst exactly max|T|. 324 // This kills the mutant where rg_diff_t cannot see a miss at all. 325 let bad4: i64 = rg_diff_t(WA, WT, 0, 0, 0, worst) 326 let want4: i64 = WA[ST_W_NP] * 3 327 gv_puts(" translation control: mismatched-components=" as *u8); gv_num(bad4) 328 gv_puts(" expected=" as *u8); gv_num(want4) 329 gv_puts(" worst-cmm=" as *u8); gv_num(worst[0]) 330 gv_puts("\n" as *u8) 331 var t4: i64 = 0 332 if bad4 == want4 { if worst[0] == 11000 { t4 = 1 } } 333 gv_check("T4 THE TRANSLATION DIFF CAN FAIL: the shifted run vs the unshifted reference mismatches in exactly 3n components with worst gap exactly 11000 cmm -- the diff sees every miss, so T3 is a measurement" as *u8, t4, ctr) 334 335 return gv_verdict("SOFTTISSUE-ROT-GATE" as *u8, ctr, "volumetric tier: exact equivariance under all 24 proper rotations and translation, with its own controls" as *u8) 336}