Fauna r4: body-Voronoi probe (Skein drawn as a diagram over the bodies, per metric); map B37-B42

Imagination probe of the operator's sketch: the bodies as Voronoi sites, the distance metric varied
(power, additive, multiplicative, equal potential, flow-stretched, Minkowski), the lattice web pulled
toward the edges. Images, logs and scripts in docs/research/2026-10-14-body-voronoi-probe/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-14 06:46:26 +02:00
commit 05a445418c
33 changed files with 428 additions and 0 deletions

View file

@ -406,6 +406,12 @@ the lore branch `eureka/aetheria-fauna-lore` `74c7e212`, note `Open Water` (`OW`
| B34 | Do the candidates pop under the krill carriage? (`run1.log`, `win.log`, `cyc.log`; fade .1, 60 steps of 1/60 s from t = 100, flow read at the member's cycle start, cycle turns excluded) | Largest presence step: band .0035, slack .013, skein .054, lattice .124 (8 steps over .1), skein-web .493 (8 steps). The lattice steps are not discontinuities: a traced member's web value rises .03 per step for 5 steps, a thin weighted border sweeping a fixed candidate. Widening the search window to 7x7 changes nothing; lowering the weight boost to .6 removes the lattice steps (.047); reading the lattice drift at the member's cycle start, as the flow is read, removes them at full boost: lattice .011, skein-web .066, 0 steps over .1 (`cyc.log`). |
| B35 | How fast do hosted sets turn over? | Members changed in 1 s, all terms / flow frozen at t's cycle (wells only): band .032 / .032; skein .244 / .106; lattice .594 / .449 (.262 with the drift at cycle start); skein-web .305 / .186 (.216 with the drift at cycle start); slack .089 / .089. The strands ride their wells at about 6 u/s across 16 u candidates, so a tenth of strand members change each second through the fade: motion, not popping (B34). |
| B36 | What does a stencil predator cost? (Python, one evaluation) | krill .26 ms, minnows 3.19 ms (9 krill), sharks 29.3 ms (81 krill): each ring-stencil level multiplies the cost by 9. Hosted: minnows 274 of 5,432 candidates at 48 u; sharks 19 of 755 at 128 u; lanterns 36 of 3,063 at 64 u. |
| B37 | What does a diagram over the bodies draw, per metric? (`docs/research/2026-10-14-body-voronoi-probe/`, README there; sites = the six bodies, weight = each well's Gaussian width `sigma = r / sqrt(128)`, edge = first-order border distance through a 40 u Gaussian, warped as the strands) | `power` (`d^2 - sigma^2`): long, nearly straight edges spanning the zone between the bodies, a triangle of filaments (`edges_power.png`). `add` (`d - sigma`): long hyperbolic arcs, two near-parallel bars north of the sun (`edges_add.png`). `mult` (`d / sigma`): rings round the small bodies only; the sun's cell covers most of the zone (sigma ratio up to 4.7:1; the ring round a 274-mass planet is about 264 u) (`edges_mult.png`). `pot-newton` (equal `m/d`, i.e. multiplicative with weight = mass, CultLib `tape-register-model.md` 5a): rings of about 12 u, the sun swallows the zone. `pot-well` (equal depth on the zone's real well profile): rings like `mult`, plus dotted arcs where each well's compact support ends (r/2). `flow-aniso` (`mult`, across-flow distance 2.5x): edges shattered into short fragments, because the metric turns with the flow on the flow's 512 u scale. `lp05`: axis-aligned bars across the zone; `lpinf`: square cells round the small bodies. Correlation with the B20 Physarum map: mult-strands .388, power-strands .371, mult .335, flow-aniso .322, power .313, skein-web .296, add .263. |
| B38 | Do they pop as the bodies orbit? (16 u krill, fade .1, 60 steps of 1/60 s from t = 100, flow warp and web drift at the member's cycle start, bodies now; `run2.log`, `run3.log`) | Worst presence step / members changed in 1 s / hosted: skein-web .014 / .205 / 1,501; power .029 / .312 / 3,817; power-strands .029 / .269 / 4,965; flow-aniso .037 / .300 / 4,090; pot-newton .048 / .299 / 689; add .090 / .338 / 4,077; mult .091 / .279 / 3,270; mult-strands .091 / .238 / 4,462; pot-well, lp05, lpinf 1.0 (1, 72 and 237 steps over .1). `mult`'s .091 is the web, not the edge: without the cell boost .023, without the alignment .055 (`diag2.log`); the cell weights are read from the moving edges now. |
| B39 | Why did B34's reading pop more? (`run1.log`, `diag_edges.log`, `trace.py`) | `trophic_probe.py` reads only the flow's scroll at the member's cycle start; the slope terms (swirl 6, slide 1.5) read the wells now. The slope terms use the normalised slope direction, which turns quickly on flat ground as the wells move, and the 20 s warp is a lever on it: at a candidate on slope .019 the warped point moved 1.4 u per 1/60 s. With the whole warp at cycle start, the edges alone drop from .152 to .023 (mult) and .126 to .029 (power), and the ruled skein-web from .066 to .014. B34's skein-web number was this reading's. |
| B40 | Where does the first-order border distance break? | Where the metric's gradient is singular or jumps: `lp05` (singular on the axes through each site), `lpinf` (jumps on the diagonals), `pot-well` (unbounded at the support rim): presence steps of 1.0. Also where the second-nearest site changes, the normaliser `|grad(F2 - F1)|` jumps: invisible inside the 40 u edge, visible in a 4x halo (power's web with no cell boost: .197-.301, `diag2.log`). A raw `F2 - F1` profile has no such jump but its width varies with the weights. |
| B41 | Does the 5x5 lattice window hold the body-pulled weights? (`diag2.log`, 3,385 points) | Cell weights span .5-2.6 (5.2:1), and the alignment stretches across-edge distance up to 2x more. 5x5 against 7x7: mult differs at 1 point by .28; power and add at none. CultLib `lattice-site-weight.md` (`21281a39`) finds the window must grow with the weight ratio (5x5 off by .017 at about 12:1); the alignment is a query-point anisotropy, which does change winners, unlike a query-point scalar weight. |
| B42 | What does one evaluation visit? | Every variant is pointwise over explicit sites; none needs a grid. Large layer: the 6 bodies (the two winners' gradient is analytic). Web: 25 cells, each reading the large layer at its centre (25 x 6), so 181 sites per evaluation; with strands 193 (6 soft-Gabriel edges, strand and halo); skein-web 187. Python: .36-.74 ms per evaluation. CultMath: `mult` and `power` are gap (2) as mapped; `add`, Minkowski and `flow-aniso` extend gap (2) with a metric parameter, `flow-aniso` also with a direction operand; `pot-well` is the zone's well profile as a per-site score, an Aetheria function, not a CultMath metric. The web's per-cell read of the large layer is the cell-centre weight placement of `cultmath-tapes:question:lattice-site-weight-source`. |
### Decisions made here, not forks
@ -450,6 +456,8 @@ the lore branch `eureka/aetheria-fauna-lore` `74c7e212`, note `Open Water` (`OW`
`aetheria-fauna:question:skein-filament-construction` (which construction is the Skein; images per option in
`docs/research/2026-10-14-trophic-dag-probe/`) and `aetheria-fauna:question:trophic-graph-tape` (hand-written CultMath
compositions now and the tape later, or unpark the tape for this).
`aetheria-fauna:question:body-diagram-metric` (which metric draws the large-scale Skein from the bodies, after the
operator's sketch; images per option in `docs/research/2026-10-14-body-voronoi-probe/`, facts B37-B42).
### Cut sequence (r4, one line each; each is mapped when it is about to run; r3 lines 4-25 stand after line 7)

View file

@ -0,0 +1,54 @@
# Body-Voronoi probe (2026-10-14)
Serves the operator's sketch after `aetheria-fauna:ruling:skein-filament-construction-ruled`: "I was imagining the
voronoi cells to be the gravity objects themselves, aren't there a bunch of fun ways to play with the distance
metrics to get filaments that way too?" Facts are map rows B37-B42 in `docs/aetheria-fauna-map.md`. Prior art:
CultLib `docs/research/tape-register-model.md` section 5 (`dfc720e7`) and `docs/research/lattice-site-weight.md`
(`21281a39`).
`body_voronoi_probe.py` imports `trophic_probe.py` (`../2026-10-14-trophic-dag-probe/`: orbiting wells, slope terms
on, the flow warp, the lattice web, the members and pop probes) and replaces only the large-scale filament layer.
Same zone, same frame (t = 0), same 16 u krill candidates and thresholds, same fade .1.
**The large layer.** A diagram over the zone's six bodies (sites = bodies, read now), drawn as an edge profile: the
distance to the border to first order, `(F2 - F1) / |grad(F2 - F1)|`, through a Gaussian of half-width 40 u. Each
body's weight is its well's Gaussian width `sigma = r / sqrt(128)` (the zone's `pp(d/r)^16` term is about
`exp(-d^2 / 2 sigma^2)`): sun 482 u, planets 180, 180, 146, 103, gas giant 305. Bent by the same flow warp as the
strands (20 s).
| Variant | Metric `d_i` |
|---|---|
| `pot-well` | Equal well depth on the zone's real profile: `-ln(depth_i * pp(d/r_i)^16)` |
| `pot-newton` | Equal point-mass potential `m/d`: multiplicative with weight = mass (section 5a's identity) |
| `mult` | `d / sigma_i`: Apollonius circles, her shader's trick |
| `add` | `d - sigma_i`: hyperbola arcs |
| `power` | `d^2 - sigma_i^2`: straight edges, the adhesion model's tiling |
| `flow-aniso` | `mult` with the across-flow component stretched 2.5x; the flow direction is the warp's own |
| `lp05`, `lpinf` | `mult` in the Minkowski p = .5 and p = inf (Chebyshev) norms, axis-aligned |
| `skein-web` | Control: the ruled Skein (`trophic_probe.py`), strands plus web |
| `mult-strands`, `power-strands` | The diagram's edges and the strands together, with the web |
**The web.** `trophic_probe.lattice_web` with two changes: each lattice cell's hash weight is raised by up to 1.6x by
the large-edge profile (3x wider) at the cell centre, `(cx + .5) * LAT` (cell-centre weight in
`lattice-site-weight.md`'s terms; `trophic_probe.py`'s well proximity is also read at the cell centre, not at the
jittered site), and the cell metric is stretched up to 2x across the large edge's normal at the query point, so cells
shrink and align along the edges. The web hangs within 4 edge widths of a large edge, or on the band.
Images (zone 4,000 u across, red = bodies): `density_<variant>.png` (krill habitat density, t = 0),
`members_<variant>.png` (hosted krill candidates), `edges_<variant>.png` (the large layer alone, no web; also for the
pruned variants).
Logs:
- `run1.log`: all variants as `trophic_probe.py` reads the flow (scroll at the member's cycle start, the slope terms
with the wells now). `run2.log` and `run3.log` (`power-strands`): the whole warp at cycle start, slope terms
included (`WARP_AT_CYCLE=1`). The member images here are run 2 and 3's.
- `diag_edges.log`: the large layer alone, run-1 reading. `trace.py`: the worst step traced to the warped point
moving 1.4 u per 1/60 s.
- `diag2.log` (run-2 reading): web ablation (boost, align) and the lattice window, 5x5 against 7x7.
To rerun: copy this folder, `../2026-10-14-trophic-dag-probe/trophic_probe.py` and
`../2026-10-14-fauna-habitat-probe/habitat_probe.py` into one scratch directory, then
`py -3 -u body_voronoi_probe.py 256` (about 15 minutes), the same with `WARP_AT_CYCLE=1` (writes into `cyc/`),
`py -3 -u diag.py` with `DIAG=edges`, and `WARP_AT_CYCLE=1 py -3 -u diag2.py`. `habitat_probe.py` reads the B20 map
from `C:\aeth-wt\fauna-target-r3\docs\research\2026-10-13-fauna-placement-probe\output.txt`. Python 3 only, no
packages. Hashes are the probe's own integer hash, not CultMath `pcg3d`; the images are qualitative evidence.

View file

@ -0,0 +1,177 @@
"""Body-Voronoi probe (Imagination, aetheria-fauna r4). Pure Python 3, no packages.
The operator's sketch after skein-web was ruled: the Voronoi cells are the gravity bodies themselves, and the
distance metric is the place to play. The large-scale filament layer here is a diagram over the zone's bodies
(sites = bodies, orbiting), drawn as an F2 - F1 edge profile: distance to the border to first order,
(F2 - F1) / |grad(F2 - F1)| through a Gaussian of width EW_U. The lattice web of trophic_probe.py hangs off it, its
per-site weight boosted by the large edge's profile at each lattice site (cells shrink near the edges) and its
metric stretched across the edge normal at the query point (cells align along the edges).
Reuses trophic_probe.py (orbiting wells, slope terms on, the flow warp, the lattice web, members and pop probes) and
habitat_probe.py (Zone.GetHeight/GetNormal and FlowField transliterations). Hashes are the probe's own.
py -3 -u body_voronoi_probe.py [grid=256] [only=comma,list]
"""
import sys, os, math, time
ARGS = sys.argv[1:]
sys.argv = [sys.argv[0], "256"]
import trophic_probe as T
H = T.H
OUT = os.path.dirname(os.path.abspath(__file__))
G = int(ARGS[0]) if ARGS else 256
ONLY = ARGS[1].split(",") if len(ARGS) > 1 else None
T.WEB_AT_CYCLE = True # ruled with skein-web: flow and web drift read at the member's cycle start
EW_U = 40.0 # large edge half-width (u)
HALO = 4.0 # web hangs within this many edge widths of a large edge (or on the band)
ANISO = 2.5 # flow-stretched metric: distance across the flow counts this many times more
ALIGN = 1.0 # lattice cells: across-edge component stretched by up to 1 + ALIGN near a large edge
VISITS = [0] # sites visited per evaluation (the CultMath cost measure)
# sigma: each well's Gaussian width. Zone's term pp(d/r)^16 = (1 - 4 d^2/r^2)^16 ~ exp(-64 d^2/r^2) = exp(-d^2 / 2 s^2)
SIG = [w["r"] / math.sqrt(128.0) for w in H.WELLS]
MASS = [b[1] for b in H.BODIES]
# ---------------- per-site metrics: d_i(q, flow direction) ----------------
def m_mult(i, dx, dz, fd): VISITS[0] += 1; return math.hypot(dx, dz) / SIG[i]
def m_add(i, dx, dz, fd): VISITS[0] += 1; return math.hypot(dx, dz) - SIG[i]
def m_power(i, dx, dz, fd): VISITS[0] += 1; return dx * dx + dz * dz - SIG[i] ** 2
def m_newton(i, dx, dz, fd): VISITS[0] += 1; return math.hypot(dx, dz) / MASS[i] # potential m/d equal: w = mass
def m_well(i, dx, dz, fd):
"""Equal well depth on the zone's real profile: depth_i * pp(d/r_i)^16 equal (its log, so smaller is deeper)."""
VISITS[0] += 1; w = H.WELLS[i]; u = 4 * (dx * dx + dz * dz) / w["r"] ** 2
return 1e9 if u >= 1 else -(math.log(w["depth"]) + w["exp"] * math.log(1 - u))
def m_aniso(i, dx, dz, fd):
VISITS[0] += 1; a = dx * fd[0] + dz * fd[1]; c = -dx * fd[1] + dz * fd[0]
return math.sqrt(a * a + (ANISO * c) ** 2) / SIG[i]
def m_lp(p):
def f(i, dx, dz, fd):
VISITS[0] += 1
if p == math.inf: return max(abs(dx), abs(dz)) / SIG[i]
return (abs(dx) ** p + abs(dz) ** p) ** (1 / p) / SIG[i]
return f
def body_border(q, metric, fd=(1.0, 0.0)):
"""Distance (u) from q to the nearest border of the body diagram under `metric`, first order, and the border's
unit normal (grad of F2 - F1). Visits every body once; the gradient re-evaluates only the two winners."""
ds = sorted((metric(i, q[0] - w["pos"][0], q[1] - w["pos"][1], fd), i) for i, w in enumerate(H.WELLS))
(d1, i1), (d2, i2) = ds[0], ds[1]
if d2 >= 1e8: return 1e9, (1.0, 0.0)
def g(x, z):
a, b = H.WELLS[i1]["pos"], H.WELLS[i2]["pos"]
return metric(i2, x - b[0], z - b[1], fd) - metric(i1, x - a[0], z - a[1], fd)
e = 1.0; v = VISITS[0]
gx = (g(q[0] + e, q[1]) - g(q[0] - e, q[1])) / (2 * e); gz = (g(q[0], q[1] + e) - g(q[0], q[1] - e)) / (2 * e)
VISITS[0] = v # the gradient is analytic in C#; the probe's finite difference is not counted
gl = math.hypot(gx, gz) or 1e-9
return (d2 - d1) / gl, (gx / gl, gz / gl)
def edge_profile(dist, widen=1.0): return math.exp(-(dist / (EW_U * widen)) ** 2)
# ---------------- the lattice web, weights pulled toward the large edges ----------------
SITE_W = {} # per tick: lattice site -> large-edge profile at its centre (cleared by set_time)
_set_time = T.set_time
def set_time(t): SITE_W.clear(); _set_time(t)
T.set_time = set_time
def lattice_web_edges(q, t, metric, near, nrm):
"""trophic_probe.lattice_web with the well-proximity boost replaced by the large-edge profile at each lattice
site, and the cell metric stretched by (1 + ALIGN * near) across the large edge's normal at the query point."""
if T.CUR_T0[0] is not None: t = T.CUR_T0[0]
LAT = T.LAT; px, pz = q[0] / LAT, q[1] / LAT; nx, nz = math.floor(px), math.floor(pz); fx, fz = px - nx, pz - nz
st = 1.0 + ALIGN * near; m = 8.0; f1 = 8.0; f2 = 8.0; KW = T.KW
for j in range(-T.WIN, T.WIN + 1):
for i in range(-T.WIN, T.WIN + 1):
cx, cz = nx + i, nz + j; VISITS[0] += 1
ox, oz = H.hash01(cx, cz, 11), H.hash01(cx, cz, 12); wt = ox * .5 + .5
key = (cx, cz, metric)
if key not in SITE_W:
SITE_W[key] = edge_profile(body_border(((cx + .5) * LAT, (cz + .5) * LAT), metric)[0], 3.0)
else: VISITS[0] += len(H.WELLS) # count the per-site evaluation even when the probe memoises it
wt *= 1.0 + T.BOOST * SITE_W[key]
ox = .5 + .5 * math.sin(T.OMEGA * t + 6.2831 * ox); oz = .5 + .5 * math.sin(T.OMEGA * t + 6.2831 * oz)
vx, vz = i - fx + ox, j - fz + oz
a = vx * nrm[0] + vz * nrm[1]; c = -vx * nrm[1] + vz * nrm[0]
d = math.hypot(st * a, c) * wt
if d < f1: f1, f2 = d, f1
elif d < f2: f2 = d
h = T.smooth(0.0, 1.0, .5 + .5 * (m - d) / KW)
m = m + (d - m) * h - h * (1 - h) * KW / (1 + 3 * KW)
gap0 = .25 * KW / (1 + 3 * KW)
soft = max(0.0, min(1.0, (f1 - m) / gap0))
return max(soft ** 2, math.exp(-((f2 - f1) / T.EW) ** 2))
WARP_AT_CYCLE = os.environ.get("WARP_AT_CYCLE") == "1"
_warp = T.warp
if WARP_AT_CYCLE: OUT = os.path.join(OUT, "cyc"); os.makedirs(OUT, exist_ok=True)
def warp(x, z, t, sc, tau=None):
"""WARP_AT_CYCLE=1: the whole flow warp at the member's cycle start, slope terms included (the wells at t0).
trophic_probe reads only the scroll at cycle start; its slope terms read the wells now (B34's runs)."""
t0 = T.CUR_T0[0]
if not WARP_AT_CYCLE or t0 is None: return _warp(x, z, t, sc, tau)
keep = [w["pos"] for w in H.WELLS]
for (kind, m, d, ph), w in zip(H.BODIES, H.WELLS):
a = ph + (2 * math.pi * t0 / d if d > 0 else 0.0); w["pos"] = (math.cos(a) * d, math.sin(a) * d)
try: return _warp(x, z, t0, sc, tau)
finally:
for w, p in zip(H.WELLS, keep): w["pos"] = p
T.warp = warp
def flow_dir_of(x, z, q):
"""The flow direction the warp used: u = (x - q) / TAU, so it is read when the warp is."""
ux, uz = x - q[0], z - q[1]; l = math.hypot(ux, uz) or 1e-9; return ux / l, uz / l
def flow_dir(x, z, t, sc):
ux, uz = T.flow_at(x, z, t, sc); l = math.hypot(ux, uz) or 1e-9; return ux / l, uz / l
def body_web(metric, strands=False):
def D(x, z, t, sc):
s, b, c = T.env(x, z, t); q = T.warp(x, z, t, sc)
fd = flow_dir_of(x, z, q) if metric is m_aniso else (1.0, 0.0)
dist, nrm = body_border(q, metric, fd); e = edge_profile(dist); halo = edge_profile(dist, HALO)
big = e; near = halo
if strands:
st = T.strands(q); sh = T.strands(q, widen=4.0); VISITS[0] += 2 * len(T.pairs(False))
big = max(e, st); near = max(halo, sh)
web = lattice_web_edges(T.warp(x, z, t, sc, T.WEB_TAU), t, metric, halo, nrm) * max(near, b)
return max(big, .8 * web) * (1 - c)
return D
def skein_web_counted(x, z, t, sc):
VISITS[0] += 2 * len(T.pairs(False)) + 25 * (1 + len(H.WELLS)); return T.D_skein_web(x, z, t, sc)
VARIANTS = {
"pot-well": body_web(m_well), "pot-newton": body_web(m_newton), "mult": body_web(m_mult),
"add": body_web(m_add), "power": body_web(m_power), "flow-aniso": body_web(m_aniso),
"lp05": body_web(m_lp(.5)), "lpinf": body_web(m_lp(math.inf)),
"skein-web": skein_web_counted, "mult-strands": body_web(m_mult, strands=True),
"power-strands": body_web(m_power, strands=True),
}
def run(name, fn, cands, t=0.0):
t1 = time.time(); vals = T.grid(fn, t, G); T.save(vals, os.path.join(OUT, "density_" + name), G)
cr = T.phys_corr(vals, G)
a = T.members(fn, cands, t); b = T.members(fn, cands, t + 1); n = max(1, len(a))
W2 = 600; px = [(10, 14, 30)] * (W2 * W2)
for c in cands:
if (c[0], c[1]) in a:
i, j = int((c[2] / (2 * H.R) + .5) * W2), int((c[3] / (2 * H.R) + .5) * W2)
if 0 <= i < W2 and 0 <= j < W2: px[j * W2 + i] = (235, 230, 170)
H.mark_bodies(px, W2); H.write_png(os.path.join(OUT, "members_" + name + ".png"), W2, W2, px)
worst, big, nn = T.pop_probe(fn, cands, 100.0, 1500)
T.set_time(0.0); sc = H.scroll(0.0); VISITS[0] = 0; SITE_W.clear(); k = 40
c0 = time.perf_counter()
for m in range(k): SITE_W.clear(); fn(300.0 + 7 * m, 200.0 - 5 * m, 0.0, sc)
ms = (time.perf_counter() - c0) / k * 1e3
print(f"{name}: corr physarumA {cr:.3f}; members {len(a)}; changed in 1 s {len(a ^ b) / n:.3f}; "
f"presence step max {worst:.4f} ({big} steps > .1 of {nn}); sites visited per evaluation {VISITS[0] / k:.0f}; "
f"{ms:.2f} ms/eval (Python); {time.time() - t1:.0f}s", flush=True)
def main():
print(f"warp at cycle start {WARP_AT_CYCLE}; grid {G}; sigma {[round(s) for s in SIG]}; edge width {EW_U} u; web at cycle start {T.WEB_AT_CYCLE}", flush=True)
cands = T.cand_list(16.0)
for name, fn in VARIANTS.items():
if ONLY and name not in ONLY: continue
run(name, fn, cands)
if __name__ == "__main__": main()

BIN
docs/research/2026-10-14-body-voronoi-probe/density_add.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
docs/research/2026-10-14-body-voronoi-probe/density_mult.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,28 @@
"""Diagnostics: where the presence steps over .1 come from, and the large layer alone (edges_<name>.png)."""
import sys; sys.argv = ['x', '256']
import body_voronoi_probe as B
T, H = B.T, B.H
mode = sys.argv[1:] or []
cands = T.cand_list(16.0)
def edges_only(metric):
def D(x, z, t, sc):
s, b, c = T.env(x, z, t); q = T.warp(x, z, t, sc)
fd = B.flow_dir_of(x, z, q) if metric is B.m_aniso else (1.0, 0.0)
return B.edge_profile(B.body_border(q, metric, fd)[0]) * (1 - c)
return D
MET = {"pot-well": B.m_well, "pot-newton": B.m_newton, "mult": B.m_mult, "add": B.m_add, "power": B.m_power,
"flow-aniso": B.m_aniso, "lp05": B.m_lp(.5), "lpinf": B.m_lp(float('inf'))}
import os
which = os.environ.get("DIAG", "edges")
if which == "edges":
for n, m in MET.items():
f = edges_only(m); vals = T.grid(f, 0.0, 256); T.save(vals, os.path.join(B.OUT, "edges_" + n), 256)
w, big, nn = T.pop_probe(f, cands, 100.0, 1500)
print(f"edges-only {n}: presence step max {w:.4f} ({big} steps > .1 of {nn})", flush=True)
else:
for boost, align in ((0.0, 1.0), (1.6, 0.0), (0.0, 0.0)):
T.BOOST, B.ALIGN = boost, align
for n in ("mult", "power"):
w, big, nn = T.pop_probe(B.VARIANTS[n], cands, 100.0, 1500)
print(f"BOOST {boost} ALIGN {align} {n}: presence step max {w:.4f} ({big} steps > .1 of {nn})", flush=True)
print("diag done", flush=True)

View file

@ -0,0 +1,11 @@
BOOST 0.0 ALIGN 1.0 mult: presence step max 0.0234 (0 steps > .1 of 1500)
BOOST 0.0 ALIGN 1.0 power: presence step max 0.3012 (2 steps > .1 of 1500)
BOOST 1.6 ALIGN 0.0 mult: presence step max 0.0545 (0 steps > .1 of 1500)
BOOST 1.6 ALIGN 0.0 power: presence step max 0.0287 (0 steps > .1 of 1500)
BOOST 0.0 ALIGN 0.0 mult: presence step max 0.0234 (0 steps > .1 of 1500)
BOOST 0.0 ALIGN 0.0 power: presence step max 0.1970 (1 steps > .1 of 1500)
window mult: WIN 2 vs 3 max |diff| 0.2794, points differing > .01: 1 of 3385
window power: WIN 2 vs 3 max |diff| 0.0000, points differing > .01: 0 of 3385
window add: WIN 2 vs 3 max |diff| 0.0000, points differing > .01: 0 of 3385
site boost profile range 0.000..1.000: weight range .5*(1+1.6*min) .. 1.0*(1+1.6*max) = 0.50..2.60, ratio 5.2:1; with ALIGN the across-edge distance is stretched up to 2.0x more
diag2 done

View file

@ -0,0 +1,26 @@
"""Run with WARP_AT_CYCLE=1. (a) Which part of the web pops: BOOST/ALIGN ablation on mult and power.
(b) Lattice search window against the body-pulled weight range: WIN 2 vs WIN 3 on the density at 4,000 points."""
import os, sys, math; sys.argv = ['x', '256']
import body_voronoi_probe as B
T, H = B.T, B.H
cands = T.cand_list(16.0)
for boost, align in ((0.0, 1.0), (1.6, 0.0), (0.0, 0.0)):
T.BOOST, B.ALIGN = boost, align
for n in ("mult", "power"):
w, big, nn = T.pop_probe(B.VARIANTS[n], cands, 100.0, 1500)
print(f"BOOST {boost} ALIGN {align} {n}: presence step max {w:.4f} ({big} steps > .1 of {nn})", flush=True)
T.BOOST, B.ALIGN = 1.6, 1.0
T.set_time(0.0); sc = H.scroll(0.0)
pts = [(((k * 0.6180339887) % 1 - .5) * 2 * H.R * .95, ((k * 0.7548776662) % 1 - .5) * 2 * H.R * .95) for k in range(4000)]
pts = [p for p in pts if p[0] ** 2 + p[1] ** 2 < H.R ** 2]
wmin, wmax = 9, 0
for n in ("mult", "power", "add"):
f = B.VARIANTS[n]; T.WIN = 2; a = [f(x, z, 0.0, sc) for x, z in pts]
T.WIN = 3; b = [f(x, z, 0.0, sc) for x, z in pts]; T.WIN = 2
d = [abs(u - v) for u, v in zip(a, b)]
print(f"window {n}: WIN 2 vs 3 max |diff| {max(d):.4f}, points differing > .01: {sum(x > .01 for x in d)} of {len(d)}", flush=True)
ws = [v for v in B.SITE_W.values()]
print(f"site boost profile range {min(ws):.3f}..{max(ws):.3f}: weight range .5*(1+1.6*min) .. 1.0*(1+1.6*max) = "
f"{.5 * (1 + 1.6 * min(ws)):.2f}..{1 + 1.6 * max(ws):.2f}, ratio {(1 + 1.6 * max(ws)) / (.5 * (1 + 1.6 * min(ws))):.1f}:1; "
f"with ALIGN the across-edge distance is stretched up to {1 + B.ALIGN:.1f}x more", flush=True)
print("diag2 done", flush=True)

View file

@ -0,0 +1,8 @@
edges-only pot-well: presence step max 1.0000 (2 steps > .1 of 1500)
edges-only pot-newton: presence step max 0.1869 (6 steps > .1 of 1500)
edges-only mult: presence step max 0.1522 (6 steps > .1 of 1500)
edges-only add: presence step max 0.1903 (8 steps > .1 of 1500)
edges-only power: presence step max 0.1260 (9 steps > .1 of 1500)
edges-only flow-aniso: presence step max 0.1645 (30 steps > .1 of 1500)
edges-only lp05: presence step max 1.0000 (100 steps > .1 of 1500)
edges-only lpinf: presence step max 0.5637 (34 steps > .1 of 1500)

BIN
docs/research/2026-10-14-body-voronoi-probe/edges_add.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

BIN
docs/research/2026-10-14-body-voronoi-probe/edges_lp05.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
docs/research/2026-10-14-body-voronoi-probe/edges_lpinf.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
docs/research/2026-10-14-body-voronoi-probe/edges_mult.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
docs/research/2026-10-14-body-voronoi-probe/edges_power.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
docs/research/2026-10-14-body-voronoi-probe/members_add.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
docs/research/2026-10-14-body-voronoi-probe/members_mult.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,11 @@
grid 256; sigma [482, 180, 180, 305, 146, 103]; edge width 40.0 u; web at cycle start True
pot-well: corr physarumA 0.279; members 3566; changed in 1 s 0.327; presence step max 1.0000 (1 steps > .1 of 1500); sites visited per evaluation 181; 0.76 ms/eval (Python); 85s
pot-newton: corr physarumA 0.338; members 683; changed in 1 s 0.318; presence step max 0.1869 (8 steps > .1 of 1500); sites visited per evaluation 181; 0.65 ms/eval (Python); 90s
mult: corr physarumA 0.335; members 3280; changed in 1 s 0.296; presence step max 0.1522 (8 steps > .1 of 1500); sites visited per evaluation 181; 0.52 ms/eval (Python); 86s
add: corr physarumA 0.263; members 4088; changed in 1 s 0.347; presence step max 0.1144 (7 steps > .1 of 1500); sites visited per evaluation 181; 0.54 ms/eval (Python); 88s
power: corr physarumA 0.313; members 3866; changed in 1 s 0.306; presence step max 0.1260 (9 steps > .1 of 1500); sites visited per evaluation 181; 0.65 ms/eval (Python); 88s
flow-aniso: corr physarumA 0.322; members 4076; changed in 1 s 0.311; presence step max 0.1645 (22 steps > .1 of 1500); sites visited per evaluation 181; 0.62 ms/eval (Python); 99s
lp05: corr physarumA 0.274; members 6682; changed in 1 s 0.307; presence step max 1.0000 (115 steps > .1 of 1500); sites visited per evaluation 181; 0.87 ms/eval (Python); 88s
lpinf: corr physarumA 0.307; members 3756; changed in 1 s 0.282; presence step max 1.0000 (237 steps > .1 of 1500); sites visited per evaluation 181; 0.66 ms/eval (Python); 87s
skein-web: corr physarumA 0.296; members 1475; changed in 1 s 0.216; presence step max 0.0656 (0 steps > .1 of 1500); sites visited per evaluation 187; 0.41 ms/eval (Python); 110s
mult-strands: corr physarumA 0.388; members 4443; changed in 1 s 0.248; presence step max 0.1522 (8 steps > .1 of 1500); sites visited per evaluation 193; 0.73 ms/eval (Python); 99s

View file

@ -0,0 +1,11 @@
warp at cycle start True; grid 256; sigma [482, 180, 180, 305, 146, 103]; edge width 40.0 u; web at cycle start True
pot-well: corr physarumA 0.279; members 3588; changed in 1 s 0.326; presence step max 1.0000 (1 steps > .1 of 1500); sites visited per evaluation 181; 0.65 ms/eval (Python); 86s
pot-newton: corr physarumA 0.338; members 689; changed in 1 s 0.299; presence step max 0.0483 (0 steps > .1 of 1500); sites visited per evaluation 181; 0.60 ms/eval (Python); 84s
mult: corr physarumA 0.335; members 3270; changed in 1 s 0.279; presence step max 0.0910 (0 steps > .1 of 1500); sites visited per evaluation 181; 0.51 ms/eval (Python); 85s
add: corr physarumA 0.263; members 4077; changed in 1 s 0.338; presence step max 0.0899 (0 steps > .1 of 1500); sites visited per evaluation 181; 0.50 ms/eval (Python); 84s
power: corr physarumA 0.313; members 3817; changed in 1 s 0.312; presence step max 0.0287 (0 steps > .1 of 1500); sites visited per evaluation 181; 0.74 ms/eval (Python); 85s
flow-aniso: corr physarumA 0.322; members 4090; changed in 1 s 0.300; presence step max 0.0373 (0 steps > .1 of 1500); sites visited per evaluation 181; 0.66 ms/eval (Python); 88s
lp05: corr physarumA 0.274; members 6662; changed in 1 s 0.306; presence step max 1.0000 (72 steps > .1 of 1500); sites visited per evaluation 181; 0.74 ms/eval (Python); 87s
lpinf: corr physarumA 0.307; members 3761; changed in 1 s 0.275; presence step max 1.0000 (237 steps > .1 of 1500); sites visited per evaluation 181; 0.61 ms/eval (Python); 85s
skein-web: corr physarumA 0.296; members 1501; changed in 1 s 0.205; presence step max 0.0138 (0 steps > .1 of 1500); sites visited per evaluation 187; 0.36 ms/eval (Python); 106s
mult-strands: corr physarumA 0.388; members 4462; changed in 1 s 0.238; presence step max 0.0910 (0 steps > .1 of 1500); sites visited per evaluation 193; 0.62 ms/eval (Python); 89s

View file

@ -0,0 +1,2 @@
warp at cycle start True; grid 256; sigma [482, 180, 180, 305, 146, 103]; edge width 40.0 u; web at cycle start True
power-strands: corr physarumA 0.371; members 4965; changed in 1 s 0.269; presence step max 0.0287 (0 steps > .1 of 1500); sites visited per evaluation 193; 0.60 ms/eval (Python); 88s

View file

@ -0,0 +1,26 @@
"""Trace the worst presence step of the edges-only layer: where it is and what moves."""
import sys, os, math; sys.argv = ['x', '256']
import body_voronoi_probe as B
T, H = B.T, B.H
cands = T.cand_list(16.0)
name = os.environ.get("M", "mult"); met = {"mult": B.m_mult, "power": B.m_power, "add": B.m_add}[name]
def f(x, z, t, sc):
s, b, c = T.env(x, z, t); q = T.warp(x, z, t, sc); return B.edge_profile(B.body_border(q, met)[0]) * (1 - c), q
T.set_time(100.0)
near = [c for c in cands if abs(T.density_at(lambda x, z, t, sc: f(x, z, t, sc)[0], c, 100.0)[0] - c[4]) < .3][:1500]
best = (0, None)
prev = {}
for k in range(61):
tt = 100 + k / 60; T.set_time(tt)
for c in near:
sc, t0 = T.cyc_scroll(tt, c[5]); d, q = f(c[2], c[3], tt, sc); p = T.presence(d, c[4])
if c in prev and prev[c][1] == t0:
st = abs(p - prev[c][0])
if st > best[0]: best = (st, c, k, d, prev[c][2], q, prev[c][3], t0)
prev[c] = (p, t0, d, q)
st, c, k, d, dprev, q, qprev, t0 = best
print(f"{name}: step {st:.3f} at cand ({c[2]:.0f},{c[3]:.0f}) thr {c[4]:.2f} frame {k}: density {dprev:.3f} -> {d:.3f}; warped q {qprev} -> {q}; t0 {t0}")
for kk in range(k - 3, k + 3):
tt = 100 + kk / 60; T.set_time(tt); sc, t0 = T.cyc_scroll(tt, c[5]); s, b, cc = T.env(c[2], c[3], tt)
qq = T.warp(c[2], c[3], tt, sc); dist, n = B.body_border(qq, met)
print(f" frame {kk}: t0 {t0:.3f} slope {s:.4f} core {cc:.3f} border dist {dist:.2f} warped q ({qq[0]:.2f},{qq[1]:.2f})")