Skip to content

← JAX Vectorization & Control Flow step 21 of 25

Medium Primitives

Dynamic Slice

Jump to the task

Move the window, keep its size fixed

Suppose you need three neighboring values from a vector, but the start position is only known when the function runs. lax.dynamic_slice lets that position change under JIT while keeping the output shape fixed. For a one-dimensional array, pass one start index and one window size:

import jax
import jax.numpy as jnp
from jax import lax

@jax.jit
def three_values(x, start):
    return lax.dynamic_slice(x, (start,), (3,))

x = jnp.arange(10, dtype=jnp.float32)
three_values(x, 2)  # [2.0, 3.0, 4.0]
three_values(x, 5)  # [5.0, 6.0, 7.0]

Each start is an integer scalar, one per input axis. It does not have to be int32, though this exercise uses that conversion. An arbitrary array of indices is not a single start position.

Static size means known during tracing

Ordinary slicing such as x[start:start + 3] fails under JIT when start is traced. Dynamic slicing allows the start to vary, but its window size must still be known during tracing. Calling int(size) does not make a traced size static; it raises ConcretizationTypeError.

For the function you will write, mark window_size as static explicitly:

compiled = jax.jit(extract_window, static_argnames=("window_size",))
compiled(x, 2.0, window_size=3.0)  # [2.0, 3.0, 4.0]
compiled(x, 5.0, window_size=3.0)  # [5.0, 6.0, 7.0]

Pass a Python number for that static argument, not a JAX array. Changing the size can require a new compilation because the result's shape changes. The float representation here is part of the exercise interface; sizes and starts must have whole-number values.

At the boundary, the window shifts

The result keeps its requested length. For x = [0, 1, ..., 9] and size 3, start 9 returns [7, 8, 9], not a shortened [9] and not wrapped values [9, 0, 1]. The start is adjusted so the window fits.

Negative starts are interpreted relative to the end by default, then adjusted if necessary. Start -1 also returns [7, 8, 9]; start -4 returns [6, 7, 8]. A start below -len(x) is adjusted to the beginning. This is not circular slicing or validation of the requested position.

Problem

Implement extract_window(x, start_idx, window_size) using lax.dynamic_slice.

  • x is a one-dimensional real floating-point JAX array of length n.
  • start_idx is a whole-number scalar representable as int32, possibly supplied as a float. Convert it with jnp.int32.
  • window_size is a whole Python number with 0 <= window_size <= n. Convert it with int. When using JIT, make this argument static.
  • Return a contiguous array of length window_size with x's dtype, using the boundary behavior above. Size zero returns an empty array.

Output tests check values and shapes, but cannot enforce which slicing API you used. Fractional indices and sizes are outside the contract.