{"record":{"id":"010e48c43c9072a7","repo":"tracel-ai/burn","slug":"cross-product-requires-dimension-to-have-size-3","errorCode":null,"errorMessage":"Cross product requires dimension {} to have size 3, but got {} and {}","messagePattern":"Cross product requires dimension (.+?) to have size 3, but got (.+?) and (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/burn-cubecl/src/kernel/cross.rs","lineNumber":52,"sourceCode":"    let b2 = rhs.read(base_pos + 2);\n\n    // Compute cross product: a × b\n    let x = a1 * b2 - a2 * b1;\n    let y = a2 * b0 - a0 * b2;\n    let z = a0 * b1 - a1 * b0;\n\n    // Store result\n    output.write(base_pos, x);\n    output.write(base_pos + 1, y);\n    output.write(base_pos + 2, z);\n}\n\npub(crate) fn cross(lhs: CubeTensor, rhs: CubeTensor, dim: usize) -> CubeTensor {\n    let ndims = lhs.meta.num_dims();\n\n    // Validate that the cross dimension has size 3\n    if lhs.meta.shape()[dim] != 3 || rhs.meta.shape()[dim] != 3 {\n        panic!(\n            \"Cross product requires dimension {} to have size 3, but got {} and {}\",\n            dim,\n            lhs.meta.shape()[dim],\n            rhs.meta.shape()[dim]\n        );\n    }\n\n    // The kernel reads each 3-vector from contiguous memory, so it expects the\n    // cross dimension to be the last (innermost) and physically contiguous.\n    // For non-last dims we permute the cross dim to the last position, run the\n    // kernel, then permute the result back. swap_dims only updates strides, so\n    // make the permuted operands contiguous before launch.\n    if dim != ndims - 1 {\n        let last = ndims - 1;\n        let lhs = into_contiguous(swap_dims(lhs, dim, last));\n        let rhs = into_contiguous(swap_dims(rhs, dim, last));\n        let result = cross(lhs, rhs, last);\n        return swap_dims(result, dim, last);","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/tracel-ai/burn/blob/d16f7ba2ed0d41408189384044cc886fb4c8f957/crates/burn-cubecl/src/kernel/cross.rs#L34-L70","documentation":"The cross-product kernel requires the operand dimension `dim` to have exactly size 3 on both inputs (a cross product is only defined for 3-vectors). The kernel validates this on the host before launch and panics otherwise.","triggerScenarios":"Calling `tensor.cross(other, dim)` (burn cross op) where `lhs.shape()[dim] != 3` or `rhs.shape()[dim] != 3`, or where `dim` is out of bounds for one tensor.","commonSituations":"Accidentally passing the batch dimension instead of the vector dimension; tensors with trailing vector size other than 3 (e.g. 2D vectors or padded 4-vectors); mismatched tensors of different ranks.","solutions":["Slice/squeeze the tensors so the cross dimension has exactly 3 elements","Pass the correct `dim` index (the one with size 3)","For 4-component vectors, drop the padding component (e.g. xyzw -> xyz) before crossing","Check both tensors have the same rank and the target dim exists in both"],"exampleFix":"// before\ntensor.cross(other, -1) // dim has size 4\n// after\nlet a = tensor.slice_dim(-1, 0..3);\nlet b = other.slice_dim(-1, 0..3);\na.cross(b, tensor.dims().len() - 1);","handlingStrategy":"validation","validationCode":"assert_eq!(tensor.shape()[dim], 3, \"cross dim must be size 3\");\nassert_eq!(other.shape()[dim], 3, \"cross dim must be size 3\");","typeGuard":"fn can_cross(a: &TensorBase, b: &TensorBase, dim: usize) -> bool {\n    a.shape().get(dim) == Some(&3) && b.shape().get(dim) == Some(&3)\n}","tryCatchPattern":null,"preventionTips":["Verify dim indexes the vector component, not the batch axis","Slice to 3 components for xyzw/quaternion-like tensors","Check rank and shape compatibility of both operands before ops"],"tags":["gpu","kernel","shape-validation","cross-product"],"backgroundTag":"invalid-shape-for-op","analyzedSha":"d16f7ba2ed0d41408189384044cc886fb4c8f957","analyzedAt":"2026-09-05T13:19:14.260Z","contentChangedAt":"2026-09-05T13:19:14.260Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}