tracel-ai/burn · error

Failed to broadcast lhs

Error message

Failed to broadcast lhs

What it means

broadcast_for_binary_ops aligns two tensors for elementwise comparison/remainder ops by broadcasting the left-hand array to the merged shape. If lhs cannot be broadcast to that shape (its trailing dims neither match nor are 1), ndarray returns Err and this expect() panics.

Source

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

        } 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)
}

/// The mean of zero elements, which is `0 / 0`.
///
/// `NaN` for a float, matching numpy and torch. Integers have no such value, so an integer mean of
/// nothing is rejected rather than silently reported as some other number.
pub(crate) fn empty_mean<E: NdArrayElement>() -> E {
    assert!(
        E::dtype().is_float(),
        "Cannot compute mean of empty tensor for the integer type {:?}",
        E::dtype()
    );
    0.elem::<E>() / 0.elem::<E>()

View on GitHub (pinned to d16f7ba2ed)

Solutions

  1. Print/check both tensor dims and make them broadcastable: equal dims, or one side's dim == 1 (right-aligned)
  2. Insert reshape/expand or squeeze/unsqueeze so ranks align before the comparison
  3. Fix upstream ops (cat, slice, reshape) that produced divergent shapes

Example fix

// before
let a: Tensor<NdArray, 2> = ...; // [2, 3]
let b: Tensor<NdArray, 2> = ...; // [4, 3]
let eq = a.equal(b); // panic
// after
let a2 = a.reshape([1, 2, 3]);
let eq = a2.equal(b.reshape([1, 4, 3]).transpose()); // make shapes align/broadcastable
Defensive patterns

Strategy: validation

Validate before calling

fn broadcastable(a: &[usize], b: &[usize]) -> bool {
    let n = a.len().max(b.len());
    (0..n).all(|i| {
        let da = a.get(a.len().checked_sub(1 + i).unwrap_or(usize::MAX)).copied();
        let db = b.get(b.len().checked_sub(1 + i).unwrap_or(usize::MAX)).copied();
        match (da, db) { (Some(x), Some(y)) => x == y || x == 1 || y == 1, _ => true }
    })
}
// assert!(broadcastable(&lhs.dims(), &rhs.dims()));

Prevention

When it happens

Trigger: Calling remainder, equal, greater, greater_equal, lower_equal or lower on tensors whose shapes are not broadcastable - e.g. [2, 3] vs [4, 3], or trailing dims that don't match and neither side is 1.

Common situations: Comparing tensors from different pipeline branches whose batch/spatial dims diverged; comparing [N] with [M]; a reshape dropped/changed a dim upstream so comparison operands no longer align.

Related errors


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