tracel-ai/burn · error

Incompatible shapes for broadcasting: {:?} and {:?}

Error message

Incompatible shapes for broadcasting: {:?} and {:?}

What it means

When preparing a binary elementwise op, both operands are broadcast to a common shape. Broadcasting fails when, at some trailing-aligned dimension, neither side is 1 and the two dimensions differ. The backend panics with the two incompatible shapes.

Source

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

        let lhs_dim = if i < lhs_shape.len() {
            lhs_shape[lhs_shape.len() - 1 - i]
        } else {
            1
        };
        let rhs_dim = if i < rhs_shape.len() {
            rhs_shape[rhs_shape.len() - 1 - i]
        } else {
            1
        };

        if lhs_dim == rhs_dim {
            broadcast_shape[ndims - 1 - i] = lhs_dim;
        } else if lhs_dim == 1 {
            broadcast_shape[ndims - 1 - i] = rhs_dim;
        } else if rhs_dim == 1 {
            broadcast_shape[ndims - 1 - i] = lhs_dim;
        } else {
            panic!(
                "Incompatible shapes for broadcasting: {:?} and {:?}",
                lhs_shape, rhs_shape
            );
        }
    }

    // Create IxDyn from broadcast shape
    let broadcast_dim = ndarray::IxDyn(&broadcast_shape);

    // Broadcast both arrays
    let lhs_broadcast = lhs
        .broadcast(broadcast_dim.clone())
        .expect("Failed to broadcast lhs");
    let rhs_broadcast = rhs
        .broadcast(broadcast_dim)
        .expect("Failed to broadcast rhs");

    (lhs_broadcast, rhs_broadcast)

View on GitHub (pinned to d16f7ba2ed)

Solutions

  1. Reshape/unsqueeze the smaller tensor so its dimensions align with broadcast rules (trailing dimensions must be equal or 1).
  2. Expand one operand explicitly to the target shape before the op.
  3. Fix the pipeline so both operands are produced with compatible shapes.
  4. Print lhs.dims() and rhs.dims() and verify each trailing dimension pair is equal or one of them is 1.

Example fix

// before
let a = Tensor::<_,_,NdArray>::zeros([3, 4], &device);
let b = Tensor::zeros([5, 4], &device);
let c = a.greater(b);
// after
let b = Tensor::zeros([1, 4], &device).repeat(0, 3); // or fix dims to [3,4]
let c = a.greater(b);
Defensive patterns

Strategy: validation

Validate before calling

fn can_broadcast(a: &[usize], b: &[usize]) -> bool {
    a.iter().rev().zip(b.iter().rev()).all(|(x, y)| x == y || *x == 1 || *y == 1)
}
assert!(can_broadcast(&lhs.dims(), &rhs.dims()));

Type guard

fn can_broadcast(a: &[usize], b: &[usize]) -> bool {
    a.iter().rev().zip(b.iter().rev()).all(|(x, y)| x == y || *x == 1 || *y == 1)
}

Prevention

When it happens

Trigger: Applying elementwise ops (remainder, equal, greater, greater_equal, lower_equal, lower) on tensors whose shapes cannot be broadcast, e.g. [3, 4] vs [5, 4] or [3, 4] vs [2].

Common situations: Comparing tensors of different batch sizes; comparing a [N] tensor with a [M, N] tensor expecting implicit prepending broadcast (not supported the same way); dimension count errors from squeeze/unsqueeze omissions.

Related errors


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