Affine forms map: Pass 5 probe results, authority map, and the tape target's pointer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
a796614276
commit
32ba1bfa53
2 changed files with 281 additions and 1 deletions
|
|
@ -5,7 +5,8 @@ the Self opened the `cultmath-tapes` campaign from pass 2; pass 3 revises the
|
|||
two CultLib cuts after their first Hands reports and the operator's challenge
|
||||
to the march, and changes the site field to envelope-then-noise; pass 4
|
||||
(section "Pass 4") revises `interval-ops` to r3 after its second Hands report
|
||||
and Soul's verdict, and names the one shipped void (Q9). Where a later pass and
|
||||
and Soul's verdict, and names the one shipped void (Q9). Pass 5 (section "Pass 5") maps
|
||||
affine forms against CultLib `main` at `a7966142`. Where a later pass and
|
||||
an earlier section disagree, the later pass wins. Where this map and the Body
|
||||
disagree, the Body wins and this map is stale.
|
||||
|
||||
|
|
@ -1007,3 +1008,279 @@ beside the two CultLib r2 specs.
|
|||
- `:root:has(body[data-slug])` for the ritual seeds: supported in every
|
||||
current browser; a browser without `:has` gets the default seeds, which is
|
||||
acceptable for a fallback of a fallback.
|
||||
|
||||
## Pass 5: affine forms (ruling `affine-forms-unparked`)
|
||||
|
||||
Imagination pass 5, 2026-10-03. It maps the affine-arithmetic piece of the
|
||||
tape target, unparked on its own by ruling
|
||||
`cultmath-tapes:ruling:affine-forms-unparked`. It answers follow-up
|
||||
`cultmath-tapes:follow_up:wells-oracle-shortfall-affine-forms`. No tape,
|
||||
pruning or interpreter is built; tape steps 2-5 stay parked under
|
||||
`tape-target-unparks-when-asura-stable`. Pinned head: CultLib `main` at
|
||||
`a7966142`. Every `file:line` in this section is against that commit.
|
||||
|
||||
### What was measured (body facts)
|
||||
|
||||
The probe is a renamed copy of `NoiseBoundTests.cs` with five more bounds for
|
||||
`FogField`. It lives on the local branch `scratch/imagination-affine-probe`
|
||||
(`5fa8e9ce`). That branch was never pushed to origin and is not for merging.
|
||||
It was run on Yggdrasil through the ygg-verify stopgap (`dotnet/sdk:10.0`,
|
||||
about 3 minutes). It re-runs `Run(FogField.Wells(w), 8, 64, 0x5A7E + 8)`, so
|
||||
the tiles, rays and seeds are those of the landed (c) row at `N = 8`. V0
|
||||
reproduces the landed figures exactly (0.378 and 0.194).
|
||||
|
||||
Constants the probe needed, each sampled over 2e5 seeded points with a 1.25
|
||||
margin (probe grade, not provenance):
|
||||
- the Frobenius norm of the Hessian of `snoise(float3)`, max 55.15, so
|
||||
`M = 68.94`;
|
||||
- the gradient length of `snoise(float2)`, max 7.35, so `L2 = 9.19`;
|
||||
- for comparison, the mean `|snoise_grad|` is 2.59, against `L = 10.10`.
|
||||
|
||||
The bounds:
|
||||
- **V0**: the landed bound.
|
||||
- **V1**: V0 with the detail octave's ball no longer enlarged by the warp. The
|
||||
detail reads the unwarped point (`NoiseBoundTests.cs:860`, `Noise.Detail(p, ...)` of the unwarped `p`), yet `Bound`
|
||||
hands it `ball.w`, which carries `D` (`:875`, `:890`).
|
||||
- **V2**: V1 with the warp centred. Each phase's coverage ball is centred at
|
||||
`c + flow(c) shift_k`, with radius `r + |shift_k| rho`, where
|
||||
`rho = FlowAmplitude L2 FlowFrequency r sqrt 2` bounds how far the flow moves
|
||||
over the ball. This is the mean-value form of the warp: interval arithmetic
|
||||
only, no affine noise.
|
||||
- **V3**: V2 with the centred form of the noise,
|
||||
`n(c) +- min(L R, |g(c)| R + M R^2 / 2)` from one `snoise_grad`.
|
||||
- **V4**: V3 with a reduced affine form. Three shared symbols (depth `t`,
|
||||
slopes `u` and `v`) carry the envelope's plane term and every noise term's
|
||||
linear part, and one error symbol takes the rest. It applies where the fade
|
||||
is the point 1, and is intersected with V3.
|
||||
- **V5**: V4 with one shared symbol, depth `t`, plus one error symbol:
|
||||
Gamito's `t0 + t1 e1 + t2 e2`.
|
||||
|
||||
A gradient evaluation is counted as 2 snoise, an estimate. V3 to V5 gate a
|
||||
probe on the best result any centre value and gradient could give.
|
||||
|
||||
Range ceiling. A cell is range-nonempty if the density is positive at any of
|
||||
5 x 5 slopes over the tile's slope square (corners included) times 9 depths
|
||||
over the cell, or if it is in the oracle mask. Its efficiency is the oracle
|
||||
march's cost over the cost of marching the range mask, with probes free. The
|
||||
sampling can miss nonzero points, so this figure is an upper estimate. No
|
||||
sound enclosure can beat it.
|
||||
|
||||
| (c), `N = 8` | efficiency (oracle) | cost ratio | dense cells/ray | probe overhead | cull |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| warp 0, V0 landed interval | 0.378 | 2.74x | 12.40 | 0.961 | 0.952 |
|
||||
| warp 0, V2 centred interval | 0.368 | 2.67x | 12.40 | 0.937 | 0.952 |
|
||||
| warp 0, V3 centred noise | 0.466 | 3.38x | 8.67 | 0.868 | 0.974 |
|
||||
| warp 0, V5 affine, one symbol | **0.511** | **3.71x** | 7.81 | 0.859 | 0.979 |
|
||||
| warp 0, V4 affine, three symbols | 0.534 | 3.88x | 7.37 | 0.853 | 0.981 |
|
||||
| warp 0, range ceiling | 0.859 | | | | 0.995 |
|
||||
| warp 60, V0 landed interval | 0.194 | 1.39x | 31.55 | 0.993 | 0.842 |
|
||||
| warp 60, V1 detail unwarped | 0.205 | 1.47x | 27.74 | 0.963 | 0.864 |
|
||||
| warp 60, V2 centred interval | 0.347 | 2.49x | 13.71 | 0.938 | 0.945 |
|
||||
| warp 60, V3 centred noise | 0.441 | 3.16x | 9.52 | 0.870 | 0.969 |
|
||||
| warp 60, V5 affine, one symbol | **0.477** | **3.42x** | 8.71 | 0.864 | 0.974 |
|
||||
| warp 60, V4 affine, three symbols | 0.502 | 3.60x | 8.16 | 0.856 | 0.977 |
|
||||
| warp 60, range ceiling | 0.854 | | | | 0.995 |
|
||||
|
||||
Every variant's transmittance equals the dense march's (max |dT| 0). No
|
||||
variant's pre-pass proved a cell empty where any of the 225 samples per cell
|
||||
was nonzero (0 enclosure violations).
|
||||
|
||||
What this establishes:
|
||||
|
||||
1. **The oracle cannot be reached by any sound bound.** The oracle mask
|
||||
samples one depth per ray. A bound must cover the whole slice of the tile,
|
||||
so the best any exact enclosure could do is about 0.86 of the oracle. That
|
||||
matches the 0.57-0.85 that conservative visibility methods reach against
|
||||
exact sets (`docs/research/culling-efficiency-oracle-prior-art.md`). An
|
||||
oracle target of 0.90 is unreachable by construction.
|
||||
2. **The warp diagnosis was half right.** At warp `D` most of the gap is the
|
||||
warp enlarging the radius by its maximum (V0 to V2: 0.194 to 0.347). That
|
||||
is fixed by evaluating the flow at the ball's centre and enlarging the
|
||||
radius only by how far the flow can move across the ball. It takes no
|
||||
affine noise, and it costs one more snoise per probe, for the second phase
|
||||
ball. The detail octave's spurious warp (V1) is worth little on its own.
|
||||
3. **Affine noise buys the rest:** the centred form, then the shared depth
|
||||
symbol. Against the centred interval, affine with one symbol closes about a fifth
|
||||
of the oracle gap at warp 0 and at warp `D` (efficiency 0.368 to 0.511 and
|
||||
0.347 to 0.477). Its combined cost ratio is 1.39x and 1.37x the centred
|
||||
interval's, and 1.35x and 2.46x the landed interval's. It reaches 0.59 and
|
||||
0.56 of the range ceiling.
|
||||
4. **Three symbols buy about 5% over one** (0.534 against 0.511, 0.502
|
||||
against 0.477). One symbol fits a `float3`. Three need a struct or a
|
||||
`float4` plus a `float`, and the HLSL mirror's harness compares struct
|
||||
returns but has no struct parameters (`packages/cultmath/docs/design.md:126-135`).
|
||||
5. **The probe overhead falls** from 0.96 to 0.86 under every centred or
|
||||
affine bound. Each probe costs a gradient and proves more, so the total
|
||||
cost still drops by 1.35x to 2.46x. Today's (c) assertion, overhead at
|
||||
least 0.90 (`NoiseBoundTests.cs:1558`, `:1588`), would reject the cheaper
|
||||
march. That is question `affine-wells-contract`.
|
||||
|
||||
### The model
|
||||
|
||||
An affine form is a transient value, not a persistent kind. The persistent
|
||||
things this pass adds or changes:
|
||||
|
||||
| Thing | Identity | Lifecycle | Authority |
|
||||
| --- | --- | --- | --- |
|
||||
| `SNOISE_HESSIAN` (C#), `CULTMATH_SNOISE_HESSIAN` (HLSL, lowered to GLSL) | The constant's name; one value in `math.Affine.cs`, mirrored | Measured once by `NoiseBoundTests.MeasureHessian` (slow, explicit), as `SNOISE_LIPSCHITZ` is. It is re-measured and re-pinned whenever the `snoise` kernel changes. | `MeasureHessian` sets it; `HessianConstantPinsSampledCurvature` pins it; no consumer carries its own |
|
||||
| `af_*` families in `fixtures/glsl-parity.json` | Family signature string, appended after the last existing family | Regenerated only by `CULTMATH_WRITE_GLSL=1`; earlier cases stay byte-identical | `GlslMirrorTests` generator |
|
||||
| The (c) contracts | `IntervalSkipHalvesEvaluations` assertions | Changed only by a ruling | Operator (question `affine-wells-contract`) |
|
||||
| `SNOISE2_LIPSCHITZ` (C#), mirrored in HLSL | The constant's name in `math.Interval.cs`, beside `SNOISE_LIPSCHITZ`, with an `iv_snoise_ball(float2, float)` overload | Measured by `MeasureLipschitz2` (slow, explicit) and pinned, as `L` is | CultMath. The (c) field's flow is `snoise(float2)`, and a bound on a CultMath function belongs to CultMath, not to a test-local helper. A consumer's own flow, such as the site's texture, keeps its own variation bound |
|
||||
|
||||
### Representation
|
||||
|
||||
**A reduced affine form with one shared symbol: `float3(x0, a, e)`, meaning
|
||||
`x0 + a·ε + e·δ`.** Here `ε ∈ [-1, 1]` is shared by every form over the same
|
||||
region, and `δ ∈ [-1, 1]` is a private error, with `e ≥ 0`.
|
||||
- This is Messine's AF1 as Gamito and Maddock use it for gradient noise,
|
||||
`t0 + t1 e1 + t2 e2`.
|
||||
- Sharp and Jacobson's measurements show that this fixed-symbol form is the
|
||||
fast one on GPUs: 1.0x against 8.4x for full affine.
|
||||
- It fits one `float3` register, as an interval fits a `float2`. It needs no
|
||||
struct parameter, so the mirror, lowering and fixture machinery take it
|
||||
unchanged.
|
||||
- V4 against V5 prices the two extra symbols at about 5% efficiency.
|
||||
|
||||
For a tile probe `ε` is the depth parameter: `z = z_m + h ε` with
|
||||
`h = (z1 - z0) / 2`. The footprint goes into the error. For a single ray it is
|
||||
Gamito's form exactly.
|
||||
|
||||
A point in space is not a new type. It is the three arguments
|
||||
`(centre: float3, axis: float3, radius: float)`, the set
|
||||
`{centre + axis ε + w : |w| ≤ radius}`. `af_snoise` and `af_fbm` take a point
|
||||
in that form, and `af_frustum_axis` and `af_frustum_ball` produce one for a
|
||||
tile slice.
|
||||
|
||||
Ops, named by the (c) consumer, plus `af_mul` because affine arithmetic is
|
||||
defined by its product, as `iv_mul` is for intervals:
|
||||
- `af_point`, `af_symbol(x0, a)`, `af_from_iv`, `af_range` (the bridge to
|
||||
`iv_*`);
|
||||
- `af_add`, `af_sub`, `af_neg`, `af_scale`, `af_add_iv`, `af_mul`;
|
||||
- `af_snoise`, `af_fbm`;
|
||||
- `af_frustum_axis`, `af_frustum_ball`.
|
||||
|
||||
No others until a consumer names one, which is the rule `design.md` already
|
||||
states for intervals.
|
||||
|
||||
The interval side gains `SNOISE2_LIPSCHITZ` and the overload
|
||||
`iv_snoise_ball(float2, float)`. The (c) field's flow is `snoise(float2)`, and
|
||||
centring the warp needs a bound on it, which belongs to its owner.
|
||||
|
||||
`af_snoise(centre, axis, radius)` makes one `snoise_grad` call. With
|
||||
`R = |axis| + radius`, it returns the narrower of two sound forms:
|
||||
- the Lipschitz form `(n, 0, L R)`;
|
||||
- the centred form `(n, g · axis, |g| radius + M R² / 2)`.
|
||||
|
||||
`e` absorbs the op's own float rounding and an allowance for a GPU's gradient
|
||||
error. The allowance is derived in the doc comment and measured on WebGL2 by
|
||||
the fixture's enclosure check, as `iv_frustum_ball`'s is.
|
||||
|
||||
The warp stays the consumer's, so CultMath owns no flow. The rule, stated in
|
||||
`design.md` and tested for the (c) field: a warp whose value at the centre is
|
||||
known, and whose variation over the ball is at most `ρ`, is enclosed by moving
|
||||
the centre by `w(c)` and adding `ρ` to the radius. Interval balls benefit from
|
||||
it too (V2).
|
||||
|
||||
### Authority map
|
||||
|
||||
- **Owner.** `src/CultMath/math.Affine.cs` owns affine-form semantics on the
|
||||
CPU. `shaders/CultMath.Affine.hlsl` mirrors it bit for bit, and is the shader
|
||||
semantic authority. `shaders/CultMath.glsl` is derived from the HLSL.
|
||||
- **Inputs.**
|
||||
- `snoise_grad`, whose `.w` is bit-equal to `snoise`;
|
||||
- `SNOISE_LIPSCHITZ`;
|
||||
- `SNOISE_HESSIAN`;
|
||||
- the caller's forms and points.
|
||||
- **Outputs.** `float3` forms, and `float2` intervals through `af_range`.
|
||||
- **Derived state.**
|
||||
- The GLSL text is generated by `GlslLowering`, unchanged.
|
||||
- The fixture families are generated.
|
||||
- The (c) efficiency rows are printed measurements, not state.
|
||||
- **Forbidden writers.**
|
||||
- A consumer linearising noise itself: the probe's V4 and V5 did this by
|
||||
hand, and Hands must call `af_snoise`.
|
||||
- A second curvature constant anywhere.
|
||||
- Hand-edited `CultMath.glsl`.
|
||||
- An affine or interval evaluator over `CultMath.Expr` (the parked tape's,
|
||||
see collisions below).
|
||||
- A flow or envelope function in CultMath. Consumers compose those, as for
|
||||
intervals.
|
||||
- **Shared paths.** The test field's `FogField`, the site ground and
|
||||
Aetheria's nebula all reach affine noise through `af_snoise` or `af_fbm`,
|
||||
and a tile slice through `af_frustum_*`.
|
||||
- **Deletion line.**
|
||||
- In `FogField.Bound` (`NoiseBoundTests.cs:875`), the warp-enlarged ball is
|
||||
replaced by the centred warp, and the detail ball stops carrying the warp
|
||||
(`:890`).
|
||||
- The (c) probe-overhead assertion (`:1558`, `:1588`) is replaced as the
|
||||
ruling on `affine-wells-contract` says.
|
||||
- `design.md`'s "What they do not: ... affine forms" (`:584-587`) and "That
|
||||
gap belongs to the tape target's affine forms" (`:623-624`) are rewritten.
|
||||
- The tape target's open item on representation is closed.
|
||||
|
||||
### Cut order
|
||||
|
||||
1. **`affine-forms`, the one she can see.**
|
||||
- It adds C# `math.Affine.cs` and `AffineTests`, plus `SNOISE_HESSIAN`'s
|
||||
provenance.
|
||||
- The (c) field gains the centred warp and the affine bound.
|
||||
- (c) is re-measured with three bound rows on the same tiles: landed
|
||||
interval, centred interval, affine. The range ceiling is printed beside
|
||||
the oracle.
|
||||
- An explicit, env-gated test writes a cull-map PNG of one (c) frame: dense
|
||||
cells per ray for each bound, and the transmittance. Self publishes it.
|
||||
- `design.md` is updated.
|
||||
- It is blocked on `affine-wells-contract` only for the assertion's shape.
|
||||
2. **`affine-shaders`.**
|
||||
- It adds `CultMath.Affine.hlsl` and its Unity copy and `.meta`, the
|
||||
include, and the regenerated `CultMath.glsl`.
|
||||
- Fixture families are appended, with an enclosure check for `af_snoise`
|
||||
and `af_frustum_ball`.
|
||||
- The dxc and glslang compiles and the WebGL2 golden-fixture run happen on
|
||||
Starfire.
|
||||
- Both cuts share the branch `hands/cultmath-affine-forms` and merge
|
||||
together after cut 2's Soul pass. Main never carries a C#-only `af_*`, so
|
||||
three-runtimes-agree holds at every merged commit.
|
||||
|
||||
Writing the C# before the HLSL inverts the usual order, because HLSL is the
|
||||
semantic authority. The cost is small: the ops are plain arithmetic in the
|
||||
common subset. Cut 2 changes the C# if the HLSL dialect forces something,
|
||||
never the reverse.
|
||||
|
||||
### Verification venue
|
||||
|
||||
Heavy work runs on Yggdrasil through the stopgap: `dotnet test`, Stryker scoped
|
||||
to `math.Affine.cs`, the (c) re-measure and the cull-map render. Starfire runs
|
||||
only the Windows and GPU legs, one job at a time:
|
||||
- `dxc -T lib_6_3 -HV 2021` on `CultMath.hlsl`;
|
||||
- `glslangValidator` 16.6.0 on the lowered GLSL (ruling
|
||||
`operator-compiler-downloads`);
|
||||
- the WebGL2 golden-fixture run on the GTX 1070, under ANGLE D3D11 in a
|
||||
browser. It is seeded from the glsl-lowering Soul pass's `gpu.js`
|
||||
(follow-up `glsl-browser-parity-run`).
|
||||
|
||||
### Collisions
|
||||
|
||||
- **`cycles-converters` r1** edits the same seams:
|
||||
- the include list (`CultMath.hlsl:509-510`);
|
||||
- `HlslMirrorTests.cs:64-67`;
|
||||
- `build-unity-package.ps1:82-87`;
|
||||
- the fixture's family list.
|
||||
|
||||
Whichever merges second rebases, regenerates `CultMath.glsl` and the fixture
|
||||
(`CULTMATH_WRITE_GLSL=1`), and appends its families after the first's.
|
||||
Nothing semantic collides.
|
||||
- **`expr-ir` r1 and question `material-ir-in-cultmath`.** No file collides,
|
||||
and neither cut waits on the other.
|
||||
- Under `cultmath-ir-now`, the parked tape's step 2 will add interval and
|
||||
affine evaluators over `ExprGraph`, and those will call `iv_*` and `af_*`
|
||||
as op semantics. That is one more reason the affine ops are plain value
|
||||
functions over `float3`, with no struct state.
|
||||
- Under `aetheria-local`, nothing here changes.
|
||||
- The `expr-ir` spec's negative check forbids `Interval(Evaluator)?` under
|
||||
`Expr`. Self may add `Affine` to it at its next revision.
|
||||
- **`site-ground` r3** (not dispatched): the void (e) and the site's flow
|
||||
texture are not measured here. Follow-up `site-ground-affine-and-centred-warp`
|
||||
carries both. Centring the warp in the site needs a bound on the flow
|
||||
texture's variation, and the texture stays the one motion owner (it is
|
||||
sampled at the centre, never re-derived).
|
||||
|
|
|
|||
|
|
@ -108,6 +108,9 @@ source.
|
|||
- Run the (a) spike first, or go straight to (c)?
|
||||
- Pick the interval representation (plain intervals or affine arithmetic) and
|
||||
the noise bound strategy.
|
||||
Affine forms for noise and warp were unparked on 2026-10-03 (ruling
|
||||
`cultmath-tapes:ruling:affine-forms-unparked`): one shared symbol, mapped in
|
||||
`docs/cultmath-interval-ground-cut.md`, "Pass 5".
|
||||
- Choose the bytecode register model for the HLSL interpreter, given GPU
|
||||
register pressure.
|
||||
- Decide whether the brush-grammar station project lives in Aetheria or in
|
||||
|
|
|
|||
Loading…
Reference in a new issue