Skip to content

Implement remaining IndexingUpdateOp variants for scatter/select_assign across backends #5522

Description

@antimora

Feature description

Follow-up to #4477. IndexingUpdateOp now has all five variants (Assign, Add, Mul, Min, Max) and both Tensor::scatter and Tensor::select_assign take it, but most backends still only implement a subset and fall through to unimplemented!() at runtime.

scatter_nd / select-family aside, here is the current state on main for the element-wise ops:

scatter (element-wise)

Backend Assign Add Mul Min Max
ndarray yes yes yes no no
flex no yes yes no no
cubecl (wgpu/cuda/metal/rocm) no yes yes no no
tch no yes yes no no
autodiff yes yes yes no no
bool (bridge) no yes (or) no no no

select_assign

Backend Assign Add Mul Min Max
ndarray yes yes yes no no
flex no yes yes no no
cubecl no yes yes no no
tch no yes yes no no
bool (bridge) no yes (or) no no no

scatter_nd (for contrast, already complete)

All five reductions are implemented in ndarray, flex and cubecl. This is the shape the element-wise ops should reach.

So the concrete gaps are:

  • Assign missing in burn-flex, burn-cubecl and burn-tch for both scatter and select_assign (ndarray has it).
  • Min / Max missing in every backend for both scatter and select_assign.

The default trait bodies in burn-backend/src/backend/ops/tensor.rs (float_scatter, float_select_assign) forward only Add, so a backend that does not override them silently inherits Add-only support and panics at runtime rather than failing to compile.

Feature motivation

This is what blocks the ONNX side from finishing tracel-ai/burn-onnx#486.

ONNX ScatterElements has five reduction modes that map one-to-one onto IndexingUpdateOp. ScatterND can already lower all five to scatter_nd because that op is complete. ScatterElements cannot:

ONNX reduction Lowering today in burn-onnx
add scatter(..., Add)
mul scatter(..., Mul)
none gather-diff-scatter-add workaround, because Assign is unavailable on the default CPU backend (flex)
max data + (updates - gathered).clamp_min(0) scattered with Add
min data + (updates - gathered).clamp_max(0) scattered with Add

The none/max/min rewrites are correct for unique indices but diverge from ONNX semantics when indices contains duplicates, where ONNX defines the reduction as applied repeatedly. They also do an extra gather pass over the data. Native Assign/Min/Max would remove both problems and let all five modes be a single scatter() call.

Bool tensors are a smaller related gap: the bridge only maps Add to bool_scatter_or, and or cannot clear a bit, so ONNX ScatterElements/ScatterND on bool has to round-trip through i64.

Suggest a Solution

  • Implement Assign for scatter and select_assign in burn-flex, burn-cubecl and burn-tch.
  • Implement Min / Max for scatter and select_assign across the backends. The cubecl side already has BinaryMinOp / BinaryMaxOp wired up for scatter_nd_kernel, so the element-wise scatter_kernel should be able to reuse them.
  • Extend burn-autodiff to cover whichever of these are differentiable (Min/Max gradients route to the argmin/argmax side, same as scatter_nd).
  • Consider making the missing combinations a compile-time or documented capability rather than a runtime unimplemented!(), so downstream code generators can pick a supported lowering instead of discovering the gap on the first forward pass.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementEnhance existing features

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions