Pipelines and presets

The track and gate pipelines as ordered lists of stages: the spec types, the presets, the planner and the Program it builds, and the Params of the built-in stages. Generating tracks and gates shows how to use them; Pipeline stages describes the default stages and the planner.

TrackGenerator(spec) builds the spec it is given, usually from presets.track; TrackGenerator(config) builds the spec of legacy.to_pipeline_spec with build(). GateGenerator does the same with presets.gates (a spec whose BatchSpec.max_gates is set) and legacy.to_gate_pipeline_spec. track_gen.pipeline is a subpackage outside track_gen.__all__: import it explicitly (import track_gen does not import it).

The track and gate pipelines as data.

A PipelineSpec lists the stages of the track pipeline in order (StageRef: kind, name, params) over one BatchSpec. build() plans it (every channel a stage reads has an earlier writer, every channel is allocated once, each stage’s scratch is namespaced, validity stages get their fail bits), runs each stage’s build once and returns a Program: a GraphRunner launchable whose enqueue runs the stages in order, recordable into a caller’s CUDA graph like TrackGenerator.generate; a pipeline with HOST stages runs in Segment s (its HOST stages eagerly, the rest from graphs). presets.track builds the default track spec with one argument per replaceable stage, and TrackGenerator(spec) runs any spec.

The relax, width, elevation and validity stages come from track_gen.plugin.registry (TrackGen’s built-ins and third-party plugins); the others (resample, frames, offset, lift) are TrackGen’s built-ins. BUILTIN_FAIL_BITS names the valid.fail bits of the built-in checks.

A gate pipeline (BatchSpec.max_gates set; presets.gates, GateGenerator(spec)) builds a Program whose result is a GateSequence: a gate generator’s anchors (or a track generator’s stations), an order stage, the sphere solve, an elevation stage (the track ones, rewired to the gates), the lift, tangents, frames and validity.gate_default (GATE_FAIL_BITS).

build(spec, out=...) (and the facades’ out=) writes into a caller-owned result, a view of a larger one included (mixed batches); randomize.uniform stages (presets.track(randomize=...)) draw dynamic params per env in the graph; generator="external" ingests caller-provided centerlines through Program.inputs (InputInfo), loaded with load_centerlines().

Producer documents the generator surface a Course drives (result, layout, enqueue(ctx, *, advance, mask), and optional members): a Program implements it, and so can a third-party producer (e.g. a mixed batch of generators writing views of one result; MixedBatch itself is not shipped).

This package is public API under the package’s deprecation policy (a deprecated name warns and keeps working through 0.x; see API reference). import track_gen does not import it.

Specs

class StageRef[source]

Bases: object

One stage slot of a PipelineSpec.

kind

The stage kind ("generator", "gate_generator", "resample", "relax", "order", …).

Type:

str

name

The stage name within its kind ("bezier", "xpbd", …).

Type:

str

params

The stage’s Params instance, a mapping of its fields (frozen here, coerced and validated by build), or None for its defaults (the generator: Params.from_legacy of a default config).

Type:

Any

id

The slot id (default: kind). Two stages of one kind need explicit ids. Must not contain "/" (it namespaces the stage’s scratch and salts).

Type:

str | None

ports

local port -> channel rewiring, applied to the stage’s read and write ports.

Type:

Mapping[str, str]

capacity

The (centerline) generator’s {"raw": num_points}: the DEFAULT raw point count (num_points, 256 when absent), which a Stage generator’s own Stage.capacity() overrides. build rejects any other key, and any capacity on another stage (a gate generator’s capacity is its Stage.capacity()).

Type:

Mapping[str, int]

__init__(kind, name, params=None, id=None, ports=<factory>, capacity=<factory>)
Parameters:
Return type:

None

classmethod __new__(*args, **kwargs)
class BatchSpec[source]

Bases: object

The batch every stage shares.

num_envs

E.

Type:

int

device

The Warp device string.

Type:

str

n_max

Per-env point capacity of the centerline channels (N_max); in a gate pipeline, read only by a centerline sub-pipeline (stations).

Type:

int

closed

Closed loops: a bool, True (closed=False is refused by build in 0.x, naming the stages that support closed loops only; every Layout the planner builds carries it).

Type:

bool

scale

TrackGenConfig.scale, handed to the stages as StageBuilder.scale.

Type:

float

on_invalid

What an invalid env (fail_mask != 0) leaves in the Track: "keep" (default: its computed geometry, valid = 0) or "poison" (count = 0, NaN geometry, length = winding = 0, so every utility’s degenerate count < 3 path fires; fail_mask and truncated are kept). A gate pipeline: count = 0, every per-gate field NaN, valid = 0 (fail_mask kept).

Type:

str

max_gates

None for a track pipeline (the result is a Track); an int >= 2 makes it a gate pipeline (e.g. gates()), whose result is a GateSequence of max_gates slots per env.

Type:

int | None

__init__(num_envs, device, n_max, closed=True, scale=1.0, on_invalid='keep', max_gates=None)
Parameters:
Return type:

None

classmethod __new__(*args, **kwargs)
class PipelineSpec[source]

Bases: object

A pipeline: the BatchSpec and the ordered stage slots.

The editing operations return a NEW spec; they only check that an anchor id exists (ValueError listing the ids). Everything else is validated by track_gen.pipeline.build().

batch

The BatchSpec.

Type:

track_gen.pipeline.spec.BatchSpec

stages

The ordered StageRef slots: the generator first (preceded only by its own randomizers, randomize.uniform stages drawing its dynamic params).

Type:

tuple[track_gen.pipeline.spec.StageRef, …]

require_caller_capturable

Refuse, at build (PlanError naming them), any CaptureClass.HOST stage: the Program is then recordable into a caller’s capture as a whole. Default False (HOST stages run eagerly between the graph launches of the rest; a caller capture of such a Program raises NotCapturableError).

Type:

bool

property ids: tuple[str, ...]

The slot id of every stage, in order.

replace(stage_id, ref)[source]

Put ref in slot stage_id; ref keeps the slot’s id when its own id is None.

Parameters:
Return type:

PipelineSpec

insert_after(stage_id, ref)[source]

Insert ref right after slot stage_id.

Parameters:
Return type:

PipelineSpec

insert_before(stage_id, ref)[source]

Insert ref right before slot stage_id.

Parameters:
Return type:

PipelineSpec

remove(stage_id)[source]

Drop slot stage_id.

Parameters:

stage_id (str)

Return type:

PipelineSpec

with_randomize(randomize)[source]

This spec with per-env uniform draws of DYNAMIC float32 params: each a randomize.uniform stage (id "randomize.<stage>.<field>") inserted right before the stage it draws.

The helper behind presets.track(randomize=) / presets.gates(randomize=), for a spec built any other way, e.g. from a config:

from track_gen import (GateGenConfig, GateGenerator, PerEnvSeededRNG,
                       TrackGenConfig, TrackGenerator)
from track_gen.pipeline import legacy

E, d = 64, "cuda:0"
rng = PerEnvSeededRNG(seeds=0, num_envs=E, device=d)
cfg = TrackGenConfig(num_envs=E, device=d, z_profile="uniform", z_max=0.6)
spec = legacy.to_pipeline_spec(cfg).with_randomize(
    {"generator.rad": (0.1, 0.35), "elevation.z_max": (0.2, 0.6)})
gen = TrackGenerator(spec, rng)
# a GateGenConfig: its gate spec, then GateGenerator
gcfg = GateGenConfig(num_envs=E, device=d, generator="polar")
spec = legacy.to_gate_pipeline_spec(gcfg).with_randomize(
    {"generator.polar_radial_jitter": (0.2, 0.8)})
gen = GateGenerator(spec, rng)

A field the spec already randomizes is refused, not replaced: to change a draw range (e.g. at a curriculum phase), replace that randomizer, spec.replace("randomize.generator.rad", StageRef("randomize", "uniform", {"stage": "generator", "field": "rad", "lo": 0.1, "hi": 0.4})), or write its live lo / hi (gen.params["randomize.generator.rad"].set("hi", 0.4)).

Parameters:

randomize – {"<stage id>.<field>": (lo, hi)} (the key split at its LAST dot) or a sequence of StageRef("randomize", "uniform", {...}); None returns this spec. The course size is "generator.size" (the track generators but external, the bezier / hull gate generators) or "normalize.size" (the polar / voronoi / checkpoint gate pipelines), the bar width "gate_frames.gate_width".

Raises:
  • ValueError – a malformed key, a target stage the spec does not have (lists the stage ids), or a field (or a stage id) the spec already has a randomizer for.

  • TypeError – a range that is not a (lo, hi) pair, or a sequence item that is not a StageRef("randomize", ...).

Return type:

PipelineSpec

The field’s rules (dynamic, float32, the range inside its declared bounds, the cross-field rules) are checked by track_gen.pipeline.build().

to_dict()[source]

The spec as JSON data ("format": "track_gen.pipeline/2"): Params become {field: value} (tuples as lists, enums as their values); "require_caller_capturable": true only when it is set.

Return type:

dict

classmethod from_dict(data)[source]

Read to_dict() data back (params stay mappings; build coerces them). No registry is needed.

track_gen.pipeline/1 data (written by earlier releases) is migrated, with one DeprecationWarning: the half_width params of relax.xpbd / offset.default / validity.default are dropped (each must equal the width stage’s half_width, else ValueError), every width stage between the first relax and the first frames stage moves to just before that relax, and a relax.xpbd without a width stage before it (a relaxed_stations gate spec in format /1) gets width.constant with that half-width. With width.constant (or no width stage) the migrated spec builds the outputs of the /1 spec byte for byte; another width stage now runs before relax and is read per point by XPBD and the offset, so its outputs change (the warning says so). A width stage after the first frames stage of a spec whose relax.xpbd has no width before it (an earlier width stage reading cl.kappa) is refused with ValueError: it cannot be both the width XPBD reads and a stage after frames.

Parameters:

data (Mapping)

Return type:

PipelineSpec

__init__(batch, stages, require_caller_capturable=False)
Parameters:
Return type:

None

Presets

Preset pipelines: track() builds the default track PipelineSpec and gates() the default gate one, with one argument per replaceable stage.

track() reads the registry only for what shapes the stage list (the generator’s provides, and whether a width stage’s Params has a half_width field); an unknown name is not an error there: it fails at track_gen.pipeline.build() (or TrackGenerator construction) with the registry’s message. gates() reads the registry for the generator (its kind, provides, orderings, preferred and attributes) and the stations stage’s Params.

A stage is selected as a name ("xpbd"), a (name, params) pair (params: a Params instance, a mapping of its fields, or None for its defaults) or a StageRef of the slot’s kind; a plugin’s stage is selected the same way, by its registered name (relax="acme.taubin"). Edit the result with the PipelineSpec operations:

from track_gen import TrackGenerator
from track_gen.pipeline import StageRef, presets

spec = presets.track(num_envs=64, device="cuda:0",
                     relax=("xpbd", {"relax_iters": 40}),
                     elevation=StageRef("elevation", "noise",
                                        {"z_min": -0.3, "z_max": 0.3,
                                         "z_noise_amplitude": 0.2}))
spec = spec.with_randomize({"generator.rad": (0.2, 0.4)})
gen = TrackGenerator(spec)
track(*, num_envs, device, n_max=384, scale=1.0, size=1.0, generator='bezier', generator_params=None, num_points=256, half_width=0.1, spacing=None, relax='auto', width='constant', elevation='flat', checks=('default',), on_invalid='keep', z_base=0.0, registry=None, randomize=None)[source]

The track pipeline: generator -> resample -> width -> relax -> uniform resample -> frames -> offset -> elevation -> checks -> lift (the width runs before relax: cl.half_width is the per-point half-width XPBD, the offset and validity read).

presets.track(num_envs=E, device=d) has the stages of the spec TrackGenerator(TrackGenConfig(num_envs=E, device=d)) builds, and the generators produce byte-identical Tracks (the two specs do not compare equal: the preset’s stages carry plain values or no params where the config’s carry Params instances).

A generator that provides attributes has the stage computing them dropped: "z" (its raw.z is carried to cl.z): no elevation stage, lift.elevated; "half_width" (raw.half_width -> cl.half_width): no width stage.

Parameters:
  • num_envs (int) – E.

  • device (str) – The Warp device.

  • n_max (int) – Per-env point capacity (N_max).

  • scale (float) – TrackGenConfig.scale.

  • size (float) – The INITIAL per-env course size: the generator’s dynamic size (generator.size; TrackGenConfig.size), a multiplier applied where the generator applies scale (the plan view only: widths and z are absolute). 1.0 (default) adds nothing to the spec. A generator that declares no size (external) raises ValueError, as does a size in its params too. Per-env values: gen.params["generator"], randomize={"generator.size": (lo, hi)}. Size up with n_max.

  • generator (str | StageRef) – A registered generator name, or a StageRef("generator", ...) (then pass its params inside it).

  • generator_params – The generator’s params (a Params instance or a mapping; None: its defaults).

  • num_points (int) – The generator’s default raw point count (capacity["raw"]).

  • half_width (float) – The track half-width: the value of a width stage whose Params has a half_width field (width.constant, or a plugin’s), unless its params set it, and the default spacing. A width stage without the field (width.from_channel) or a generator that provides its width takes its width elsewhere: then an explicit half_width other than 0.1 with an explicit spacing reaches nothing and raises ValueError.

  • spacing (float | None) – The constant-spacing step (default 0.6 * half_width).

  • relax (str | tuple | StageRef | None) – The relax stage (None: relax.none). "auto" (default): the generator’s preferred["relax"] ("none" for generator/external: ingested geometry is not moved), else "xpbd" (every built-in generator: unchanged output). An explicit name overrides it (relax can repair ingested tracks).

  • width (str | tuple | StageRef) – The width stage (runs before relax).

  • elevation (str | tuple | StageRef | None) – The elevation stage (None: elevation.flat). The lift is lift.flat for elevation.flat, else lift.elevated.

  • checks (Sequence[str | tuple | StageRef]) – The validity stages, in order: a sequence of selections whose first is "default" (validity.default; plugin checks follow it). The first one’s id is "validity"; each further one’s is its StageRef.id or its name.

  • on_invalid (str) – "keep" (default) or "poison" (BatchSpec.on_invalid).

  • z_base (float) – The altitude of the flat lift (elevation.flat; the flat TrackGenConfig.z_base). Another elevation takes its altitude from its own params, so a nonzero z_base with it raises ValueError.

  • registry – The plugin registry the generator’s provides and a width stage’s Params are looked up in (default track_gen.plugin.registry; an unknown name is reported by build).

  • randomize – Per-env uniform draws of DYNAMIC float32 params: {"<stage id>.<field>": (lo, hi)} (e.g. {"generator.rad": (0.1, 0.35)} or {"generator.polar_radial_jitter": (0.2, 0.8), "elevation.z_max": (0.2, 0.6)} (with a non-flat elevation) on the built-ins’ dynamic knobs, {"generator.amp": (0.05, 0.35)} for a plugin generator’s, {"width.scale": (0.8, 1.2)} with width.from_channel) or a sequence of StageRef("randomize", "uniform", {...}). Each becomes a randomize.uniform stage (id "randomize.<stage>.<field>") inserted right before its target (with_randomize()). The range must lie inside the field’s declared bounds; a static knob is refused at build (listing the pipeline’s drawable fields).

Raises:
  • ValueError – an invalid selection (wrong StageRef kind, width=None, checks not starting with "default", a duplicate check id, an unknown on_invalid, generator_params with a generator StageRef, params setting a half_width other than half_width, elevation.flat params setting a non-zero z_base, a width other than the default, a non-flat elevation or a non-zero z_base for a generator that provides them, a half_width that reaches no stage).

  • TypeError – a selection of another type, or checks given as one selection instead of a sequence.

Return type:

PipelineSpec

gates(*, num_envs, device, generator='bezier', generator_params=None, source='auto', gates=None, max_gates=None, min_gates=4, order=None, stations=None, gate_radius=0.025, gate_solve_iters=8, gate_width=0.0, gate_align='yaw_only', elevation='flat', z_base=0.0, z_valid_grade=0.0, checks=('gate_default',), scale=1.0, size=1.0, on_invalid='keep', n_max=384, num_points=256, half_width=0.1, spacing=None, relax='auto', width='constant', spacing_metric='xy', elevation_on=None, registry=None, randomize=None)[source]

The gate pipeline: generator -> order -> [normalize] -> gate_relax -> [gate_arclen -> elevation] -> lift -> tangents -> gate_frames -> checks.

presets.gates(num_envs=E, device=d) equals the spec of GateGenerator(GateGenConfig(num_envs=E, device=d)) (same stages, equal params; byte-identical GateSequences).

The generator is either a gate generator (source="anchors": a registered gate_generator, whose anchors the order stage orders), or any track generator (source="stations": gates stations at equal arc length on its raw loop; "relaxed_stations": on the relaxed centerline, after the track pipeline’s resample and relax stages). source="auto" picks "anchors" for a name registered as a gate generator (the built-in names are both: anchors win), else "stations". A generator that provides per-anchor attributes has the stages computing them dropped (z: gate_arclen and elevation; tangent: tangents; half_size + roll: gate_frames.posed; validity.gate_default checks the bars of every env that has one, whatever its check_crossing).

Parameters:
  • num_envs (int) – E.

  • device (str) – The Warp device.

  • generator (str | StageRef) – A gate generator or track generator name, or a StageRef of kind "gate_generator" / "generator".

  • generator_params – The generator’s params (None: its defaults).

  • source (str) – "auto", "anchors", "stations" or "relaxed_stations".

  • gates (int | None) – The station count of a centerline source (default 12; it fills the gates field of the stations stage’s params when they do not set it).

  • max_gates (int | None) – BatchSpec.max_gates (default: 32 for anchors, the station count for stations).

  • min_gates (int) – validity.gate_default’s fewest real gates.

  • order (str | tuple | StageRef | None) – The order stage (default: the generator’s preferred["order"] when it accepts it, else "ccw" when it accepts it, else the first order it accepts; "raw" for stations).

  • stations (str | tuple | StageRef | None) – The stations stage (default stations.raw / stations.relaxed with {"gates": gates}).

  • gate_radius (float) – The sphere solve (gate_relax.spheres, or gate_relax.none without iterations or radius) and the spacing check.

  • gate_solve_iters (int) – The sphere solve (gate_relax.spheres, or gate_relax.none without iterations or radius) and the spacing check.

  • gate_width (float) – The FULL bar width (half_size = 0.5 * gate_width).

  • gate_align (str) – "yaw_only" or "full_tangent".

  • elevation (str | tuple | StageRef | None) – The elevation stage (None: flat), its ports rewired to the gates by the planner.

  • z_base (float) – The altitude of every real gate with elevation.flat (its z_base; GateGenConfig.z_base). Another elevation takes its altitude from its own params, so a nonzero z_base with it (or with a generator that provides z) raises ValueError.

  • z_valid_grade (float) – The validity grade cap (0: off).

  • checks (Sequence[str | tuple | StageRef]) – The validity stages: "gate_default" first, plugin checks after. validity.gate_default gets min_gates, gate_radius, gate_width and z_valid_grade unless its params set them (a Params instance is taken as is). Its gate_width and check_crossing are inert (the bars of every env that has one are checked); posed gates still get check_crossing="always", filled only so that existing posed specs stay identical.

  • scale (float) – BatchSpec.scale.

  • size (float) – The INITIAL per-env course size (GateGenConfig.size), routed to the stage that holds it: normalize.size for a gate generator re-normalized by normalize.extent (polar / voronoi / checkpoint; also one that declares a size of its own, which the normalization erases), else generator.size for a gate generator that declares it (bezier / hull) and for a stations source’s track generator. 1.0 (default) adds nothing to the spec; a generator with neither raises ValueError, as does a size in its params too.

  • on_invalid (str) – "keep" or "poison".

  • n_max (int) – The centerline sub-pipeline of a stations source (as track(); relax="auto": the generator’s preferred["relax"], else "xpbd"): a stations source reads n_max and num_points, a relaxed_stations source all six; setting one that the source does not read raises ValueError.

  • num_points (int) – The centerline sub-pipeline of a stations source (as track(); relax="auto": the generator’s preferred["relax"], else "xpbd"): a stations source reads n_max and num_points, a relaxed_stations source all six; setting one that the source does not read raises ValueError.

  • half_width (float) – The centerline sub-pipeline of a stations source (as track(); relax="auto": the generator’s preferred["relax"], else "xpbd"): a stations source reads n_max and num_points, a relaxed_stations source all six; setting one that the source does not read raises ValueError.

  • spacing (float | None) – The centerline sub-pipeline of a stations source (as track(); relax="auto": the generator’s preferred["relax"], else "xpbd"): a stations source reads n_max and num_points, a relaxed_stations source all six; setting one that the source does not read raises ValueError.

  • relax (str | tuple | StageRef | None) – The centerline sub-pipeline of a stations source (as track(); relax="auto": the generator’s preferred["relax"], else "xpbd"): a stations source reads n_max and num_points, a relaxed_stations source all six; setting one that the source does not read raises ValueError.

  • width (str | tuple | StageRef) – The centerline sub-pipeline of a stations source (as track(); relax="auto": the generator’s preferred["relax"], else "xpbd"): a stations source reads n_max and num_points, a relaxed_stations source all six; setting one that the source does not read raises ValueError.

  • spacing_metric (str) – "xy" (default: the stations are equally spaced in plan view) or "xyz" (in 3D arc length: the loop needs its altitude, from a generator that provides z or an elevation run on the relaxed centerline; ValueError when the loop has none). Filled into the stations stage’s params (a stations source only).

  • elevation_on (str | None) – Where the elevation runs for source="relaxed_stations": "gates" (the gates’ own profile, after the order) or "centerline" (on the relaxed centerline, before the stations, whose altitude the gates then carry by index). Default: "centerline" for "xyz", "gates" for "xy". An explicit value with a generator that provides z (no elevation runs) raises ValueError.

  • registry – The plugin registry the generator is looked up in (default track_gen.plugin.registry).

  • randomize – Per-env uniform draws of DYNAMIC float32 params, as track() (e.g. {"generator.polar_radial_jitter": (0.2, 0.8)} on the polar gate generator, {"elevation.z_noise_amplitude": (0.1, 0.6)} with an open window such as elevation=("noise", {"z_min": -0.5, "z_max": 0.5}) (the default [0, 0] window clamps every hill away), or {"generator.m": (3.0, 9.0)} for a plugin gate generator’s dynamic m): each a randomize.uniform stage inserted right before its target.

Raises:
  • ValueError – an unknown generator name, an invalid selection or argument (gates= with an anchors source, gates= and a stations selection setting another count, order= with a legacy gate generator, an explicit elevation, z_base or gate_width for a generator that provides them, a nonzero z_base with another elevation than flat, a centerline argument the source does not read, validity.gate_default params setting another gate_radius / gate_width, gate_align, on_invalid).

  • TypeError – a selection of another type.

  • track_gen.plugin.PluginLoadError – the generator belongs to a quarantined plugin.

Return type:

PipelineSpec

StageSel

The type of a preset’s stage keyword: a stage name, a (name, params) pair, a StageRef, or None.

From a configuration

TrackGenConfig / GateGenConfig <-> PipelineSpec: the default stage lists.

to_pipeline_spec() is what TrackGenerator(config) builds, and to_gate_pipeline_spec() what GateGenerator(config) builds: the default track and gate stage lists, with each stage’s params read from the config’s flat fields. A config keeps the outputs it had before the pipeline existed, launch for launch.

to_pipeline_spec(config, *, registry=None)[source]

The default PipelineSpec of a TrackGenConfig.

Ids, in order: generator, resample (resample.constant_spacing), width (width.constant, before relax), relax (relax.<relax_solver>, or relax.none when relax_enable=False), uniform (resample.uniform), frames, offset, elevation (elevation.<z_profile>), validity and lift (lift.flat on the flat profile, else lift.elevated). A generator that provides "z" has no elevation (lift.elevated), one that provides "half_width" no width stage. Each stage’s params are its Params class’s from_legacy(config) (the flat field names); the generator’s are its resolved generator_params. relax_solver and z_profile name any stage registered under relax / elevation; a half_width field of their Params is config.half_width (the stages with the field share the nominal width).

Parameters:
  • config – A TrackGenConfig.

  • registry – The plugin registry the generator, relax_solver and z_profile are resolved through (default track_gen.plugin.registry).

Raises:
  • TypeError – config is a GateGenConfig (use to_gate_pipeline_spec()).

  • ValueError – an unknown or oracle-only relax_solver, an unknown z_profile, smooth_finish=True (as TrackGenerator), the generator’s params are invalid, a non-flat z_profile / non-zero z_base for a generator that provides z, or a size other than 1 for a generator that declares no size (or whose Params do not map it).

  • track_gen.plugin.PluginLoadError – relax_solver / z_profile names a stage of a quarantined plugin.

Return type:

PipelineSpec

to_gate_pipeline_spec(config, *, registry=None)[source]

The default gate PipelineSpec of a GateGenConfig (what GateGenerator(config) builds).

Ids, in order: generator (gate_generator.<generator>), order (order.<gate_ordering>; gate_ordering=None is the generator’s default order, stages.gates._default_order: its preferred["order"] when it accepts it, else "ccw" when it accepts it, else the first order it accepts; "ccw" for the five built-ins), normalize (normalize.extent, for a generator whose preferred["normalize"] is "extent": polar, checkpoint, voronoi), the carry_<attr> of each attribute the generator provides, gate_relax (gate_relax.spheres, or gate_relax.none when gate_solve_iters == 0 or the float32 2 * gate_radius is 0), gate_arclen (unless the profile is flat), elevation (elevation.<z_profile>; both dropped when the generator provides z), lift (lift.gates), tangents (tangents.central, dropped when it provides tangent), gate_frames and validity (validity.gate_default). A legacy GateGeneratorSpec orders its own gates: no order / normalize stage; it reads the resolved name from its run config (never None). BatchSpec(n_max=384, max_gates=config.max_gates, scale=config.scale).

Raises:
  • ValueError – an unknown generator, gate_ordering (not a registered order, or not one the generator supports) or z_profile; min_gates / max_gates incompatible with the generator’s capacity; an explicit z_profile / z_base / gate_width for a generator that provides them; invalid generator params; an elevation plugin whose Params.from_legacy reads a field GateGenConfig lacks; a size other than 1 no stage applies (a gate generator without size and no normalize.extent: e.g. a legacy spec).

  • track_gen.plugin.PluginLoadError – a name of a quarantined plugin.

Return type:

PipelineSpec

generator_config(spec)[source]

The TrackGenConfig build binds the generator with: the batch (num_envs, device, N_max, scale), num_points = capacity.get("raw", 256) and generator_params from the generator’s StageRef. Enough for the built-ins and Stage generators; a GeneratorSpec that reads other config fields needs TrackGenerator.

Parameters:

spec (PipelineSpec)

gate_generator_config(spec, *, registry=None)[source]

The GateGenConfig of a gate pipeline: what build binds a gate generator with, and GateGenerator._config for any gate spec. The batch (num_envs, device, max_gates, scale), the generator name and params (a centerline source: generator_params=None, its params live in its own binding), min_gates (validity.gate_default’s, clamped to max_gates), gate_ordering (the order stage’s name; a legacy generator: "ccw" if it supports it, else its first ordering), gate_width / gate_radius / gate_solve_iters / gate_align / z_valid_grade / z_profile from their stages; other fields default. A legacy GateGeneratorSpec in a PipelineSpec sees only these fields.

Parameters:

spec (PipelineSpec)

Planner and Program

build(spec, *, registry=None, seeds=None, out=None, stream=None, prepare=True)[source]

Plan, allocate and bind spec; return its Program.

The generator (the first stage) is resolved through registry exactly as the facades resolve config.generator: a centerline generator (kind="generator") with the config track_gen.pipeline.legacy.generator_config() derives from the spec, a gate generator (kind="gate_generator") with track_gen.pipeline.legacy.gate_generator_config(). The open-kind stages (relax / width / elevation / validity / order / stations) are resolved through registry too (the class is captured at build: later registry edits never change a built Program); the others come from TrackGen’s built-in catalogue. spec.batch.max_gates selects the result: a Track (None) or a GateSequence.

Parameters:
  • spec (PipelineSpec) – The PipelineSpec.

  • registry – The plugin registry (default track_gen.plugin.registry).

  • seeds – A SeedState, or anything SeedState accepts (None, an int, an [E] int32 wp.array, a PerEnvSeededRNG).

  • out – Optional pre-allocated Track (GateSequence) to write into (every field checked; generation and fail_mask may be None).

  • stream – Optional default wp.Stream of the Program.

  • prepare (bool) – Run the warm-up generation (Program.prepare()) before returning (otherwise the first Program.run() does).

Raises:
  • PlanError – an invalid spec (see Pipeline stages), incl. an unknown relax / width / elevation / validity / order / stations name (the registry’s message) and a HOST stage under PipelineSpec.require_caller_capturable.

  • ValueError – invalid params (the stage’s own message), an unknown generator (the registry’s message), an out= or seeds mismatch.

  • TypeError – params or seeds of the wrong type.

  • track_gen.plugin.PluginLoadError – a stage named explicitly is provided by a quarantined plugin.

  • track_gen.exec.CaptureError – called inside a capture (before allocating).

class Program[source]

Bases: object

A built pipeline: every buffer allocated once, every stage’s enqueue bound.

Created by track_gen.pipeline.build() (never directly). A GraphRunner launchable: run() records into a caller’s capture, replays the Program’s own graph on CUDA, or runs eagerly, exactly as TrackGenerator.generate does.

One generation (enqueue()) is: [seeds.advance(mask)] -> the effective seeds (seed) -> the dynamic-parameter / input latch -> [masked: hold the unmasked envs’ result rows] -> every stage in order (EVERY env) -> the commit (masked: restore the unmasked envs; derive valid; on_invalid="poison") -> result.generation <- epoch (masked envs only, with a mask). It allocates nothing and never host-syncs, except inside its HOST stages (their ctx.host() readbacks, uploads and waits on the call’s stream). See track_gen.stages.commit for what the commit costs (nothing for an unmasked generation of the default pipeline). The result is a Track, or a GateSequence for a gate pipeline (BatchSpec.max_gates set).

A Program whose stages include CaptureClass.HOST ones is split into segments (consecutive stages of equal host-ness): outside a capture its HOST segments run eagerly and every other segment replays its own graph on CUDA (enqueue_segment()); a caller’s capture refuses it before any launch (NotCapturableError naming host_stage_ids).

__init__(*, _token=None, spec, layout, binding, result, seeds, seed, inputs, params, pairs, enqueues, versions, scratch, stage_ids, kinds, gen_scratch, stream=None, owner=None, commit=None, fail_bits=None, fail_owner=None, capture_classes=None, segments=None, on_invalid='keep', writers=None, ports=None, attributes=None, input_info=None)[source]
Parameters:

on_invalid (str)

Return type:

None

property spec: PipelineSpec

The resolved spec, in which every StageRef.params is the stage’s Params instance (the generator’s resolved Params; None for a legacy spec without one).

property layout

The result’s Layout, whose stride is N_max for a track and max_gates for a gate pipeline (each stage was built with the layout of its own point domain).

property device

The wp.Device.

property result: Track

the GateSequence) every generation writes: the same instance, with the same array addresses, for the Program’s lifetime.

Type:

The Track (a gate pipeline

property seeds: SeedState

The per-env SeedState (seeds and epoch).

property seed: wp.array

The [E] int32 effective seed (channel "seed"), written by the prologue of every generation; stages read it as StageBuilder.seed.

property inputs: Mapping

The persistent device inputs, "regen_mask" ([E] int32, zeroed) and every input a stage declared (StageBuilder.input). Stable pointers.

Latched inputs (per="env") are snapshotted at the start of a generation, so a write takes effect for the envs the next generation regenerates. Unlatched inputs (per="raw", e.g. an ingestion generator’s centerline.*) are read DURING the generation: write them on the generation’s stream (or order the streams with events). input_info tells them apart.

property input_info: Mapping[str, InputInfo]

name -> InputInfo of every entry of inputs (its dtype, per, length, declaring stage, and whether it is LATCHED at the start of a generation or read during it).

property params: ParamTable

Runtime parameters keyed by stage id (live [E] arrays of the dynamic fields). The dynamic fields of the built-ins are the generators’ shape knobs (rad, handle_clamp_frac, hull_displacement, the polar, voronoi and checkpoint knobs), the elevation stages’ z_min / z_max / z_max_step / z_noise_amplitude, width.from_channel’s scale / lo / hi and a randomize.uniform stage’s lo / hi (see Dynamic fields); every other field is static (set raises KeyError; build a new generator to change it). A field drawn by a randomize.* stage maps to its drawn array (written every generation by the randomizer; set raises).

property capture_class: CaptureClass

The largest of the generator’s and every stage’s CaptureClass (Stage.capture_class of its params; a legacy spec: STATIC, or HOST with capturable=False): STATIC, BOUNDED (an iterative stage; still recordable into a caller’s capture) or HOST (see segments). The built-in stages are STATIC.

property segments: tuple[Segment, ...]

maximal runs of consecutive stages of equal host-ness (one segment unless some stage is HOST). Outside a capture each HOST segment runs eagerly and every other segment replays its own graph on CUDA.

Type:

The Program’s Segment s

property host_stage_ids: tuple[str, ...]

the Program is recordable into a caller’s capture).

Type:

The ids of the CaptureClass.HOST stages (()

property fail_bits: Mapping[str, int]

the built-in checks’ (BUILTIN_FAIL_BITS, bits 0-7; a gate pipeline: GATE_FAIL_BITS, bits 0-5) then each plugin validity stage’s (16, 17, … in pipeline order). Track.fail_bits / GateSequence.fail_bits is the same mapping.

Type:

name -> bit of every valid.fail bit, in assignment order

property on_invalid: str

"keep" or "poison" (BatchSpec.on_invalid).

property channels: Mapping[str, tuple[str, ...]]

channel -> ids of the stages that write it, in order ("seed": ("<prologue>",)), the carriers’ implicit writes included (a resample writing the cl image of a generator’s raw attribute).

property attributes: Mapping[str, AttributeInfo]

key -> AttributeInfo of every per-point attribute the result exports (Track.attributes / GateSequence.attributes): the source of truth for what reached the result (gate attributes are best effort). Empty when no stage exports one.

property capturable: bool

Whether the Program may be recorded into a caller’s capture (it has no HOST stage).

property legacy_stream: bool

Whether the generator is a legacy-ABI spec (launches on Warp’s current stream; see GraphRunner).

property runner: GraphRunner

The GraphRunner that runs the Program.

property prepared: bool

Whether prepare() ran (one real generation outside any capture; build(..., prepare=True) and the facades run it). Part of the track_gen.pipeline.Producer surface.

property default_stream: wp.Stream | None

The stream calls run on when they pass no stream= and no ambient scope applies (build(..., stream=), the facades’ stream=; None: resolved per track_gen.exec). Part of the track_gen.pipeline.Producer surface.

property graph: wp.Graph | None

The Program’s own CUDA graph for a plain run() (None on CPU, for a Program with HOST stages, whose capturable segments get one graph each, and before the first such CUDA call).

segment_graphs(key=None)[source]

The per-segment CUDA graphs of a Program with HOST stages (its own graph, cut at each HOST segment), one per segment in order (None at a HOST segment), for the graph key key (None: a plain run(); (True, mask.ptr or 0) for advance=True); None before the first such CUDA call outside a capture, on CPU, and for a Program without HOST stages (see graph).

Return type:

tuple | None

enqueue(ctx, *, advance=False, mask=None)[source]

Launch one generation on ctx and return result.

No allocation, no branch on device data, and no host sync except inside the HOST stages (their ctx.host() operations wait on ctx’s stream). With mask the prologue, the commit and the generation write cover the masked envs only: every other env’s Track rows end the generation bit for bit as they were (validate mask first, as run() does). They are held, recomputed by the stages and restored within the generation, all on ctx’s stream: work ordered after the generation on that stream (or after an event recorded on it) never sees the recomputed rows, a reader on another stream without such a dependency can (see track_gen.stages.commit).

Parameters:
  • ctx (LaunchContext) – The LaunchContext to emit on.

  • advance (bool) – Bump seeds.epoch first (every env, or where mask[e] != 0).

  • mask (wp.array | None) – Optional [E] int32 mask.

Return type:

Track

An iterative (BOUNDED) stage runs its full iteration bound when called eagerly on CUDA (its own CUDA graph skips converged windows on device). A legacy-ABI generator (legacy_stream) launches on Warp’s current stream: when ctx’s stream is another one, the generation runs on the current stream, ordered after and before ctx’s with events (as GraphRunner does).

Raises:
Parameters:
Return type:

Track

enqueue_segment(i, ctx, *, advance=False, mask=None)[source]

Launch segment i of one generation on ctx (see segments) and return result. Segment 0 opens with the prologue (advance, the effective seeds, the latch, the masked hold), the last one closes with the commit and the generation write; enqueue() runs every segment in order. A segment that is not HOST may be recorded into a caller’s capture once the Program is prepared (e.g. record the capturable tail, run the HOST head eagerly).

One generation is every segment, in order, on one stream, with the SAME advance and mask (a recorded segment keeps the arguments it was recorded with: run the eager segments with those). This is not checked: a replayed segment runs no Python, so the Program cannot see which arguments it had. Mixing arguments, running segments out of order or skipping one leaves the result undefined.

A legacy-ABI generator (legacy_stream) lives in segment 0 and launches on Warp’s current stream: there, segment 0 follows the rule of enqueue().

Raises:
Parameters:
Return type:

Track

prepare()[source]

Run one real generation outside any capture and wait for it (the warm-up: loads every kernel the hot path uses). build(..., prepare=True) calls it.

Raises:

track_gen.exec.CaptureError – a capture is open (see GraphRunner).

Return type:

None

run(*, stream=None, sync=False, advance=False, mask=None, policy='auto')[source]

Run one generation under the GraphRunner policy (record into a caller’s capture, replay the Program’s own graph on CUDA, or eager) and return result. A Program built with prepare=False runs prepare() first (outside a capture; inside one it raises CaptureError).

Parameters:
  • stream – Optional wp.Stream (or "torch").

  • sync (bool) – Block the host until the generation finished (never while recording).

  • advance (bool) – Bump the epoch first (see TrackGenerator.generate).

  • mask (wp.array | None) – Optional [E] int32 mask of the envs to advance; requires advance=True.

  • policy (str) – "auto" or "eager" (never builds the Program’s own CUDA graph).

Raises:
Return type:

Track

tap(channel, *, after=None)[source]

The working buffer of channel: its final version, or the version visible right after stage after (what the stages after it read).

Read-only by contract: never write it. Stable for the Program’s lifetime; it holds the last generation’s values once that generation completed on its stream. Every env is recomputed each generation; the final version of a channel the Track exports (track.*, cl.winding, cl.truncated, valid.fail, and cl.arclen2 / cl.length2 under a flat lift) IS the Track’s array, so it holds the committed values (after a masked regeneration: an unmasked env’s previous track; with on_invalid="poison": the poisoned rows). One exception: track.valid is Track.valid only when validity.default is the last writer of valid.fail. After a plugin check the commit derives Track.valid from valid.fail, and tap("track.valid") is validity.default’s own 0/1 flag, which ignores the later checks (and poison): read Track.valid, or tap("valid.fail") == 0. tap("seed") is the effective seed.

Raises:

KeyError – no stage writes channel, after is not a stage id, or channel is not written by after or an earlier stage.

Parameters:
  • channel (str)

  • after (str | None)

Return type:

wp.array

class Producer[source]

Bases: Protocol

The generator surface track_gen.course.Course drives (documentation only: the library duck-types it and never isinstance-checks it).

TrackGenerator / GateGenerator (through their program) and a built Program implement it; a third-party producer (e.g. a mixed batch of several generators writing views of one result) implements the required members, and the optional ones it needs.

Required:

  • result: the result every generation writes (a Track, a GateSequence or another result the components read), the same instance with the same array addresses for the producer’s lifetime; its valid (when present) is StepResult.valid.

  • layout: the result’s Layout (num_envs, the stride, the device).

  • enqueue(ctx, *, advance=False, mask=None): one generation on ctx (a LaunchContext), allocation-free and host-sync free. With advance: bump each env’s epoch first (every env, or the masked ones). With mask ([E] int32, read on device): the unmasked envs’ result rows end the generation bit for bit as they were (a masked commit), and an all-zero mask is an exact no-op. It may run inside a CUDA conditional-node body (the course’s masked regeneration): kernels, fills and device-to-device copies on ctx’s stream only.

Optional (read when present):

  • seeds: its SeedState (course.seeds; Course.generate(seeds=) sets owned seeds through it).

  • capture_class: a CaptureClass (default STATIC); HOST runs eagerly outside a capture and is refused inside one (NotCapturableError).

  • prepared and prepare(): an unprepared producer is prepared first (its own warm-up, loading every module enqueue uses). Without them, the producer must have loaded every module its enqueue uses before it reaches a Course (e.g. one real enqueue at its construction).

  • default_stream: the course’s default stream when Course(..., stream=) is not given.

  • capacity and bounds (name -> number mappings): the static capacities (max_loop_length) and declared bounds (size_hi) a plan would give, read by ComponentContext.for_producer before the plan.

  • inputs and input_info: its persistent device inputs (listed, never bound by the course: write them in place).

property result: object

The result that every generation writes (the same instance, with the same array addresses, for the producer’s lifetime).

property layout: Layout

The result’s Layout.

enqueue(ctx, *, advance=False, mask=None)[source]

Enqueue one generation on ctx: allocation-free and host-sync free; with advance, bump the epochs first; with mask, leave the unmasked envs’ rows bit for bit as they were (see the class docstring).

Parameters:
Return type:

object

__init__(*args, **kwargs)
class Segment[source]

Bases: object

One segment of a Program (planner rule 6): a maximal run of consecutive stages of one kind, HOST (run eagerly every call) or capturable (STATIC / BOUNDED: replayed from the Program’s own graph outside a capture).

index

The segment’s position (0 opens with the seed prologue, the last one closes with the commit).

Type:

int

capture_class

CaptureClass.HOST, or the largest of its stages’ classes (STATIC / BOUNDED).

Type:

track_gen.plugin.stage.CaptureClass

stage_ids

The ids of its stages, in order.

Type:

tuple[str, …]

property host: bool

Whether the segment holds HOST stages (it runs eagerly every call).

__init__(index, capture_class, stage_ids)
Parameters:
Return type:

None

class AttributeInfo[source]

Bases: object

One exported per-point attribute of a Program (Program.attributes): what Track.attributes[key] / GateSequence.attributes[key] holds.

key

The result key: the channel name without its domain prefix ("acme.surface_id").

Type:

str

channel

The result-domain channel whose final version is the array ("cl.acme.surface_id", "gates.acme.tag").

Type:

str

dtype

The Warp dtype (wp.float32, wp.int32, wp.vec2f or wp.vec3f).

Type:

object

interp

The carry policy its generator declared ("linear", "nearest", "previous", "angle"), None for an attribute carried by index only or written by a stage on the result’s points.

Type:

str | None

padding

The value past count[e]: NaN for floats (every vector component), -1 for int32.

Type:

object

domain

"cl" (a Track’s points) or "gates" (a GateSequence’s slots).

Type:

str

__init__(key, channel, dtype, interp, padding, domain)
Parameters:
Return type:

None

classmethod __new__(*args, **kwargs)
class InputInfo[source]

Bases: object

One persistent device input of a Program (Program.input_info): what Program.inputs[name] is and how a generation reads it.

name

The input’s name ("regen_mask", a stage’s StageBuilder.input name; "centerline.xy", … for an ingestion generator).

Type:

str

dtype

The Warp dtype.

Type:

object

per

"env" ([E]) or "raw" ([E * raw], a centerline generator’s per-point input).

Type:

str

length

The array’s length.

Type:

int

latched

True: snapshotted (latched) at the start of every generation for the envs it regenerates, like a dynamic param (a write takes effect at the next generation); False: read DURING the generation (a per="raw" input, "regen_mask"), so a writer must be stream-ordered with the generation (the same stream, or events).

Type:

bool

stage_id

The id of the stage that declared it (None for "regen_mask").

Type:

str | None

__init__(name, dtype, per, length, latched, stage_id)
Parameters:
Return type:

None

classmethod __new__(*args, **kwargs)
exception PlanError[source]

Bases: ValueError

A pipeline spec the planner refuses; raised before anything is allocated, except for the build-time rules checked right after a stage’s build (its declared write ports, its fail bits, a plugin channel’s dtype).

Fail bits

The bits of fail_mask, the fail bits that say why an env is not valid.

BUILTIN_FAIL_BITS: Mapping[str, int]

The valid.fail bits of TrackGen’s built-in checks, a read-only mapping: {"generator": 0, "turning": 1, "finite": 2, "thickness": 3, "w_floor": 4, "border": 5, "grade": 6, "truncation": 7}. Bits 8 to 15 are reserved for future built-in checks, 16 to 30 are assigned to plugin checks by StageBuilder.fail_bit(), and bit 31 is never used. Program.fail_bits and Track.fail_bits extend it with the plugin checks of a pipeline; see Fail bits.

GATE_FAIL_BITS: Mapping[str, int]

The valid.fail bits of validity.gate_default, the gate pipeline’s built-in check, a read-only mapping: {"count": 0, "finite": 1, "tangent": 2, "spacing": 3, "crossing": 4, "grade": 5}. Bits 6 to 15 are reserved, 16 to 30 go to plugin checks as for tracks. Program.fail_bits and GateSequence.fail_bits extend it with the plugin checks of a gate pipeline; see Gate fail bits.

Ingestion

Loading your own centerlines into a generator that declares the centerline.* inputs, such as the built-in external; see Ingesting your own centerlines.

load_centerlines(target, xy, *, count=None, z=None, half_width=None, attributes=None, envs=None, stream=None)[source]

Load centerlines into a generator’s centerline.* inputs (cold, blocking).

The rows reach the device inputs the generator reads (program.inputs): every generation after this call (on the generator’s stream) ingests them; a masked regeneration (generate(advance=True, mask=m)) rebuilds only the masked envs, so load new rows for those envs (envs=) before it.

Parameters:
  • target – A TrackGenerator, GateGenerator or Program whose generator declares the centerline.* inputs (generator="external").

  • xy – The plan-view points of each env: [E', m, 2] (every row m points), [E' * N, 2] (N = the raw capacity, num_points), or a sequence of E' arrays [m_e, 2] (E': the number of loaded envs). A closed loop: the last point connects to the first (do not repeat it).

  • count – Optional [E'] real point counts (default: each row’s length); a row longer than its count is cut.

  • z – The altitude of each point, as xy without its last axis: required when the generator was built with_z, refused otherwise.

  • half_width – The half-width of each point, alike; > 0. In the data’s units, like the pipeline’s spacing. Required with with_half_width.

  • attributes – {key: rows} for the ingested attributes ((key, dtype, interp) of ExternalParams.attributes): every one is required, with values of its kind (integers for int32, real numbers otherwise).

  • envs – The env indices of the rows (default: every env, in order).

  • stream – The stream of the upload. Default: resolved like generate’s (an ambient LaunchContext.scope, the generator’s constructor stream=, torch’s capture stream, Warp’s current stream, in that order), so it is ordered after in-flight generations on that stream. Inside a scope on another stream than the generator’s, or after a generate(stream=t), pass stream= (or order the streams with events).

Return type:

None

The sizing check (with a resample.constant_spacing stage): each env’s closed perimeter L (3D with z) must need floor(L / spacing) + 1 <= n_max points at the pipeline’s spacing, and at least 3: set spacing / n_max (presets.track(spacing=..., n_max=...)), or rescale the data. Exact without relax (the external default); a close pre-check with relax on (the device truncated bit stays authoritative).

Raises:
  • TypeError – a target without a Program; an input of the wrong type.

  • ValueError – a generator without centerline.xy; a shape / env-count mismatch; count outside [3, N]; a non-finite point, altitude or half-width; a half-width <= 0; values of the wrong kind (non-numeric data; non-integer or out-of-range data for an int32 attribute); an input the generator was not built with; an input it WAS built with left out (z= for with_z, half_width= for with_half_width, each ingested attribute: every per-point input is loaded with the rows); an unknown attribute; the sizing check.

  • track_gen.exec.CaptureError – called inside a capture (before any work).

Return type:

None

Built-in stage parameters

Each built-in stage’s Params. Most keep the TrackGenConfig / GateGenConfig field names (their from_legacy copies the flat values, see Pipeline stages); these take more: width.from_channel (see Width), the stations stages (see 3D station spacing), randomize.uniform (see Per-env randomization: randomize=), the external generator (see Ingesting your own centerlines), and normalize.extent, whose one field is the per-env size of the polar, voronoi and checkpoint gate generators (key normalize.size, set by GateGenConfig.size; see Course size: size). A dynamic field of these classes is a live per-env array (Domain randomization and curricula).

Params classes of the built-in track and gate stages.

Like the built-in generators’ Params, every class keeps the FLAT TrackGenConfig field names and from_legacy(config) copies the flat values (no casts), except WidthFromChannelParams (dynamic scale / lo / hi) and StationsParams (gates, spacing_metric), which have no TrackGenConfig counterpart. Every field is static except width.from_channel’s and the elevation profiles’ altitude window and shape knobs (z_min / z_max of elevation.uniform / random_walk / noise, z_max_step and z_noise_amplitude; per-env arrays, latched each generation, read by the warp_zprofile twins), normalize.extent’s size and gate_frames.default’s gate_width (the course size of the polar / voronoi / checkpoint gate generators and the per-env bar width; validity.gate_default’s gate_width / check_crossing are inert: it checks the bars of every env per env). The stage hands its Params instance (or, for elevation, a namespace built from its static view) to the legacy wrappers as their config argument, so the same values reach the same float() / int() conversions and the launches are unchanged.

validate() re-runs the TrackGenConfig.__post_init__ rules of the class’s own fields with the same messages: a spec made from a TrackGenConfig always passes; the rules exist for hand-built specs. The stage Params map the fields a config HAS (from_legacy of a GateGenConfig leaves z_control_points and check_crossing at their class defaults).

class WidthFromChannelParams[source]

Bases: Params

width.from_channel: cl.half_width = clamp(scale * src, lo, hi) from a float32 per-point channel the user wires (StageRef.ports={"src": "cl.<pkg>.<name>"}, e.g. the carried image of a generator’s raw.<pkg>.<name>). A NaN source point stays NaN (and fails the width floor). lo defaults to 0: a source that pinches below w_floor fails validity bit 4 instead of being clamped away. lo <= hi is checked at build; a run-time hi below lo clamps to lo (the kernel uses max(hi, lo) as the upper bound).

__init__(*, scale=1.0, lo=0.0, hi=1000000000.0)
Parameters:
Return type:

None

classmethod __new__(*args, **kwargs)
class StationsParams[source]

stations.raw / stations.relaxed: gates anchors at equal arc length along a centerline loop: plan-view (spacing_metric="xy") or 3D ("xyz", which needs the loop’s altitude: a generator that provides z, or an elevation run on the relaxed centerline).

classmethod from_legacy(cfg)

An instance holding the values of the config fields of the same names, for the fields cfg (a TrackGenConfig or a GateGenConfig) has; the others keep their class defaults.

__init__(*, gates=12, spacing_metric='xy')
Parameters:
  • gates (int)

  • spacing_metric (str)

Return type:

None

classmethod __new__(*args, **kwargs)
class NormalizeExtentParams[source]

normalize.extent: the per-env size of the normalized gate layout (GateGenConfig.size for the polar / voronoi / checkpoint gate generators).

classmethod from_legacy(cfg)

An instance holding the values of the config fields of the same names, for the fields cfg (a TrackGenConfig or a GateGenConfig) has; the others keep their class defaults.

__init__(*, size=1.0)
Parameters:

size (float)

Return type:

None

classmethod __new__(*args, **kwargs)

Randomizers

randomize.uniform: per-env uniform draws of another stage’s DYNAMIC param, in the graph (curriculum and domain randomization).

A randomizer is an ordinary STATIC stage of kind randomize (built-in only in 0.x), listed right BEFORE the stage whose field it draws (only the generator’s randomizers may precede the generator): every generation it writes u * (hi - lo) + lo per env into the target field’s array, keyed by the env’s effective seed and its own salt, so the draws are deterministic per (seed, epoch), differ across envs, are redrawn by advance=True (every replay of a caller graph) and reach the target in the SAME generation. Its lo / hi are dynamic (a curriculum writes them, in its own graph too). A drawn field is a single UNLATCHED array owned by the randomizer (params[stage][field] observes it; set raises). The drawable fields are the dynamic float32 fields of the pipeline: the built-in generators’ shape knobs and the elevation profiles’ window / grade / amplitude (e.g. "generator.rad", "generator.polar_radial_jitter", "elevation.z_max"; the course size, "generator.size" on every built-in track generator but external and on the bezier / hull gate generators, "normalize.size" on the polar / voronoi / checkpoint gate pipelines, and "gate_frames.gate_width"), plugin Params’ and width.from_channel’s scale / lo / hi. An int32 dynamic field (bezier / hull’s min_num_points) is not drawable in 0.x. The initial range must lie inside the field’s declared bounds (compared in float32) and the draws are clamped to them. A knob bezier’s style_sampling draws (its *_range set) is not drawable (set the range to None): style_sampling’s rad_range / handle_clamp_frac_range are superseded by randomize={"generator.rad": ..., "generator.handle_clamp_frac": ...} (different random streams, same distributions).

class RandomizeUniform[source]

Bases: Stage

randomize.uniform (see the module docstring).

__init__()
classmethod __new__(*args, **kwargs)
class RandomizeUniformParams[source]

Bases: Params

randomize.uniform: params[stage][field] = lo + u * (hi - lo) per env, u uniform in [0, 1) (clamped to the field’s declared lo / hi).

__init__(*, stage='', field='', lo=0.0, hi=1.0)
Parameters:
Return type:

None

classmethod __new__(*args, **kwargs)

The external generator

generator/external: ingest caller-provided centerlines.

A centerline generator Stage (kind="generator", name="external") that reads the raw closed centerline of every env from device inputs the caller fills (Program.inputs, stable pointers; track_gen.pipeline.load_centerlines() loads numpy data outside the hot path, or the caller’s own graph writes them on the generation’s stream), so a centerline from elsewhere (a dataset, a learned model, a hand-drawn track) runs through the standard pipeline (resample, width, frames, offset, validity, gates through stations, …). Built only from the public plugin surface: StageBuilder.input(per="raw"), Stage.declare, Stage.seeded = False and Stage.preferred = {"relax": "none"} (presets.track(relax="auto") runs no relax: the ingested geometry is not moved; pass relax="xpbd" to repair it).

Inputs (N = the raw capacity, presets.track(num_points=N)):

  • centerline.xy: [E * N] vec2f, UNLATCHED (read during the generation); initial value: a circle of radius 0.6 * scale per env (a fresh generator yields valid tracks at the default scale / half_width / spacing; with settings in the data’s units the placeholder is too small for the spacing: load centerlines before the first generation);

  • centerline.count: [E] int32, LATCHED like every per-env input; the real point count of each row (initial N);

  • centerline.z (with_z): [E * N] float32, the altitude (provides z);

  • centerline.half_width (with_half_width): [E * N] float32, the half-width (provides half_width; initial 0.1 * scale, a placeholder);

  • centerline.<pkg>.<name> per ingested attribute: [E * N] of its dtype, carried with its interp and exported as Track.attributes["<pkg>.<name>"].

Per generation: each input is copied first (the snapshot the pipeline consumes), padded past count[e] with the row’s last real point (exact: the padding slots are zero-length segments, so the resample sees the count[e]-point loop), then the validity is computed on the COPIES: gen.valid[e] = 1 iff 3 <= count[e] <= N, the first count[e] points are finite and, when present, z is finite and half_width finite and > 0 (else fail bit 0, "generator"; poisoned with on_invalid="poison").

No per-env size: the ingested geometry is taken as given (a size would scale it about the data’s own origin while its altitude, half-width and attributes stay, and load_centerlines()’ host sizing check could not see it). Scale centerline.xy in your own graph; generator.size (a randomizer, set("size"), a flat TrackGenConfig.size other than 1) raises with that hint.

class External[source]

Bases: Stage

generator/external (see the module docstring).

kind: ClassVar[str] = 'generator'
name: ClassVar[str] = 'external'
output: ClassVar[str | None] = 'centerline'
Params

alias of ExternalParams

capture: ClassVar[CaptureClass] = 0
writes: ClassVar[Mapping[str, str]] = {'valid': 'gen.valid', 'xy': 'raw.xy'}
preferred: ClassVar[Mapping[str, str]] = {'relax': 'none'}
seeded: ClassVar[bool] = False
classmethod declare(params)[source]

(writes, provides) for these params: raw.xy and gen.valid always; raw.z and provides z with with_z; raw.half_width and provides half_width with with_half_width; one raw.<key> per declared attribute (see track_gen.plugin.Stage.declare()).

build(b, p)[source]

Allocate everything through b, ONCE, and return enqueue(ctx).

enqueue emits only ctx.launch / ctx.copy / ctx.fill / ctx.bounded_loop / track_gen.ops work: no allocation, no host sync, no branch on device data or on ctx.capturing (a HOST stage reaches the host through ctx.host()).

class ExternalParams[source]

Bases: Params

generator/external: which per-point channels the caller provides.

__init__(*, with_z=False, with_half_width=False, attributes=())
Parameters:
Return type:

None

classmethod __new__(*args, **kwargs)

The other built-in stages

Their fields keep the TrackGenConfig / GateGenConfig names, and from_legacy reads them from a config.

class ConstantSpacingParams[source]

resample.constant_spacing: arc-length resample of the raw loop to a constant step, padded to N_max.

classmethod from_legacy(cfg)

An instance holding the values of the config fields of the same names (every field of this class is a flat TrackGenConfig / GateGenConfig field), uncast.

__init__(*, spacing=0.06)
Parameters:

spacing (float)

Return type:

None

classmethod __new__(*args, **kwargs)
class ElevationFlatParams[source]

elevation.flat: every real point at z_base. On a gate pipeline it fills gates.z with z_base (GateGenConfig.z_base); on a track it must stay 0 (the flat lift adds presets.track(z_base=...)).

classmethod from_legacy(cfg)

An instance holding the values of the config fields of the same names, for the fields cfg (a TrackGenConfig or a GateGenConfig) has; the others keep their class defaults.

__init__(*, z_base=0.0)
Parameters:

z_base (float)

Return type:

None

classmethod __new__(*args, **kwargs)
class ElevationNoiseParams[source]

elevation.noise: periodic harmonic noise around z_base (z_min / z_max / z_noise_amplitude dynamic: the difficulty knob is z_noise_amplitude in a wide, symmetric window).

classmethod from_legacy(cfg)

An instance holding the values of the config fields of the same names, for the fields cfg (a TrackGenConfig or a GateGenConfig) has; the others keep their class defaults.

__init__(*, z_base=0.0, z_min=0.0, z_max=0.0, z_noise_amplitude=0.0, z_noise_harmonics=3)
Parameters:
Return type:

None

classmethod __new__(*args, **kwargs)
class ElevationRandomWalkParams[source]

elevation.random_walk: a grade-capped random walk over the knots (z_min / z_max / z_max_step dynamic: the difficulty knob is z_max_step in a wide window).

classmethod from_legacy(cfg)

An instance holding the values of the config fields of the same names, for the fields cfg (a TrackGenConfig or a GateGenConfig) has; the others keep their class defaults.

__init__(*, z_base=0.0, z_min=0.0, z_max=0.0, z_max_step=0.0, z_control_points=10)
Parameters:
Return type:

None

classmethod __new__(*args, **kwargs)
class ElevationUniformParams[source]

elevation.uniform: i.i.d. knot altitudes in [z_min, z_max] (z_min / z_max dynamic: the difficulty knob is z_max with z_min fixed).

classmethod from_legacy(cfg)

An instance holding the values of the config fields of the same names, for the fields cfg (a TrackGenConfig or a GateGenConfig) has; the others keep their class defaults.

__init__(*, z_min=0.0, z_max=0.0, z_control_points=10)
Parameters:
Return type:

None

classmethod __new__(*args, **kwargs)
class GateFramesParams[source]

gate_frames.default: the gate frames, half_size = 0.5 * gate_width (gate_width is dynamic: the initial value of a per-env width).

classmethod from_legacy(cfg)

An instance holding the values of the config fields of the same names, for the fields cfg (a TrackGenConfig or a GateGenConfig) has; the others keep their class defaults.

__init__(*, gate_width=0.0, gate_align='yaw_only')
Parameters:
Return type:

None

classmethod __new__(*args, **kwargs)
class GateFramesPosedParams[source]

gate_frames.posed: the gate frames of a generator that provides per-gate half_size and roll.

classmethod from_legacy(cfg)

An instance holding the values of the config fields of the same names, for the fields cfg (a TrackGenConfig or a GateGenConfig) has; the others keep their class defaults.

__init__(*, gate_align='yaw_only')
Parameters:

gate_align (str)

Return type:

None

classmethod __new__(*args, **kwargs)
class GateRelaxParams[source]

gate_relax.spheres: separate overlapping gate spheres (plan view) to a centre distance of 2 * gate_radius, for at most gate_solve_iters sweeps.

classmethod from_legacy(cfg)

An instance holding the values of the config fields of the same names, for the fields cfg (a TrackGenConfig or a GateGenConfig) has; the others keep their class defaults.

__init__(*, gate_radius=0.025, gate_solve_iters=8)
Parameters:
  • gate_radius (float)

  • gate_solve_iters (int)

Return type:

None

classmethod __new__(*args, **kwargs)
class GateValidityParams[source]

validity.gate_default: the fused per-env gate validity check. The bar-crossing check runs per env on every env with a non-zero bar, so gate_width and check_crossing are INERT (kept so specs and dicts load; a gate_width other than its default 0 must still equal gate_frames’ initial width; removed in 1.0).

classmethod from_legacy(cfg)

An instance holding the values of the config fields of the same names, for the fields cfg (a TrackGenConfig or a GateGenConfig) has; the others keep their class defaults.

__init__(*, min_gates=4, gate_radius=0.025, gate_width=0.0, z_valid_grade=0.0, check_crossing='width')
Parameters:
Return type:

None

classmethod __new__(*args, **kwargs)
class LiftFlatParams[source]

lift.flat: the constant altitude of every point.

classmethod from_legacy(cfg)

An instance holding the values of the config fields of the same names (every field of this class is a flat TrackGenConfig / GateGenConfig field), uncast.

__init__(*, z_base=0.0)
Parameters:

z_base (float)

Return type:

None

classmethod __new__(*args, **kwargs)
class OffsetParams[source]

offset.default: the outer / inner borders at cl.half_width (the per-point half-width channel the width stage writes).

classmethod from_legacy(cfg)

An instance holding the values of the config fields of the same names (every field of this class is a flat TrackGenConfig / GateGenConfig field), uncast.

__init__()
Return type:

None

classmethod __new__(*args, **kwargs)
class ValidityParams[source]

validity.default: the fused per-env validity gate (the thickness and width floor checks read cl.half_width).

classmethod from_legacy(cfg)

An instance holding the values of the config fields of the same names (every field of this class is a flat TrackGenConfig / GateGenConfig field), uncast.

__init__(*, turning_tol=0.1, w_floor=0.001, relax_tol=0.02, z_valid_grade=0.0, validity_border_check=False)
Parameters:
Return type:

None

classmethod __new__(*args, **kwargs)
class WidthConstantParams[source]

width.constant: one half-width for every point.

classmethod from_legacy(cfg)

An instance holding the values of the config fields of the same names (every field of this class is a flat TrackGenConfig / GateGenConfig field), uncast.

__init__(*, half_width=0.1)
Parameters:

half_width (float)

Return type:

None

classmethod __new__(*args, **kwargs)
class XpbdParams[source]

relax.xpbd: the XPBD relaxation (band / rest length setup + solve). The half-width is cl.half_width (the width stage runs before relax).

classmethod from_legacy(cfg)

An instance holding the values of the config fields of the same names (every field of this class is a flat TrackGenConfig / GateGenConfig field), uncast.

__init__(*, relax_band=None, relax_iters=50, relax_margin=0.15, relax_sep_relax=1.0, relax_spc_relax=1.0, relax_bend_relax=1.5, relax_sep_every=20, relax_sep_cache_slots=16, relax_sep_cache_skin=0.5, relax_accel='chebyshev', relax_cheby_rho=0.98, relax_cheby_gamma=0.9, relax_cheby_start=8, relax_smooth_passes=5, relax_smooth_spacing_iters=10)
Parameters:
  • relax_band (int | None)

  • relax_iters (int)

  • relax_margin (float)

  • relax_sep_relax (float)

  • relax_spc_relax (float)

  • relax_bend_relax (float)

  • relax_sep_every (int)

  • relax_sep_cache_slots (int)

  • relax_sep_cache_skin (float)

  • relax_accel (str)

  • relax_cheby_rho (float)

  • relax_cheby_gamma (float)

  • relax_cheby_start (int)

  • relax_smooth_passes (int)

  • relax_smooth_spacing_iters (int)

Return type:

None

classmethod __new__(*args, **kwargs)