Results

The result objects a generator writes, their Layout, the capability protocols the runtime utilities accept, and the check they run; Results: Track, GateSequence, Centerline explains how to read them. Every result holds per-env arrays ([E], such as valid) and per-point arrays ([E * stride]). Track, GateSequence, Layout and the protocols are also importable from track_gen, and track_gen.plugin.Layout is the same class as track_gen.results.Layout.

The result objects: Track, GateSequence, Centerline, their Layout, the capability protocols and check_source().

Every result is flat, fixed-stride device storage: per-point arrays [E * stride] (NaN past count[e]; int32 -1) and per-env arrays [E], described by a Layout (num_envs, stride, device, closed). Each result class offers:

  • validate(need=()): check every field against the layout (host only); a hand-built result with a mismatched field is rejected with a message naming the field and the expected shape;

  • view(e0, e1): a zero-copy env slice (kernel-safe: every kernel indexes from the view’s element 0), e.g. TrackGenerator(..., out=big.view(0, 4)) for mixed batches;

  • copy_from(src, *, stream=None, ctx=None): stream-ordered copies, no allocation, capture-safe (the hot-path snapshot);

  • clone(*, stream=None): an owned deep copy (allocates: cold);

  • alloc(layout, *, attributes=None): a fully allocated result in the “no track yet” state (cold).

Each protocol is a capability a family of consumers shares, and each consumer checks the subset of it it reads (check_source(..., members=...)), so a utility accepts any object with those members (a Track, a Centerline, a user’s own object): HasCenterline (TrackLocalizer, localize.curvature / speed_profile), HasBand (CollisionChecker, PropSampler, CheckpointSampler, HeightFieldBaker), HasGates (a gates-mode Course’s course line), HasGateFrames (its frame checker, CheckpointSet.from_gates) and HasGeneration (generation-aware consumers; checked together with another protocol: check_source(obj, (HasCenterline, HasGeneration), ...)), HasValidity (the per-env valid flags) and HasCheckpoints (an ordered checkpoint set: what ProgressTracker reads). A shipped protocol never changes: a new capability is a new protocol, composed with the existing ones. The library never relies on isinstance against them (their runtime semantics differ across Python versions): consumers check their source with check_source().

Optional reads: a consumer that reads a member only when the source has it (valid: the invalid-env sentinels; generation: the generation-aware self-reset) names it in check_source(..., optional=...): a present member is checked like a required one (same messages), a missing (or None) one is skipped.

class Layout[source]

Bases: object

The shape of a batch: the envs, the per-env row capacity, the device and the topology. Handed to Params.validate, Stage.capacity and the StageBuilder, and carried by every result (Track.layout, GateSequence.layout, Centerline.layout).

track_gen.results.Layout, track_gen.Layout and track_gen.plugin.Layout are this class.

num_envs

E (an int >= 1; numpy integers are accepted).

Type:

int

stride

The per-env row capacity (an int >= 1): N_max for a Track (and a track stage), max_gates for a GateSequence (and a gate stage), and for a Centerline its own capacity (max_gates * samples_per_gate for a gates-mode Course’s CourseLine.centerline). A channel’s own row stride is StageBuilder.stride_of.

Type:

int

device

The device string, stored AS GIVEN ("cuda" stays "cuda"). The layouts TrackGen makes (the planner, alloc, view, an inferred one) store the canonical str(wp.get_device(d)) ("cuda:0"), so == holds between them; the library’s own checks compare the canonical device, so a hand-set Layout(E, S, "cuda") matches "cuda:0".

Type:

str

closed

Whether the loops are closed. Only True is supported in 0.x (a BatchSpec(closed=False) is refused by the planner, a source with closed=False by every utility).

Type:

bool

Raises:
  • TypeError – a non-integer (or bool) num_envs / stride, a non-str device, a non-bool closed.

  • ValueError – num_envs < 1 or stride < 1.

__init__(num_envs, stride, device, closed=True)
Parameters:
Return type:

None

classmethod __new__(*args, **kwargs)

Tracks and gate sequences

class Track[source]

Bases: object

Final batched result of the track generation pipeline.

All boundary arrays are index-aligned: outer[i], center[i] and inner[i] share a single cross-section normal. half_width[i] is the band’s half-width there (‖outer[i] - center[i]‖ in plan view). Fields are wp.array; convert at the boundary via the wp bridge.

Warning

TrackGenerator.generate() returns the SAME Track instance on every call and overwrites its buffers in place. A reference held across two generate() calls will see mutated data. Call Track.clone() to obtain a fully-owned deep copy before the next call (or copy_from into a preallocated Track.alloc result: no allocation, capture-safe).

Methods (see track_gen.results): validate() (check every field against the layout), view() (a zero-copy env slice, e.g. out= of a mixed batch), copy_from() (stream-ordered copies, no allocation: the hot-path snapshot), clone() (an owned copy; allocates) and alloc() (a fully allocated result).

outer

Flat [E * N_max] vec3f outer boundary points. Each real point’s z follows the configured TrackGenConfig.z_profile — 0 only for the default "flat" profile at z_base=0 — and shares the center point’s altitude at the same index (level cross-sections; see 2.5D tracks). Reshape via wp.to_torch(...).view(E, N_max, 3). Points at i >= count[e] are NaN-padded (NaN xy; z carries the flat path’s z_base, 0 by default).

Type:

wp.array

center

Flat [E * N_max] vec3f centerline points. z follows the configured TrackGenConfig.z_profile (see outer). Reshape via wp.to_torch(...).view(E, N_max, 3). Index-aligned with outer and inner; ‖outer[i] - center[i]‖ gives the per-point half-width (an XY-plane distance: level cross-sections share a Z, so the vertical component cancels). Points at i >= count[e] are NaN-padded.

Type:

wp.array

inner

Flat [E * N_max] vec3f inner boundary points. z follows the configured TrackGenConfig.z_profile (see outer). Reshape via wp.to_torch(...).view(E, N_max, 3). Points at i >= count[e] are NaN-padded.

Type:

wp.array

tangent

Flat [E * N_max] vec3f unit tangent vectors at each centerline point. For the default "flat" profile this is the planar tangent (z = 0) from central differences over the 2D centerline; for a non-flat z_profile it is instead a true 3D central difference over the LIFTED centerline (see 2.5D tracks). NaN-padded past count[e].

Type:

wp.array

normal

Flat [E * N_max] vec3f unit left-normals at each centerline point — still the planar left-normal, always z = 0, computed as normal = (-tangent2d.y, tangent2d.x, 0) (the PLAN-VIEW tangent rotated +90° about +z) BEFORE the elevation lift. For a non-flat z_profile this predates the 3D recompute described under tangent: the two fields are no longer literally related by that rotation formula (normal’s XY direction still matches tangent’s XY projection, but tangent is unit-length in 3D, so its XY projection alone is sub-unit and rotating it will not reproduce a unit normal). Which boundary it faces is winding-dependent (see winding): it points toward outer for clockwise loops and toward inner for counter-clockwise loops. The outer/inner split itself is winding-agnostic (assigned by signed-area magnitude), so use outer/inner — not the normal’s sign — to identify the boundaries. NaN-padded past count[e].

Type:

wp.array

arclen

Flat [E * N_max] float32 cumulative arc length along the centerline at each point. For the default "flat" profile this is the plan-view (2D) arc length; for a non-flat z_profile it is the true 3D arc length of the LIFTED centerline (strictly >= the plan-view length whenever the profile is non-constant; see 2.5D tracks). Reshape via wp.to_torch(...).view(E, N_max). NaN-padded past count[e].

Type:

wp.array

length

[E] float32 total arc length (perimeter) of each track’s centerline loop — plan-view for the default "flat" profile, true 3D length otherwise (see arclen).

Type:

wp.array

valid

[E] int32 environment validity flags (0 or 1). A track is valid when: turning number ≈ 1, no NaN points, width floor exceeded everywhere, minimum thickness ≥ (1 − relax_tol) * half_width, the track was not truncated at N_max (see truncated), and (when validity_border_check=True) zero border self-intersections. Convert via wp.to_torch(...).bool().

Type:

wp.array

count

[E] int32 real point counts, derived from constant-spacing resampling as count[e] = floor(perimeter / spacing) + 1, capped at N_max. All other arrays are NaN-padded for indices i >= count[e].

Type:

wp.array

winding

[E] float32 signed loop winding: +1.0 for a counter-clockwise centerline, -1.0 for clockwise (the sign of the loop’s signed area), and 0.0 for degenerate/empty loops. Winding is generator-dependent (bezier and hull wind clockwise; polar, voronoi and checkpoint wind counter-clockwise), so this field lets a consumer orient itself: for a CCW loop outer lies to the right of increasing arc length, for a CW loop to the left.

Type:

wp.array

truncated

[E] int32 N_max truncation flags (0 or 1), written on device by the constant-spacing resample. 1 means the loop needed more than N_max points (floor(perimeter / spacing) + 1 > N_max), so count[e] was clamped to N_max and the tail of the loop was dropped; such envs always have valid == 0. Increase N_max (or spacing) if any are set. TrackGenerator always allocates it; None on a Track built elsewhere (e.g. the standalone inflate_warp path).

Type:

wp.array or None

generation

[E] int32: the seed epoch (TrackGenerator.seeds.epoch) each env’s current track was built at. 0 until the first advance. It is the epoch, not a change counter: a reseed at the same epoch (rng.set_seeds_warp, Course.generate(seeds=...)) does not change it, and after seeds.reset_epoch() the same values come back. Only while epochs are advanced and nothing reseeds or resets them does it change exactly when an env gets a NEW track (generate(advance=True, ...) / Course.regenerate(advance=True, ...)), so that comparing it on device to a stored copy detects every regeneration. TrackGenerator always allocates it; None on a Track built elsewhere.

Type:

wp.array or None

fail_mask

[E] int32 per-env fail bitmask: bit i set means check i failed (fail_bits names the bits: the built-in checks’ 0-7 are generator, turning, finite, thickness, w_floor, border, grade, truncation; plugin checks own 16-30). When it is not None, valid[e] == (fail_mask[e] == 0). TrackGenerator always allocates it; None on a Track built elsewhere (e.g. the standalone inflate_warp path).

Type:

wp.array or None

fail_bits

name -> bit index of every bit of fail_mask the pipeline assigns (TrackGenerator.program.fail_bits); the border / grade bits are named even when their check is off (then never set). Empty on a Track built elsewhere.

Type:

Mapping[str, int]

half_width

Flat [E * N_max] float32 half-width of the band at each centerline point (the offset’s: ‖outer[i] - center[i]‖ in plan view), NaN past count[e]. The constant half_width of the config on every real point at constant width (width.constant); the per-point width of a width stage or a generator that provides one otherwise. TrackGenerator always allocates it; None on a Track built elsewhere.

Type:

wp.array or None

attributes

The per-point attributes the pipeline exports (StageBuilder.write(..., export=True)), keyed by the channel name without its domain prefix (raw.acme.surface_id -> "acme.surface_id"): flat [E * N_max] arrays of the attribute’s dtype, carried to the final points with its interp policy, NaN (floats) / -1 (int32) past count[e]. Stable pointers for the generator’s lifetime. Empty unless a stage exports one (TrackGenerator.program.attributes describes them).

Type:

Mapping[str, wp.array]

layout

The batch’s Layout (num_envs, stride = N_max, device, closed), set on every Track TrackGen makes (a generator’s result, a view(), clone(), alloc()); None on one built by hand (validate() infers it).

Type:

Layout or None

validate(need=())

Check every field of this result against its layout; return the layout.

The layout is layout when set, else inferred (num_envs from count, stride from the first per-point field, the device of count, closed=True; nothing is mutated). Then every array field that is not None must be a contiguous 1-D array of its dtype, [E * stride] (per point) or [E] (per env), on the layout’s device (compared canonically: "cuda" == "cuda:0"), and every attributes entry a contiguous [E * stride] float32 / int32 / vec2f / vec3f array on that device. Host only (a few microseconds): no device work, safe inside a capture.

Parameters:

need – Field names that must not be None (an optional field a consumer reads).

Returns:

The stored or inferred Layout.

Raises:
  • ValueError – the first field that does not match, naming it and the expected shape (e.g. Track.inner: expected a contiguous [3072] vec3f     ([E*stride] = [8*384]) array on cpu, got shape (3064,) vec3f on cpu); a field of need that is None; a count that is not an array.

  • TypeError – layout is neither a Layout nor None.

Return type:

Layout

view(e0, e1)

A zero-copy view of envs [e0, e1) of this result.

Every array is a slice of this result’s array (per point [e0 * stride : e1 * stride], per env [e0 : e1]): the same memory, read and written in place, and kept alive by the view. None fields stay None; attributes are sliced alike; fail_bits is shared; layout is Layout(e1 - e0, stride, device, closed). A view is a result like any other: every TrackGen kernel indexes it from its own element 0, so it can be generated into (TrackGenerator(..., out= big.view(0, 4)): mixed batches), consumed by the utilities, copied, cloned and viewed again. No device work and no allocation: callable inside a capture.

Raises:
Parameters:
copy_from(src, *, stream=None, ctx=None)

Copy every field this result holds from src (same class, same layout).

One stream-ordered device copy per held field (a field None here is skipped) and per attributes key of this result (src may hold more keys). No allocation and no host sync, so it is the hot-path form of a snapshot (copy into a preallocated alloc() result) and it records into a caller’s capture (memcpy nodes). Host metadata (fail_bits, layout) is not copied. t.copy_from(t) is a no-op.

The copies run on ctx, else stream= > an ambient LaunchContext.scope > the stream that produced src (the generator’s constructor stream=) > an enclosing torch capture > Warp’s current stream. copy_from only knows the producer’s DEFAULT stream: pass the stream you generated on (or order the streams with events) when a generation ran elsewhere.

Parameters:
  • src – A result of the same class (Track / GateSequence / Centerline).

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

  • ctx – Optional LaunchContext (not with stream=).

Raises:
  • TypeError – src is not of this result class; stream= and ctx= together.

  • ValueError – either side does not validate; the layouts differ (canonical device); a field held here is None in src; an attribute key here is missing from src or has another dtype; a partial overlap of a field with its source.

Return type:

None

clone(*, stream=None)

A fully-owned deep copy (new memory for every held field and attribute).

COLD: allocates and blocks every call (use copy_from() into a preallocated alloc() result on a hot path). Allocated and copied on Warp’s CURRENT stream (where wp.empty allocates), then a host wait on that stream: the clone is complete when it returns, so it is safe to read or write on ANY stream right away (.numpy(), a utility, copy_from on a generator’s stream). When the result was produced on another stream (stream=, else an ambient scope, else the generator’s constructor stream=), Warp’s current stream first waits on it. None fields stay None, attributes are copied, fail_bits is shared (an immutable mapping), layout is set; the class (a subclass) is kept.

Parameters:

stream – The stream that PRODUCED this result (wp.Stream or "torch"), e.g. track = gen.generate(stream=s, sync=False); snap = track.clone(stream=s).

Raises:
  • track_gen.exec.CaptureError – inside any capture (Warp CPU / CUDA or torch), before allocating; inside a graph, copy_from into a preallocated alloc() result instead.

  • ValueError – this result does not validate (a malformed hand-built result).

classmethod alloc(layout, *, attributes=None)

A result with every field allocated, in the “no track yet” state.

Per-point fields (and attributes) hold their padding (NaN; -1 for int32), count / valid / truncated / generation / fail_mask are 0, length / winding 0; layout is layout with the canonical device; fail_bits is empty. Allocation and fills run on Warp’s CURRENT stream (like wp.full), then a host wait on it: the result is complete when it returns, so it can be read or written on ANY stream right away (snap.copy_from(track) on a generator’s stream, TrackGenerator(..., out=big.view(0, 4), stream=s)). COLD: allocates and blocks.

Parameters:
  • layout (Layout) – The Layout (num_envs, stride, device, closed).

  • attributes – Optional {key: dtype} of attributes to allocate (Track and GateSequence): key a plugin key "<package>.<name>", dtype wp.float32, wp.int32, wp.vec2f or wp.vec3f (or its name, "float32", as in ExternalParams.attributes).

Raises:
  • TypeError – layout is not a Layout; an attribute dtype outside the four; attributes on a Centerline.

  • ValueError – an attribute key that is not "<package>.<name>".

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

fail_mask and fail_bits say why an env is invalid (its fail bits): the bit table is BUILTIN_FAIL_BITS plus the pipeline’s plugin checks (see Fail bits). half_width is the band’s per-point half-width, and attributes the per-point attributes the pipeline exports (see Per-point attributes in the result). layout is None on a Track built by hand (Layout).

__init__(outer, center, inner, tangent, normal, arclen, length, valid, count, winding, truncated=None, generation=None, fail_mask=None, fail_bits=<factory>, half_width=None, attributes=<factory>, layout=None)
Parameters:
  • outer (array)

  • center (array)

  • inner (array)

  • tangent (array)

  • normal (array)

  • arclen (array)

  • length (array)

  • valid (array)

  • count (array)

  • winding (array)

  • truncated (array | None)

  • generation (array | None)

  • fail_mask (array | None)

  • fail_bits (Mapping[str, int])

  • half_width (array | None)

  • attributes (Mapping[str, array])

  • layout (Layout | None)

Return type:

None

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

Bases: object

Batched fixed-stride gate result returned by GateGenerator.

Gate pose arrays are flat [E * max_gates] vec3f buffers; reshape via wp.to_torch(...).view(E, max_gates, 3). count[e] gives the real gate count for environment e and slots i >= count[e] are NaN-padded (any NaN component marks a padded slot). Each real slot’s z follows the configured GateGenConfig.z_profile; it is 0 only for the default "flat" profile at z_base=0.

Warning

GateGenerator.generate() returns the SAME GateSequence instance on every call and overwrites its buffers in place. A reference held across two generate() calls will see mutated data. Call GateSequence.clone() to obtain a fully-owned deep copy before the next call.

Methods (see track_gen.results): validate() (check every field against the layout), view() (a zero-copy env slice, e.g. out= of a mixed batch), copy_from() (stream-ordered copies, no allocation), clone() (an owned copy; allocates) and alloc() (a fully allocated result).

position

Flat [E * max_gates] vec3f gate centres. Reshape via wp.to_torch(...).view(E, max_gates, 3). Slots i >= count[e] are NaN-padded.

Type:

wp.array

tangent

Flat [E * max_gates] vec3f unit tangent vectors at each gate centre. NaN-padded past count[e].

Type:

wp.array

forward

Flat [E * max_gates] vec3f unit pose forward (x-axis of orientation): the physical gate-plane normal used for pass detection; equals tangent for full_tangent and for planar yaw_only gates, the horizontal projection of tangent for sloped yaw_only gates; NaN-padded past count[e].

Type:

wp.array

orientation

Flat [E * max_gates] quatf gate poses (wp.quatf(x, y, z, w)): the roll-free frame with local x = gate forward, y = left, z = up. NaN-padded past count[e].

Type:

wp.array

half_size

Flat [E * max_gates] float32 square-opening half-extents (0.5 * gate_width, or the generator’s own when it provides half_size). NaN-padded past count[e].

Type:

wp.array

left

Flat [E * max_gates] vec3f left gate endpoints (position + half_size * left_axis). NaN-padded past count[e].

Type:

wp.array

right

Flat [E * max_gates] vec3f right gate endpoints (position - half_size * left_axis). NaN-padded past count[e].

Type:

wp.array

valid

[E] int32 environment validity flags (0 or 1). Convert to a boolean tensor via wp.to_torch(...).bool().

Type:

wp.array

count

[E] int32 real gate counts per env. All other arrays are NaN-padded for slots i >= count[e].

Type:

wp.array

generation

[E] int32: the seed epoch (GateGenerator.seeds.epoch) each env’s current gates were built at. 0 until the first advance. It is the epoch, not a change counter: a reseed at the same epoch (rng.set_seeds_warp, Course.generate(seeds=...)) does not change it, and after seeds.reset_epoch() the same values come back. Only while epochs are advanced and nothing reseeds or resets them does it change exactly when an env gets a NEW sequence (generate(advance=True, ...) / Course.regenerate(advance=True, ...)), so that comparing it on device to a stored copy detects every regeneration. GateGenerator always allocates it; None on a GateSequence built elsewhere.

Type:

wp.array or None

fail_mask

[E] int32 per-env fail bitmask: bit i set means check i failed (fail_bits names the bits: validity.gate_default’s 0-5 are count, finite, tangent, spacing, crossing, grade; plugin checks own 16-30). When it is not None, valid[e] == (fail_mask[e] == 0). GateGenerator and warp_gate.alloc_gate_sequence always allocate it (finalize_gate_sequence writes it); None on a GateSequence built by hand.

Type:

wp.array or None

fail_bits

name -> bit index of every bit of fail_mask the pipeline assigns (GateGenerator.program.fail_bits); the crossing / grade bits are named even when their check is off (then never set). Empty on a GateSequence built elsewhere.

Type:

Mapping[str, int]

attributes

The per-gate attributes the pipeline exports, keyed by the channel name without its domain prefix (anchors.acme.tag / gates.acme.tag -> "acme.tag"): flat [E * max_gates] arrays, NaN (floats) / -1 (int32) past count[e]. Best effort: an attribute with no path to the gate slots (a non-raw order, a plugin stations stage, a channel a stage writes on the centerline, e.g. an elevation with elevation_on="centerline") is left out (GateGenerator.program.attributes lists what is there). Empty unless a stage exports one.

Type:

Mapping[str, wp.array]

layout

The batch’s Layout (stride = max_gates), set on every GateSequence TrackGen makes (a generator’s result, a view(), clone(), alloc()); None on one built by hand (validate() infers it).

Type:

Layout or None

validate(need=())

Check every field of this result against its layout; return the layout.

The layout is layout when set, else inferred (num_envs from count, stride from the first per-point field, the device of count, closed=True; nothing is mutated). Then every array field that is not None must be a contiguous 1-D array of its dtype, [E * stride] (per point) or [E] (per env), on the layout’s device (compared canonically: "cuda" == "cuda:0"), and every attributes entry a contiguous [E * stride] float32 / int32 / vec2f / vec3f array on that device. Host only (a few microseconds): no device work, safe inside a capture.

Parameters:

need – Field names that must not be None (an optional field a consumer reads).

Returns:

The stored or inferred Layout.

Raises:
  • ValueError – the first field that does not match, naming it and the expected shape (e.g. Track.inner: expected a contiguous [3072] vec3f     ([E*stride] = [8*384]) array on cpu, got shape (3064,) vec3f on cpu); a field of need that is None; a count that is not an array.

  • TypeError – layout is neither a Layout nor None.

Return type:

Layout

view(e0, e1)

A zero-copy view of envs [e0, e1) of this result.

Every array is a slice of this result’s array (per point [e0 * stride : e1 * stride], per env [e0 : e1]): the same memory, read and written in place, and kept alive by the view. None fields stay None; attributes are sliced alike; fail_bits is shared; layout is Layout(e1 - e0, stride, device, closed). A view is a result like any other: every TrackGen kernel indexes it from its own element 0, so it can be generated into (TrackGenerator(..., out= big.view(0, 4)): mixed batches), consumed by the utilities, copied, cloned and viewed again. No device work and no allocation: callable inside a capture.

Raises:
Parameters:
copy_from(src, *, stream=None, ctx=None)

Copy every field this result holds from src (same class, same layout).

One stream-ordered device copy per held field (a field None here is skipped) and per attributes key of this result (src may hold more keys). No allocation and no host sync, so it is the hot-path form of a snapshot (copy into a preallocated alloc() result) and it records into a caller’s capture (memcpy nodes). Host metadata (fail_bits, layout) is not copied. t.copy_from(t) is a no-op.

The copies run on ctx, else stream= > an ambient LaunchContext.scope > the stream that produced src (the generator’s constructor stream=) > an enclosing torch capture > Warp’s current stream. copy_from only knows the producer’s DEFAULT stream: pass the stream you generated on (or order the streams with events) when a generation ran elsewhere.

Parameters:
  • src – A result of the same class (Track / GateSequence / Centerline).

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

  • ctx – Optional LaunchContext (not with stream=).

Raises:
  • TypeError – src is not of this result class; stream= and ctx= together.

  • ValueError – either side does not validate; the layouts differ (canonical device); a field held here is None in src; an attribute key here is missing from src or has another dtype; a partial overlap of a field with its source.

Return type:

None

clone(*, stream=None)

A fully-owned deep copy (new memory for every held field and attribute).

COLD: allocates and blocks every call (use copy_from() into a preallocated alloc() result on a hot path). Allocated and copied on Warp’s CURRENT stream (where wp.empty allocates), then a host wait on that stream: the clone is complete when it returns, so it is safe to read or write on ANY stream right away (.numpy(), a utility, copy_from on a generator’s stream). When the result was produced on another stream (stream=, else an ambient scope, else the generator’s constructor stream=), Warp’s current stream first waits on it. None fields stay None, attributes are copied, fail_bits is shared (an immutable mapping), layout is set; the class (a subclass) is kept.

Parameters:

stream – The stream that PRODUCED this result (wp.Stream or "torch"), e.g. track = gen.generate(stream=s, sync=False); snap = track.clone(stream=s).

Raises:
  • track_gen.exec.CaptureError – inside any capture (Warp CPU / CUDA or torch), before allocating; inside a graph, copy_from into a preallocated alloc() result instead.

  • ValueError – this result does not validate (a malformed hand-built result).

classmethod alloc(layout, *, attributes=None)

A result with every field allocated, in the “no track yet” state.

Per-point fields (and attributes) hold their padding (NaN; -1 for int32), count / valid / truncated / generation / fail_mask are 0, length / winding 0; layout is layout with the canonical device; fail_bits is empty. Allocation and fills run on Warp’s CURRENT stream (like wp.full), then a host wait on it: the result is complete when it returns, so it can be read or written on ANY stream right away (snap.copy_from(track) on a generator’s stream, TrackGenerator(..., out=big.view(0, 4), stream=s)). COLD: allocates and blocks.

Parameters:
  • layout (Layout) – The Layout (num_envs, stride, device, closed).

  • attributes – Optional {key: dtype} of attributes to allocate (Track and GateSequence): key a plugin key "<package>.<name>", dtype wp.float32, wp.int32, wp.vec2f or wp.vec3f (or its name, "float32", as in ExternalParams.attributes).

Raises:
  • TypeError – layout is not a Layout; an attribute dtype outside the four; attributes on a Centerline.

  • ValueError – an attribute key that is not "<package>.<name>".

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

fail_mask and fail_bits say why an env is invalid: the bit table is GATE_FAIL_BITS plus the pipeline’s plugin checks (see Gate fail bits). fail_mask is None on a GateSequence built elsewhere; when it is set, valid[e] == (fail_mask[e] == 0). attributes holds the per-gate attributes the pipeline exports (see Gate plugin channels).

__init__(position, tangent, forward, orientation, half_size, left, right, valid, count, generation=None, fail_mask=None, fail_bits=<factory>, attributes=<factory>, layout=None)
Parameters:
  • position (array)

  • tangent (array)

  • forward (array)

  • orientation (array)

  • half_size (array)

  • left (array)

  • right (array)

  • valid (array)

  • count (array)

  • generation (array | None)

  • fail_mask (array | None)

  • fail_bits (Mapping[str, int])

  • attributes (Mapping[str, array])

  • layout (Layout | None)

Return type:

None

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

Bases: object

A batched closed 3D centerline without a road band.

What a gates-mode Course threads through its gates (course.course_line.centerline; the course line is internal to Course in 0.x): the members a centerline consumer reads (HasCenterline: TrackLocalizer, localize.curvature / speed_profile), with the same flat storage as a Track: per-point arrays [E * stride] (NaN past count[e]), per-env arrays [E].

center

[E * stride] vec3f centerline points.

Type:

warp._src.types.array

tangent

[E * stride] vec3f unit tangents.

Type:

warp._src.types.array

arclen

[E * stride] float32 cumulative arc length.

Type:

warp._src.types.array

length

[E] float32 loop length.

Type:

warp._src.types.array

count

[E] int32 real point counts.

Type:

warp._src.types.array

valid

[E] int32 validity flags (0 / 1).

Type:

warp._src.types.array

generation

[E] int32 generation epoch, or None.

Type:

warp._src.types.array | None

layout

Its Layout (None on one built by hand: validate() infers it).

Type:

track_gen._src.types.Layout | None

validate(need=())

Check every field of this result against its layout; return the layout.

The layout is layout when set, else inferred (num_envs from count, stride from the first per-point field, the device of count, closed=True; nothing is mutated). Then every array field that is not None must be a contiguous 1-D array of its dtype, [E * stride] (per point) or [E] (per env), on the layout’s device (compared canonically: "cuda" == "cuda:0"), and every attributes entry a contiguous [E * stride] float32 / int32 / vec2f / vec3f array on that device. Host only (a few microseconds): no device work, safe inside a capture.

Parameters:

need – Field names that must not be None (an optional field a consumer reads).

Returns:

The stored or inferred Layout.

Raises:
  • ValueError – the first field that does not match, naming it and the expected shape (e.g. Track.inner: expected a contiguous [3072] vec3f     ([E*stride] = [8*384]) array on cpu, got shape (3064,) vec3f on cpu); a field of need that is None; a count that is not an array.

  • TypeError – layout is neither a Layout nor None.

Return type:

Layout

view(e0, e1)

A zero-copy view of envs [e0, e1) of this result.

Every array is a slice of this result’s array (per point [e0 * stride : e1 * stride], per env [e0 : e1]): the same memory, read and written in place, and kept alive by the view. None fields stay None; attributes are sliced alike; fail_bits is shared; layout is Layout(e1 - e0, stride, device, closed). A view is a result like any other: every TrackGen kernel indexes it from its own element 0, so it can be generated into (TrackGenerator(..., out= big.view(0, 4)): mixed batches), consumed by the utilities, copied, cloned and viewed again. No device work and no allocation: callable inside a capture.

Raises:
Parameters:
copy_from(src, *, stream=None, ctx=None)

Copy every field this result holds from src (same class, same layout).

One stream-ordered device copy per held field (a field None here is skipped) and per attributes key of this result (src may hold more keys). No allocation and no host sync, so it is the hot-path form of a snapshot (copy into a preallocated alloc() result) and it records into a caller’s capture (memcpy nodes). Host metadata (fail_bits, layout) is not copied. t.copy_from(t) is a no-op.

The copies run on ctx, else stream= > an ambient LaunchContext.scope > the stream that produced src (the generator’s constructor stream=) > an enclosing torch capture > Warp’s current stream. copy_from only knows the producer’s DEFAULT stream: pass the stream you generated on (or order the streams with events) when a generation ran elsewhere.

Parameters:
  • src – A result of the same class (Track / GateSequence / Centerline).

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

  • ctx – Optional LaunchContext (not with stream=).

Raises:
  • TypeError – src is not of this result class; stream= and ctx= together.

  • ValueError – either side does not validate; the layouts differ (canonical device); a field held here is None in src; an attribute key here is missing from src or has another dtype; a partial overlap of a field with its source.

Return type:

None

clone(*, stream=None)

A fully-owned deep copy (new memory for every held field and attribute).

COLD: allocates and blocks every call (use copy_from() into a preallocated alloc() result on a hot path). Allocated and copied on Warp’s CURRENT stream (where wp.empty allocates), then a host wait on that stream: the clone is complete when it returns, so it is safe to read or write on ANY stream right away (.numpy(), a utility, copy_from on a generator’s stream). When the result was produced on another stream (stream=, else an ambient scope, else the generator’s constructor stream=), Warp’s current stream first waits on it. None fields stay None, attributes are copied, fail_bits is shared (an immutable mapping), layout is set; the class (a subclass) is kept.

Parameters:

stream – The stream that PRODUCED this result (wp.Stream or "torch"), e.g. track = gen.generate(stream=s, sync=False); snap = track.clone(stream=s).

Raises:
  • track_gen.exec.CaptureError – inside any capture (Warp CPU / CUDA or torch), before allocating; inside a graph, copy_from into a preallocated alloc() result instead.

  • ValueError – this result does not validate (a malformed hand-built result).

classmethod alloc(layout, *, attributes=None)

A result with every field allocated, in the “no track yet” state.

Per-point fields (and attributes) hold their padding (NaN; -1 for int32), count / valid / truncated / generation / fail_mask are 0, length / winding 0; layout is layout with the canonical device; fail_bits is empty. Allocation and fills run on Warp’s CURRENT stream (like wp.full), then a host wait on it: the result is complete when it returns, so it can be read or written on ANY stream right away (snap.copy_from(track) on a generator’s stream, TrackGenerator(..., out=big.view(0, 4), stream=s)). COLD: allocates and blocks.

Parameters:
  • layout (Layout) – The Layout (num_envs, stride, device, closed).

  • attributes – Optional {key: dtype} of attributes to allocate (Track and GateSequence): key a plugin key "<package>.<name>", dtype wp.float32, wp.int32, wp.vec2f or wp.vec3f (or its name, "float32", as in ExternalParams.attributes).

Raises:
  • TypeError – layout is not a Layout; an attribute dtype outside the four; attributes on a Centerline.

  • ValueError – an attribute key that is not "<package>.<name>".

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

__init__(center, tangent, arclen, length, count, valid, generation=None, layout=None)
Parameters:
  • center (array)

  • tangent (array)

  • arclen (array)

  • length (array)

  • count (array)

  • valid (array)

  • generation (array | None)

  • layout (Layout | None)

Return type:

None

classmethod __new__(*args, **kwargs)

Capability protocols

What each runtime utility reads from its source (Capability protocols). A shipped protocol never changes: a new capability is a new protocol.

class HasCenterline[source]

Bases: Protocol

A batched closed centerline: what TrackLocalizer and localize.curvature / speed_profile read. Track and Centerline satisfy it.

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

Bases: Protocol

A centerline with its road band: what CollisionChecker, PropSampler, CheckpointSampler and HeightFieldBaker read. Track satisfies it.

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

Bases: Protocol

Gate positions: what a gates-mode Course’s course line reads. GateSequence satisfies it.

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

Bases: HasGates, Protocol

Posed gates: what a gates-mode Course’s frame checker and CheckpointSet.from_gates read. GateSequence satisfies it.

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

Bases: Protocol

The per-env generation epoch ([E] int32): a Track / GateSequence / Centerline whose generation is set. Per env only, so check it together with the protocol that gives the stride: check_source(obj, (HasCenterline, HasGeneration), consumer=...).

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

Bases: Protocol

The per-env validity flags ([E] int32, 1 = valid): a Track / GateSequence / Centerline / CheckpointSet whose valid is set. Per env only, so check it together with the protocol that gives the stride: check_source(obj, (HasCenterline, HasValidity), optional=("valid",), ...).

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

Bases: Protocol

An ordered per-env checkpoint set: what ProgressTracker reads. CheckpointSet satisfies it (CheckpointSampler’s set, or CheckpointSet.from_gates of a GateSequence).

__init__(*args, **kwargs)
classmethod __new__(*args, **kwargs)
check_source(obj, protocol, *, consumer, topology='closed', members=None, optional=())[source]

Check that obj holds what a consumer reads; return its Layout.

The members of protocol (or the narrower members) are read with getattr (a __getattr__ proxy works; obj.validate is never called): a missing (or None) member raises TypeError. protocol may be a tuple of protocols ((HasCenterline, HasGeneration)): their members are checked together, against one layout. The layout is obj.layout when it is a Layout (on a Track / GateSequence / Centerline it must be one, or None; another object’s unrelated layout attribute is ignored), else inferred from the checked members only: num_envs from count (else the first per-env member), stride from the first per-point member (1, i.e. undefined, when the checked members are all per env, e.g. HasGeneration alone), the device of the member that gave num_envs, closed=True. Only those members are checked (their dtype and per-point / per-env length come from the result schemas: center is [E * stride] vec3f, count [E] int32, …), as flat 1-D arrays on the layout’s device, contiguous; fields outside them are never looked at. Host only.

Parameters:
  • obj – The source (a Track, GateSequence, Centerline, or any object with the members).

  • protocol – The capability protocol (HasCenterline, HasBand, …), or a tuple of them.

  • consumer (str) – The consumer’s name, prefixed to every message ("CollisionChecker").

  • topology (str) – The consumer’s topology: "closed" refuses a source whose layout has closed=False; "any" accepts both.

  • members (tuple[str, ...] | None) – The members the consumer reads when it is a subset of the protocol’s.

  • optional – Members of protocol read only when present: each one the source has (not None) is checked like a required member (same messages), a missing one is skipped (valid / generation: a consumer then falls back to “every env valid” / “no self-reset”).

Returns:

The source’s Layout (canonical device).

Raises:
  • TypeError – protocol is not a capability protocol (or a tuple of them); a missing member (naming the protocol, its members and the missing ones); a TrackGen result whose layout is neither a Layout nor None.

  • ValueError – members (or optional) that are not members of protocol; a member that does not match (CollisionChecker: Track.inner: expected a contiguous     [3072] vec3f ([E*stride] = [8*384]) array on cpu, got shape (3064,) vec3f on     cpu); a closed=False source for a closed-only consumer.

Return type:

Layout