cultmath map: lattice-weighted-cellular section (LW1-LW7), Pass 7 line 4 split into 4a/4b, Q4/Q5/Q9 and site-ground-field marked ruled; weighted-node probes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
911fc75531
commit
9bf1e82f7e
8 changed files with 211 additions and 23 deletions
|
|
@ -825,7 +825,7 @@ above. Everything else recovered carries over.
|
|||
|
||||
**The site scene** (`site-ground` r3; the brush parameters in this paragraph,
|
||||
`Rc = 640`, `e = 4`, are superseded by pass 4's one shipped void, `Rh = 198`,
|
||||
`S = 50`, `Rc = 248`, `e = 0.40`, pending Q9): the original's units. Outer volume: an
|
||||
`S = 50`, `Rc = 248`, `e = 0.40`, as Q9 ruled): the original's units. Outer volume: an
|
||||
unbounded uniform fill (chosen over a shell: every ray then ends in a wall
|
||||
and the march terminates by opacity, never by a far plane; `Z1 = 2 Rc` bounds
|
||||
the pre-pass range only). The carve: `d = |p − c_v|`, `g(d) = (1 −
|
||||
|
|
@ -1105,7 +1105,7 @@ assumption failed: the pre-band's 0-snoise steps, or a body start later than
|
|||
one more probe; the main pass reads it and never decides it. The Beer-Lambert
|
||||
step is the consumer's integration, not a CultMath function.
|
||||
- `(Rc, e)` are no longer authored; they are derived from `(Rh, S)`. The site
|
||||
section's `Rc = 640, e = 4` is no longer an owner of the look (Q9 pending).
|
||||
section's `Rc = 640, e = 4` is no longer an owner of the look (Q9 ruled (a), `operator-shipped-void`).
|
||||
- (c)'s contract is derived from its own run (0.90 of the ceiling); no absolute
|
||||
target is written for (c).
|
||||
- Deletion line: the false strictness sentence, the Tint camera citation, the
|
||||
|
|
@ -1125,6 +1125,10 @@ mind's copies need re-admission by Self.
|
|||
|
||||
## Questions
|
||||
|
||||
Q4-Q9 are all ruled; the options below are kept as the record of what was
|
||||
asked. Q9: (a), ruling `operator-shipped-void` (`rh198-s50`). Q4: (a), ruling
|
||||
`operator-palette-tokens`. Q5: (a), ruling `operator-wash-carry`.
|
||||
|
||||
**Q9. The one shipped void** (pass 4; finding `shipped-void-two-scenes`).
|
||||
Authored as `(Rh, S)` with `(Rc, e)` derived; `CARVE = 1.5`, `A = 20`,
|
||||
`F0 = 0.01`, `K = 1/30`, the camera as site-ground r3.
|
||||
|
|
@ -1878,10 +1882,14 @@ Aetheria twin.
|
|||
|
||||
### Questions (admitted in the mind)
|
||||
|
||||
- `tape-register-model`: the HLSL interpreter's register model. Gates the
|
||||
interpreter cut only.
|
||||
- `tape-register-model`: the HLSL interpreter's register model. Ruled
|
||||
`tape-register-model-ruled` (groupshared registers).
|
||||
- `lattice-site-weight-source`: how a weighted lattice node gets a per-site
|
||||
weight (T7c). Gates fauna gap (1) only.
|
||||
weight (T7c). Re-asked as `lattice-weight-from-bodies` and ruled
|
||||
`lattice-weight-from-bodies-ruled` (node-reads-bodies).
|
||||
- `lattice-window-sound-or-measured` (open, from section
|
||||
"lattice-weighted-cellular"): a proven ring search or a fixed measured 7x7
|
||||
window. Gates line 4a's search loop.
|
||||
|
||||
Prior art in hand: `F:\Projects\aetheria-material-path-prior-art.md` section 4
|
||||
(GPU interpreters: Fidget's crates, Dolphin ubershaders, Laine et al. on
|
||||
|
|
@ -1899,9 +1907,11 @@ putting them.
|
|||
Fauna's first cut, `fauna-trophic-habitat`, evaluates the habitat graph in C# at
|
||||
candidates. It needs `expr-ir` r2, gaps (2) and (4) with (3) under the strands,
|
||||
gap (1), and their ops registered (line 5). It does not need the interpreter or
|
||||
gap (5); its presenter does. The critical path is `cycles-converters` merge,
|
||||
then line 3 beside the ruling on `lattice-site-weight-source` and line 4, then
|
||||
line 5; line 1 runs now, off that path.
|
||||
gap (5); its presenter does. Lines 1 and 3 and `cycles-converters` are on
|
||||
main (`911fc755`). The critical path is line 4a, after the ruling on
|
||||
`lattice-window-sound-or-measured`, then line 5. Line 4b, the shader twins,
|
||||
is not on the path to fauna's first cut; it is on the path to fauna's presenter,
|
||||
with line 6.
|
||||
|
||||
1. `expr-ir` r2 (spec admitted): the graph, builder with substitution, decode
|
||||
validation, wire shape, C# evaluator. Touches no file `cycles-converters`
|
||||
|
|
@ -1916,12 +1926,14 @@ line 5; line 1 runs now, off that path.
|
|||
whose strand kernel is its only consumer.
|
||||
3. `trophic-nodes` (CultLib): spec `cut-trophic-nodes.r1` admitted; see
|
||||
"trophic-nodes" below for its body facts and decisions.
|
||||
4. `lattice-weighted-cellular` (CultLib): gap (1), 2D, `pcg3d` cells, zone
|
||||
time, smooth-min blend, a stated weight range sizing its window. Its
|
||||
question is ruled (`lattice-weight-from-bodies-ruled`): it reads the body
|
||||
span and weights each cell at its centre through line 3's power cells and
|
||||
border, so it waits on 3 and maps on 3's merged head (both append fixture
|
||||
families).
|
||||
4. Gap (1), mapped in section "lattice-weighted-cellular" on 3's merged head
|
||||
(`911fc755`), in two cuts. 4a, `lattice-weighted-cellular`
|
||||
(`cut-lattice-weighted-cellular.r1`), is C#: the node reads the body span
|
||||
and weights each cell at its centre through line 3's exact `power_border`.
|
||||
It waits on question `lattice-window-sound-or-measured`. 4b,
|
||||
`lattice-weighted-shaders` (`cut-lattice-weighted-shaders.r1`), adds the
|
||||
twins of its four steps, the GLSL, the families and the lavapipe bounds.
|
||||
It waits on 4a.
|
||||
5. `expr-trophic-ops` (CultLib): the span input kind and ops for 2-4 in the op
|
||||
table; the strand reduction over the gated edge span (the graph has no loop,
|
||||
so a span op is a CultMath function: a C# span overload over a step with its
|
||||
|
|
@ -1994,6 +2006,58 @@ alone. Spec: `cultmath-tapes:cut_spec:cut-trophic-nodes.r1`.
|
|||
is re-posing the lowering and shader testing (question `device-run-driver`);
|
||||
if that lands first, only the shader commits are re-mapped.
|
||||
|
||||
### lattice-weighted-cellular
|
||||
|
||||
Written by Imagination (agent `imagination-ag4-lattice-weighted`, session
|
||||
`self-2026-10-16-ag4`) against CultLib `origin/main` `911fc755`, where
|
||||
`trophic-nodes` is merged (`7e6881af`). Ruling `lattice-weight-from-bodies-ruled`
|
||||
(node-reads-bodies). Line 4 splits into two cuts because of Hands' budget:
|
||||
`cut-lattice-weighted-cellular.r1` (C#) and then `cut-lattice-weighted-shaders.r1`
|
||||
(twins, GLSL, families, lavapipe bounds). One question,
|
||||
`lattice-window-sound-or-measured`, gates the first cut's search loop.
|
||||
|
||||
#### Body facts (lattice-weighted-cellular)
|
||||
|
||||
The probes are numpy float64 scripts in
|
||||
`docs/research/lattice-site-weight/weighted-node/` (`probe.py` is the node, and
|
||||
each other script is one measure). They use 6 bodies orbiting with period equal to
|
||||
distance and a sigma ratio of 5:1, `cellSize 260`, `falloff 120` (the body probe's
|
||||
`3 * EW_U`), `KW .35` and boost 1.6, the ruled look.
|
||||
|
||||
| # | Question | Result |
|
||||
|---|---|---|
|
||||
| LW1 | What weight does the ruled look use? (Aetheria `body_voronoi_probe.py` `lattice_web_edges`, master `4e620671`) | `base(.5..1) * (1 + 1.6 * exp(-(border(cell centre) / 120)^2))`. The border is the body diagram's first-order distance under the ruled power metric (`body-diagram-metric-ruled`). Distance is stretched by `1 + near` (up to 2x) across the body border's normal at the query point. Output `max(soft^2, exp(-((F2 - F1)/.1)^2))`, with soft from her smooth-min fold. |
|
||||
| LW2 | Window sufficiency (`a.py`, 67,600 points against a 19x19 box) | 7x7 has no F1/F2 misses at ratio 5.2, 10 and 20 with stretch 1 or 2. 5x5 misses .1-.3% of points with stretch 2 (output error up to .92) and .04% at 20:1. A worst-case proof covers 7x7 only for ratio x stretch up to about 1.5. |
|
||||
| LW3 | Are the weights continuous in time? (`c.py`, 576 cell centres, 300 s at 1/60 s) | First-order border: max step .68, and 113 steps over .01, where the second-nearest body changes. Exact `power_border`: max .0032, none over .01. |
|
||||
| LW4 | Does her fold (from 8, row-major) depend on the window? (`d3.py`, 40,000 straddles of lattice cell boundaries) | Yes. With F1 and F2 exact, soft jumps .114 at 5x5 and .056 at 7x7 with stretch 2. It vanishes at 13x13 on this corpus, but nothing proves that. |
|
||||
| LW5 | Which fold start is window-independent and continuous? (`d3.py`, `g.py`) | Start at F1 (F1 cell skipped): no window jump, but at F1 identity swaps m jumps .011 and soft .25, because the polynomial smooth min is not associative. Start at F1 + k and fold every cell: swaps jump 4e-15 and window jumps are 0 once the box holds every `d < F1 + 2k`. Its overshoot above m is at most .017k per step (computed), hence the clamp to F1 + k. |
|
||||
| LW6 | Look of start F1 + k against hers (`h.py`, 40,000 points, 13x13) | Mean output difference 0.0000, max .032 (stretch 1) and .030 (stretch 2). |
|
||||
| LW7 | Ring-exit cost (`e.py`) | Cells visited: at the ruled look median 25, max 49 (stretch 1) and 81 (stretch 2); at boost 4 with stretch 2, max 121; at boost 9, max 81. The worst-case sound fixed box is 19x19 to 67x67. |
|
||||
|
||||
#### Decisions (defaults, not forks)
|
||||
|
||||
- **The weight reads the exact power border** (`power_border`, trophic-nodes),
|
||||
not the first-order one the ruled look's probe used: LW3, under
|
||||
`aetheria-fauna:ruling:habitat-no-pop-ruled`. The look moves where the
|
||||
first-order distance was wrong (LW3's jumps are the same places).
|
||||
- **Her fold starts at F1 + k and is clamped to F1 + k**, in absolute row-major
|
||||
order (LW4-LW6). The look is unchanged to within .03, and the node is
|
||||
window-independent by construction, which the search's exactness needs.
|
||||
- **The alignment stretch is in the node.** It is a query-point anisotropy, and
|
||||
it does change winners (B41). Only the node sees the candidate sites, so a
|
||||
caller cannot apply it. Stretch 1 is isotropic.
|
||||
- **float4(F1, F2, smooth, id), no gradients, no struct.** fauna's composition
|
||||
reads values only, and a struct would owe a wire shape (TN4). Gradients can be
|
||||
added later as their own function, if a consumer asks.
|
||||
- **The range is public constants**, so line 5's graph validator reads the range
|
||||
from CultMath rather than restating it: boost up to 4 (10:1), stretch up to 2,
|
||||
smoothing up to 1.
|
||||
- **The base weight takes its own hash lane** (`pcg3d(cell).z`), not the jitter
|
||||
phase the probe reused.
|
||||
- **Steps for shaders, the span function in C# only**, by trophic-nodes'
|
||||
convention: `lattice_site`, `lattice_weight`, `lattice_distance`,
|
||||
`lattice_smooth_step`.
|
||||
|
||||
## Pass 8: volumetric prioritized (ruling `volumetric-work-prioritized`)
|
||||
|
||||
Written by Imagination (agent `imagination-volumetric-frontier`, session
|
||||
|
|
@ -2043,10 +2107,11 @@ authoring framework" she will build on. The kernel cascade (the probe and the Ke
|
|||
Lab) is the earlier exploration, and it stays a line below.
|
||||
|
||||
**Correction to the brief's expectation.** The next cut is not site-ground. The
|
||||
site ground's field is open: the Span Lab framework, the kernel cascade, or r3's
|
||||
marched void. The r3 machinery (the interval pre-pass, the centred warp, the
|
||||
debug-versus-dense readback) is shaped for a marched field. Question
|
||||
`site-ground-field` asks this.
|
||||
site ground's field was then open between the Span Lab framework, the kernel
|
||||
cascade and r3's marched void. The r3 machinery (the interval pre-pass, the
|
||||
centred warp, the debug-versus-dense readback) is shaped for a marched field.
|
||||
Question `site-ground-field` asked this. It is ruled `site-ground-field-ruled`:
|
||||
the Span Lab framework with the void as its brush.
|
||||
|
||||
### `SNOISE_LIPSCHITZ`: 10.099 in the lab, 7.964 in CultMath
|
||||
|
||||
|
|
@ -2128,8 +2193,8 @@ is traversal, which is unmeasured. Optical depth through it is a sum of per-kern
|
|||
segment integrals, so it is order-independent. Additive blending of each kernel's
|
||||
screen footprint would give exact transmittance in WebGL2, with no compute and no
|
||||
sorting. Emission is not order-independent. This is a hypothesis with no probe
|
||||
behind it. The cascade becomes a cut only if `site-ground-field` or the operator's
|
||||
nebula work picks it.
|
||||
behind it. `site-ground-field-ruled` picked the Span Lab framework, so the cascade
|
||||
becomes a cut only if the operator's nebula work picks it.
|
||||
|
||||
### Cut sequence (volumetric line)
|
||||
|
||||
|
|
@ -2170,9 +2235,9 @@ nebula work picks it.
|
|||
(`CULTMATH_PI` through `CULTMATH_SNOISE_THIRD` and `CULTMATH_CYCLES_*`). So one
|
||||
reflection test can pin every shader constant to its C# twin.
|
||||
3. The curl primitive, once the operator rules on curl (the Aetheria flow test).
|
||||
4. Question `site-ground-field`, then `cut-site-ground.r1` on the field it picks.
|
||||
The marched option carries all three site follow-ups; the other options
|
||||
withdraw the two marched-only ones.
|
||||
4. `cut-site-ground.r1` on the Span Lab framework (`site-ground-field-ruled`).
|
||||
Under that option the two marched-only site follow-ups are withdrawn, and
|
||||
only the third carries.
|
||||
5. The kernel cascade, if picked.
|
||||
|
||||
## Pass 9: the shader pipeline (rulings `glsl-es-via-dxc-spirv-cross`, `conforming-run-lavapipe`, `real-device-native-angle`)
|
||||
|
|
|
|||
11
docs/research/lattice-site-weight/weighted-node/a.py
Normal file
11
docs/research/lattice-site-weight/weighted-node/a.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import numpy as np, probe as P
|
||||
g = np.linspace(-3200, 3200, 260); X, Z = np.meshgrid(g, g); q = np.stack([X, Z], -1)
|
||||
rng = np.random.default_rng(3); ang = rng.uniform(0, 6.283, X.shape); nrm = np.stack([np.cos(ang), np.sin(ang)], -1)
|
||||
for boost, S in [(1.6, 1), (1.6, 2), (4, 1), (4, 2), (9, 1)]:
|
||||
ref = P.node(q, 100.0, 9, boost, S, nrm)
|
||||
row = []
|
||||
for W in (2, 3, 4, 5):
|
||||
r = P.node(q, 100.0, W, boost, S, nrm)
|
||||
miss = np.mean((np.abs(r[0]-ref[0])>1e-12)|(np.abs(r[1]-ref[1])>1e-12))
|
||||
row.append(f"W{W}: miss {miss:.5f} dOut {np.abs(r[3]-ref[3]).max():.3f}")
|
||||
print(f"boost {boost} S {S} ratio {2*(1+boost):.1f}x{S}:", "; ".join(row))
|
||||
10
docs/research/lattice-site-weight/weighted-node/c.py
Normal file
10
docs/research/lattice-site-weight/weighted-node/c.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import numpy as np, probe as P
|
||||
g = (np.arange(-12, 12) + .5) * 260; X, Z = np.meshgrid(g, g); c = np.stack([X, Z], -1).reshape(-1, 2)
|
||||
for ex in (False, True):
|
||||
prev = None; worst = 0; big = 0
|
||||
for t in np.arange(0, 300, 1/60):
|
||||
Pw = np.exp(-(P.border(c, P.bodies(t), ex) / P.FALL) ** 2)
|
||||
if prev is not None:
|
||||
s = np.abs(Pw - prev); worst = max(worst, s.max()); big += (s > .01).sum()
|
||||
prev = Pw
|
||||
print("exact" if ex else "first-order", f"cell-centre profile: max step per 1/60 s {worst:.4f}, steps > .01: {big} (576 centres x 18000 ticks)")
|
||||
9
docs/research/lattice-site-weight/weighted-node/d3.py
Normal file
9
docs/research/lattice-site-weight/weighted-node/d3.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import numpy as np, probe as P
|
||||
rng = np.random.default_rng(9); n = 15000
|
||||
kx = rng.integers(-12, 12, n); z = rng.uniform(-3100, 3100, n); eps = 1e-6
|
||||
qa = np.stack([kx * 260.0 - eps, z], -1)[None]; qb = np.stack([kx * 260.0 + eps, z], -1)[None]
|
||||
for W, fold, boost, S in [(6,"f1kw",1.6,2),(6,"her",1.6,2),(6,"f1kw",4,2)]:
|
||||
ang = rng.uniform(0, 6.283, n); nrm = np.stack([np.cos(ang), np.sin(ang)], -1)[None]
|
||||
a = P.node(qa, 100.0, W, boost, S, nrm, True, fold); b = P.node(qb, 100.0, W, boost, S, nrm, True, fold)
|
||||
j = np.abs(a[3] - b[3]); js = np.abs(a[2]-b[2]); jf = np.abs(a[0]-b[0])
|
||||
print(f"W{W} {fold} B{boost} S{S}: cell-boundary jump out max {j.max():.4f} (>.01: {np.mean(j>.01):.4f}), soft max {js.max():.4f}, F1 max {jf.max():.2e}")
|
||||
13
docs/research/lattice-site-weight/weighted-node/e.py
Normal file
13
docs/research/lattice-site-weight/weighted-node/e.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import numpy as np, probe as P
|
||||
g = np.linspace(-3200, 3200, 200); X, Z = np.meshgrid(g, g); q = np.stack([X, Z], -1)
|
||||
rng = np.random.default_rng(3); ang = rng.uniform(0, 6.283, X.shape); nrm = np.stack([np.cos(ang), np.sin(ang)], -1)
|
||||
wmin = .5
|
||||
for boost, S in [(1.6, 1), (1.6, 2), (4, 2), (9, 1)]:
|
||||
f1, f2, soft, out = P.node(q, 100.0, 6, boost, S, nrm)
|
||||
C = np.maximum(f2, f1 + P.KW)
|
||||
k = np.floor(C / wmin).astype(int) + 1 # first ring whose lower bound (k-1)*wmin >= C is excluded... rings visited 0..k
|
||||
k = np.ceil(C / wmin).astype(int) # rings 0..k visited (ring k+1 has lb k*wmin >= C)
|
||||
cells = (2 * k + 1) ** 2
|
||||
wmax = 1.0 * (1 + boost); Usound = 1.58 * wmax * S
|
||||
Wsound = int(np.ceil((Usound + P.KW) / wmin))
|
||||
print(f"B{boost} S{S}: rings needed p50 {np.median(k):.0f} p99 {np.percentile(k,99):.0f} max {k.max()} -> cells p50 {np.median(cells):.0f} max {cells.max()}; worst-case sound W {Wsound} ({(2*Wsound+1)**2} cells)")
|
||||
14
docs/research/lattice-site-weight/weighted-node/g.py
Normal file
14
docs/research/lattice-site-weight/weighted-node/g.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import numpy as np, probe as P, sys
|
||||
fold = sys.argv[1]; rng = np.random.default_rng(2); jm = js = 0; nsw = 0
|
||||
for k in range(3):
|
||||
o = rng.uniform(-2500, 2500, 2); a = rng.uniform(0, 3.1416); d = np.array([np.cos(a), np.sin(a)])
|
||||
s = np.arange(0, 15 * 260, .5); P.node((o + s[:, None] * d)[None], 100.0, 3, 1.6, 1.0, None, True, fold); a1 = P.node.a1[0].copy()
|
||||
idx = np.nonzero(np.diff(a1))[0][:40]
|
||||
lo = s[idx]; hi = s[idx + 1]; ref = a1[idx]
|
||||
for _ in range(40):
|
||||
mid = .5 * (lo + hi); P.node((o + mid[:, None] * d)[None], 100.0, 3, 1.6, 1.0, None, True, fold)
|
||||
same = P.node.a1[0] == ref; lo = np.where(same, mid, lo); hi = np.where(same, hi, mid)
|
||||
ra = P.node((o + lo[:, None] * d)[None], 100.0, 3, 1.6, 1.0, None, True, fold); ma = P.node.m[0].copy()
|
||||
rb = P.node((o + hi[:, None] * d)[None], 100.0, 3, 1.6, 1.0, None, True, fold); mb = P.node.m[0]
|
||||
jm = max(jm, np.abs(ma - mb).max()); js = max(js, np.abs(ra[2] - rb[2]).max()); nsw += len(idx)
|
||||
print(fold, f"F1 swaps {nsw}: max jump m {jm:.2e}, soft {js:.2e}")
|
||||
6
docs/research/lattice-site-weight/weighted-node/h.py
Normal file
6
docs/research/lattice-site-weight/weighted-node/h.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import numpy as np, probe as P
|
||||
g = np.linspace(-3200, 3200, 200); X, Z = np.meshgrid(g, g); q = np.stack([X, Z], -1)
|
||||
rng = np.random.default_rng(3); ang = rng.uniform(0, 6.283, X.shape); nrm = np.stack([np.cos(ang), np.sin(ang)], -1)
|
||||
for S in (1, 2):
|
||||
a = P.node(q, 100.0, 6, 1.6, S, nrm, True, "her"); b = P.node(q, 100.0, 6, 1.6, S, nrm, True, "f1kw")
|
||||
d = np.abs(a[3] - b[3]); print(f"S{S} look her vs f1kw: mean {d.mean():.4f} p99 {np.percentile(d,99):.3f} max {d.max():.3f}; mean out {a[3].mean():.3f}")
|
||||
60
docs/research/lattice-site-weight/weighted-node/probe.py
Normal file
60
docs/research/lattice-site-weight/weighted-node/probe.py
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
"""Weighted lattice cellular probe (Imagination, lattice-weighted-cellular). numpy, float64.
|
||||
Bodies orbit (period = distance s). Cell weight = base(hash)*(1+BOOST*exp(-(border(centre)/FALL)^2)),
|
||||
border over the bodies' power scores (site.w = sigma). Her fold: row-major, m from 8; variant m0f1 starts m at F1."""
|
||||
import numpy as np, sys
|
||||
LAT, FALL, KW, EW, OMEGA = 260.0, 120.0, .35, .10, .05
|
||||
GAP0 = .25 * KW / (1 + 3 * KW)
|
||||
rng = np.random.default_rng(7)
|
||||
NB = 6
|
||||
DIST = np.array([0.0, 700, 1200, 1800, 2400, 3000]); PH = rng.uniform(0, 6.283, NB)
|
||||
SIG = np.array([1500, 300, 450, 350, 900, 600]) / np.sqrt(128.0) * 4 # sigma ratio 5:1, edges well inside
|
||||
def bodies(t):
|
||||
a = PH + np.where(DIST > 0, 2 * np.pi * t / np.maximum(DIST, 1), 0)
|
||||
return np.stack([np.cos(a) * DIST, np.sin(a) * DIST], -1)
|
||||
|
||||
def h32(x):
|
||||
x = x.astype(np.uint32)
|
||||
x ^= x >> 16; x *= np.uint32(0x7feb352d); x ^= x >> 15; x *= np.uint32(0x846ca68b); x ^= x >> 16
|
||||
return x
|
||||
def hash01(cx, cz, s):
|
||||
k = ((cx.astype(np.int64) * 73856093) ^ (cz.astype(np.int64) * 19349663) ^ (s * 83492791)) & 0xffffffff
|
||||
return (h32(h32(k)) >> 8) / 16777216.0
|
||||
|
||||
def border(q, B, exact):
|
||||
"""q: (...,2). Power border distance over bodies B."""
|
||||
sc = ((q[..., None, :] - B) ** 2).sum(-1) - SIG ** 2
|
||||
o = np.argsort(sc, -1); i1 = o[..., 0]; i2 = o[..., 1]
|
||||
s1 = B[i1]
|
||||
if not exact:
|
||||
s2 = B[i2]; return (np.take_along_axis(sc, i2[..., None], -1)[..., 0] - np.take_along_axis(sc, i1[..., None], -1)[..., 0]) / (2 * np.linalg.norm(s2 - s1, axis=-1))
|
||||
best = np.full(q.shape[:-1], 1e30)
|
||||
w1 = SIG[i1]
|
||||
for k in range(NB):
|
||||
d = B[k] - s1; L = np.linalg.norm(d, axis=-1)
|
||||
pl = np.maximum((((s1 - q) * d).sum(-1) + .5 * ((d * d).sum(-1) + w1 ** 2 - SIG[k] ** 2)) / np.where(L > 0, L, 1), 0)
|
||||
best = np.where((L > 0) & (pl < best), pl, best)
|
||||
return best
|
||||
|
||||
def node(q, t, W, boost, S=1.0, nrm=None, exact=True, fold="her"):
|
||||
B = bodies(t); p = q / LAT; n = np.floor(p); f = p - n
|
||||
if nrm is None: nrm = np.zeros_like(q); nrm[..., 0] = 1
|
||||
ds = []
|
||||
for j in range(-W, W + 1):
|
||||
for i in range(-W, W + 1):
|
||||
cx, cz = n[..., 0] + i, n[..., 1] + j
|
||||
base = .5 + .5 * hash01(cx, cz, 1)
|
||||
P = np.exp(-(border(np.stack([(cx + .5) * LAT, (cz + .5) * LAT], -1), B, exact) / FALL) ** 2)
|
||||
wt = base * (1 + boost * P)
|
||||
ox = .5 + .5 * np.sin(OMEGA * t + 6.2831 * hash01(cx, cz, 2)); oz = .5 + .5 * np.sin(OMEGA * t + 6.2831 * hash01(cx, cz, 3))
|
||||
vx, vz = i - f[..., 0] + ox, j - f[..., 1] + oz
|
||||
a = vx * nrm[..., 0] + vz * nrm[..., 1]; c = -vx * nrm[..., 1] + vz * nrm[..., 0]
|
||||
ds.append(np.hypot(S * a, c) * wt)
|
||||
D = np.stack(ds, -1); srt = np.sort(D, -1); f1, f2 = srt[..., 0], srt[..., 1]; a1 = np.argmin(D, -1)
|
||||
m = np.full(f1.shape, 8.0) if fold == "her" else (f1 + KW if fold == "f1kw" else f1.copy())
|
||||
for k in range(D.shape[-1]):
|
||||
d = D[..., k]
|
||||
h = np.clip(.5 + .5 * (m - d) / KW, 0, 1); h = h * h * (3 - 2 * h)
|
||||
mn = m + (d - m) * h - h * (1 - h) * KW / (1 + 3 * KW)
|
||||
m = np.where((fold == "m0f1") & (a1 == k), m, mn)
|
||||
soft = np.clip((f1 - m) / GAP0, 0, 1); node.m = m; node.a1 = a1
|
||||
return f1, f2, soft, np.maximum(soft ** 2, np.exp(-((f2 - f1) / EW) ** 2))
|
||||
Loading…
Reference in a new issue