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}