Track relaxation — setup and constraints

Relaxation is a PBD/XPBD-style position projection over the constant-spacing centerline beads. It does not allocate dense [E,N,N] tensors, does not run an optimizer, and does not store a per-constraint Lagrange-multiplier history. Instead, every fixed sweep reads one complete position buffer, computes local position corrections, and writes a second buffer (see Track relaxation — the solver). This page covers the per-env setup and the three corrections each sweep applies, then the gate coupling that ties the separation target to the downstream validity gate.

Setup

Setup happens once, before the sweeps, in band_l0_inplace (kernel _band_l0_k):

  • \(L_0[e] = \text{perimeter}(\text{center}[e]) / \text{count}[e]\), the rest edge length for the constant-spacing bead chain.

  • \(\text{band}[e] = \text{round}(2 \cdot \text{half\_width} / L_0[e])\), clamped to a minimum of 1, unless config.relax_band overrides it. The band excludes immediate geometric neighbours from the separation scan, so the road does not try to push apart points that are adjacent along the same local segment. The identical _separation_band @wp.func is shared with the pipeline’s _validity_k, so the band definition lives in exactly one place.

  • \(\text{target} = 2 \cdot \text{half\_width} \cdot (1 + \text{relax\_margin})\) is the non-local separation distance — a slightly inflated road diameter (default relax_margin = 0.15).

  • \(R_{\min} = \text{half\_width} \cdot (1 + \text{relax\_margin})\) is the local curvature-radius target used by the bending correction. Note \(\text{target} = 2\,R_{\min}\) exactly.

Per-sweep corrections

Each sweep applies three corrections per real bead i (kernels _step_kernel / _step_cached_kernel):

Non-local separation

For every bead j whose circular index distance from i exceeds band[e], if the current Euclidean distance is below target, bead i receives a push along \(x_i - x_j\) of magnitude \(0.5\,(\text{target} - \text{dist})/\text{dist}\). The pushes are averaged over all colliding candidates for that bead (Jacobi averaging), then scaled by relax_sep_relax. This is the term that opens self-approaches and makes enough room for a constant-width road.

Edge spacing

The two incident edges (i-1, i) and (i, i+1) are corrected toward the rest length \(L_0[e]\) with the local formula \(0.25\big(\tfrac{\ell_n - L_0}{\ell_n}d_n - \tfrac{\ell_p - L_0}{\ell_p}d_p\big)\), scaled by relax_spc_relax, so the relaxed loop keeps near-constant bead spacing instead of stretching into a few long chords.

Bending / radius guard

The local Menger curvature through (i-1, i, i+1) gives a radius estimate. If \(\text{radius} < R_{\min}\), the bead is pushed toward the midpoint of its neighbours by a scale of \(\text{relax\_bend\_relax}\cdot (R_{\min} - \text{radius})/R_{\min}\), clamped to 1 so the bead never passes the chord midpoint in a single sweep (the flip clamp). This removes jagged under-radius corners introduced by generation or by separation pushes.

The three corrections sum into a single per-bead step \(\Delta x = \text{sr}\cdot\text{sep} + \text{pr}\cdot\text{spc} + \text{bscale}\cdot\text{toward}\) written to the output buffer.

Count-aware kernels

The solver is count-aware. Kernels launch over the static stride N_max, but threads with i >= count[e] copy the NaN-padded tail through unchanged. When every count[e] == N_max, the same kernels reduce to the old fixed-N parity path.

The relax_* knobs

Knob

Effect

relax_sep_relax

Scale factor applied to each separation push (damping). Default 1.0.

relax_spc_relax

Scale factor applied to edge-spacing corrections. Default 1.0.

relax_bend_relax

Scale factor applied to bending/radius-guard corrections. Default 1.5 (over-corrects slightly to remove sharp corners quickly).

relax_margin

Inflation margin: target = 2*half_width*(1+relax_margin). Default 0.15.

relax_band

Override the auto-computed separation exclusion band. Default None (auto).

relax_iters

Number of Jacobi sweeps. Default 50 (see Track relaxation — convergence and tuning).

The separation execution knobs (relax_sep_every, relax_sep_cache_slots, relax_sep_cache_skin) and the Chebyshev knobs (relax_accel, relax_cheby_rho, relax_cheby_gamma, relax_cheby_start) are covered in Track relaxation — the solver.

Gate coupling — a load-bearing overshoot

The separation target is not the validity gate’s floor; it deliberately overshoots it, and that overshoot is load-bearing. Recent experiments established the relationship and, critically, that weakening it collapses yield.

At the library defaults (half_width = hw, relax_margin = 0.15, relax_tol = 0.02):

  • the separation target is \(2\,\text{hw}\,(1.15) = 2.3\,\text{hw}\), and \(\text{target} = 2\,R_{\min}\) exactly;

  • the validity gate requires \(\tfrac{1}{2}\,\text{sep}_{\min} \ge (1 - \text{relax\_tol})\,\text{hw}\), i.e. \(\text{sep}_{\min} \ge 1.96\,\text{hw}\).

So the solver aims a bead pair across a legal minimum-radius hairpin at \(2.3\,\text{hw}\) while the gate would accept it at \(1.96\,\text{hw}\). Because \(\text{target} = 2\,R_{\min}\), a pair straddling a legal minimum-radius hairpin sits at zero separation slack relative to the target — the constraint pushes it exactly as hard as the geometry allows. At converged equilibrium the solver therefore holds tight hairpins a comfortable ~4–8% above the gate line, not on it.

That headroom is what carries yield. Two changes that look like harmless efficiency tweaks were both measured to be dead ends because they let near-band pairs settle onto the gate floor instead of above it:

  • Widening the exclusion band (relax_band = auto + 1) removes the near-hairpin pairs from the separation scan entirely. Defaults-regime yield collapsed 0.99 → 0.74.

  • Capping near-band pair targets at the chord-feasible distance (only pushing pairs apart to the distance a minimum-radius arc geometrically permits, rather than to the full target) removes exactly the overshoot. Near-band pairs then settle onto the gate floor and float across it under float noise, again failing the gate.

Warning

These are recorded as measured dead ends, not options. Do not widen the exclusion band and do not cap near-band pair targets at the chord-feasible distance: both let near-band pairs settle onto the gate floor, and yield falls off a cliff. The overshoot (target = 2*R_min = 2.3*hw at defaults) is deliberate margin — keep it.

Disabling relaxation

If relax_enable=False, _run_pipeline bypasses band_l0_inplace and xpbd_solve_inplace and inflates the constant-spacing centerline directly (an identity pass-through, useful for ablations). Otherwise the relaxed output is re-uniformized by the inflation stage before frame, offset, validity, and arclength are computed.