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>
This commit is contained in:
parent
3b1ea71eef
commit
e739ff1e52
1 changed files with 51 additions and 0 deletions
|
|
@ -334,6 +334,57 @@ public sealed class CellularAndSminGradTests
|
|||
}
|
||||
}
|
||||
|
||||
[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
|
||||
// exactness proof's own margin (F1, F2 always < sqrt(3), radius-2 cells' lower bound is
|
||||
// exactly 2) making a genuine win from any single |offset component| = 2 cell astronomically
|
||||
// rare over uniformly random points (zero hits in 3,000,000 samples, measured separately).
|
||||
// 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).
|
||||
var offsets = new[]
|
||||
{
|
||||
new float3(2.0f, 0.0f, 0.0f), new float3(-2.0f, 0.0f, 0.0f),
|
||||
new float3(0.0f, 2.0f, 0.0f), new float3(0.0f, -2.0f, 0.0f),
|
||||
new float3(0.0f, 0.0f, 2.0f), new float3(0.0f, 0.0f, -2.0f),
|
||||
};
|
||||
|
||||
foreach (var offset in offsets)
|
||||
{
|
||||
var found = false;
|
||||
for (var qz = -80; qz <= 80 && !found; qz++)
|
||||
for (var qy = -80; qy <= 80 && !found; qy++)
|
||||
for (var qx = -80; qx <= 80 && !found; qx++)
|
||||
{
|
||||
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.
|
||||
|
||||
found = true;
|
||||
var result = math.cellular(p);
|
||||
Assert.Equal(f1, result.nearest.w, precision: 3);
|
||||
Assert.Equal(f2 - f1, result.edge.w, precision: 3);
|
||||
}
|
||||
|
||||
Assert.True(found, $"could not construct a query point where offset {offset.x},{offset.y},{offset.z} ever wins F1 or F2");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PrunedSearchIsBitIdenticalToTheUnprunedFiveCubedSearch()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue