Linear layer forward: `x @ weight^T + bias`. Matches candle_nn::Linear::forward() semantics exactly: - For contiguous 3D/4D inputs: reshape to 2D → matmul → reshape back (avoids slow broadcast_matmul on CUDA/CPU) - For non-contiguous 3D+: uses broadcast_left on weight - No dtype conversion (caller is responsible)
(
&self,
x: &Tensor,
weight: &Tensor,
bias: Option<&Tensor>,
)
| 204 | /// - For non-contiguous 3D+: uses broadcast_left on weight |
| 205 | /// - No dtype conversion (caller is responsible) |
| 206 | fn linear_forward( |
| 207 | &self, |
| 208 | x: &Tensor, |
| 209 | weight: &Tensor, |
| 210 | bias: Option<&Tensor>, |
| 211 | ) -> Result<Tensor> { |
| 212 | let out = match x.dims() { |
| 213 | [b1, b2, m, k] => { |
| 214 | if x.is_contiguous() { |
| 215 | let w = weight.t()?; |
| 216 | x.reshape((b1 * b2 * m, *k))? |
| 217 | .matmul(&w)? |
| 218 | .reshape((*b1, *b2, *m, ()))? |
| 219 | } else { |
| 220 | let w = weight.broadcast_left((*b1, *b2))?.t()?; |
| 221 | x.matmul(&w)? |
| 222 | } |
| 223 | } |
| 224 | [bsize, m, k] => { |
| 225 | if x.is_contiguous() { |
| 226 | let w = weight.t()?; |
| 227 | x.reshape((bsize * m, *k))? |
| 228 | .matmul(&w)? |
| 229 | .reshape((*bsize, *m, ()))? |
| 230 | } else { |
| 231 | let w = weight.broadcast_left(*bsize)?.t()?; |
| 232 | x.matmul(&w)? |
| 233 | } |
| 234 | } |
| 235 | _ => x.matmul(&weight.t()?)?, |
| 236 | }; |
| 237 | match bias { |
| 238 | Some(b) => out.broadcast_add(b), |
| 239 | None => Ok(out), |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | /// RMS normalization: `x * weight / sqrt(mean(x^2) + eps)`. |
| 244 | fn rms_norm(&self, x: &Tensor, weight: &Tensor, eps: f32) -> Result<Tensor> { |
nothing calls this directly
no test coverage detected