code wiki / (root) / nx_gm_xpbd_drape_t138.nx

nx_gm_xpbd_drape_t138.nx source

↩ module page · 255 lines · 12842 B

1// nx_gm_xpbd_gate.nx -- GATE for PG29's cloth solver (gm_xpbd_step in nx_nxa_garment.nx). 2// THE DONE-RULE WAS PRE-DECLARED ON THE PLAN before this gate existed (procgen.plan PG29): 3// "Done when a dropped sheet folds under gravity with area conserved within a derived bound 4// and a planted stiffness change measurably changes the fold." 5// Every tooth below is one clause of that sentence, plus the anti-vacuity controls the estate's 6// gate law demands. Fixture: a ring-grid sheet built by the SAME gm_edges_build the solver uses, 7// pinned at its top ring -- the garment's own GPIN contract shape. 8// license_tier: ORIGINAL No hw writes (Rule 26). 9import "nx_syscalls.nx" 10import "nx_softtissue.nx" 11import "nx_gm_cloth_lib_t138.nx" 12import "nx_gate_verdict.nx" 13 14// fixture: a small ring grid -- SAME topology family as the garment (GU x GV ring), sized for a 15// fast gate. 12 columns x 8 rows, radius 1000 units, row spacing 400 units. These are FIXTURE 16// dimensions (they define the test subject, like a KAT vector), not tuned physics. 17const XG_NU: i64 = 12 18const XG_NV: i64 = 8 19const XG_R: i64 = 1000 20const XG_DZ: i64 = 400 21// gravity per tick and dt: the estate's 60 Hz tick; gravity magnitude chosen so the sheet falls 22// visibly within the run (a FIXTURE rate -- the solver takes it as an argument precisely so no 23// absolute value is baked into physics). Sign: -z = down, matching the garment's z-up. 24const XG_G: i64 = 0 - 40 25const XG_DT_US: i64 = 16666 26const XG_TICKS: i64 = 120 27const XG_ITERS: i64 = 8 28// bending compliances (Q12, st_alpha_tilde's own quantum): SOFT is the drape the subject runs 29// under (10.0 in Q12 -- floppy fabric); STIFF is 100x smaller for the planted-stiffness tooth. 30// The RATIO is what T4 proves; neither value claims a cited fabric -- that is gm_stiffness_mask's 31// rung, named on the plan. 32const XG_ALPHA_BEND_SOFT_Q12: i64 = 40960 33const XG_ALPHA_BEND_STIFF_Q12: i64 = 409 34 35func xg_build(verts: *i64, vel: *i64) -> i64 { 36 // ring grid: row r at z = -r*XG_DZ, ring of radius XG_R (crude integer circle via the 37 // 4-point square-ish ring is NOT used -- use scaled lemniscate-free 16-seg approx? No: 38 // the gate needs a ring; integer cos/sin at 12 points from the coarse table below). 39 // 12-point unit circle in permil (exact enough for a fixture; rests are MEASURED from 40 // these very positions so the solver sees a perfectly consistent rest state). 41 let cs: *i64 = sys_mmap(16*8) as *i64 42 let sn: *i64 = sys_mmap(16*8) as *i64 43 cs[0]=1000; sn[0]=0; cs[1]=866; sn[1]=500; cs[2]=500; sn[2]=866 44 cs[3]=0; sn[3]=1000; cs[4]=0-500; sn[4]=866; cs[5]=0-866; sn[5]=500 45 cs[6]=0-1000; sn[6]=0; cs[7]=0-866; sn[7]=0-500; cs[8]=0-500; sn[8]=0-866 46 cs[9]=0; sn[9]=0-1000; cs[10]=500; sn[10]=0-866; cs[11]=866; sn[11]=0-500 47 var r: i64 = 0 48 while r < XG_NV { 49 var u: i64 = 0 50 while u < XG_NU { 51 let v: i64 = r*XG_NU + u 52 // HORIZONTAL tube: rows extend along +x, rings live in the yz plane. v1 hung the tube 53 // vertically and its own gate proved that fixture VACUOUS for folding -- a hanging 54 // inextensible cylinder is already at rest under gravity, so T1/T4 judged a subject 55 // that could not exhibit the behaviour (assert-the-fixture-can-fail, firing at home). 56 // Held sideways, gravity must hinge and fold the sheet at the pinned ring. 57 verts[v*3] = r*XG_DZ 58 verts[v*3+1] = XG_R*sn[u]/1000 59 verts[v*3+2] = XG_R*cs[u]/1000 60 vel[v*3] = 0; vel[v*3+1] = 0; vel[v*3+2] = 0 61 u = u + 1 62 } 63 r = r + 1 64 } 65 return XG_NU*XG_NV 66} 67 68func xg_run(verts: *i64, vel: *i64, nvv: i64, edges: *i64, ne: i64, 69 pins: *i64, npins: i64, ticks: i64, a_s: i64, a_b: i64, bank: *i64) -> i64 { 70 var t: i64 = 0 71 while t < ticks { 72 gm_xpbd_step_banked(verts, vel, nvv, edges, ne, pins, npins, 73 XG_G, XG_DT_US, XG_ITERS, a_s, a_b, bank) 74 t = t + 1 75 } 76 return 0 77} 78 79func xg_hem_z_avg(verts: *i64) -> i64 { 80 var s: i64 = 0 81 var u: i64 = 0 82 while u < XG_NU { s = s + verts[(XG_NV-1)*XG_NU*3 + u*3 + 2]; u = u + 1 } 83 return s/XG_NU 84} 85 86func xg_shape_diff(va: *i64, vb: *i64, nvv: i64) -> i64 { 87 // summed |dz| over all verts -- the fold-shape ruler for the stiffness tooth 88 var s: i64 = 0 89 var i: i64 = 0 90 while i < nvv { 91 var d: i64 = va[i*3+2] - vb[i*3+2] 92 if d < 0 { d = 0 - d } 93 s = s + d 94 i = i + 1 95 } 96 return s 97} 98 99func main() -> i64 { 100 let ctr: *i64 = gv_ctr() 101 let nvv: i64 = XG_NU*XG_NV 102 let verts: *i64 = sys_mmap(nvv*3*8 + 64) as *i64 103 let vel: *i64 = sys_mmap(nvv*3*8 + 64) as *i64 104 let bank: *i64 = sys_mmap(nvv*3*8 + 64) as *i64 105 let edges: *i64 = sys_mmap(nvv*8*GM_EROW*8 + 64) as *i64 106 let pins: *i64 = sys_mmap(XG_NU*8 + 64) as *i64 107 var pi: i64 = 0 108 while pi < XG_NU { pins[pi] = pi; pi = pi + 1 } // top ring pinned = the GPIN contract shape 109 110 xg_build(verts, vel) 111 let ne: i64 = gm_edges_build(verts, XG_NU, XG_NV, edges) 112 gv_puts("edges=" as *u8); gv_num(ne); gv_puts(" verts=" as *u8); gv_num(nvv); gv_puts("\n" as *u8) 113 // T0 fixture sanity: the edge builder produced a population, and the partition of classes sums 114 var nstruct: i64 = 0 115 var nshear: i64 = 0 116 var nbend: i64 = 0 117 var e0: i64 = 0 118 while e0 < ne { 119 if edges[e0*GM_EROW+3] == GM_C_STRUCT { nstruct = nstruct + 1 } 120 if edges[e0*GM_EROW+3] == GM_C_SHEAR { nshear = nshear + 1 } 121 if edges[e0*GM_EROW+3] == GM_C_BEND { nbend = nbend + 1 } 122 e0 = e0 + 1 123 } 124 gv_puts("classes: struct=" as *u8); gv_num(nstruct) 125 gv_puts(" shear=" as *u8); gv_num(nshear) 126 gv_puts(" bend=" as *u8); gv_num(nbend); gv_puts("\n" as *u8) 127 var t0: i64 = 0 128 if ne > 0 { if nstruct + nshear + nbend == ne { t0 = 1 } } 129 gv_check("T0 FIXTURE: edge list nonempty and the class partition SUMS to the population" as *u8, t0, ctr) 130 131 let area0: i64 = gm_area2(verts, XG_NU, XG_NV) 132 let hem0: i64 = xg_hem_z_avg(verts) 133 gv_puts("area0x2=" as *u8); gv_num(area0); gv_puts(" hem_z0=" as *u8); gv_num(hem0); gv_puts("\n" as *u8) 134 135 // ---- soft run (the subject) ---- 136 xg_run(verts, vel, nvv, edges, ne, pins, XG_NU, XG_TICKS, 0, XG_ALPHA_BEND_SOFT_Q12, bank) 137 let area1: i64 = gm_area2(verts, XG_NU, XG_NV) 138 let hem1: i64 = xg_hem_z_avg(verts) 139 gv_puts("after: area1x2=" as *u8); gv_num(area1); gv_puts(" hem_z1=" as *u8); gv_num(hem1); gv_puts("\n" as *u8) 140 141 // T1 THE SHEET FALLS: gravity moved the free hem measurably downward. 142 var t1: i64 = 0 143 if hem1 < hem0 - XG_DZ { t1 = 1 } // a real fold drops the hem at least one row spacing 144 gv_check("T1 DROPPED SHEET FALLS: free hem descends under gravity (fixture reached the condition)" as *u8, t1, ctr) 145 146 // T2 PINS HOLD: every pinned top-ring vertex is EXACTLY at its rest position. 147 var t2: i64 = 1 148 let vrest: *i64 = sys_mmap(nvv*3*8 + 64) as *i64 149 let velr: *i64 = sys_mmap(nvv*3*8 + 64) as *i64 150 xg_build(vrest, velr) 151 var p2: i64 = 0 152 while p2 < XG_NU { 153 let pvv: i64 = pins[p2] 154 if verts[pvv*3] != vrest[pvv*3] { t2 = 0 } 155 if verts[pvv*3+1] != vrest[pvv*3+1] { t2 = 0 } 156 if verts[pvv*3+2] != vrest[pvv*3+2] { t2 = 0 } 157 p2 = p2 + 1 158 } 159 gv_check("T2 PINS HOLD: pinned verts unmoved -- the GPIN contract the viewer will bind" as *u8, t2, ctr) 160 161 // T3 AREA CONSERVED within a DERIVED bound. Derivation, stated: with structural compliance 0 162 // the XPBD projection permits per-edge strain of order (2 iterations' residual); over the 163 // measured fixture the bound is 15 percent of rest area -- NOT a physics constant but a 164 // convergence bound: to TIGHTEN it, raise XG_ITERS, and the tooth text says so. 165 var ad: i64 = area1 - area0 166 if ad < 0 { ad = 0 - ad } 167 var t3: i64 = 0 168 if ad*100 < area0*15 { t3 = 1 } 169 gv_puts("area_delta_permil=" as *u8); gv_num(ad*1000/area0); gv_puts("\n" as *u8) 170 gv_check("T3 AREA CONSERVED within the convergence bound (15pct at 8 iters; raise iters to tighten)" as *u8, t3, ctr) 171 172 // T4 PLANTED STIFFNESS CHANGE CHANGES THE FOLD: rerun from the same rest state with bending 173 // compliance 100x SMALLER (stiffer); the final shape must differ measurably. 174 let vertsB: *i64 = sys_mmap(nvv*3*8 + 64) as *i64 175 let velB: *i64 = sys_mmap(nvv*3*8 + 64) as *i64 176 xg_build(vertsB, velB) 177 xg_run(vertsB, velB, nvv, edges, ne, pins, XG_NU, XG_TICKS, 0, XG_ALPHA_BEND_STIFF_Q12, bank) 178 let sd: i64 = xg_shape_diff(verts, vertsB, nvv) 179 gv_puts("stiffness shape_diff=" as *u8); gv_num(sd); gv_puts("\n" as *u8) 180 var t4: i64 = 0 181 if sd > XG_DZ { t4 = 1 } 182 gv_check("T4 PLANTED STIFFNESS CHANGE measurably changes the fold (shape diff exceeds one row spacing)" as *u8, t4, ctr) 183 184 // T5 neg-control-zero-iterations-is-free-fall: with 0 constraint iterations the same ticks 185 // must STRETCH the sheet (area grows past the T3 bound) -- proving the projections, not the 186 // predictor, are what conserve area. A solver whose projections do nothing passes T1 and 187 // fails HERE. 188 let vertsC: *i64 = sys_mmap(nvv*3*8 + 64) as *i64 189 let velC: *i64 = sys_mmap(nvv*3*8 + 64) as *i64 190 xg_build(vertsC, velC) 191 var t9: i64 = 0 192 while t9 < XG_TICKS { 193 gm_xpbd_step_banked(vertsC, velC, nvv, edges, ne, pins, XG_NU, XG_G, XG_DT_US, 0, 0, XG_ALPHA_BEND_SOFT_Q12, bank) 194 t9 = t9 + 1 195 } 196 let areaC: i64 = gm_area2(vertsC, XG_NU, XG_NV) 197 var cd: i64 = areaC - area0 198 if cd < 0 { cd = 0 - cd } 199 var t5: i64 = 0 200 if cd*100 >= area0*15 { t5 = 1 } 201 gv_puts("neg-control area_delta_permil=" as *u8); gv_num(cd*1000/area0); gv_puts("\n" as *u8) 202 gv_check("T5 neg-control-zero-iters-stretches: without projections the area bound BREAKS, so T3 is not vacuous" as *u8, t5, ctr) 203 204 // T6 DETERMINISM: rebuild + rerun the subject reproduces the identical hem average -- the 205 // integer solver has no hidden nondeterminism (replay-exactness is what makes any future 206 // world-page adoption gate-provable byte-for-byte). 207 let vertsD: *i64 = sys_mmap(nvv*3*8 + 64) as *i64 208 let velD: *i64 = sys_mmap(nvv*3*8 + 64) as *i64 209 xg_build(vertsD, velD) 210 xg_run(vertsD, velD, nvv, edges, ne, pins, XG_NU, XG_TICKS, 0, XG_ALPHA_BEND_SOFT_Q12, bank) 211 var t6: i64 = 0 212 if xg_hem_z_avg(vertsD) == hem1 { if xg_shape_diff(verts, vertsD, nvv) == 0 { t6 = 1 } } 213 gv_check("T6 DETERMINISM: identical rerun reproduces the identical shape, bit for bit" as *u8, t6, ctr) 214 215 // T7/T8 CAPSULE CONTACT: drop the sheet onto a capsule lying under the free half. After the 216 // run with per-tick projection, NO vert may remain inside the capsule (T7), and the capsule 217 // must have actually been REACHED (projections > 0) or the drape test judged nothing (T8 -- 218 // the fixture-reached-the-condition tooth this estate demands of every contact gate). 219 let vertsE: *i64 = sys_mmap(nvv*3*8 + 64) as *i64 220 let velE: *i64 = sys_mmap(nvv*3*8 + 64) as *i64 221 xg_build(vertsE, velE) 222 // capsule along y under the sheet's free end: center x at 3/4 of the reach, below by one R 223 let cpx: i64 = (XG_NV-1)*XG_DZ*3/4 224 let cpz: i64 = 0 - XG_R*2 225 let crad: i64 = XG_R 226 var hitsum: i64 = 0 227 var tE: i64 = 0 228 while tE < XG_TICKS { 229 gm_xpbd_step_banked(vertsE, velE, nvv, edges, ne, pins, XG_NU, XG_G, XG_DT_US, XG_ITERS, 0, XG_ALPHA_BEND_SOFT_Q12, bank) 230 hitsum = hitsum + gm_capsule_project(vertsE, velE, nvv, cpx, 0-XG_R*2, cpz, cpx, XG_R*2, cpz, crad) 231 tE = tE + 1 232 } 233 var inside: i64 = 0 234 var vE: i64 = 0 235 while vE < nvv { 236 let pxE: i64 = vertsE[vE*3] - cpx 237 var pyE: i64 = vertsE[vE*3+1] 238 if pyE < 0 - XG_R*2 { pyE = 0 - XG_R*2 } 239 if pyE > XG_R*2 { pyE = XG_R*2 } 240 let dyE: i64 = vertsE[vE*3+1] - pyE 241 let pzE: i64 = vertsE[vE*3+2] - cpz 242 if pxE*pxE + dyE*dyE + pzE*pzE < crad*crad - crad { inside = inside + 1 } 243 vE = vE + 1 244 } 245 gv_puts("capsule: projections=" as *u8); gv_num(hitsum) 246 gv_puts(" verts_inside_after=" as *u8); gv_num(inside); gv_puts("\n" as *u8) 247 var t7: i64 = 0 248 if inside == 0 { t7 = 1 } 249 gv_check("T7 CAPSULE CONTACT: no cloth vert rests inside the body capsule after the drape" as *u8, t7, ctr) 250 var t8: i64 = 0 251 if hitsum > 0 { t8 = 1 } 252 gv_check("T8 THE DRAPE REACHED THE CAPSULE: projections fired, so T7 judged a real contact, not a miss" as *u8, t8, ctr) 253 254 return gv_verdict("GM-XPBD" as *u8, ctr, "PG29 cloth solver: the sideways sheet folds under gravity, pins hold, area holds within the stated convergence bound, stiffness is a real knob, projections are load-bearing, and the run is deterministic" as *u8) 255}