tracel-ai/burn · error

Can't differentiate deform conv 2d backward.

Error message

Can't differentiate deform conv 2d backward.

What it means

q_to_device (moving a quantized tensor to another device) is stubbed in BackendRouter's QTensorOps and panics. Related stubs (q_reshape, etc.) show the whole router quantized-tensor surface is unimplemented.

Source

Thrown at crates/burn-autodiff/src/ops/module.rs:915

                    weight.primitive,
                    None,
                    None,
                    options,
                )),
            },
        }
    }

    fn deform_conv2d_backward(
        _x: AutodiffTensor<B>,
        _offset: AutodiffTensor<B>,
        _weight: AutodiffTensor<B>,
        _mask: Option<AutodiffTensor<B>>,
        _bias: Option<AutodiffTensor<B>>,
        _output_grad: AutodiffTensor<B>,
        _options: DeformConvOptions<2>,
    ) -> DeformConv2dBackward<Self> {
        panic!("Can't differentiate deform conv 2d backward.");
    }

    fn conv_transpose2d(
        x: AutodiffTensor<B>,
        weight: AutodiffTensor<B>,
        bias: Option<AutodiffTensor<B>>,
        options: ConvTransposeOptions<2>,
    ) -> AutodiffTensor<B> {
        #[derive(Debug)]
        struct ConvTranspose2DWithBias;
        #[derive(Debug)]
        struct ConvTranspose2DNoBias;

        impl<B: Backend> Backward<B, 3> for ConvTranspose2DWithBias {
            type State = (NodeId, NodeId, NodeId, ConvTransposeOptions<2>);

            fn backward(
                self,

View on GitHub (pinned to d16f7ba2ed)

Solutions

  1. Move the tensor while it is still float (float to_device), then quantize on the target device's concrete backend.
  2. Handle quantized tensors on the concrete backend that supports q_to_device.
  3. Avoid device transfers of quantized tensors under BackendRouter until quantized ops are routed.

Example fix

// before
let q = q_tensor.to_device(&gpu_device); // panics on BackendRouter
// after
let f = ConcreteBackend::dequantize(q_tensor, FloatDType::F32);
let f = ConcreteBackend::float_to_device(f, &gpu_device);
let q = ConcreteBackend::quantize(f, &scheme, qparams);
Defensive patterns

Strategy: validation

Validate before calling

fn q_device_transfer_ok(dtype: &burn_tensor::DType) -> bool {
    !matches!(dtype, burn_tensor::DType::QFloat(_)) // move floats across devices, not quantized
}

Type guard

fn is_quantized(dtype: &burn_tensor::DType) -> bool {
    matches!(dtype, burn_tensor::DType::QFloat(_))
}

Try / catch

std::panic::catch_unwind(std::panic::AssertUnwindSafe(||
    q_tensor.clone().to_device(&device)
)).map_err(|_| anyhow::anyhow!("q_to_device is a stub on BackendRouter; transfer before quantizing"))

Prevention

When it happens

Trigger: Calling q_to_device / Tensor::to_device on a quantized tensor whose backend is BackendRouter; multi-GPU or CPU/GPU placement changes for quantized tensors under the router.

Common situations: Moving quantized checkpoints to GPU for inference; device management in code generic over Backend using the router; quantized tensors crossing device boundaries.

Related errors


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