Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
using System;
|
|
|
|
|
using CultMath;
|
|
|
|
|
using Xunit;
|
|
|
|
|
|
|
|
|
|
namespace CultMath.Tests;
|
|
|
|
|
|
|
|
|
|
public sealed class CellularAndSminGradTests
|
|
|
|
|
{
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
// Central-difference tolerances for smin_grad and cellular (F5), sized separately: the two
|
|
|
|
|
// functions have different curvature, so the same eps does not carry the same truncation
|
|
|
|
|
// error, and an earlier shared 3e-4 tolerance was fragile against both (Soul, cut 2a-i: measured
|
|
|
|
|
// max error smin 2.74e-4, cellular nearest 1.25e-4, cellular edge 1.88e-4 on a small sample —
|
|
|
|
|
// and re-measured here at 5.3e-4, 2.0e-4, 2.6e-4 on a larger one — against that 3e-4 tolerance,
|
|
|
|
|
// sometimes under 1.1x headroom, not the "~6e-5 noise" an earlier version of this comment
|
|
|
|
|
// claimed). Each tolerance below is set from its own measured worst case with real headroom.
|
|
|
|
|
|
|
|
|
|
// smin_grad's value is exactly quadratic in position within a smoothing band (math.cs comment on
|
|
|
|
|
// smin_grad: h is affine in b.w - a.w, which is itself affine in position, so value(q) has zero
|
|
|
|
|
// third derivative there): central differences have no truncation error from curvature, only
|
|
|
|
|
// float32 rounding noise, so a larger eps directly dilutes that noise with no accuracy cost.
|
|
|
|
|
// Measured max error at eps=0.01f over 10,000 samples: 4.37e-5 (up from eps=0.001f's 5.3e-4,
|
|
|
|
|
// confirming the noise is eps-limited, not curvature-limited); this tolerance keeps about 5.7x
|
|
|
|
|
// headroom over that. A uniform-scale mutant on the returned gradient (a 1.01x M9/M16-style
|
|
|
|
|
// mutant applied here) still dies down to a 1.0005x scale (0.05%); it survives at 1.0002x.
|
|
|
|
|
private const float SminGradientTolerance = 2.5e-4f;
|
|
|
|
|
|
|
|
|
|
// cellular's F1/F2 are Euclidean distance fields: real curvature away from a feature point means
|
|
|
|
|
// central differences DO carry O(eps^2) truncation error, so (unlike smin_grad) a larger eps
|
|
|
|
|
// makes things worse, not better; a sweep at eps in {0.0002f, 0.0003f, 0.0005f, 0.0007f, 0.001f}
|
|
|
|
|
// over 8,000 samples found the committed eps=0.0005f already close to the sweet spot. Measured
|
|
|
|
|
// max error at that eps: nearest 2.04e-4, edge 2.57e-4; this tolerance keeps just over 5x
|
|
|
|
|
// headroom over the larger of the two. M9 (grad1 * 1.01) and M16 (grad2 * 1.01) both still die
|
|
|
|
|
// down to a 1.002x scale (0.2%); both survive at 1.001x.
|
|
|
|
|
private const float CellularGradientTolerance = 1.3e-3f;
|
Fix batch 1 (cellular/smin_grad): exact search, decorrelated id, finite F2=0
Cut 2a-i fix batch 1, findings F1, F3, F4, F5, F6, F7 (Soul falsification of
the first landing):
- cellular now searches a provably exact 5x5x5 neighbourhood (radius 2), not
3x3x3: 3x3x3 missed the true F2 for 9 of 400,000 sampled points. The
comment in math.cs proves 5x5x5 always contains the true F1 and F2 (both
are always < sqrt(3), every excluded cell is always > 2). A cheap
position-only lower-bound prune skips the pcg3d/jitter/distance work for
cells that provably cannot beat the running F2, without changing any
output bit (F6).
- F2 = 0 now gets the same degenerate-gradient guard F1 = 0 already had,
fixing a NaN that was reachable past |p| of about 2^23 (F3).
- id now comes from pcg4d over the winning cell alone, never from the pcg3d
hash that produced that cell's jitter, so id and jitter are no longer
deterministic functions of each other (F4).
- cellular_unit maps the top 24 bits instead of a plain divide-by-2^32, so
the mapping is always inside [0, 1); the old formula rounded
0xFFFFFF80..0xFFFFFFFF up to exactly 1.0f (F7).
- Central-difference tolerances tightened from 0.02 to 3e-4 (measured float
noise at the committed eps is about 6e-5), and cellular's own gradient
test now differentiates cellular's RETURNED .w values instead of a
duplicate FindF1F2 search, so a mutant that corrupts only the returned
gradient can't hide behind an independently-computed value (F1, F5).
- The struct-mirror comparison in HlslSourceCompatibilityTests now checks
field name and declared type in declaration order, not just position, so
a reordered-and-relabelled mirror struct (H2) or a renamed/retyped one
(H3) both fail the walk instead of surviving on values alone (F2).
- FindF1F2 widened to a true independent 7x7x7 brute-force oracle; new tests
pin production's exact agreement with it, the pruned search's bit-for-bit
equivalence to an unpruned 5x5x5 reference, no NaN at 2^24, and id's
correlation with jitter under 0.05.
- Comment/doc fix: smin_grad is Quilez's quadratic-polynomial smin, not
cubic.
Both shaders/CultMath.hlsl and unity/.../Shaders/CultMath.hlsl stay
byte-identical.
Manually verified mutants M9 (grad1 x 1.01), M16 (grad2 x 1.01), and M18 (id
from hash.y) all die against these tests; H2/H3 struct mutants die via the
new comparator tests. Local dotnet test: 191/191 passing. FXC cs_5_0 compile
of a scratch kernel using both cultmath_smin_grad and cultmath_cellular
succeeds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 20:40:28 +02:00
|
|
|
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
// ---- smin_grad ----
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void SminValueNeverExceedsTheOrdinaryMinimum()
|
|
|
|
|
{
|
2026-09-25 19:23:54 +02:00
|
|
|
var random = new System.Random(0xACE1);
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
for (var i = 0; i < 256; i++)
|
|
|
|
|
{
|
|
|
|
|
var a = new float4(RandomUnit(random), RandomScalar(random));
|
|
|
|
|
var b = new float4(RandomUnit(random), RandomScalar(random));
|
|
|
|
|
var k = 0.05f + random.NextSingle() * 5.0f;
|
|
|
|
|
|
|
|
|
|
var result = math.smin_grad(a, b, k);
|
|
|
|
|
|
|
|
|
|
Assert.True(result.w <= math.min(a.w, b.w) + 1.0e-4f, $"a.w={a.w} b.w={b.w} k={k} smin={result.w}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void SminMatchesTheHardMinimumAndItsGradientOutsideTheBand()
|
|
|
|
|
{
|
2026-09-25 19:23:54 +02:00
|
|
|
var random = new System.Random(0xACE2);
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
for (var i = 0; i < 64; i++)
|
|
|
|
|
{
|
|
|
|
|
var gradA = RandomUnit(random);
|
|
|
|
|
var gradB = RandomUnit(random);
|
|
|
|
|
var k = 0.1f + random.NextSingle() * 2.0f;
|
|
|
|
|
|
|
|
|
|
// Push b comfortably above a, past the smoothing band, so a is the ordinary minimum.
|
|
|
|
|
var aw = random.NextSingle() * 10.0f - 5.0f;
|
|
|
|
|
var bw = aw + k * (1.5f + random.NextSingle());
|
|
|
|
|
var a = new float4(gradA, aw);
|
|
|
|
|
var b = new float4(gradB, bw);
|
|
|
|
|
|
|
|
|
|
var lower = math.smin_grad(a, b, k);
|
|
|
|
|
Assert.Equal(aw, lower.w, precision: 4);
|
|
|
|
|
Assert.Equal(gradA.x, lower.x, precision: 4);
|
|
|
|
|
Assert.Equal(gradA.y, lower.y, precision: 4);
|
|
|
|
|
Assert.Equal(gradA.z, lower.z, precision: 4);
|
|
|
|
|
|
|
|
|
|
// Argument order must not matter: b below a is symmetric.
|
|
|
|
|
var higher = math.smin_grad(b, a, k);
|
|
|
|
|
Assert.Equal(aw, higher.w, precision: 4);
|
|
|
|
|
Assert.Equal(gradA.x, higher.x, precision: 4);
|
|
|
|
|
Assert.Equal(gradA.y, higher.y, precision: 4);
|
|
|
|
|
Assert.Equal(gradA.z, higher.z, precision: 4);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void SminGradientMatchesCentralDifferencesOfItsOwnValue()
|
|
|
|
|
{
|
2026-09-25 19:23:54 +02:00
|
|
|
var random = new System.Random(0xACE3);
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
var tested = 0;
|
|
|
|
|
while (tested < 200)
|
|
|
|
|
{
|
|
|
|
|
var cf = RandomUnit(random);
|
|
|
|
|
var cg = RandomUnit(random);
|
|
|
|
|
var offF = random.NextSingle() * 4.0f - 2.0f;
|
|
|
|
|
var offG = random.NextSingle() * 4.0f - 2.0f;
|
|
|
|
|
var k = 0.2f + random.NextSingle() * 2.0f;
|
|
|
|
|
var p = new float3(random.NextSingle() * 4.0f - 2.0f, random.NextSingle() * 4.0f - 2.0f, random.NextSingle() * 4.0f - 2.0f);
|
|
|
|
|
|
|
|
|
|
float F(float3 q) => math.dot(q, cf) + offF;
|
|
|
|
|
float G(float3 q) => math.dot(q, cg) + offG;
|
|
|
|
|
float Smin(float3 q) => math.smin_grad(new float4(cf, F(q)), new float4(cg, G(q)), k).w;
|
|
|
|
|
|
|
|
|
|
// Stay clear of the |a.w - b.w| = k seam: central differences straddling a kink would
|
|
|
|
|
// disagree with either side's analytic gradient (the same reason cellular excludes a
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
// band around its own F1 = F2 set). The band has an absolute floor, not just a
|
|
|
|
|
// fraction of k: eps grew to 0.01f below (SminGradientTolerance's comment), and a
|
|
|
|
|
// fraction-of-k-only band could be narrower than the diff can shift by eps*|cf-cg|
|
|
|
|
|
// (up to about 0.035 here) when k is small, letting p +/- eps step across the seam even
|
|
|
|
|
// though p itself was clear of it.
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
var diff = F(p) - G(p);
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
if (Math.Abs(Math.Abs(diff) - k) < Math.Max(0.05f * k, 0.05f))
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
tested++;
|
|
|
|
|
var analytic = math.smin_grad(new float4(cf, F(p)), new float4(cg, G(p)), k);
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
|
|
|
|
|
// eps=0.01f, not 0.001f: smin is exactly quadratic within a band (no truncation error
|
|
|
|
|
// from curvature; see SminGradientTolerance's comment), so the larger eps only dilutes
|
|
|
|
|
// float32 rounding noise, at no accuracy cost, while still comfortably inside the band
|
|
|
|
|
// exclusion above.
|
|
|
|
|
const float eps = 0.01f;
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
var numeric = new float3(
|
|
|
|
|
(Smin(p + new float3(eps, 0.0f, 0.0f)) - Smin(p - new float3(eps, 0.0f, 0.0f))) / (2.0f * eps),
|
|
|
|
|
(Smin(p + new float3(0.0f, eps, 0.0f)) - Smin(p - new float3(0.0f, eps, 0.0f))) / (2.0f * eps),
|
|
|
|
|
(Smin(p + new float3(0.0f, 0.0f, eps)) - Smin(p - new float3(0.0f, 0.0f, eps))) / (2.0f * eps));
|
|
|
|
|
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
AssertWithinTolerance(numeric.x, analytic.x, SminGradientTolerance);
|
|
|
|
|
AssertWithinTolerance(numeric.y, analytic.y, SminGradientTolerance);
|
|
|
|
|
AssertWithinTolerance(numeric.z, analytic.z, SminGradientTolerance);
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void SminGradientIsNotAConstantBlendOrTheSwappedFactor()
|
|
|
|
|
{
|
|
|
|
|
// Falsifies two named mutants directly: blending the gradient with a constant 0.5 instead of
|
|
|
|
|
// h, or with (1 - h) instead of h. Here a is comfortably the minimum (h saturates to 1 in the
|
|
|
|
|
// correct formula), so the correct gradient is exactly a's, unmixed. A constant-0.5 blend or a
|
|
|
|
|
// swapped (1 - h) blend both pull in b's gradient, which differs from a's here.
|
|
|
|
|
var a = new float4(1.0f, 0.0f, 0.0f, -3.0f);
|
|
|
|
|
var b = new float4(0.0f, 1.0f, 0.0f, 5.0f);
|
|
|
|
|
|
|
|
|
|
var result = math.smin_grad(a, b, 0.25f);
|
|
|
|
|
|
|
|
|
|
Assert.Equal(1.0f, result.x, precision: 5);
|
|
|
|
|
Assert.Equal(0.0f, result.y, precision: 5);
|
|
|
|
|
Assert.Equal(0.0f, result.z, precision: 5);
|
|
|
|
|
Assert.Equal(-3.0f, result.w, precision: 5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- cellular ----
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void F1NeverExceedsF2()
|
|
|
|
|
{
|
2026-09-25 19:23:54 +02:00
|
|
|
var random = new System.Random(0xCE11);
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
for (var i = 0; i < 256; i++)
|
|
|
|
|
{
|
|
|
|
|
var p = new float3(random.NextSingle() * 20.0f - 10.0f, random.NextSingle() * 20.0f - 10.0f, random.NextSingle() * 20.0f - 10.0f);
|
|
|
|
|
var result = math.cellular(p);
|
|
|
|
|
Assert.True(result.edge.w >= -1.0e-4f, $"F2 - F1 = {result.edge.w} at {p.x},{p.y},{p.z}");
|
Fix batch 1 (cellular/smin_grad): exact search, decorrelated id, finite F2=0
Cut 2a-i fix batch 1, findings F1, F3, F4, F5, F6, F7 (Soul falsification of
the first landing):
- cellular now searches a provably exact 5x5x5 neighbourhood (radius 2), not
3x3x3: 3x3x3 missed the true F2 for 9 of 400,000 sampled points. The
comment in math.cs proves 5x5x5 always contains the true F1 and F2 (both
are always < sqrt(3), every excluded cell is always > 2). A cheap
position-only lower-bound prune skips the pcg3d/jitter/distance work for
cells that provably cannot beat the running F2, without changing any
output bit (F6).
- F2 = 0 now gets the same degenerate-gradient guard F1 = 0 already had,
fixing a NaN that was reachable past |p| of about 2^23 (F3).
- id now comes from pcg4d over the winning cell alone, never from the pcg3d
hash that produced that cell's jitter, so id and jitter are no longer
deterministic functions of each other (F4).
- cellular_unit maps the top 24 bits instead of a plain divide-by-2^32, so
the mapping is always inside [0, 1); the old formula rounded
0xFFFFFF80..0xFFFFFFFF up to exactly 1.0f (F7).
- Central-difference tolerances tightened from 0.02 to 3e-4 (measured float
noise at the committed eps is about 6e-5), and cellular's own gradient
test now differentiates cellular's RETURNED .w values instead of a
duplicate FindF1F2 search, so a mutant that corrupts only the returned
gradient can't hide behind an independently-computed value (F1, F5).
- The struct-mirror comparison in HlslSourceCompatibilityTests now checks
field name and declared type in declaration order, not just position, so
a reordered-and-relabelled mirror struct (H2) or a renamed/retyped one
(H3) both fail the walk instead of surviving on values alone (F2).
- FindF1F2 widened to a true independent 7x7x7 brute-force oracle; new tests
pin production's exact agreement with it, the pruned search's bit-for-bit
equivalence to an unpruned 5x5x5 reference, no NaN at 2^24, and id's
correlation with jitter under 0.05.
- Comment/doc fix: smin_grad is Quilez's quadratic-polynomial smin, not
cubic.
Both shaders/CultMath.hlsl and unity/.../Shaders/CultMath.hlsl stay
byte-identical.
Manually verified mutants M9 (grad1 x 1.01), M16 (grad2 x 1.01), and M18 (id
from hash.y) all die against these tests; H2/H3 struct mutants die via the
new comparator tests. Local dotnet test: 191/191 passing. FXC cs_5_0 compile
of a scratch kernel using both cultmath_smin_grad and cultmath_cellular
succeeds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 20:40:28 +02:00
|
|
|
|
|
|
|
|
// Both returned .w values, checked against the independent 7x7x7 brute-force oracle (F1,
|
|
|
|
|
// F6), not a value the test derived some other way.
|
|
|
|
|
var (f1, f2) = FindF1F2(p);
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
Assert.Equal(f1, result.nearest.w, precision: 3);
|
Fix batch 1 (cellular/smin_grad): exact search, decorrelated id, finite F2=0
Cut 2a-i fix batch 1, findings F1, F3, F4, F5, F6, F7 (Soul falsification of
the first landing):
- cellular now searches a provably exact 5x5x5 neighbourhood (radius 2), not
3x3x3: 3x3x3 missed the true F2 for 9 of 400,000 sampled points. The
comment in math.cs proves 5x5x5 always contains the true F1 and F2 (both
are always < sqrt(3), every excluded cell is always > 2). A cheap
position-only lower-bound prune skips the pcg3d/jitter/distance work for
cells that provably cannot beat the running F2, without changing any
output bit (F6).
- F2 = 0 now gets the same degenerate-gradient guard F1 = 0 already had,
fixing a NaN that was reachable past |p| of about 2^23 (F3).
- id now comes from pcg4d over the winning cell alone, never from the pcg3d
hash that produced that cell's jitter, so id and jitter are no longer
deterministic functions of each other (F4).
- cellular_unit maps the top 24 bits instead of a plain divide-by-2^32, so
the mapping is always inside [0, 1); the old formula rounded
0xFFFFFF80..0xFFFFFFFF up to exactly 1.0f (F7).
- Central-difference tolerances tightened from 0.02 to 3e-4 (measured float
noise at the committed eps is about 6e-5), and cellular's own gradient
test now differentiates cellular's RETURNED .w values instead of a
duplicate FindF1F2 search, so a mutant that corrupts only the returned
gradient can't hide behind an independently-computed value (F1, F5).
- The struct-mirror comparison in HlslSourceCompatibilityTests now checks
field name and declared type in declaration order, not just position, so
a reordered-and-relabelled mirror struct (H2) or a renamed/retyped one
(H3) both fail the walk instead of surviving on values alone (F2).
- FindF1F2 widened to a true independent 7x7x7 brute-force oracle; new tests
pin production's exact agreement with it, the pruned search's bit-for-bit
equivalence to an unpruned 5x5x5 reference, no NaN at 2^24, and id's
correlation with jitter under 0.05.
- Comment/doc fix: smin_grad is Quilez's quadratic-polynomial smin, not
cubic.
Both shaders/CultMath.hlsl and unity/.../Shaders/CultMath.hlsl stay
byte-identical.
Manually verified mutants M9 (grad1 x 1.01), M16 (grad2 x 1.01), and M18 (id
from hash.y) all die against these tests; H2/H3 struct mutants die via the
new comparator tests. Local dotnet test: 191/191 passing. FXC cs_5_0 compile
of a scratch kernel using both cultmath_smin_grad and cultmath_cellular
succeeds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 20:40:28 +02:00
|
|
|
Assert.Equal(f2 - f1, result.edge.w, precision: 3);
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void NearestFeatureWinsEvenWhenItIsInTheLastVisitedNeighbourCell()
|
|
|
|
|
{
|
|
|
|
|
// math.cellular's own search order visits neighbour offsets with dz outermost and dx
|
Fix batch 1 (cellular/smin_grad): exact search, decorrelated id, finite F2=0
Cut 2a-i fix batch 1, findings F1, F3, F4, F5, F6, F7 (Soul falsification of
the first landing):
- cellular now searches a provably exact 5x5x5 neighbourhood (radius 2), not
3x3x3: 3x3x3 missed the true F2 for 9 of 400,000 sampled points. The
comment in math.cs proves 5x5x5 always contains the true F1 and F2 (both
are always < sqrt(3), every excluded cell is always > 2). A cheap
position-only lower-bound prune skips the pcg3d/jitter/distance work for
cells that provably cannot beat the running F2, without changing any
output bit (F6).
- F2 = 0 now gets the same degenerate-gradient guard F1 = 0 already had,
fixing a NaN that was reachable past |p| of about 2^23 (F3).
- id now comes from pcg4d over the winning cell alone, never from the pcg3d
hash that produced that cell's jitter, so id and jitter are no longer
deterministic functions of each other (F4).
- cellular_unit maps the top 24 bits instead of a plain divide-by-2^32, so
the mapping is always inside [0, 1); the old formula rounded
0xFFFFFF80..0xFFFFFFFF up to exactly 1.0f (F7).
- Central-difference tolerances tightened from 0.02 to 3e-4 (measured float
noise at the committed eps is about 6e-5), and cellular's own gradient
test now differentiates cellular's RETURNED .w values instead of a
duplicate FindF1F2 search, so a mutant that corrupts only the returned
gradient can't hide behind an independently-computed value (F1, F5).
- The struct-mirror comparison in HlslSourceCompatibilityTests now checks
field name and declared type in declaration order, not just position, so
a reordered-and-relabelled mirror struct (H2) or a renamed/retyped one
(H3) both fail the walk instead of surviving on values alone (F2).
- FindF1F2 widened to a true independent 7x7x7 brute-force oracle; new tests
pin production's exact agreement with it, the pruned search's bit-for-bit
equivalence to an unpruned 5x5x5 reference, no NaN at 2^24, and id's
correlation with jitter under 0.05.
- Comment/doc fix: smin_grad is Quilez's quadratic-polynomial smin, not
cubic.
Both shaders/CultMath.hlsl and unity/.../Shaders/CultMath.hlsl stay
byte-identical.
Manually verified mutants M9 (grad1 x 1.01), M16 (grad2 x 1.01), and M18 (id
from hash.y) all die against these tests; H2/H3 struct mutants die via the
new comparator tests. Local dotnet test: 191/191 passing. FXC cs_5_0 compile
of a scratch kernel using both cultmath_smin_grad and cultmath_cellular
succeeds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 20:40:28 +02:00
|
|
|
// innermost over a 5x5x5 block (F6), and it also prunes cells whose position-only lower
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
// bound already exceeds the running F2. Offsets with any |component| = 2 are not provably
|
|
|
|
|
// unreachable as F1: their minimum possible distance is 1 (e.g. (2, 0, 0), whose lower bound
|
|
|
|
|
// per the exactness proof in math.cs is max(0, 2-1) = 1), well inside the query's own cell's
|
|
|
|
|
// sqrt(3) ceiling, so a |component| = 2 offset winning F1 is rare rather than impossible
|
|
|
|
|
// (Soul, cut 2a-i: measured 3 F1 wins in 2,000,000 samples). It is still rare enough that the
|
|
|
|
|
// 3x3x3 corners remain the practically-reachable "last" cases for this test. (dx, dy, dz)
|
Fix batch 1 (cellular/smin_grad): exact search, decorrelated id, finite F2=0
Cut 2a-i fix batch 1, findings F1, F3, F4, F5, F6, F7 (Soul falsification of
the first landing):
- cellular now searches a provably exact 5x5x5 neighbourhood (radius 2), not
3x3x3: 3x3x3 missed the true F2 for 9 of 400,000 sampled points. The
comment in math.cs proves 5x5x5 always contains the true F1 and F2 (both
are always < sqrt(3), every excluded cell is always > 2). A cheap
position-only lower-bound prune skips the pcg3d/jitter/distance work for
cells that provably cannot beat the running F2, without changing any
output bit (F6).
- F2 = 0 now gets the same degenerate-gradient guard F1 = 0 already had,
fixing a NaN that was reachable past |p| of about 2^23 (F3).
- id now comes from pcg4d over the winning cell alone, never from the pcg3d
hash that produced that cell's jitter, so id and jitter are no longer
deterministic functions of each other (F4).
- cellular_unit maps the top 24 bits instead of a plain divide-by-2^32, so
the mapping is always inside [0, 1); the old formula rounded
0xFFFFFF80..0xFFFFFFFF up to exactly 1.0f (F7).
- Central-difference tolerances tightened from 0.02 to 3e-4 (measured float
noise at the committed eps is about 6e-5), and cellular's own gradient
test now differentiates cellular's RETURNED .w values instead of a
duplicate FindF1F2 search, so a mutant that corrupts only the returned
gradient can't hide behind an independently-computed value (F1, F5).
- The struct-mirror comparison in HlslSourceCompatibilityTests now checks
field name and declared type in declaration order, not just position, so
a reordered-and-relabelled mirror struct (H2) or a renamed/retyped one
(H3) both fail the walk instead of surviving on values alone (F2).
- FindF1F2 widened to a true independent 7x7x7 brute-force oracle; new tests
pin production's exact agreement with it, the pruned search's bit-for-bit
equivalence to an unpruned 5x5x5 reference, no NaN at 2^24, and id's
correlation with jitter under 0.05.
- Comment/doc fix: smin_grad is Quilez's quadratic-polynomial smin, not
cubic.
Both shaders/CultMath.hlsl and unity/.../Shaders/CultMath.hlsl stay
byte-identical.
Manually verified mutants M9 (grad1 x 1.01), M16 (grad2 x 1.01), and M18 (id
from hash.y) all die against these tests; H2/H3 struct mutants die via the
new comparator tests. Local dotnet test: 191/191 passing. FXC cs_5_0 compile
of a scratch kernel using both cultmath_smin_grad and cultmath_cellular
succeeds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 20:40:28 +02:00
|
|
|
// = (1, 1, 1) is the last of those the fixed iteration order visits, and if pruning or search
|
|
|
|
|
// order were broken, this is the offset most likely to be skipped. Rather than hand-picking a
|
|
|
|
|
// hash value, search random points for one whose true nearest feature (by independent brute
|
|
|
|
|
// force over the same 5x5x5 block) happens to live in exactly that cell, at a comfortable
|
|
|
|
|
// margin from the F1 = F2 seam, then check math.cellular agrees.
|
2026-09-25 19:23:54 +02:00
|
|
|
var random = new System.Random(0xCE14);
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
var lastOffset = new float3(1.0f, 1.0f, 1.0f);
|
|
|
|
|
for (var attempt = 0; attempt < 5000; attempt++)
|
|
|
|
|
{
|
|
|
|
|
var p = new float3(random.NextSingle() * 8.0f - 4.0f, random.NextSingle() * 8.0f - 4.0f, random.NextSingle() * 8.0f - 4.0f);
|
|
|
|
|
var (offset, f1, f2) = FindNearestOffset(p);
|
|
|
|
|
if (!offset.Equals(lastOffset) || f1 < 0.05f || f2 - f1 < 0.05f)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
var cell = new float3(MathF.Floor(p.x), MathF.Floor(p.y), MathF.Floor(p.z));
|
|
|
|
|
var feature = FeaturePoint(cell + lastOffset);
|
|
|
|
|
var expectedGradient = (p - feature) / f1;
|
|
|
|
|
|
|
|
|
|
var result = math.cellular(p);
|
|
|
|
|
Assert.Equal(f1, result.nearest.w, precision: 4);
|
|
|
|
|
Assert.Equal(expectedGradient.x, result.nearest.x, precision: 4);
|
|
|
|
|
Assert.Equal(expectedGradient.y, result.nearest.y, precision: 4);
|
|
|
|
|
Assert.Equal(expectedGradient.z, result.nearest.z, precision: 4);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Assert.Fail("did not find a random point whose nearest feature lives in the last-visited neighbour cell");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void NearestGradientIsFiniteZeroAtAFeaturePointInsteadOfNaN()
|
|
|
|
|
{
|
|
|
|
|
var feature = FeaturePoint(new float3(4.0f, -2.0f, 9.0f));
|
|
|
|
|
var result = math.cellular(feature);
|
|
|
|
|
|
|
|
|
|
Assert.Equal(0.0f, result.nearest.w, precision: 4);
|
|
|
|
|
Assert.True(float.IsFinite(result.nearest.x) && float.IsFinite(result.nearest.y) && float.IsFinite(result.nearest.z));
|
|
|
|
|
Assert.Equal(0.0f, result.nearest.x, precision: 5);
|
|
|
|
|
Assert.Equal(0.0f, result.nearest.y, precision: 5);
|
|
|
|
|
Assert.Equal(0.0f, result.nearest.z, precision: 5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam()
|
|
|
|
|
{
|
2026-09-25 19:23:54 +02:00
|
|
|
var random = new System.Random(0xCE12);
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
var tested = 0;
|
|
|
|
|
var attempts = 0;
|
|
|
|
|
while (tested < 120 && attempts < 20000)
|
|
|
|
|
{
|
|
|
|
|
attempts++;
|
|
|
|
|
var p = new float3(random.NextSingle() * 6.0f - 3.0f, random.NextSingle() * 6.0f - 3.0f, random.NextSingle() * 6.0f - 3.0f);
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
var (f1, f2, f3) = FindF1F2F3(p);
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
|
|
|
|
|
// Exclude a band around the F1 = F2 set: the identity of the nearest and second-nearest
|
|
|
|
|
// feature swap there, a genuine kink in F1 and F2 alike, not something central differences
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
// can approximate. Also exclude a band around F2 = F3: edge (F2 - F1) kinks there too,
|
|
|
|
|
// since the second-nearest feature's identity swaps (Soul, cut 2a-i: an unfiltered sample
|
|
|
|
|
// hit a 0.78 edge error this way). Also skip the exact feature point itself (F1 = 0),
|
|
|
|
|
// covered separately.
|
|
|
|
|
if (f2 - f1 < 0.05f || f1 < 0.01f || f3 - f2 < 0.05f)
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
tested++;
|
|
|
|
|
var result = math.cellular(p);
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
|
|
|
|
|
// eps=0.0005f: unlike smin_grad, cellular's F1/F2 are Euclidean distance fields with real
|
|
|
|
|
// curvature, so central differences carry O(eps^2) truncation error and a larger eps is
|
|
|
|
|
// worse, not better (see CellularGradientTolerance's comment for the measured sweep).
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
const float eps = 0.0005f;
|
|
|
|
|
|
Fix batch 1 (cellular/smin_grad): exact search, decorrelated id, finite F2=0
Cut 2a-i fix batch 1, findings F1, F3, F4, F5, F6, F7 (Soul falsification of
the first landing):
- cellular now searches a provably exact 5x5x5 neighbourhood (radius 2), not
3x3x3: 3x3x3 missed the true F2 for 9 of 400,000 sampled points. The
comment in math.cs proves 5x5x5 always contains the true F1 and F2 (both
are always < sqrt(3), every excluded cell is always > 2). A cheap
position-only lower-bound prune skips the pcg3d/jitter/distance work for
cells that provably cannot beat the running F2, without changing any
output bit (F6).
- F2 = 0 now gets the same degenerate-gradient guard F1 = 0 already had,
fixing a NaN that was reachable past |p| of about 2^23 (F3).
- id now comes from pcg4d over the winning cell alone, never from the pcg3d
hash that produced that cell's jitter, so id and jitter are no longer
deterministic functions of each other (F4).
- cellular_unit maps the top 24 bits instead of a plain divide-by-2^32, so
the mapping is always inside [0, 1); the old formula rounded
0xFFFFFF80..0xFFFFFFFF up to exactly 1.0f (F7).
- Central-difference tolerances tightened from 0.02 to 3e-4 (measured float
noise at the committed eps is about 6e-5), and cellular's own gradient
test now differentiates cellular's RETURNED .w values instead of a
duplicate FindF1F2 search, so a mutant that corrupts only the returned
gradient can't hide behind an independently-computed value (F1, F5).
- The struct-mirror comparison in HlslSourceCompatibilityTests now checks
field name and declared type in declaration order, not just position, so
a reordered-and-relabelled mirror struct (H2) or a renamed/retyped one
(H3) both fail the walk instead of surviving on values alone (F2).
- FindF1F2 widened to a true independent 7x7x7 brute-force oracle; new tests
pin production's exact agreement with it, the pruned search's bit-for-bit
equivalence to an unpruned 5x5x5 reference, no NaN at 2^24, and id's
correlation with jitter under 0.05.
- Comment/doc fix: smin_grad is Quilez's quadratic-polynomial smin, not
cubic.
Both shaders/CultMath.hlsl and unity/.../Shaders/CultMath.hlsl stay
byte-identical.
Manually verified mutants M9 (grad1 x 1.01), M16 (grad2 x 1.01), and M18 (id
from hash.y) all die against these tests; H2/H3 struct mutants die via the
new comparator tests. Local dotnet test: 191/191 passing. FXC cs_5_0 compile
of a scratch kernel using both cultmath_smin_grad and cultmath_cellular
succeeds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 20:40:28 +02:00
|
|
|
// Differentiate cellular's own returned .w values (F1): calling cellular again at p +/-
|
|
|
|
|
// eps and reading result.nearest.w / result.edge.w, not a duplicate FindF1F2 search. A
|
|
|
|
|
// mutant that corrupts only the returned gradient, leaving the returned value alone,
|
|
|
|
|
// would survive a check built on an independent value oracle; it cannot survive this one.
|
|
|
|
|
float NearestValueAt(float3 q) => math.cellular(q).nearest.w;
|
|
|
|
|
float EdgeValueAt(float3 q) => math.cellular(q).edge.w;
|
|
|
|
|
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
AssertGradientMatchesCentralDifference(NearestValueAt, p, eps, result.nearest, CellularGradientTolerance);
|
|
|
|
|
AssertGradientMatchesCentralDifference(EdgeValueAt, p, eps, result.edge, CellularGradientTolerance);
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Assert.True(tested >= 100, $"only {tested} of {attempts} random points landed away from the F1=F2 band");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void IdIsConstantAcrossAFeaturesWholeRegionAndVariesBetweenCells()
|
|
|
|
|
{
|
2026-09-25 19:23:54 +02:00
|
|
|
var random = new System.Random(0xCE13);
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
var baseCell = new float3(2.0f, 2.0f, 2.0f);
|
|
|
|
|
var feature = FeaturePoint(baseCell);
|
|
|
|
|
var idAtFeature = math.cellular(feature).id;
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < 32; i++)
|
|
|
|
|
{
|
|
|
|
|
// Small offsets that stay inside the same Voronoi region (well short of any neighbour).
|
|
|
|
|
var jitter = new float3(random.NextSingle() * 0.2f - 0.1f, random.NextSingle() * 0.2f - 0.1f, random.NextSingle() * 0.2f - 0.1f);
|
|
|
|
|
var nearby = feature + jitter;
|
|
|
|
|
if (!FindNearestCell(nearby).Equals(baseCell))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
Assert.Equal(idAtFeature, math.cellular(nearby).id, precision: 6);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var otherCellId = math.cellular(FeaturePoint(baseCell + new float3(5.0f, 0.0f, 0.0f))).id;
|
|
|
|
|
Assert.NotEqual(idAtFeature, otherCellId);
|
|
|
|
|
}
|
|
|
|
|
|
Fix batch 1 (cellular/smin_grad): exact search, decorrelated id, finite F2=0
Cut 2a-i fix batch 1, findings F1, F3, F4, F5, F6, F7 (Soul falsification of
the first landing):
- cellular now searches a provably exact 5x5x5 neighbourhood (radius 2), not
3x3x3: 3x3x3 missed the true F2 for 9 of 400,000 sampled points. The
comment in math.cs proves 5x5x5 always contains the true F1 and F2 (both
are always < sqrt(3), every excluded cell is always > 2). A cheap
position-only lower-bound prune skips the pcg3d/jitter/distance work for
cells that provably cannot beat the running F2, without changing any
output bit (F6).
- F2 = 0 now gets the same degenerate-gradient guard F1 = 0 already had,
fixing a NaN that was reachable past |p| of about 2^23 (F3).
- id now comes from pcg4d over the winning cell alone, never from the pcg3d
hash that produced that cell's jitter, so id and jitter are no longer
deterministic functions of each other (F4).
- cellular_unit maps the top 24 bits instead of a plain divide-by-2^32, so
the mapping is always inside [0, 1); the old formula rounded
0xFFFFFF80..0xFFFFFFFF up to exactly 1.0f (F7).
- Central-difference tolerances tightened from 0.02 to 3e-4 (measured float
noise at the committed eps is about 6e-5), and cellular's own gradient
test now differentiates cellular's RETURNED .w values instead of a
duplicate FindF1F2 search, so a mutant that corrupts only the returned
gradient can't hide behind an independently-computed value (F1, F5).
- The struct-mirror comparison in HlslSourceCompatibilityTests now checks
field name and declared type in declaration order, not just position, so
a reordered-and-relabelled mirror struct (H2) or a renamed/retyped one
(H3) both fail the walk instead of surviving on values alone (F2).
- FindF1F2 widened to a true independent 7x7x7 brute-force oracle; new tests
pin production's exact agreement with it, the pruned search's bit-for-bit
equivalence to an unpruned 5x5x5 reference, no NaN at 2^24, and id's
correlation with jitter under 0.05.
- Comment/doc fix: smin_grad is Quilez's quadratic-polynomial smin, not
cubic.
Both shaders/CultMath.hlsl and unity/.../Shaders/CultMath.hlsl stay
byte-identical.
Manually verified mutants M9 (grad1 x 1.01), M16 (grad2 x 1.01), and M18 (id
from hash.y) all die against these tests; H2/H3 struct mutants die via the
new comparator tests. Local dotnet test: 191/191 passing. FXC cs_5_0 compile
of a scratch kernel using both cultmath_smin_grad and cultmath_cellular
succeeds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 20:40:28 +02:00
|
|
|
[Fact]
|
|
|
|
|
public void IdIsNotCorrelatedWithAnyJitterComponent()
|
|
|
|
|
{
|
|
|
|
|
// F4: id must come from hash bits the jitter never uses. A mutant that takes id from
|
|
|
|
|
// hash.x (or hash.y) makes id literally equal to that jitter component, which this catches
|
|
|
|
|
// as a correlation near 1.0, far past the 0.05 threshold. n = 20000 keeps sampling noise
|
|
|
|
|
// (measured under 0.01 at this n) well clear of that threshold.
|
|
|
|
|
const int n = 20000;
|
|
|
|
|
var random = new System.Random(0xCE15);
|
|
|
|
|
var ids = new double[n];
|
|
|
|
|
var jitterX = new double[n];
|
|
|
|
|
var jitterY = new double[n];
|
|
|
|
|
var jitterZ = new double[n];
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < n; i++)
|
|
|
|
|
{
|
|
|
|
|
var cell = new float3(random.Next(-2000, 2000), random.Next(-2000, 2000), random.Next(-2000, 2000));
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
var hash = math.pcg3d(math.int3(cell));
|
Fix batch 1 (cellular/smin_grad): exact search, decorrelated id, finite F2=0
Cut 2a-i fix batch 1, findings F1, F3, F4, F5, F6, F7 (Soul falsification of
the first landing):
- cellular now searches a provably exact 5x5x5 neighbourhood (radius 2), not
3x3x3: 3x3x3 missed the true F2 for 9 of 400,000 sampled points. The
comment in math.cs proves 5x5x5 always contains the true F1 and F2 (both
are always < sqrt(3), every excluded cell is always > 2). A cheap
position-only lower-bound prune skips the pcg3d/jitter/distance work for
cells that provably cannot beat the running F2, without changing any
output bit (F6).
- F2 = 0 now gets the same degenerate-gradient guard F1 = 0 already had,
fixing a NaN that was reachable past |p| of about 2^23 (F3).
- id now comes from pcg4d over the winning cell alone, never from the pcg3d
hash that produced that cell's jitter, so id and jitter are no longer
deterministic functions of each other (F4).
- cellular_unit maps the top 24 bits instead of a plain divide-by-2^32, so
the mapping is always inside [0, 1); the old formula rounded
0xFFFFFF80..0xFFFFFFFF up to exactly 1.0f (F7).
- Central-difference tolerances tightened from 0.02 to 3e-4 (measured float
noise at the committed eps is about 6e-5), and cellular's own gradient
test now differentiates cellular's RETURNED .w values instead of a
duplicate FindF1F2 search, so a mutant that corrupts only the returned
gradient can't hide behind an independently-computed value (F1, F5).
- The struct-mirror comparison in HlslSourceCompatibilityTests now checks
field name and declared type in declaration order, not just position, so
a reordered-and-relabelled mirror struct (H2) or a renamed/retyped one
(H3) both fail the walk instead of surviving on values alone (F2).
- FindF1F2 widened to a true independent 7x7x7 brute-force oracle; new tests
pin production's exact agreement with it, the pruned search's bit-for-bit
equivalence to an unpruned 5x5x5 reference, no NaN at 2^24, and id's
correlation with jitter under 0.05.
- Comment/doc fix: smin_grad is Quilez's quadratic-polynomial smin, not
cubic.
Both shaders/CultMath.hlsl and unity/.../Shaders/CultMath.hlsl stay
byte-identical.
Manually verified mutants M9 (grad1 x 1.01), M16 (grad2 x 1.01), and M18 (id
from hash.y) all die against these tests; H2/H3 struct mutants die via the
new comparator tests. Local dotnet test: 191/191 passing. FXC cs_5_0 compile
of a scratch kernel using both cultmath_smin_grad and cultmath_cellular
succeeds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 20:40:28 +02:00
|
|
|
var jitter = new float3(Unit(hash.x), Unit(hash.y), Unit(hash.z));
|
|
|
|
|
|
|
|
|
|
ids[i] = math.cellular(cell + jitter).id;
|
|
|
|
|
jitterX[i] = jitter.x;
|
|
|
|
|
jitterY[i] = jitter.y;
|
|
|
|
|
jitterZ[i] = jitter.z;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Assert.True(Math.Abs(Correlation(ids, jitterX)) < 0.05, "id correlates with jitter.x");
|
|
|
|
|
Assert.True(Math.Abs(Correlation(ids, jitterY)) < 0.05, "id correlates with jitter.y");
|
|
|
|
|
Assert.True(Math.Abs(Correlation(ids, jitterZ)) < 0.05, "id correlates with jitter.z");
|
|
|
|
|
}
|
|
|
|
|
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
[Fact]
|
|
|
|
|
public void JitterAndIdPassAChiSquareUniformityTest()
|
|
|
|
|
{
|
|
|
|
|
// F4/jitter uniformity: cellular hashes the winning cell's INTEGER coordinate (design.md,
|
|
|
|
|
// "Integer hashing"), never that coordinate's float32 bit pattern. Hashing the bit pattern of
|
|
|
|
|
// an integer-valued float shares more structure between adjacent cells (same exponent, a
|
|
|
|
|
// mantissa one increment apart) than the integers themselves do going into pcg3d's own
|
|
|
|
|
// mixing, and that structure survived into the jitter: Soul measured jitter.x's marginal
|
|
|
|
|
// chi-square statistic at 203.8 over 1,000,000 cells this way (15 degrees of freedom;
|
|
|
|
|
// critical value 37.7 at alpha = 0.001), soundly rejecting uniformity. 16 equal-width bins
|
|
|
|
|
// over [0, 1) give those same 15 degrees of freedom for jitter.x/y/z and id alike.
|
|
|
|
|
const int n = 200_000;
|
|
|
|
|
const int bins = 16;
|
|
|
|
|
const double criticalValue = 37.7; // chi-square, 15 df, alpha = 0.001
|
|
|
|
|
|
|
|
|
|
var jitterXCounts = new int[bins];
|
|
|
|
|
var jitterYCounts = new int[bins];
|
|
|
|
|
var jitterZCounts = new int[bins];
|
|
|
|
|
var idCounts = new int[bins];
|
|
|
|
|
var random = new System.Random(0xCE19);
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < n; i++)
|
|
|
|
|
{
|
|
|
|
|
var cell = new float3(random.Next(-100000, 100000), random.Next(-100000, 100000), random.Next(-100000, 100000));
|
|
|
|
|
var hash = math.pcg3d(math.int3(cell));
|
|
|
|
|
jitterXCounts[Bin(Unit(hash.x), bins)]++;
|
|
|
|
|
jitterYCounts[Bin(Unit(hash.y), bins)]++;
|
|
|
|
|
jitterZCounts[Bin(Unit(hash.z), bins)]++;
|
|
|
|
|
|
|
|
|
|
var idHash = math.pcg4d(new int4(math.int3(cell), 0));
|
|
|
|
|
idCounts[Bin(Unit(idHash.w), bins)]++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var expected = (double)n / bins;
|
|
|
|
|
AssertUniform(jitterXCounts, expected, criticalValue, "jitter.x");
|
|
|
|
|
AssertUniform(jitterYCounts, expected, criticalValue, "jitter.y");
|
|
|
|
|
AssertUniform(jitterZCounts, expected, criticalValue, "jitter.z");
|
|
|
|
|
AssertUniform(idCounts, expected, criticalValue, "id");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static int Bin(float value, int binCount) => Math.Clamp((int)(value * binCount), 0, binCount - 1);
|
|
|
|
|
|
|
|
|
|
private static void AssertUniform(int[] counts, double expected, double criticalValue, string name)
|
|
|
|
|
{
|
|
|
|
|
var chiSquare = 0.0;
|
|
|
|
|
foreach (var count in counts)
|
|
|
|
|
{
|
|
|
|
|
var delta = count - expected;
|
|
|
|
|
chiSquare += delta * delta / expected;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Assert.True(chiSquare < criticalValue, $"{name} chi-square {chiSquare:G6} exceeds critical value {criticalValue} ({counts.Length - 1} df)");
|
|
|
|
|
}
|
|
|
|
|
|
Fix batch 1 (cellular/smin_grad): exact search, decorrelated id, finite F2=0
Cut 2a-i fix batch 1, findings F1, F3, F4, F5, F6, F7 (Soul falsification of
the first landing):
- cellular now searches a provably exact 5x5x5 neighbourhood (radius 2), not
3x3x3: 3x3x3 missed the true F2 for 9 of 400,000 sampled points. The
comment in math.cs proves 5x5x5 always contains the true F1 and F2 (both
are always < sqrt(3), every excluded cell is always > 2). A cheap
position-only lower-bound prune skips the pcg3d/jitter/distance work for
cells that provably cannot beat the running F2, without changing any
output bit (F6).
- F2 = 0 now gets the same degenerate-gradient guard F1 = 0 already had,
fixing a NaN that was reachable past |p| of about 2^23 (F3).
- id now comes from pcg4d over the winning cell alone, never from the pcg3d
hash that produced that cell's jitter, so id and jitter are no longer
deterministic functions of each other (F4).
- cellular_unit maps the top 24 bits instead of a plain divide-by-2^32, so
the mapping is always inside [0, 1); the old formula rounded
0xFFFFFF80..0xFFFFFFFF up to exactly 1.0f (F7).
- Central-difference tolerances tightened from 0.02 to 3e-4 (measured float
noise at the committed eps is about 6e-5), and cellular's own gradient
test now differentiates cellular's RETURNED .w values instead of a
duplicate FindF1F2 search, so a mutant that corrupts only the returned
gradient can't hide behind an independently-computed value (F1, F5).
- The struct-mirror comparison in HlslSourceCompatibilityTests now checks
field name and declared type in declaration order, not just position, so
a reordered-and-relabelled mirror struct (H2) or a renamed/retyped one
(H3) both fail the walk instead of surviving on values alone (F2).
- FindF1F2 widened to a true independent 7x7x7 brute-force oracle; new tests
pin production's exact agreement with it, the pruned search's bit-for-bit
equivalence to an unpruned 5x5x5 reference, no NaN at 2^24, and id's
correlation with jitter under 0.05.
- Comment/doc fix: smin_grad is Quilez's quadratic-polynomial smin, not
cubic.
Both shaders/CultMath.hlsl and unity/.../Shaders/CultMath.hlsl stay
byte-identical.
Manually verified mutants M9 (grad1 x 1.01), M16 (grad2 x 1.01), and M18 (id
from hash.y) all die against these tests; H2/H3 struct mutants die via the
new comparator tests. Local dotnet test: 191/191 passing. FXC cs_5_0 compile
of a scratch kernel using both cultmath_smin_grad and cultmath_cellular
succeeds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 20:40:28 +02:00
|
|
|
[Fact]
|
|
|
|
|
public void NoNaNAtIntegerPointsNear2To24()
|
|
|
|
|
{
|
|
|
|
|
// F3: past |p| of about 2^23, distinct cells' jittered features can round to the same
|
|
|
|
|
// float32 value, making F2 = 0 reachable. Soul measured 4096 of 4096 integer points at 2^24
|
|
|
|
|
// producing NaN before the guard; this pins the fix over the same sample.
|
|
|
|
|
const float scale = 16777216.0f; // 2^24
|
|
|
|
|
for (var ix = 0; ix < 16; ix++)
|
|
|
|
|
for (var iy = 0; iy < 16; iy++)
|
|
|
|
|
for (var iz = 0; iz < 16; iz++)
|
|
|
|
|
{
|
|
|
|
|
var p = new float3(scale + ix, scale + iy, scale + iz);
|
|
|
|
|
var result = math.cellular(p);
|
|
|
|
|
|
|
|
|
|
Assert.True(
|
|
|
|
|
float.IsFinite(result.nearest.x) && float.IsFinite(result.nearest.y) &&
|
|
|
|
|
float.IsFinite(result.nearest.z) && float.IsFinite(result.nearest.w),
|
|
|
|
|
$"nearest not finite at {p.x},{p.y},{p.z}: {result.nearest.x},{result.nearest.y},{result.nearest.z},{result.nearest.w}");
|
|
|
|
|
Assert.True(
|
|
|
|
|
float.IsFinite(result.edge.x) && float.IsFinite(result.edge.y) &&
|
|
|
|
|
float.IsFinite(result.edge.z) && float.IsFinite(result.edge.w),
|
|
|
|
|
$"edge not finite at {p.x},{p.y},{p.z}: {result.edge.x},{result.edge.y},{result.edge.z},{result.edge.w}");
|
|
|
|
|
Assert.True(float.IsFinite(result.id), $"id not finite at {p.x},{p.y},{p.z}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void CellularUnitMapsAllOnesBitsBelowOne()
|
|
|
|
|
{
|
|
|
|
|
// F7: cellular_unit(0xFFFFFFFF) must land in [0, 1), not round up to exactly 1.0f.
|
|
|
|
|
var mapped = math.cellular_unit(-1); // -1's bit pattern is 0xFFFFFFFF.
|
|
|
|
|
Assert.True(mapped >= 0.0f && mapped < 1.0f, $"cellular_unit(0xFFFFFFFF) = {mapped}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void ProductionSearchMatchesTheBruteForceOracleExactly()
|
|
|
|
|
{
|
|
|
|
|
// F6, part 2: the production 5x5x5 search must equal the independent 7x7x7 oracle exactly
|
|
|
|
|
// (to the oracle's own float32 precision), over a large seeded sweep in the domain Soul
|
|
|
|
|
// measured ([-50, 50]^3, where 3x3x3 missed 9 of 400,000 points).
|
|
|
|
|
var random = new System.Random(0xCE16);
|
|
|
|
|
for (var i = 0; i < 20000; i++)
|
|
|
|
|
{
|
|
|
|
|
var p = new float3(random.NextSingle() * 100.0f - 50.0f, random.NextSingle() * 100.0f - 50.0f, random.NextSingle() * 100.0f - 50.0f);
|
|
|
|
|
var result = math.cellular(p);
|
|
|
|
|
var (f1, f2) = FindF1F2(p);
|
|
|
|
|
|
|
|
|
|
Assert.Equal(f1, result.nearest.w, precision: 4);
|
|
|
|
|
Assert.Equal(f2 - f1, result.edge.w, precision: 4);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
tests: kill a single-slice loop-bound gap in cellular's 5x5x5 search
Stryker (mtp, --since 29e50ad3) found 261 survivors / 24 timeouts across the
whole repo; triaging the ones in this fix batch's own new code (cellular's
loop, its prune, cellular_unit) surfaced one real gap disguised as
collateral timeout noise from a batched infinite-loop mutant: dz < 2 (and
the dy/dx equivalents), silently dropping the +2 layer on one axis alone,
passed every existing random-sampled test. That is not the sweeps being
weak: the exactness proof's own margin (F1, F2 always < sqrt(3), radius-2
cells' lower bound is exactly 2) makes a genuine win from any single
|offset component| = 2 cell astronomically rare over uniformly random
points (measured zero hits in 3,000,000 samples).
SearchReachesEveryAxisAlignedRadiusTwoSlice constructs a real example for
each of the six axis-aligned radius-2 offsets directly: for a candidate
integer cell, build the query point that is the closest point of that
cell's own unit cube to the target neighbour's actual jittered feature.
That construction finds a hit for every offset within a couple thousand
candidate cells, deterministically. This kills dz < 2 / dy < 2 / dx < 2 (all
manually confirmed).
Also manually confirmed via Stryker's own reported mutants: the dz--/dy--/
dx-- (increment-to-decrement) mutants are genuine infinite loops, correctly
caught as timeouts. The f2 >= 0.0f mutant (disabling the F2 = 0 guard) is
killed by the existing NoNaNAtIntegerPointsNear2To24 test. The remaining
survivors/timeouts in the new code (the lower-bound prune's >>= vs >
comparison, its arithmetic terms, and cellular_unit's >> vs >>> shift on an
already-unsigned operand) are equivalent mutants: since the per-axis lower
bound is always 0 or 1 by construction, and pruning is a proven-safe
optimization that never changes cellular's output, no mutation confined to
"when does the prune trigger" can be observed by any value-based test.
Verified by hand: disabling pruning entirely reproduces the unpruned
reference bit for bit, as it must.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 21:28:36 +02:00
|
|
|
[Fact]
|
|
|
|
|
public void SearchReachesEveryAxisAlignedRadiusTwoSlice()
|
|
|
|
|
{
|
|
|
|
|
// F6, part 2 (Soul finding): a single-slice loop-bound mutant on one axis (e.g. dz < 2
|
|
|
|
|
// instead of dz <= 2, silently dropping the +2 layer on that axis alone) still passed the
|
|
|
|
|
// random 20,000-point sweeps above. That is not those sweeps being weak; it is the
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
// exactness proof's own margin (F1, F2 always < sqrt(3), a radius-2 cell's lower bound is
|
|
|
|
|
// max(0, 2-1) = 1, not "exactly 2" as an earlier version of this comment claimed) making a
|
|
|
|
|
// genuine win from any single |offset component| = 2 cell rare over uniformly random points
|
|
|
|
|
// (Soul, cut 2a-i: measured about 1 hit in 28,000 samples, not the "zero in 3,000,000" an
|
|
|
|
|
// earlier version of this comment claimed).
|
tests: kill a single-slice loop-bound gap in cellular's 5x5x5 search
Stryker (mtp, --since 29e50ad3) found 261 survivors / 24 timeouts across the
whole repo; triaging the ones in this fix batch's own new code (cellular's
loop, its prune, cellular_unit) surfaced one real gap disguised as
collateral timeout noise from a batched infinite-loop mutant: dz < 2 (and
the dy/dx equivalents), silently dropping the +2 layer on one axis alone,
passed every existing random-sampled test. That is not the sweeps being
weak: the exactness proof's own margin (F1, F2 always < sqrt(3), radius-2
cells' lower bound is exactly 2) makes a genuine win from any single
|offset component| = 2 cell astronomically rare over uniformly random
points (measured zero hits in 3,000,000 samples).
SearchReachesEveryAxisAlignedRadiusTwoSlice constructs a real example for
each of the six axis-aligned radius-2 offsets directly: for a candidate
integer cell, build the query point that is the closest point of that
cell's own unit cube to the target neighbour's actual jittered feature.
That construction finds a hit for every offset within a couple thousand
candidate cells, deterministically. This kills dz < 2 / dy < 2 / dx < 2 (all
manually confirmed).
Also manually confirmed via Stryker's own reported mutants: the dz--/dy--/
dx-- (increment-to-decrement) mutants are genuine infinite loops, correctly
caught as timeouts. The f2 >= 0.0f mutant (disabling the F2 = 0 guard) is
killed by the existing NoNaNAtIntegerPointsNear2To24 test. The remaining
survivors/timeouts in the new code (the lower-bound prune's >>= vs >
comparison, its arithmetic terms, and cellular_unit's >> vs >>> shift on an
already-unsigned operand) are equivalent mutants: since the per-axis lower
bound is always 0 or 1 by construction, and pruning is a proven-safe
optimization that never changes cellular's output, no mutation confined to
"when does the prune trigger" can be observed by any value-based test.
Verified by hand: disabling pruning entirely reproduces the unpruned
reference bit for bit, as it must.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 21:28:36 +02:00
|
|
|
// Hunting for one by choosing candidate query POINTS is the wrong end of the search; this
|
|
|
|
|
// instead scans candidate integer CELLS and, for each, builds the query point most likely to
|
|
|
|
|
// realize that cell's neighbour as the winner: the closest point of the query cell's own unit
|
|
|
|
|
// cube to the target neighbour's actual jittered feature. That construction finds a real
|
|
|
|
|
// example for every one of the six axis-aligned radius-2 offsets within a couple thousand
|
|
|
|
|
// candidate cells, deterministically (pcg3d has no randomness to get lucky or unlucky with).
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
foreach (var offset in AxisAlignedRadiusTwoOffsets)
|
tests: kill a single-slice loop-bound gap in cellular's 5x5x5 search
Stryker (mtp, --since 29e50ad3) found 261 survivors / 24 timeouts across the
whole repo; triaging the ones in this fix batch's own new code (cellular's
loop, its prune, cellular_unit) surfaced one real gap disguised as
collateral timeout noise from a batched infinite-loop mutant: dz < 2 (and
the dy/dx equivalents), silently dropping the +2 layer on one axis alone,
passed every existing random-sampled test. That is not the sweeps being
weak: the exactness proof's own margin (F1, F2 always < sqrt(3), radius-2
cells' lower bound is exactly 2) makes a genuine win from any single
|offset component| = 2 cell astronomically rare over uniformly random
points (measured zero hits in 3,000,000 samples).
SearchReachesEveryAxisAlignedRadiusTwoSlice constructs a real example for
each of the six axis-aligned radius-2 offsets directly: for a candidate
integer cell, build the query point that is the closest point of that
cell's own unit cube to the target neighbour's actual jittered feature.
That construction finds a hit for every offset within a couple thousand
candidate cells, deterministically. This kills dz < 2 / dy < 2 / dx < 2 (all
manually confirmed).
Also manually confirmed via Stryker's own reported mutants: the dz--/dy--/
dx-- (increment-to-decrement) mutants are genuine infinite loops, correctly
caught as timeouts. The f2 >= 0.0f mutant (disabling the F2 = 0 guard) is
killed by the existing NoNaNAtIntegerPointsNear2To24 test. The remaining
survivors/timeouts in the new code (the lower-bound prune's >>= vs >
comparison, its arithmetic terms, and cellular_unit's >> vs >>> shift on an
already-unsigned operand) are equivalent mutants: since the per-axis lower
bound is always 0 or 1 by construction, and pruning is a proven-safe
optimization that never changes cellular's output, no mutation confined to
"when does the prune trigger" can be observed by any value-based test.
Verified by hand: disabling pruning entirely reproduces the unpruned
reference bit for bit, as it must.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 21:28:36 +02:00
|
|
|
{
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
var p = FindPointRealizingOffset(offset);
|
|
|
|
|
var (f1, f2) = FindF1F2(p); // independent 7x7x7 oracle
|
|
|
|
|
var result = math.cellular(p);
|
|
|
|
|
Assert.Equal(f1, result.nearest.w, precision: 3);
|
|
|
|
|
Assert.Equal(f2 - f1, result.edge.w, precision: 3);
|
|
|
|
|
}
|
|
|
|
|
}
|
tests: kill a single-slice loop-bound gap in cellular's 5x5x5 search
Stryker (mtp, --since 29e50ad3) found 261 survivors / 24 timeouts across the
whole repo; triaging the ones in this fix batch's own new code (cellular's
loop, its prune, cellular_unit) surfaced one real gap disguised as
collateral timeout noise from a batched infinite-loop mutant: dz < 2 (and
the dy/dx equivalents), silently dropping the +2 layer on one axis alone,
passed every existing random-sampled test. That is not the sweeps being
weak: the exactness proof's own margin (F1, F2 always < sqrt(3), radius-2
cells' lower bound is exactly 2) makes a genuine win from any single
|offset component| = 2 cell astronomically rare over uniformly random
points (measured zero hits in 3,000,000 samples).
SearchReachesEveryAxisAlignedRadiusTwoSlice constructs a real example for
each of the six axis-aligned radius-2 offsets directly: for a candidate
integer cell, build the query point that is the closest point of that
cell's own unit cube to the target neighbour's actual jittered feature.
That construction finds a hit for every offset within a couple thousand
candidate cells, deterministically. This kills dz < 2 / dy < 2 / dx < 2 (all
manually confirmed).
Also manually confirmed via Stryker's own reported mutants: the dz--/dy--/
dx-- (increment-to-decrement) mutants are genuine infinite loops, correctly
caught as timeouts. The f2 >= 0.0f mutant (disabling the F2 = 0 guard) is
killed by the existing NoNaNAtIntegerPointsNear2To24 test. The remaining
survivors/timeouts in the new code (the lower-bound prune's >>= vs >
comparison, its arithmetic terms, and cellular_unit's >> vs >>> shift on an
already-unsigned operand) are equivalent mutants: since the per-axis lower
bound is always 0 or 1 by construction, and pruning is a proven-safe
optimization that never changes cellular's output, no mutation confined to
"when does the prune trigger" can be observed by any value-based test.
Verified by hand: disabling pruning entirely reproduces the unpruned
reference bit for bit, as it must.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 21:28:36 +02:00
|
|
|
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
// The six axis-aligned radius-2 offsets (F6, part 2): shared by
|
|
|
|
|
// SearchReachesEveryAxisAlignedRadiusTwoSlice and, via FindPointRealizingOffset, by
|
|
|
|
|
// HlslSourceCompatibilityTests' mirror-comparison inputs, so both pin the same construction.
|
|
|
|
|
internal static readonly float3[] AxisAlignedRadiusTwoOffsets =
|
|
|
|
|
{
|
|
|
|
|
new(2.0f, 0.0f, 0.0f), new(-2.0f, 0.0f, 0.0f),
|
|
|
|
|
new(0.0f, 2.0f, 0.0f), new(0.0f, -2.0f, 0.0f),
|
|
|
|
|
new(0.0f, 0.0f, 2.0f), new(0.0f, 0.0f, -2.0f),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Hunting for a query point where a radius-2 offset wins F1 or F2 by choosing candidate query
|
|
|
|
|
// POINTS is the wrong end of the search (Soul, cut 2a-i: about 1 hit in 28,000 uniformly random
|
|
|
|
|
// points); this instead scans candidate integer CELLS and, for each, builds the query point most
|
|
|
|
|
// likely to realize that cell's neighbour as the winner: the closest point of the query cell's
|
|
|
|
|
// own unit cube to the target neighbour's actual jittered feature. That construction finds a
|
|
|
|
|
// real example for every one of the six axis-aligned radius-2 offsets within a couple thousand
|
|
|
|
|
// candidate cells, deterministically (pcg3d has no randomness to get lucky or unlucky with).
|
|
|
|
|
internal static float3 FindPointRealizingOffset(float3 offset)
|
|
|
|
|
{
|
|
|
|
|
for (var qz = -80; qz <= 80; qz++)
|
|
|
|
|
for (var qy = -80; qy <= 80; qy++)
|
|
|
|
|
for (var qx = -80; qx <= 80; qx++)
|
tests: kill a single-slice loop-bound gap in cellular's 5x5x5 search
Stryker (mtp, --since 29e50ad3) found 261 survivors / 24 timeouts across the
whole repo; triaging the ones in this fix batch's own new code (cellular's
loop, its prune, cellular_unit) surfaced one real gap disguised as
collateral timeout noise from a batched infinite-loop mutant: dz < 2 (and
the dy/dx equivalents), silently dropping the +2 layer on one axis alone,
passed every existing random-sampled test. That is not the sweeps being
weak: the exactness proof's own margin (F1, F2 always < sqrt(3), radius-2
cells' lower bound is exactly 2) makes a genuine win from any single
|offset component| = 2 cell astronomically rare over uniformly random
points (measured zero hits in 3,000,000 samples).
SearchReachesEveryAxisAlignedRadiusTwoSlice constructs a real example for
each of the six axis-aligned radius-2 offsets directly: for a candidate
integer cell, build the query point that is the closest point of that
cell's own unit cube to the target neighbour's actual jittered feature.
That construction finds a hit for every offset within a couple thousand
candidate cells, deterministically. This kills dz < 2 / dy < 2 / dx < 2 (all
manually confirmed).
Also manually confirmed via Stryker's own reported mutants: the dz--/dy--/
dx-- (increment-to-decrement) mutants are genuine infinite loops, correctly
caught as timeouts. The f2 >= 0.0f mutant (disabling the F2 = 0 guard) is
killed by the existing NoNaNAtIntegerPointsNear2To24 test. The remaining
survivors/timeouts in the new code (the lower-bound prune's >>= vs >
comparison, its arithmetic terms, and cellular_unit's >> vs >>> shift on an
already-unsigned operand) are equivalent mutants: since the per-axis lower
bound is always 0 or 1 by construction, and pruning is a proven-safe
optimization that never changes cellular's output, no mutation confined to
"when does the prune trigger" can be observed by any value-based test.
Verified by hand: disabling pruning entirely reproduces the unpruned
reference bit for bit, as it must.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 21:28:36 +02:00
|
|
|
{
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
var q = new float3(qx, qy, qz);
|
|
|
|
|
var targetFeature = FeaturePoint(q + offset);
|
|
|
|
|
var p = new float3(
|
|
|
|
|
Math.Clamp(targetFeature.x, q.x, q.x + 0.999f),
|
|
|
|
|
Math.Clamp(targetFeature.y, q.y, q.y + 0.999f),
|
|
|
|
|
Math.Clamp(targetFeature.z, q.z, q.z + 0.999f));
|
|
|
|
|
|
|
|
|
|
var (f1, f2) = FindF1F2(p); // independent 7x7x7 oracle
|
|
|
|
|
var d = math.distance(p, targetFeature);
|
|
|
|
|
if (MathF.Abs(d - f1) > 1.0e-4f && MathF.Abs(d - f2) > 1.0e-4f)
|
|
|
|
|
continue; // this cell didn't realize the target offset as F1 or F2; try another.
|
|
|
|
|
|
|
|
|
|
return p;
|
tests: kill a single-slice loop-bound gap in cellular's 5x5x5 search
Stryker (mtp, --since 29e50ad3) found 261 survivors / 24 timeouts across the
whole repo; triaging the ones in this fix batch's own new code (cellular's
loop, its prune, cellular_unit) surfaced one real gap disguised as
collateral timeout noise from a batched infinite-loop mutant: dz < 2 (and
the dy/dx equivalents), silently dropping the +2 layer on one axis alone,
passed every existing random-sampled test. That is not the sweeps being
weak: the exactness proof's own margin (F1, F2 always < sqrt(3), radius-2
cells' lower bound is exactly 2) makes a genuine win from any single
|offset component| = 2 cell astronomically rare over uniformly random
points (measured zero hits in 3,000,000 samples).
SearchReachesEveryAxisAlignedRadiusTwoSlice constructs a real example for
each of the six axis-aligned radius-2 offsets directly: for a candidate
integer cell, build the query point that is the closest point of that
cell's own unit cube to the target neighbour's actual jittered feature.
That construction finds a hit for every offset within a couple thousand
candidate cells, deterministically. This kills dz < 2 / dy < 2 / dx < 2 (all
manually confirmed).
Also manually confirmed via Stryker's own reported mutants: the dz--/dy--/
dx-- (increment-to-decrement) mutants are genuine infinite loops, correctly
caught as timeouts. The f2 >= 0.0f mutant (disabling the F2 = 0 guard) is
killed by the existing NoNaNAtIntegerPointsNear2To24 test. The remaining
survivors/timeouts in the new code (the lower-bound prune's >>= vs >
comparison, its arithmetic terms, and cellular_unit's >> vs >>> shift on an
already-unsigned operand) are equivalent mutants: since the per-axis lower
bound is always 0 or 1 by construction, and pruning is a proven-safe
optimization that never changes cellular's output, no mutation confined to
"when does the prune trigger" can be observed by any value-based test.
Verified by hand: disabling pruning entirely reproduces the unpruned
reference bit for bit, as it must.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 21:28:36 +02:00
|
|
|
}
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
|
|
|
|
|
throw new InvalidOperationException($"could not construct a query point where offset {offset.x},{offset.y},{offset.z} ever wins F1 or F2");
|
tests: kill a single-slice loop-bound gap in cellular's 5x5x5 search
Stryker (mtp, --since 29e50ad3) found 261 survivors / 24 timeouts across the
whole repo; triaging the ones in this fix batch's own new code (cellular's
loop, its prune, cellular_unit) surfaced one real gap disguised as
collateral timeout noise from a batched infinite-loop mutant: dz < 2 (and
the dy/dx equivalents), silently dropping the +2 layer on one axis alone,
passed every existing random-sampled test. That is not the sweeps being
weak: the exactness proof's own margin (F1, F2 always < sqrt(3), radius-2
cells' lower bound is exactly 2) makes a genuine win from any single
|offset component| = 2 cell astronomically rare over uniformly random
points (measured zero hits in 3,000,000 samples).
SearchReachesEveryAxisAlignedRadiusTwoSlice constructs a real example for
each of the six axis-aligned radius-2 offsets directly: for a candidate
integer cell, build the query point that is the closest point of that
cell's own unit cube to the target neighbour's actual jittered feature.
That construction finds a hit for every offset within a couple thousand
candidate cells, deterministically. This kills dz < 2 / dy < 2 / dx < 2 (all
manually confirmed).
Also manually confirmed via Stryker's own reported mutants: the dz--/dy--/
dx-- (increment-to-decrement) mutants are genuine infinite loops, correctly
caught as timeouts. The f2 >= 0.0f mutant (disabling the F2 = 0 guard) is
killed by the existing NoNaNAtIntegerPointsNear2To24 test. The remaining
survivors/timeouts in the new code (the lower-bound prune's >>= vs >
comparison, its arithmetic terms, and cellular_unit's >> vs >>> shift on an
already-unsigned operand) are equivalent mutants: since the per-axis lower
bound is always 0 or 1 by construction, and pruning is a proven-safe
optimization that never changes cellular's output, no mutation confined to
"when does the prune trigger" can be observed by any value-based test.
Verified by hand: disabling pruning entirely reproduces the unpruned
reference bit for bit, as it must.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 21:28:36 +02:00
|
|
|
}
|
|
|
|
|
|
Fix batch 1 (cellular/smin_grad): exact search, decorrelated id, finite F2=0
Cut 2a-i fix batch 1, findings F1, F3, F4, F5, F6, F7 (Soul falsification of
the first landing):
- cellular now searches a provably exact 5x5x5 neighbourhood (radius 2), not
3x3x3: 3x3x3 missed the true F2 for 9 of 400,000 sampled points. The
comment in math.cs proves 5x5x5 always contains the true F1 and F2 (both
are always < sqrt(3), every excluded cell is always > 2). A cheap
position-only lower-bound prune skips the pcg3d/jitter/distance work for
cells that provably cannot beat the running F2, without changing any
output bit (F6).
- F2 = 0 now gets the same degenerate-gradient guard F1 = 0 already had,
fixing a NaN that was reachable past |p| of about 2^23 (F3).
- id now comes from pcg4d over the winning cell alone, never from the pcg3d
hash that produced that cell's jitter, so id and jitter are no longer
deterministic functions of each other (F4).
- cellular_unit maps the top 24 bits instead of a plain divide-by-2^32, so
the mapping is always inside [0, 1); the old formula rounded
0xFFFFFF80..0xFFFFFFFF up to exactly 1.0f (F7).
- Central-difference tolerances tightened from 0.02 to 3e-4 (measured float
noise at the committed eps is about 6e-5), and cellular's own gradient
test now differentiates cellular's RETURNED .w values instead of a
duplicate FindF1F2 search, so a mutant that corrupts only the returned
gradient can't hide behind an independently-computed value (F1, F5).
- The struct-mirror comparison in HlslSourceCompatibilityTests now checks
field name and declared type in declaration order, not just position, so
a reordered-and-relabelled mirror struct (H2) or a renamed/retyped one
(H3) both fail the walk instead of surviving on values alone (F2).
- FindF1F2 widened to a true independent 7x7x7 brute-force oracle; new tests
pin production's exact agreement with it, the pruned search's bit-for-bit
equivalence to an unpruned 5x5x5 reference, no NaN at 2^24, and id's
correlation with jitter under 0.05.
- Comment/doc fix: smin_grad is Quilez's quadratic-polynomial smin, not
cubic.
Both shaders/CultMath.hlsl and unity/.../Shaders/CultMath.hlsl stay
byte-identical.
Manually verified mutants M9 (grad1 x 1.01), M16 (grad2 x 1.01), and M18 (id
from hash.y) all die against these tests; H2/H3 struct mutants die via the
new comparator tests. Local dotnet test: 191/191 passing. FXC cs_5_0 compile
of a scratch kernel using both cultmath_smin_grad and cultmath_cellular
succeeds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 20:40:28 +02:00
|
|
|
[Fact]
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
public void PrunedSearchIsBitIdenticalToTheUnprunedFiveCubedSearchBelowTwoToTheTwentyFifth()
|
Fix batch 1 (cellular/smin_grad): exact search, decorrelated id, finite F2=0
Cut 2a-i fix batch 1, findings F1, F3, F4, F5, F6, F7 (Soul falsification of
the first landing):
- cellular now searches a provably exact 5x5x5 neighbourhood (radius 2), not
3x3x3: 3x3x3 missed the true F2 for 9 of 400,000 sampled points. The
comment in math.cs proves 5x5x5 always contains the true F1 and F2 (both
are always < sqrt(3), every excluded cell is always > 2). A cheap
position-only lower-bound prune skips the pcg3d/jitter/distance work for
cells that provably cannot beat the running F2, without changing any
output bit (F6).
- F2 = 0 now gets the same degenerate-gradient guard F1 = 0 already had,
fixing a NaN that was reachable past |p| of about 2^23 (F3).
- id now comes from pcg4d over the winning cell alone, never from the pcg3d
hash that produced that cell's jitter, so id and jitter are no longer
deterministic functions of each other (F4).
- cellular_unit maps the top 24 bits instead of a plain divide-by-2^32, so
the mapping is always inside [0, 1); the old formula rounded
0xFFFFFF80..0xFFFFFFFF up to exactly 1.0f (F7).
- Central-difference tolerances tightened from 0.02 to 3e-4 (measured float
noise at the committed eps is about 6e-5), and cellular's own gradient
test now differentiates cellular's RETURNED .w values instead of a
duplicate FindF1F2 search, so a mutant that corrupts only the returned
gradient can't hide behind an independently-computed value (F1, F5).
- The struct-mirror comparison in HlslSourceCompatibilityTests now checks
field name and declared type in declaration order, not just position, so
a reordered-and-relabelled mirror struct (H2) or a renamed/retyped one
(H3) both fail the walk instead of surviving on values alone (F2).
- FindF1F2 widened to a true independent 7x7x7 brute-force oracle; new tests
pin production's exact agreement with it, the pruned search's bit-for-bit
equivalence to an unpruned 5x5x5 reference, no NaN at 2^24, and id's
correlation with jitter under 0.05.
- Comment/doc fix: smin_grad is Quilez's quadratic-polynomial smin, not
cubic.
Both shaders/CultMath.hlsl and unity/.../Shaders/CultMath.hlsl stay
byte-identical.
Manually verified mutants M9 (grad1 x 1.01), M16 (grad2 x 1.01), and M18 (id
from hash.y) all die against these tests; H2/H3 struct mutants die via the
new comparator tests. Local dotnet test: 191/191 passing. FXC cs_5_0 compile
of a scratch kernel using both cultmath_smin_grad and cultmath_cellular
succeeds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 20:40:28 +02:00
|
|
|
{
|
|
|
|
|
// F6, part 2: the lower-bound prune inside math.cellular's loop must never change the
|
|
|
|
|
// result. Compares against an independent, unpruned 5x5x5 reference that visits every cell
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
// in the same fixed order, over a large seeded sweep in [-50, 50]^3.
|
|
|
|
|
//
|
|
|
|
|
// This bit-identity has a domain: it holds for |p| < 2^25 (design.md, "Precision domain").
|
|
|
|
|
// Above that, cell + offset starts to round in float32, so the pruned loop's recomputed
|
|
|
|
|
// neighbour and the unpruned loop's neighbour at the same nominal (dx, dy, dz) can land on
|
|
|
|
|
// different actual cells, and the two searches diverge (Soul, cut 2a-i: 180 of 1,100,000
|
|
|
|
|
// points differ at |p| ~= 3.4e7). [-50, 50]^3 is comfortably inside the proven domain.
|
Fix batch 1 (cellular/smin_grad): exact search, decorrelated id, finite F2=0
Cut 2a-i fix batch 1, findings F1, F3, F4, F5, F6, F7 (Soul falsification of
the first landing):
- cellular now searches a provably exact 5x5x5 neighbourhood (radius 2), not
3x3x3: 3x3x3 missed the true F2 for 9 of 400,000 sampled points. The
comment in math.cs proves 5x5x5 always contains the true F1 and F2 (both
are always < sqrt(3), every excluded cell is always > 2). A cheap
position-only lower-bound prune skips the pcg3d/jitter/distance work for
cells that provably cannot beat the running F2, without changing any
output bit (F6).
- F2 = 0 now gets the same degenerate-gradient guard F1 = 0 already had,
fixing a NaN that was reachable past |p| of about 2^23 (F3).
- id now comes from pcg4d over the winning cell alone, never from the pcg3d
hash that produced that cell's jitter, so id and jitter are no longer
deterministic functions of each other (F4).
- cellular_unit maps the top 24 bits instead of a plain divide-by-2^32, so
the mapping is always inside [0, 1); the old formula rounded
0xFFFFFF80..0xFFFFFFFF up to exactly 1.0f (F7).
- Central-difference tolerances tightened from 0.02 to 3e-4 (measured float
noise at the committed eps is about 6e-5), and cellular's own gradient
test now differentiates cellular's RETURNED .w values instead of a
duplicate FindF1F2 search, so a mutant that corrupts only the returned
gradient can't hide behind an independently-computed value (F1, F5).
- The struct-mirror comparison in HlslSourceCompatibilityTests now checks
field name and declared type in declaration order, not just position, so
a reordered-and-relabelled mirror struct (H2) or a renamed/retyped one
(H3) both fail the walk instead of surviving on values alone (F2).
- FindF1F2 widened to a true independent 7x7x7 brute-force oracle; new tests
pin production's exact agreement with it, the pruned search's bit-for-bit
equivalence to an unpruned 5x5x5 reference, no NaN at 2^24, and id's
correlation with jitter under 0.05.
- Comment/doc fix: smin_grad is Quilez's quadratic-polynomial smin, not
cubic.
Both shaders/CultMath.hlsl and unity/.../Shaders/CultMath.hlsl stay
byte-identical.
Manually verified mutants M9 (grad1 x 1.01), M16 (grad2 x 1.01), and M18 (id
from hash.y) all die against these tests; H2/H3 struct mutants die via the
new comparator tests. Local dotnet test: 191/191 passing. FXC cs_5_0 compile
of a scratch kernel using both cultmath_smin_grad and cultmath_cellular
succeeds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 20:40:28 +02:00
|
|
|
var random = new System.Random(0xCE18);
|
|
|
|
|
for (var i = 0; i < 20000; i++)
|
|
|
|
|
{
|
|
|
|
|
var p = new float3(random.NextSingle() * 100.0f - 50.0f, random.NextSingle() * 100.0f - 50.0f, random.NextSingle() * 100.0f - 50.0f);
|
|
|
|
|
var pruned = math.cellular(p);
|
|
|
|
|
var unpruned = UnprunedCellular5x5x5(p);
|
|
|
|
|
|
|
|
|
|
Assert.Equal(unpruned.nearest.x, pruned.nearest.x);
|
|
|
|
|
Assert.Equal(unpruned.nearest.y, pruned.nearest.y);
|
|
|
|
|
Assert.Equal(unpruned.nearest.z, pruned.nearest.z);
|
|
|
|
|
Assert.Equal(unpruned.nearest.w, pruned.nearest.w);
|
|
|
|
|
Assert.Equal(unpruned.edge.x, pruned.edge.x);
|
|
|
|
|
Assert.Equal(unpruned.edge.y, pruned.edge.y);
|
|
|
|
|
Assert.Equal(unpruned.edge.z, pruned.edge.z);
|
|
|
|
|
Assert.Equal(unpruned.edge.w, pruned.edge.w);
|
|
|
|
|
Assert.Equal(unpruned.id, pruned.id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Independent of math.cellular's own pruning: visits every cell of the 5x5x5 block, in the same
|
|
|
|
|
// fixed order, unconditionally.
|
|
|
|
|
private static CultCellular UnprunedCellular5x5x5(float3 p)
|
|
|
|
|
{
|
|
|
|
|
var cell = new float3(MathF.Floor(p.x), MathF.Floor(p.y), MathF.Floor(p.z));
|
|
|
|
|
var f1 = 1.0e30f;
|
|
|
|
|
var f2 = 1.0e30f;
|
|
|
|
|
var c1 = float3.zero;
|
|
|
|
|
var c2 = float3.zero;
|
|
|
|
|
var cellId = float3.zero;
|
|
|
|
|
|
|
|
|
|
for (var dz = -2; dz <= 2; dz++)
|
|
|
|
|
for (var dy = -2; dy <= 2; dy++)
|
|
|
|
|
for (var dx = -2; dx <= 2; dx++)
|
|
|
|
|
{
|
|
|
|
|
var neighbor = cell + new float3(dx, dy, dz);
|
|
|
|
|
var feature = FeaturePoint(neighbor);
|
|
|
|
|
var d = math.distance(p, feature);
|
|
|
|
|
if (d < f1) { f2 = f1; c2 = c1; f1 = d; c1 = feature; cellId = neighbor; }
|
|
|
|
|
else if (d < f2) { f2 = d; c2 = feature; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var grad1 = f1 > 0.0f ? (p - c1) / f1 : float3.zero;
|
|
|
|
|
var grad2 = f2 > 0.0f ? (p - c2) / f2 : float3.zero;
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
var idHash = math.pcg4d(new int4(math.int3(cellId), 0));
|
Fix batch 1 (cellular/smin_grad): exact search, decorrelated id, finite F2=0
Cut 2a-i fix batch 1, findings F1, F3, F4, F5, F6, F7 (Soul falsification of
the first landing):
- cellular now searches a provably exact 5x5x5 neighbourhood (radius 2), not
3x3x3: 3x3x3 missed the true F2 for 9 of 400,000 sampled points. The
comment in math.cs proves 5x5x5 always contains the true F1 and F2 (both
are always < sqrt(3), every excluded cell is always > 2). A cheap
position-only lower-bound prune skips the pcg3d/jitter/distance work for
cells that provably cannot beat the running F2, without changing any
output bit (F6).
- F2 = 0 now gets the same degenerate-gradient guard F1 = 0 already had,
fixing a NaN that was reachable past |p| of about 2^23 (F3).
- id now comes from pcg4d over the winning cell alone, never from the pcg3d
hash that produced that cell's jitter, so id and jitter are no longer
deterministic functions of each other (F4).
- cellular_unit maps the top 24 bits instead of a plain divide-by-2^32, so
the mapping is always inside [0, 1); the old formula rounded
0xFFFFFF80..0xFFFFFFFF up to exactly 1.0f (F7).
- Central-difference tolerances tightened from 0.02 to 3e-4 (measured float
noise at the committed eps is about 6e-5), and cellular's own gradient
test now differentiates cellular's RETURNED .w values instead of a
duplicate FindF1F2 search, so a mutant that corrupts only the returned
gradient can't hide behind an independently-computed value (F1, F5).
- The struct-mirror comparison in HlslSourceCompatibilityTests now checks
field name and declared type in declaration order, not just position, so
a reordered-and-relabelled mirror struct (H2) or a renamed/retyped one
(H3) both fail the walk instead of surviving on values alone (F2).
- FindF1F2 widened to a true independent 7x7x7 brute-force oracle; new tests
pin production's exact agreement with it, the pruned search's bit-for-bit
equivalence to an unpruned 5x5x5 reference, no NaN at 2^24, and id's
correlation with jitter under 0.05.
- Comment/doc fix: smin_grad is Quilez's quadratic-polynomial smin, not
cubic.
Both shaders/CultMath.hlsl and unity/.../Shaders/CultMath.hlsl stay
byte-identical.
Manually verified mutants M9 (grad1 x 1.01), M16 (grad2 x 1.01), and M18 (id
from hash.y) all die against these tests; H2/H3 struct mutants die via the
new comparator tests. Local dotnet test: 191/191 passing. FXC cs_5_0 compile
of a scratch kernel using both cultmath_smin_grad and cultmath_cellular
succeeds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 20:40:28 +02:00
|
|
|
return new CultCellular(new float4(grad1, f1), new float4(grad2 - grad1, f2 - f1), Unit(idHash.w));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static double Correlation(double[] a, double[] b)
|
|
|
|
|
{
|
|
|
|
|
var n = a.Length;
|
|
|
|
|
var meanA = 0.0;
|
|
|
|
|
var meanB = 0.0;
|
|
|
|
|
for (var i = 0; i < n; i++) { meanA += a[i]; meanB += b[i]; }
|
|
|
|
|
meanA /= n;
|
|
|
|
|
meanB /= n;
|
|
|
|
|
|
|
|
|
|
var cov = 0.0;
|
|
|
|
|
var varA = 0.0;
|
|
|
|
|
var varB = 0.0;
|
|
|
|
|
for (var i = 0; i < n; i++)
|
|
|
|
|
{
|
|
|
|
|
var da = a[i] - meanA;
|
|
|
|
|
var db = b[i] - meanB;
|
|
|
|
|
cov += da * db;
|
|
|
|
|
varA += da * da;
|
|
|
|
|
varB += db * db;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return cov / Math.Sqrt(varA * varB);
|
|
|
|
|
}
|
|
|
|
|
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
// ---- shared fixtures: an independent brute-force Worley search over the same public pcg3d
|
|
|
|
|
// hash cellular itself is documented to use (design.md, "Integer hashing"), so these tests do
|
Fix batch 1 (cellular/smin_grad): exact search, decorrelated id, finite F2=0
Cut 2a-i fix batch 1, findings F1, F3, F4, F5, F6, F7 (Soul falsification of
the first landing):
- cellular now searches a provably exact 5x5x5 neighbourhood (radius 2), not
3x3x3: 3x3x3 missed the true F2 for 9 of 400,000 sampled points. The
comment in math.cs proves 5x5x5 always contains the true F1 and F2 (both
are always < sqrt(3), every excluded cell is always > 2). A cheap
position-only lower-bound prune skips the pcg3d/jitter/distance work for
cells that provably cannot beat the running F2, without changing any
output bit (F6).
- F2 = 0 now gets the same degenerate-gradient guard F1 = 0 already had,
fixing a NaN that was reachable past |p| of about 2^23 (F3).
- id now comes from pcg4d over the winning cell alone, never from the pcg3d
hash that produced that cell's jitter, so id and jitter are no longer
deterministic functions of each other (F4).
- cellular_unit maps the top 24 bits instead of a plain divide-by-2^32, so
the mapping is always inside [0, 1); the old formula rounded
0xFFFFFF80..0xFFFFFFFF up to exactly 1.0f (F7).
- Central-difference tolerances tightened from 0.02 to 3e-4 (measured float
noise at the committed eps is about 6e-5), and cellular's own gradient
test now differentiates cellular's RETURNED .w values instead of a
duplicate FindF1F2 search, so a mutant that corrupts only the returned
gradient can't hide behind an independently-computed value (F1, F5).
- The struct-mirror comparison in HlslSourceCompatibilityTests now checks
field name and declared type in declaration order, not just position, so
a reordered-and-relabelled mirror struct (H2) or a renamed/retyped one
(H3) both fail the walk instead of surviving on values alone (F2).
- FindF1F2 widened to a true independent 7x7x7 brute-force oracle; new tests
pin production's exact agreement with it, the pruned search's bit-for-bit
equivalence to an unpruned 5x5x5 reference, no NaN at 2^24, and id's
correlation with jitter under 0.05.
- Comment/doc fix: smin_grad is Quilez's quadratic-polynomial smin, not
cubic.
Both shaders/CultMath.hlsl and unity/.../Shaders/CultMath.hlsl stay
byte-identical.
Manually verified mutants M9 (grad1 x 1.01), M16 (grad2 x 1.01), and M18 (id
from hash.y) all die against these tests; H2/H3 struct mutants die via the
new comparator tests. Local dotnet test: 191/191 passing. FXC cs_5_0 compile
of a scratch kernel using both cultmath_smin_grad and cultmath_cellular
succeeds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 20:40:28 +02:00
|
|
|
// not anchor to cellular's own control flow or iteration order. FindF1F2 searches 7x7x7 (radius
|
|
|
|
|
// 3), strictly wider than production's own proven-sufficient 5x5x5 (F6), so it is a true
|
|
|
|
|
// independent oracle rather than a second copy of the algorithm under test. ----
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
internal static float3 FeaturePoint(float3 cell)
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
{
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
var hash = math.pcg3d(math.int3(cell));
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
var jitter = new float3(Unit(hash.x), Unit(hash.y), Unit(hash.z));
|
|
|
|
|
return cell + jitter;
|
|
|
|
|
}
|
|
|
|
|
|
Fix batch 1 (cellular/smin_grad): exact search, decorrelated id, finite F2=0
Cut 2a-i fix batch 1, findings F1, F3, F4, F5, F6, F7 (Soul falsification of
the first landing):
- cellular now searches a provably exact 5x5x5 neighbourhood (radius 2), not
3x3x3: 3x3x3 missed the true F2 for 9 of 400,000 sampled points. The
comment in math.cs proves 5x5x5 always contains the true F1 and F2 (both
are always < sqrt(3), every excluded cell is always > 2). A cheap
position-only lower-bound prune skips the pcg3d/jitter/distance work for
cells that provably cannot beat the running F2, without changing any
output bit (F6).
- F2 = 0 now gets the same degenerate-gradient guard F1 = 0 already had,
fixing a NaN that was reachable past |p| of about 2^23 (F3).
- id now comes from pcg4d over the winning cell alone, never from the pcg3d
hash that produced that cell's jitter, so id and jitter are no longer
deterministic functions of each other (F4).
- cellular_unit maps the top 24 bits instead of a plain divide-by-2^32, so
the mapping is always inside [0, 1); the old formula rounded
0xFFFFFF80..0xFFFFFFFF up to exactly 1.0f (F7).
- Central-difference tolerances tightened from 0.02 to 3e-4 (measured float
noise at the committed eps is about 6e-5), and cellular's own gradient
test now differentiates cellular's RETURNED .w values instead of a
duplicate FindF1F2 search, so a mutant that corrupts only the returned
gradient can't hide behind an independently-computed value (F1, F5).
- The struct-mirror comparison in HlslSourceCompatibilityTests now checks
field name and declared type in declaration order, not just position, so
a reordered-and-relabelled mirror struct (H2) or a renamed/retyped one
(H3) both fail the walk instead of surviving on values alone (F2).
- FindF1F2 widened to a true independent 7x7x7 brute-force oracle; new tests
pin production's exact agreement with it, the pruned search's bit-for-bit
equivalence to an unpruned 5x5x5 reference, no NaN at 2^24, and id's
correlation with jitter under 0.05.
- Comment/doc fix: smin_grad is Quilez's quadratic-polynomial smin, not
cubic.
Both shaders/CultMath.hlsl and unity/.../Shaders/CultMath.hlsl stay
byte-identical.
Manually verified mutants M9 (grad1 x 1.01), M16 (grad2 x 1.01), and M18 (id
from hash.y) all die against these tests; H2/H3 struct mutants die via the
new comparator tests. Local dotnet test: 191/191 passing. FXC cs_5_0 compile
of a scratch kernel using both cultmath_smin_grad and cultmath_cellular
succeeds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 20:40:28 +02:00
|
|
|
// Mirrors math.cellular_unit's top-24-bit [0, 1) mapping (F7) exactly, so this fixture never
|
|
|
|
|
// drifts from production's own jitter/id mapping.
|
|
|
|
|
private static float Unit(int bits) => ((uint)bits >> 8) * (1.0f / 16777216.0f);
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
|
|
|
|
|
private static (float f1, float f2) FindF1F2(float3 p)
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
{
|
|
|
|
|
var (f1, f2, _) = FindF1F2F3(p);
|
|
|
|
|
return (f1, f2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Same independent 7x7x7 oracle as FindF1F2, plus the third-nearest distance. Central-difference
|
|
|
|
|
// tests need this: excluding only the F1=F2 gap is not enough, because a small F2=F3 gap is also
|
|
|
|
|
// a kink (the identity of the second-nearest feature swaps there), and hitting one produces a
|
|
|
|
|
// large, curvature-driven finite-difference error unrelated to eps or float noise (Soul, cut
|
|
|
|
|
// 2a-i: an unfiltered large sample hit an edge error of 0.78 this way, versus ~2e-4 once F2=F3
|
|
|
|
|
// is also excluded).
|
|
|
|
|
private static (float f1, float f2, float f3) FindF1F2F3(float3 p)
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
{
|
|
|
|
|
var cell = new float3(MathF.Floor(p.x), MathF.Floor(p.y), MathF.Floor(p.z));
|
|
|
|
|
var f1 = float.PositiveInfinity;
|
|
|
|
|
var f2 = float.PositiveInfinity;
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
var f3 = float.PositiveInfinity;
|
Fix batch 1 (cellular/smin_grad): exact search, decorrelated id, finite F2=0
Cut 2a-i fix batch 1, findings F1, F3, F4, F5, F6, F7 (Soul falsification of
the first landing):
- cellular now searches a provably exact 5x5x5 neighbourhood (radius 2), not
3x3x3: 3x3x3 missed the true F2 for 9 of 400,000 sampled points. The
comment in math.cs proves 5x5x5 always contains the true F1 and F2 (both
are always < sqrt(3), every excluded cell is always > 2). A cheap
position-only lower-bound prune skips the pcg3d/jitter/distance work for
cells that provably cannot beat the running F2, without changing any
output bit (F6).
- F2 = 0 now gets the same degenerate-gradient guard F1 = 0 already had,
fixing a NaN that was reachable past |p| of about 2^23 (F3).
- id now comes from pcg4d over the winning cell alone, never from the pcg3d
hash that produced that cell's jitter, so id and jitter are no longer
deterministic functions of each other (F4).
- cellular_unit maps the top 24 bits instead of a plain divide-by-2^32, so
the mapping is always inside [0, 1); the old formula rounded
0xFFFFFF80..0xFFFFFFFF up to exactly 1.0f (F7).
- Central-difference tolerances tightened from 0.02 to 3e-4 (measured float
noise at the committed eps is about 6e-5), and cellular's own gradient
test now differentiates cellular's RETURNED .w values instead of a
duplicate FindF1F2 search, so a mutant that corrupts only the returned
gradient can't hide behind an independently-computed value (F1, F5).
- The struct-mirror comparison in HlslSourceCompatibilityTests now checks
field name and declared type in declaration order, not just position, so
a reordered-and-relabelled mirror struct (H2) or a renamed/retyped one
(H3) both fail the walk instead of surviving on values alone (F2).
- FindF1F2 widened to a true independent 7x7x7 brute-force oracle; new tests
pin production's exact agreement with it, the pruned search's bit-for-bit
equivalence to an unpruned 5x5x5 reference, no NaN at 2^24, and id's
correlation with jitter under 0.05.
- Comment/doc fix: smin_grad is Quilez's quadratic-polynomial smin, not
cubic.
Both shaders/CultMath.hlsl and unity/.../Shaders/CultMath.hlsl stay
byte-identical.
Manually verified mutants M9 (grad1 x 1.01), M16 (grad2 x 1.01), and M18 (id
from hash.y) all die against these tests; H2/H3 struct mutants die via the
new comparator tests. Local dotnet test: 191/191 passing. FXC cs_5_0 compile
of a scratch kernel using both cultmath_smin_grad and cultmath_cellular
succeeds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 20:40:28 +02:00
|
|
|
for (var dz = -3; dz <= 3; dz++)
|
|
|
|
|
for (var dy = -3; dy <= 3; dy++)
|
|
|
|
|
for (var dx = -3; dx <= 3; dx++)
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
{
|
|
|
|
|
var d = math.distance(p, FeaturePoint(cell + new float3(dx, dy, dz)));
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
if (d < f1) { f3 = f2; f2 = f1; f1 = d; }
|
|
|
|
|
else if (d < f2) { f3 = f2; f2 = d; }
|
|
|
|
|
else if (d < f3) { f3 = d; }
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
}
|
|
|
|
|
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
return (f1, f2, f3);
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static (float3 offset, float f1, float f2) FindNearestOffset(float3 p)
|
|
|
|
|
{
|
|
|
|
|
var cell = new float3(MathF.Floor(p.x), MathF.Floor(p.y), MathF.Floor(p.z));
|
|
|
|
|
var f1 = float.PositiveInfinity;
|
|
|
|
|
var f2 = float.PositiveInfinity;
|
|
|
|
|
var bestOffset = float3.zero;
|
Fix batch 1 (cellular/smin_grad): exact search, decorrelated id, finite F2=0
Cut 2a-i fix batch 1, findings F1, F3, F4, F5, F6, F7 (Soul falsification of
the first landing):
- cellular now searches a provably exact 5x5x5 neighbourhood (radius 2), not
3x3x3: 3x3x3 missed the true F2 for 9 of 400,000 sampled points. The
comment in math.cs proves 5x5x5 always contains the true F1 and F2 (both
are always < sqrt(3), every excluded cell is always > 2). A cheap
position-only lower-bound prune skips the pcg3d/jitter/distance work for
cells that provably cannot beat the running F2, without changing any
output bit (F6).
- F2 = 0 now gets the same degenerate-gradient guard F1 = 0 already had,
fixing a NaN that was reachable past |p| of about 2^23 (F3).
- id now comes from pcg4d over the winning cell alone, never from the pcg3d
hash that produced that cell's jitter, so id and jitter are no longer
deterministic functions of each other (F4).
- cellular_unit maps the top 24 bits instead of a plain divide-by-2^32, so
the mapping is always inside [0, 1); the old formula rounded
0xFFFFFF80..0xFFFFFFFF up to exactly 1.0f (F7).
- Central-difference tolerances tightened from 0.02 to 3e-4 (measured float
noise at the committed eps is about 6e-5), and cellular's own gradient
test now differentiates cellular's RETURNED .w values instead of a
duplicate FindF1F2 search, so a mutant that corrupts only the returned
gradient can't hide behind an independently-computed value (F1, F5).
- The struct-mirror comparison in HlslSourceCompatibilityTests now checks
field name and declared type in declaration order, not just position, so
a reordered-and-relabelled mirror struct (H2) or a renamed/retyped one
(H3) both fail the walk instead of surviving on values alone (F2).
- FindF1F2 widened to a true independent 7x7x7 brute-force oracle; new tests
pin production's exact agreement with it, the pruned search's bit-for-bit
equivalence to an unpruned 5x5x5 reference, no NaN at 2^24, and id's
correlation with jitter under 0.05.
- Comment/doc fix: smin_grad is Quilez's quadratic-polynomial smin, not
cubic.
Both shaders/CultMath.hlsl and unity/.../Shaders/CultMath.hlsl stay
byte-identical.
Manually verified mutants M9 (grad1 x 1.01), M16 (grad2 x 1.01), and M18 (id
from hash.y) all die against these tests; H2/H3 struct mutants die via the
new comparator tests. Local dotnet test: 191/191 passing. FXC cs_5_0 compile
of a scratch kernel using both cultmath_smin_grad and cultmath_cellular
succeeds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 20:40:28 +02:00
|
|
|
for (var dz = -2; dz <= 2; dz++)
|
|
|
|
|
for (var dy = -2; dy <= 2; dy++)
|
|
|
|
|
for (var dx = -2; dx <= 2; dx++)
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
{
|
|
|
|
|
var offset = new float3(dx, dy, dz);
|
|
|
|
|
var d = math.distance(p, FeaturePoint(cell + offset));
|
|
|
|
|
if (d < f1) { f2 = f1; f1 = d; bestOffset = offset; }
|
|
|
|
|
else if (d < f2) { f2 = d; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (bestOffset, f1, f2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static float3 FindNearestCell(float3 p)
|
|
|
|
|
{
|
|
|
|
|
var cell = new float3(MathF.Floor(p.x), MathF.Floor(p.y), MathF.Floor(p.z));
|
|
|
|
|
var best = float.PositiveInfinity;
|
|
|
|
|
var bestCell = cell;
|
|
|
|
|
for (var dz = -1; dz <= 1; dz++)
|
|
|
|
|
for (var dy = -1; dy <= 1; dy++)
|
|
|
|
|
for (var dx = -1; dx <= 1; dx++)
|
|
|
|
|
{
|
|
|
|
|
var neighbour = cell + new float3(dx, dy, dz);
|
|
|
|
|
var d = math.distance(p, FeaturePoint(neighbour));
|
|
|
|
|
if (d < best) { best = d; bestCell = neighbour; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bestCell;
|
|
|
|
|
}
|
|
|
|
|
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
private static void AssertGradientMatchesCentralDifference(Func<float3, float> scalarField, float3 p, float eps, float4 valueAndGradient, float tolerance)
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
{
|
|
|
|
|
var numeric = new float3(
|
|
|
|
|
(scalarField(p + new float3(eps, 0.0f, 0.0f)) - scalarField(p - new float3(eps, 0.0f, 0.0f))) / (2.0f * eps),
|
|
|
|
|
(scalarField(p + new float3(0.0f, eps, 0.0f)) - scalarField(p - new float3(0.0f, eps, 0.0f))) / (2.0f * eps),
|
|
|
|
|
(scalarField(p + new float3(0.0f, 0.0f, eps)) - scalarField(p - new float3(0.0f, 0.0f, eps))) / (2.0f * eps));
|
|
|
|
|
|
tests: fix hashing fixtures, false comments, and gradient tolerance
CellularAndSminGradTests.cs changes for cut 2a-i's pass-2 findings:
- FeaturePoint and the two other independent pcg3d/pcg4d call sites now hash
int3(cell)/int4(int3(cellId), 0) to match production's integer-hashing fix
(previous commit); the oracle would otherwise diverge from cellular() and
every comparison test would fail.
- New JitterAndIdPassAChiSquareUniformityTest: jitter.x/y/z and id each pass
a chi-square uniformity test (16 bins, 15 df, critical value 37.7 at
alpha=0.001) over 200,000 cells. This is the behavioural pin for the
hashing fix; reverting to pcg3d(cell)'s float-bit hash fails it.
- Two false comments corrected to the measured facts: the
NearestFeatureWins test's claim that |component|=2 offsets are "provably
never the true nearest" is false (their lower bound is 1, not sqrt(3); Soul
measured 3 F1 wins in 2,000,000 samples), and SearchReachesEveryAxis...'s
claim of "lower bound is exactly 2" and "zero hits in 3,000,000" is also
false (lower bound is 1; Soul measured about 1 hit in 28,000).
- PrunedSearchIsBitIdentical... renamed to
...BelowTwoToTheTwentyFifth and its comment states that domain explicitly
(design.md, previous commit).
- GradientTolerance split into SminGradientTolerance (2.5e-4f) and
CellularGradientTolerance (1.3e-3f): the two functions have different
curvature (smin_grad is exactly quadratic in a band; cellular's F1/F2 are
real distance fields), so a shared 3e-4 tolerance carried almost no
headroom over either's actual measured noise (re-measured here at 5.3e-4
for smin's old eps, 2.0e-4/2.6e-4 for cellular), not the "~6e-5" the old
comment claimed. smin_grad's eps moves from 0.001f to 0.01f (no truncation
cost there; dilutes float noise 8x) with a matching widened seam-exclusion
band so p +/- eps cannot step across the seam the check at p already
avoided. cellular's eps stays at 0.0005f (a small sweep found it already
near the truncation/noise sweet spot for a real distance field). Both new
tolerances keep >5x headroom over their own measured worst case and both
still die under a 1.01x-scale mutant on the returned gradient — smin down
to 1.0005x, cellular (M9/M16-style, either gradient) down to 1.002x.
- GradientMatchesCentralDifferencesAwayFromTheF1EqualsF2Seam also excludes a
band around F2 = F3 now, not just F1 = F2: the second-nearest feature's
identity swaps there too, a genuine kink the old test could silently hit
(an unfiltered large sample found a 0.78 edge error this way, swamping the
~2e-4 the filtered test actually sees).
- AxisAlignedRadiusTwoOffsets and FindPointRealizingOffset are extracted from
SearchReachesEveryAxisAlignedRadiusTwoSlice as internal fixtures so
HlslSourceCompatibilityTests (next commit) can reuse the same construction
instead of a second copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 23:13:50 +02:00
|
|
|
AssertWithinTolerance(numeric.x, valueAndGradient.x, tolerance);
|
|
|
|
|
AssertWithinTolerance(numeric.y, valueAndGradient.y, tolerance);
|
|
|
|
|
AssertWithinTolerance(numeric.z, valueAndGradient.z, tolerance);
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
}
|
|
|
|
|
|
2026-09-25 19:28:36 +02:00
|
|
|
// A fixed decimal-place Assert.Equal snaps two numbers that agree to within a few times 1e-5 to
|
|
|
|
|
// different rounded digits whenever their true difference straddles a rounding boundary (seen:
|
|
|
|
|
// 0.824987829 vs 0.825006127, agreeing to 2e-5, rounding to 0.82 and 0.83). An absolute-tolerance
|
|
|
|
|
// check states the actual claim (central differences agree with the analytic gradient within the
|
|
|
|
|
// finite-difference step's own truncation error) without that artifact.
|
|
|
|
|
private static void AssertWithinTolerance(float expected, float actual, float tolerance) =>
|
|
|
|
|
Assert.True(MathF.Abs(expected - actual) <= tolerance, $"expected {expected}, actual {actual}, tolerance {tolerance}");
|
|
|
|
|
|
2026-09-25 19:23:54 +02:00
|
|
|
private static float3 RandomUnit(System.Random random) =>
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
new(random.NextSingle() * 2.0f - 1.0f, random.NextSingle() * 2.0f - 1.0f, random.NextSingle() * 2.0f - 1.0f);
|
|
|
|
|
|
2026-09-25 19:23:54 +02:00
|
|
|
private static float RandomScalar(System.Random random) => random.NextSingle() * 20.0f - 10.0f;
|
Test smin_grad and cellular's invariants behaviourally
CellularAndSminGradTests pins the rules the cut map calls out, each against a
mutant it must kill:
- smin's value never exceeds the ordinary minimum, and matches min with that
input's own gradient once |a.w - b.w| exceeds k on either side.
- smin_grad's gradient matches central differences of its own .w at random
seeded points (the formula is exact, not an approximation, so no exclusion
band is needed away from the |a.w-b.w|=k seam itself).
- A direct case falsifies blending the gradient with a constant 0.5 or with
(1 - h) instead of h.
- cellular's F1 never exceeds F2, matched against an independent brute-force
Worley search that shares only the public pcg3d hash, not cellular's control
flow.
- The true nearest feature wins even when an independent search finds it in
the (1, 1, 1) neighbour offset, the last one cellular's own loop order
visits, ruling out a "first found" or early-exit mutant.
- The gradient at a feature point is finite zero, not NaN.
- Both nearest and edge gradients match central differences away from a band
around the F1 = F2 set, where the nearest and second-nearest identity swap
discontinuously.
- id is constant across small moves that stay in the same feature's region and
differs from a distant cell's id.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 19:18:43 +02:00
|
|
|
}
|