diff --git a/OPSTACK-PROGRESS.md b/OPSTACK-PROGRESS.md index d2c87ae..63c9791 100644 --- a/OPSTACK-PROGRESS.md +++ b/OPSTACK-PROGRESS.md @@ -680,3 +680,50 @@ scaffolding, and **resume Phase 1 step 3** — the port is proven correct (SDF e the final rounding differs, 0 isosurface crossings). --- + +## 2026-07-27 — hypothesis 5 dead too. One unambiguous check left, then I stop chasing. + +``` +CONST-Blend == verbatim : 4874 / 5000 +CONST-Blend != runtime-Blend : 0 +``` + +`Blend`'s constness is **not** the variable: the const and runtime carve forms are bit-identical to +each other, and both miss the verbatim on the same 126. Five hypotheses, five dead. + +**Worse, one of the numbers I reasoned from was circular.** `FORCENOINLINE == operator stack : +5000/5000` cannot fail by construction — it feeds `S.Sdf` to a carve and compares against the density +the stack computed *from that same `S.Sdf`*. It measures nothing. I read it as corroboration. + +The two carve bodies have now been dumped from the file and diffed: **character-identical, same +translation unit.** So of the three things I keep calling identical — expression, TU, input — one is +false, and the counters cannot say which, because the SDF comparison only ran *inside* the mismatch +branch. + +**The unambiguous check added:** feed my carve the SDF the verbatim reports using, compare to the +verbatim's own output, and count `S.Sdf != VerbSdf` **directly, with no enclosing condition**. + +- `Recon == Ver` everywhere **and** SDFs equal everywhere ⇒ same function, same input, different + output ⇒ the difference is a measurement artefact, not a code one. +- SDFs differ ⇒ they were never equal outside the mismatch set, the earlier counter was misleading, + and the fault is back in the lattice after all. + +### Proportion check — this is the last build I would spend on it + +**The port is already verified on every axis that affects the game:** SDF bit-exact on 126/126, the +lattice/hashes/`Capsule` exactly right, **0 isosurface crossings out of 20000**, geometry identical, +window-invariant across threads, and every box verdict survives brute force. The open question is why +the *final rounding* differs by 1-2 ULP — and no decision anywhere in this project turns on the +answer. + +**So: if this check doesn't resolve it, accept and move to Phase 1 step 3.** An unexplained +deterministic difference deserves real effort — it is a real bug often enough to be worth six builds — +but not unbounded effort when every consequence of it is already measured and benign. + +**UNVERIFIED:** the check. + +**Next single action:** rebuild, read `UNAMBIGUOUS DISCRIMINATOR`. Then either fix or accept, and in +both cases correct `OPSTACK-PLAN §2.6` / `AUDIT §C9` / the test's INFO text, strip the diagnostic +scaffolding, and resume step 3. + +--- diff --git a/Source/VoxelForge/Private/Tests/VoxelForgeOpStackMazeTest.cpp b/Source/VoxelForge/Private/Tests/VoxelForgeOpStackMazeTest.cpp index 6856326..1ccb53c 100644 --- a/Source/VoxelForge/Private/Tests/VoxelForgeOpStackMazeTest.cpp +++ b/Source/VoxelForge/Private/Tests/VoxelForgeOpStackMazeTest.cpp @@ -454,6 +454,7 @@ bool FVoxelForgeOpStackMazeTest::RunTest(const FString& Parameters) // ── L'expérience décisive : inline vs FORCENOINLINE, même unité, même source. ── int32 InlineVsNoInline = 0, NoInlineMatchesStack = 0, InlineMatchesVerbatim = 0; int32 ConstMatchesVerbatim = 0, ConstVsRuntimeBlend = 0; + int32 ReconMatchesVerbatim = 0, StackSdfVsVerbSdf = 0; for (int32 i = 0; i < N; ++i) { const float PX = (float)Points[i].X, PY = (float)Points[i].Y, PZ = (float)Points[i].Z; @@ -461,8 +462,21 @@ bool FVoxelForgeOpStackMazeTest::RunTest(const FString& Parameters) const float Inl = -CarveInlined(S.Sdf, 2.0f, Core.BaseDensity, Core.BaseDensity); const float Noi = -CarveNoInline(S.Sdf, 2.0f, Core.BaseDensity, Core.BaseDensity); const float Cst = -CarveConstBlend(S.Sdf, Core.BaseDensity, Core.BaseDensity); - const float Ver = MazeCoreVerbatim(PX, PY, PZ, Core, World.Settings->Seed); + float VSdf = 0.0f; + const float Ver = MazeCoreVerbatim(PX, PY, PZ, Core, World.Settings->Seed, &VSdf); const float Stk = -S.Density; + + // LE DISCRIMINATEUR NON AMBIGU : on nourrit ma fonction de carve avec le SDF que le + // verbatim dit avoir utilisé, et on compare à la sortie du verbatim lui-même. + // Recon == Ver partout ⇒ ma fonction de carve EST celle du verbatim, donc l'écart + // vient de ce que S.Sdf != VSdf (et le compteur « SDF + // differs 0 » mesurait autre chose que ce que je croyais). + // Recon != Ver ⇒ deux expressions caractère pour caractère identiques, + // même unité, même entrée, sorties différentes. + // Et on compte directement S.Sdf vs VSdf, sans passer par une condition. + const float Recon = -CarveConstBlend(VSdf, Core.BaseDensity, Core.BaseDensity); + if (BitEqual(Recon, Ver)) { ++ReconMatchesVerbatim; } + if (!BitEqual(S.Sdf, VSdf)) { ++StackSdfVsVerbSdf; } if (!BitEqual(Inl, Noi)) { ++InlineVsNoInline; } if (BitEqual(Noi, Stk)) { ++NoInlineMatchesStack; } if (BitEqual(Inl, Ver)) { ++InlineMatchesVerbatim; } @@ -477,6 +491,13 @@ bool FVoxelForgeOpStackMazeTest::RunTest(const FString& Parameters) TEXT(" runtime-Blend == verbatim : %d / %d\n") TEXT(" CONST-Blend == verbatim : %d / %d <-- the tell\n") TEXT(" CONST-Blend != runtime-Blend : %d\n") + TEXT(" UNAMBIGUOUS DISCRIMINATOR (feed my carve the SDF the verbatim says it used):\n") + TEXT(" my carve(verbatim's own SDF) == verbatim : %d / %d\n") + TEXT(" stack SDF != verbatim SDF : %d (counted directly, no condition)\n") + TEXT(" If the first is %d/%d and the second is 0, then the two carves ARE the same\n") + TEXT(" function on the same input and the difference is impossible -- which would mean\n") + TEXT(" a measurement error, not a code one. If the second is nonzero, the SDFs were\n") + TEXT(" never equal outside the mismatch set and the fault is back in the lattice.\n") TEXT(" The three carve forms are character-identical apart from ONE thing: whether\n") TEXT(" `Blend` is a compile-time constant (GetMazeDensity, verbatim) or runtime data\n") TEXT(" (FSdfCarveOp holds it as a member; the parameter versions above mimic that).\n") @@ -487,7 +508,8 @@ bool FVoxelForgeOpStackMazeTest::RunTest(const FString& Parameters) TEXT(" are DATA by design, so they can never be compile-time constants again. Nothing\n") TEXT(" to fix in the port -- this is the true, permanent floor for every archetype."), N, InlineVsNoInline, NoInlineMatchesStack, N, - InlineMatchesVerbatim, N, ConstMatchesVerbatim, N, ConstVsRuntimeBlend)); + InlineMatchesVerbatim, N, ConstMatchesVerbatim, N, ConstVsRuntimeBlend, + ReconMatchesVerbatim, N, StackSdfVsVerbSdf, N, N)); } MutableGen->OriginSpineRadius = SavedSpine;