Files
smoothlife/plans/00_model_contract_and_historical_options.md
Federico Pasqua 51cff7b0f3 Add detailed phase plans for foundational macrosteps
- Macrostep 00: Model contract and retained historical options.
- Macrostep 01: Project foundation and configuration, including schema, presets, CLI, and CI setup.
- Macrostep 02: Mathematical core implementation and deterministic oracles.
- Macrostep 03: CPU-based planar simulation engine, FFT backend, and headless runner.

Provides exhaustive objectives, phase breakdowns, validation policies, and deliverables for each macrostep.
2026-07-14 16:50:54 +02:00

13 KiB
Raw Blame History

Macrostep 00 — Model contract and retained historical options

Objective

Create the permanent in-repository source of truth for the rewrite. Freeze the new deterministic behavior and exactly three retained historical options: RK4 relaxation reference, packed-unitary FFT, and sphere model. After this macrostep, normal implementation work must not depend on repeatedly reading /home/fpasqua/Nextcloud/VecchiProgetti/SmoothLifeAll.

Dependencies

None.

Required outputs

  • docs/model-contract.md: exact mathematics and update semantics.
  • docs/historical-options.md: the three retained options, their exact semantics, constraints, and rejected historical behavior.
  • docs/legacy-source-map.md: provenance with source file, line range, and source-file hash.
  • tests/fixtures/legacy/preset_rows.jsonl: schema-neutral bootstrap capture of every accepted legacy row.
  • tests/fixtures/contract/: small deterministic scalar/field fixtures.

Phase 0.1 — Freeze the shared mathematical contract

Substep 0.1.1 — State, topology, and indexing

Record these invariants verbatim in docs/model-contract.md:

  • State is a scalar A ∈ [0,1] after every committed update.
  • Base domains are periodic: a 1-D circle, 2-D torus, or 3-D torus.
  • Storage is x-fast row-major. Define and test one canonical index mapping.
  • Signed periodic offset for even extent N is i for i < N/2, otherwise i-N.
  • Sphere is a closed spherical surface; delayed time is a 2-D spatial torus with circular temporal history.

Substep 0.1.2 — Sampled kernels

Freeze the exact base construction:

ri = ra / rr
w  = ra / rb
L(r;a,w) = 0                         when r < a-w/2
           1                         when r > a+w/2
           (r-a)/w + 1/2             otherwise
KD(r) = 1 - L(r;ri,w)
KR(r) = L(r;ri,w) * (1-L(r;ra,w))
M = circular_convolution(A,KD) / sum(KD)
N = circular_convolution(A,KR) / sum(KR)

Document that weights are sampled at lattice points. Generate the complete nonzero support and warn when radii are unsuitable for the domain. The historical component cutoff is not retained.

Record the known 2-D check for ra=10, rr=3, rb=10:

sum(KR) ≈ 279.216312
sum(KD) ≈  35.524035

Substep 0.1.3 — All rule curves

Use conventional TAU = 2π; the legacy shader variable called pi actually stores tau.

Freeze rising curves 07:

0 hard:       x >= a ? 1 : 0
1 linear:     compact linear transition over a±e/2
2 Hermite:    u²(3-2u), u=(x-a+e/2)/e, compactly clamped
3 sine:       0.5*sin((TAU/2)*(x-a)/e)+0.5, compactly clamped
4 logistic:   1/(1+exp(-4(x-a)/e))
5 atan:       atan((x-a)*(TAU/2)/e)/(TAU/2)+0.5
6 atan/cos:   (1.1*atan((x-a)/e)/(TAU/4)*cos(1.4(x-a))+1)/2
7 overshoot:  (logistic-0.5)*(1+exp(-(x-a)²/e²))+0.5

For types 07, W(n;a,b)=P(n,a,sn)*(1-P(n,b,sn)). Hard windows are exactly [a,b). Types 8 and 9 are complete windows:

base = logistic(n,a,sn) * (1-logistic(n,b,sn))
mid  = (a+b)/2
type 8 = base*(1-0.2*exp(-(20*(n-mid))²))
type 9 = base*(1+0.2*exp(-(20*(n-mid))²))

The mixer uses only curves 07 at center 0.5 and width sm. Curves 6/7 and resulting targets may overshoot; clamp only at the integration/commit points specified below.

Substep 0.1.4 — Four rule constructions

Freeze:

B = W(N;b1,b2)
D = W(N;d1,d2)
Q = selected mixer at M
1: S = mix(B,D,M)
2: S = mix(B,D,Q)
3: S = W(N, mix(b1,d1,M), mix(b2,d2,M))
4: S = W(N, mix(b1,d1,Q), mix(b2,d2,Q))

Phase 0.2 — Freeze dynamics and integration

Substep 0.2.1 — Base dynamics

mode 0 / Discrete:   A' = clamp(S)
mode 1 / Growth:     dA/dt = 2S-1
mode 2 / Relaxation: dA/dt = S-A

The base discrete path ignores dt and selected integrator. Modes 3/4 found in old keyboard code are experimental and excluded from the supported contract unless later added with explicit names.

Substep 0.2.2 — Integrators and clamp points

  • Euler: clamp(A + dt*k1).
  • AB3: deterministic Euler first step, AB2 second step, AB3 thereafter: (23k_n-16k_{n-1}+5k_{n-2})/12.
  • RK4: clamp every intermediate state and the final state.
  • Rk4RelaxationReference::StageState is the default: every relaxation stage uses S(stage)-stage.
  • Rk4RelaxationReference::StepOrigin is the retained historical option: k1=S(A0)-A0; intermediate k2..k4 compute neighborhoods/targets from their clamped stage but subtract A0.
  • The RK4 reference field exists structurally only inside relaxation+RK4 configuration. It is absent for discrete, growth, Euler, and AB3 rather than carried and ignored.
  • Preserve AB history only for presentation, inspection, pause, and scheduler changes.
  • Invalidate it for reset, ordinary state load, initializer/seed, rule, dynamics, timestep, integrator, kernel geometry, historical-option selection, topology, shape, variant, or backend changes. Switching away from and later back to AB3 never revives old derivatives.
  • An exact continuation checkpoint may restore AB history only when its complete validated model/backend-independent arithmetic descriptor matches.

Undefined AB history is discarded. This list is the authoritative history-invalidation policy used by every later macrostep.

Substep 0.2.3 — FFT algorithms

Freeze one local FftAlgorithm option for planar and multiscale convolution:

  • Standard (default): conventional unscaled forward transform, inverse scaled by 1/sample_count, then sampled-kernel normalization.
  • LegacyPackedUnitary: power-of-two 1-D/2-D/3-D GPU path retaining adjacent-real packing, half-width x complex spectra, historical bit-reversal/twiddle plan stages, unitary butterfly scaling, real/complex conversion, and spectral correction sqrt(sample_count)/kernel_sum.

The legacy FFT is a complete algorithm backend, not a global compatibility mode. It uses the same complete kernels and safe ping-pong resource layer as the new implementation. Old OpenGL compatibility syntax, working-directory shaders, framebuffer feedback, and resource behavior are discarded. An explicit legacy FFT request on CPU or unsupported GPU hardware fails clearly; it never silently substitutes Standard. A small CPU stage oracle and fixtures validate its packing, plans, scaling, spectra, and convolution.

Phase 0.3 — Freeze variant contracts

Substep 0.3.1 — Multiscale

Record all six combinations in a 2×3 matrix:

  • Independent inputs: each scale receives (ring_i(A), disk_i(A)).
  • Chained inputs: (ring_0,ring_1), (ring_1,ring_2), (ring_2,disk_2).
  • Sequential: update and clamp between scales, recomputing from the current state.
  • Ordered clamped sum: evaluate all responses from one snapshot, add/clamp each in scale order.
  • Mean increment: evaluate from one snapshot, add the arithmetic mean once, then clamp.

The new implementation supports growth and corrected relaxation. It rejects discrete dynamics because the three composition names do not define an unambiguous target aggregator; adding it later requires an explicit target-aggregation enum and fixtures.

Sequential composition always recomputes every required input from the current state. Shared-snapshot methods always use one original state. Relaxation names that current or original reference state explicitly. Additive mode-0 responses, stale chained fields, texture feedback, and undefined mode-2 sources are discarded.

Substep 0.3.2 — Sphere

Freeze:

  • six K×K active faces, legacy default K=128, internal radius R=K/2;
  • direction map normalize(face_normal + tan(uπ/4)face_u + tan(vπ/4)face_v);
  • area-weighted sample accumulation;
  • distance R*acos(clamp(dot(a,b),-1,1));
  • ri=ra/3, transition widths 1;
  • planar-to-geodesic conversion R*acos(1-ra²/(2R²));
  • spherical cap area 2πR²(1-cos(r/R));
  • direct replacement and fixed A+0.1(2S-1) modes.

Freeze one local SphereModel option:

  • Corrected (default): complete edge/corner mapping and per-center normalization by the actual sum of cell_area*kernel_weight, preserving a constant field.
  • Legacy: original six-face atlas geometry, side gutters with masked corner sectors, analytic disk normalization cap(ri), analytic ring normalization cap(ra)-cap(ri), original radius conversion/search stencil, and original discrete/fixed-0.1 smooth dynamics.

Both models use clamped dot products, explicit initialization, and ping-pong writes. Unclamped acos, texture feedback, and undefined atlas contents are discarded.

Substep 0.3.3 — Delayed time

Freeze:

  • periodic x/y field and history depth 16;
  • ri=ra/3, both transition widths 1, sampled stencil normalization;
  • head is the next layer to overwrite and latest=wrap(head-1,depth);
  • causal nearest-layer selection is wrap(latest-floor(distance+0.5),depth), so delay zero reads the latest committed state;
  • direct replacement or latest+0.1(2S-1), then clamp.

The historical head anomaly is discarded and has no configuration option.

Phase 0.4 — Preset and fixture capture

Substep 0.4.1 — Schema-neutral bootstrap capture

Before the Rust schema exists, use a tiny reviewed extraction script to copy accepted source rows verbatim into JSONL with source path, line number, parsed columns, description, and source hash. This is evidence capture, not the final application importer. Expected counts are:

  • main catalogue: 188 valid rows;
  • SDL catalogue: 187 rows, largely duplicate and containing drift;
  • multiscale catalogue: 12 rows = 4 triplets;
  • sphere catalogue: 1 row;
  • DT catalogue: 29 rows.

Do not deduplicate the evidence and do not run legacy executables. Macrostep 01 converts this frozen capture into the versioned schema and performs deliberate deduplication.

Substep 0.4.2 — Freeze migration defaults absent from rows

Commit a mapping table so every imported row becomes a complete run:

  • Base: shape 1024/512²/64³ by dimension, Euler, standard FFT, seed 1, cleared C++-inspired periodic splats, palette 2, and 3-D volume style 2. Because Euler is selected, no RK4-reference field is stored; creating relaxation+RK4 later defaults that newly applicable field to StageState.
  • SDL rows: same model mapping, source-tagged and deduplicated against main rows; no separate backend.
  • Multiscale: 512², sequential composition, independent kernels, growth/relaxation only, standard FFT, seed 1, cleared base-style splats, and palette 7.
  • Sphere: K=128, R=K/2, seed 1, cleared then seeded overlays, and palette 1. Generate a corrected-default preset and a separately named SphereModel::Legacy preset from the historical row.
  • DT: no historical fixed shape existed because it followed the window; choose and record 512², depth 16, causal delay, seed 1, a seeded box field replicated into all layers, and palette 7. Record this initializer/shape as an intentional modern replacement.

The table must distinguish source facts from chosen deterministic replacements.

Substep 0.4.3 — Freeze initializer algorithms

  • Base splats: clear to zero; choose continuous uniform centers; radius uniform in [0.5ra,ra); count floor(domain_volume / product(min(2ra,axis_extent)))+1; paint periodic interval/disk/ball samples to one. Use ChaCha with a specified draw order.
  • Sphere overlays: clear first; perform 1,000 draws with random face/center, radius 27, and random binary value.
  • DT boxes: build one seeded field from 1,000 rectangles with width and height independently 1019, then replicate it to all history layers. Use one documented boundary policy; do not retain gradual filling or undefined layers.

Commit exact seeded fixture arrays so later PRNG upgrades cannot silently change tests.

Substep 0.4.4 — Golden fixtures

Commit compact fixtures for:

  • scalar curve boundaries and overshoot;
  • a rule-surface grid spanning all valid enum combinations;
  • small asymmetric 1-D/2-D/3-D fields;
  • raw and normalized kernels;
  • direct convolution outputs plus legacy packed plans/stages/unitary scaling;
  • Euler/AB/RK synthetic derivatives, including both RK4 relaxation references;
  • all six corrected multiscale combinations;
  • source-frozen corrected/legacy sphere M/N/S/next fields at face centers, edges, and masked corners for both update modes, in addition to face transforms, normalization, and area totals;
  • DT layer-coded history and radial delay shells.

Use explicit arrays for essential goldens, not only PRNG seeds.

Phase 0.5 — Source retirement protocol

  1. Hash every consulted legacy source/config file and store the hashes in docs/legacy-source-map.md.
  2. Record source paths and relevant line ranges.
  3. Mark the specification plus the new contract as the implementation authority.
  4. Future legacy consultation requires a concrete discrepancy, and its finding must be copied back into the contract with provenance.
  5. The application, build, tests, and importer outputs must not require the legacy path.

Exit gate

  • Every formula, constant, mode, topology, retained historical option, and discarded defect has a local documented decision.
  • Preset counts/grouping reconcile with the source catalogues.
  • Fixtures are readable without legacy tools.
  • A new implementer can build any mandatory backend using only this repository.