tracel-ai/burn · error

ndarray gather_nd requires contiguous indices

Error message

ndarray gather_nd requires contiguous indices

What it means

gather_nd flattens `indices` with as_slice() for direct flat indexing; non-contiguous index arrays make as_slice() return None and this expect() panics. Indices must be a densely stored integer tensor.

Source

Thrown at crates/burn-ndarray/src/ops/base.rs:416

        let k = idx_shape[m - 1];

        // Number of index tuples
        let num_indices: usize = idx_shape[..m - 1].iter().product();
        // Size of each output slice
        let slice_size: usize = data_shape[k..].iter().product();

        // Output shape: idx_shape[..m-1] ++ data_shape[k..]
        let mut out_shape_vec: Vec<usize> = idx_shape[..m - 1].to_vec();
        out_shape_vec.extend_from_slice(&data_shape[k..]);
        let out_total = num_indices * slice_size;

        let data_flat = data
            .as_slice()
            .expect("ndarray gather_nd requires contiguous data");

        let idx_flat = indices
            .as_slice()
            .expect("ndarray gather_nd requires contiguous indices");

        let strides: Vec<usize> = {
            let mut s = vec![0usize; k];
            if k > 0 {
                s[k - 1] = slice_size;
                for i in (0..k - 1).rev() {
                    s[i] = s[i + 1] * data_shape[i + 1];
                }
            }
            s
        };

        let mut output_vec: Vec<E> = vec![0.elem::<E>(); out_total];

        for n in 0..num_indices {
            let mut base_offset = 0usize;
            for j in 0..k {
                let idx_val = idx_flat[n * k + j].elem::<i64>() as usize;

View on GitHub (pinned to d16f7ba2ed)

Solutions

  1. Call .contiguous() (or .into_owned()) on the indices tensor before gather
  2. Construct index tensors contiguously from the start (from_data/from_ints) instead of reshaping views
  3. If indices come from arithmetic on views, insert a materializing op (e.g. cat of one tensor) to force a copy

Example fix

// before
let idx = raw_idx.slice([0..n]).transpose();
let out = data.gather(idx);
// after
let idx = raw_idx.slice([0..n]).transpose().contiguous();
let out = data.gather(idx);
Defensive patterns

Strategy: validation

Validate before calling

fn contiguous_idx(t: &Tensor<NdArray<Ibd>, D>) -> Tensor<NdArray<Ibd>, D> {
    t.clone().int_into_contiguous() // materialize index tensor before gather
}
// let out = data.gather(contiguous_idx(&indices));

Prevention

When it happens

Trigger: Calling Tensor::gather (gather_nd) where the `indices` tensor is a non-contiguous view (slice, permute, broadcast, or result of zero-stride expand), so ArrayD::as_slice() returns None.

Common situations: Building index tensors via broadcasting/expand then gathering; slicing a larger index tensor before gather; ONNX graphs where GatherND indices come from a transposed tensor.

Related errors


AI-assisted analysis of tracel-ai/burn@d16f7ba2ed (2026-09-05). Data as JSON: /api/errors/760a3a522c46299f. Report an issue: GitHub.