src/ops.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // brotensor public op wrappers — core dense, elementwise, norm, losses, embedding, concat, optim. | ||
| 2 | // | ||
| 3 | // Spatial/conv/pool/StyleGAN wrappers live in src/ops_spatial.cpp; | ||
| 4 | // attention/matmul/RoPE/RMSNorm/reductions live in src/ops_attention.cpp; | ||
| 5 | // quantisation/diffusion/audio/sampling/noise live in src/ops_quant_audio.cpp. | ||
| 6 | |||
| 7 | #include <brotensor/ops.h> | ||
| 8 | #include <brotensor/tensor.h> | ||
| 9 | #include <brotensor/detail/dispatch.h> | ||
| 10 | |||
| 11 | #include <cstdint> | ||
| 12 | #include <stdexcept> | ||
| 13 | #include <string> | ||
| 14 | #include <vector> | ||
| 15 | |||
| 16 | namespace brotensor { | ||
| 17 | |||
| 18 | namespace { | ||
| 19 | |||
| 20 | ✗ | [[noreturn]] void throw_empty_parts(const char* op) { | |
| 21 | ✗ | std::string m = "brotensor: "; | |
| 22 | ✗ | m += op; | |
| 23 | ✗ | m += ": parts must be non-empty"; | |
| 24 | ✗ | throw std::runtime_error(m); | |
| 25 | ✗ | } | |
| 26 | |||
| 27 | 67 | const detail::OpsVTable& vtable_from_parts( | |
| 28 | const std::vector<const Tensor*>& parts, const char* op) { | ||
| 29 |
1/2✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
|
67 | if (parts.empty() || parts[0] == nullptr) throw_empty_parts(op); |
| 30 | 67 | Device d = parts[0]->device; | |
| 31 |
2/2✓ Branch 0 taken 99 times.
✓ Branch 1 taken 67 times.
|
166 | for (std::size_t i = 1; i < parts.size(); ++i) { |
| 32 |
2/4✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 99 times.
✗ Branch 3 not taken.
|
99 | if (parts[i] && parts[i]->device != d) { |
| 33 | ✗ | std::string m = "brotensor: "; | |
| 34 | ✗ | m += op; | |
| 35 | ✗ | m += ": parts["; | |
| 36 | ✗ | m += std::to_string(i); | |
| 37 | ✗ | m += "] on different device"; | |
| 38 | ✗ | throw std::runtime_error(m); | |
| 39 | ✗ | } | |
| 40 | 99 | } | |
| 41 | 67 | return detail::ops_for(d); | |
| 42 | ✗ | } | |
| 43 | |||
| 44 | } // namespace | ||
| 45 | |||
| 46 | // ─── Dense layers + elementwise ──────────────────────────────────────────── | ||
| 47 | |||
| 48 | 1264 | void linear_forward(const Tensor& W, const Tensor& b, | |
| 49 | const Tensor& x, Tensor& y) { | ||
| 50 | 1264 | const auto& v = detail::dispatch(W, b, x, y); | |
| 51 |
1/2✓ Branch 0 taken 1264 times.
✗ Branch 1 not taken.
|
1264 | if (!v.linear_forward) detail::throw_not_implemented("linear_forward", W.device); |
| 52 | 1264 | detail::adopt_output(y, W.device); | |
| 53 | 1264 | v.linear_forward(W, b, x, y); | |
| 54 | 1264 | } | |
| 55 | |||
| 56 | 30 | void linear_backward(const Tensor& W, const Tensor& x, const Tensor& dY, | |
| 57 | Tensor& dX, Tensor& dW, Tensor& dB) { | ||
| 58 | 30 | const auto& v = detail::dispatch(W, x, dY, dX, dW, dB); | |
| 59 |
1/2✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
|
30 | if (!v.linear_backward) detail::throw_not_implemented("linear_backward", W.device); |
| 60 | 30 | detail::adopt_output(dX, W.device); | |
| 61 | 30 | detail::adopt_output(dW, W.device); | |
| 62 | 30 | detail::adopt_output(dB, W.device); | |
| 63 | 30 | v.linear_backward(W, x, dY, dX, dW, dB); | |
| 64 | 30 | } | |
| 65 | |||
| 66 | 96 | void relu_forward(const Tensor& x, Tensor& y) { | |
| 67 | 96 | const auto& v = detail::dispatch(x, y); | |
| 68 |
1/2✓ Branch 0 taken 96 times.
✗ Branch 1 not taken.
|
96 | if (!v.relu_forward) detail::throw_not_implemented("relu_forward", x.device); |
| 69 | 96 | detail::adopt_output(y, x.device); | |
| 70 | 96 | v.relu_forward(x, y); | |
| 71 | 96 | } | |
| 72 | |||
| 73 | 11 | void relu_backward(const Tensor& x, const Tensor& dY, Tensor& dX) { | |
| 74 | 11 | const auto& v = detail::dispatch(x, dY, dX); | |
| 75 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | if (!v.relu_backward) detail::throw_not_implemented("relu_backward", x.device); |
| 76 | 11 | detail::adopt_output(dX, x.device); | |
| 77 | 11 | v.relu_backward(x, dY, dX); | |
| 78 | 11 | } | |
| 79 | |||
| 80 | 99 | void tanh_forward(const Tensor& x, Tensor& y) { | |
| 81 | 99 | const auto& v = detail::dispatch(x, y); | |
| 82 |
1/2✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
|
99 | if (!v.tanh_forward) detail::throw_not_implemented("tanh_forward", x.device); |
| 83 | 99 | detail::adopt_output(y, x.device); | |
| 84 | 99 | v.tanh_forward(x, y); | |
| 85 | 99 | } | |
| 86 | |||
| 87 | 11 | void tanh_backward(const Tensor& y, const Tensor& dY, Tensor& dX) { | |
| 88 | 11 | const auto& v = detail::dispatch(y, dY, dX); | |
| 89 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | if (!v.tanh_backward) detail::throw_not_implemented("tanh_backward", y.device); |
| 90 | 11 | detail::adopt_output(dX, y.device); | |
| 91 | 11 | v.tanh_backward(y, dY, dX); | |
| 92 | 11 | } | |
| 93 | |||
| 94 | 26 | void sigmoid_forward(const Tensor& x, Tensor& y) { | |
| 95 | 26 | const auto& v = detail::dispatch(x, y); | |
| 96 |
1/2✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
|
26 | if (!v.sigmoid_forward) detail::throw_not_implemented("sigmoid_forward", x.device); |
| 97 | 26 | detail::adopt_output(y, x.device); | |
| 98 | 26 | v.sigmoid_forward(x, y); | |
| 99 | 26 | } | |
| 100 | |||
| 101 | 11 | void sigmoid_backward(const Tensor& y, const Tensor& dY, Tensor& dX) { | |
| 102 | 11 | const auto& v = detail::dispatch(y, dY, dX); | |
| 103 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | if (!v.sigmoid_backward) detail::throw_not_implemented("sigmoid_backward", y.device); |
| 104 | 11 | detail::adopt_output(dX, y.device); | |
| 105 | 11 | v.sigmoid_backward(y, dY, dX); | |
| 106 | 11 | } | |
| 107 | |||
| 108 | 1182 | void add_inplace(Tensor& y, const Tensor& x) { | |
| 109 | 1182 | const auto& v = detail::dispatch(y, x); | |
| 110 |
1/2✓ Branch 0 taken 1182 times.
✗ Branch 1 not taken.
|
1182 | if (!v.add_inplace) detail::throw_not_implemented("add_inplace", y.device); |
| 111 | 1182 | detail::adopt_output(y, y.device); | |
| 112 | 1182 | v.add_inplace(y, x); | |
| 113 | 1182 | } | |
| 114 | |||
| 115 | 4 | void axpby_inplace(Tensor& y, const Tensor& x, float a, float b) { | |
| 116 | 4 | const auto& v = detail::dispatch(y, x); | |
| 117 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | if (!v.axpby_inplace) detail::throw_not_implemented("axpby_inplace", y.device); |
| 118 | 4 | detail::adopt_output(y, y.device); | |
| 119 | 4 | v.axpby_inplace(y, x, a, b); | |
| 120 | 4 | } | |
| 121 | |||
| 122 | 16 | void add_scalar_inplace(Tensor& y, float s) { | |
| 123 | 16 | const auto& v = detail::dispatch(y); | |
| 124 |
1/2✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
|
16 | if (!v.add_scalar_inplace) detail::throw_not_implemented("add_scalar_inplace", y.device); |
| 125 | 16 | detail::adopt_output(y, y.device); | |
| 126 | 16 | v.add_scalar_inplace(y, s); | |
| 127 | 16 | } | |
| 128 | |||
| 129 | 39 | void add_channel_bias_inplace(Tensor& y, const Tensor& bias, int C, int L) { | |
| 130 | 39 | const auto& v = detail::dispatch(y); | |
| 131 |
1/2✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
|
39 | if (!v.add_channel_bias_inplace) |
| 132 | ✗ | detail::throw_not_implemented("add_channel_bias_inplace", y.device); | |
| 133 | 39 | detail::adopt_output(y, y.device); | |
| 134 | 39 | v.add_channel_bias_inplace(y, bias, C, L); | |
| 135 | 39 | } | |
| 136 | |||
| 137 | 14 | void add_row_bias_inplace(Tensor& Y, const Tensor& bias) { | |
| 138 | 14 | const auto& v = detail::dispatch(Y, bias); | |
| 139 |
1/2✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
|
14 | if (!v.add_row_bias_inplace) |
| 140 | ✗ | detail::throw_not_implemented("add_row_bias_inplace", Y.device); | |
| 141 | 14 | detail::adopt_output(Y, Y.device); | |
| 142 | 14 | v.add_row_bias_inplace(Y, bias); | |
| 143 | 14 | } | |
| 144 | |||
| 145 | 301 | void scale_inplace(Tensor& y, float s) { | |
| 146 | 301 | const auto& v = detail::dispatch(y); | |
| 147 |
1/2✓ Branch 0 taken 301 times.
✗ Branch 1 not taken.
|
301 | if (!v.scale_inplace) detail::throw_not_implemented("scale_inplace", y.device); |
| 148 | 301 | detail::adopt_output(y, y.device); | |
| 149 | 301 | v.scale_inplace(y, s); | |
| 150 | 301 | } | |
| 151 | |||
| 152 | 17 | void clamp(Tensor& y, float lo, float hi) { | |
| 153 |
3/4✓ Branch 0 taken 1 time.
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
|
17 | if (detail::jit_clamp_inplace_hook && detail::jit_clamp_inplace_hook(y, lo, hi)) { |
| 154 | 1 | return; | |
| 155 | } | ||
| 156 | 16 | const auto& v = detail::dispatch(y); | |
| 157 |
1/2✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
|
16 | if (!v.clamp) detail::throw_not_implemented("clamp", y.device); |
| 158 | 16 | detail::adopt_output(y, y.device); | |
| 159 | 16 | v.clamp(y, lo, hi); | |
| 160 | 17 | } | |
| 161 | |||
| 162 | 1263 | void mul_inplace(Tensor& y, const Tensor& x) { | |
| 163 | 1263 | const auto& v = detail::dispatch(y, x); | |
| 164 |
1/2✓ Branch 0 taken 1263 times.
✗ Branch 1 not taken.
|
1263 | if (!v.mul_inplace) detail::throw_not_implemented("mul_inplace", y.device); |
| 165 | 1263 | detail::adopt_output(y, y.device); | |
| 166 | 1263 | v.mul_inplace(y, x); | |
| 167 | 1263 | } | |
| 168 | |||
| 169 | ✗ | void div_inplace(Tensor& y, const Tensor& x) { | |
| 170 | ✗ | const auto& v = detail::dispatch(y, x); | |
| 171 | ✗ | if (!v.div_inplace) detail::throw_not_implemented("div_inplace", y.device); | |
| 172 | ✗ | detail::adopt_output(y, y.device); | |
| 173 | ✗ | v.div_inplace(y, x); | |
| 174 | ✗ | } | |
| 175 | |||
| 176 | 732 | void modulate(const Tensor& X, const Tensor& scale, const Tensor& shift, | |
| 177 | Tensor& Y) { | ||
| 178 | 732 | const auto& v = detail::dispatch(X, scale, shift, Y); | |
| 179 |
1/2✓ Branch 0 taken 732 times.
✗ Branch 1 not taken.
|
732 | if (!v.modulate) detail::throw_not_implemented("modulate", X.device); |
| 180 | 732 | detail::adopt_output(Y, X.device); | |
| 181 | 732 | v.modulate(X, scale, shift, Y); | |
| 182 | 732 | } | |
| 183 | |||
| 184 | 490 | void broadcast_mul(const Tensor& X, const Tensor& vv, Tensor& Y) { | |
| 185 | 490 | const auto& v = detail::dispatch(X, vv, Y); | |
| 186 |
1/2✓ Branch 0 taken 490 times.
✗ Branch 1 not taken.
|
490 | if (!v.broadcast_mul) detail::throw_not_implemented("broadcast_mul", X.device); |
| 187 | 490 | detail::adopt_output(Y, X.device); | |
| 188 | 490 | v.broadcast_mul(X, vv, Y); | |
| 189 | 490 | } | |
| 190 | |||
| 191 | 8 | void build_slot_mask(const Tensor& x, int offset, int K, int stride, Tensor& mask) { | |
| 192 | 8 | const auto& v = detail::dispatch(x, mask); | |
| 193 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | if (!v.build_slot_mask) detail::throw_not_implemented("build_slot_mask", x.device); |
| 194 | 8 | detail::adopt_output(mask, x.device); | |
| 195 | 8 | v.build_slot_mask(x, offset, K, stride, mask); | |
| 196 | 8 | } | |
| 197 | |||
| 198 | // ─── Reductions / norm / softmax / attention (training) ──────────────────── | ||
| 199 | |||
| 200 | 42 | void softmax_forward(const Tensor& logits, Tensor& probs, const float* mask) { | |
| 201 | 42 | const auto& v = detail::dispatch(logits, probs); | |
| 202 |
1/2✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
|
42 | if (!v.softmax_forward) detail::throw_not_implemented("softmax_forward", logits.device); |
| 203 | 42 | detail::adopt_output(probs, logits.device); | |
| 204 | 42 | v.softmax_forward(logits, probs, mask); | |
| 205 | 42 | } | |
| 206 | |||
| 207 | 46 | void softmax_rows_forward(const Tensor& X, Tensor& Y, int rows, int cols) { | |
| 208 | 46 | const auto& v = detail::dispatch(X, Y); | |
| 209 |
1/2✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
|
46 | if (!v.softmax_rows_forward) detail::throw_not_implemented("softmax_rows_forward", X.device); |
| 210 | 46 | detail::adopt_output(Y, X.device); | |
| 211 | 46 | v.softmax_rows_forward(X, Y, rows, cols); | |
| 212 | 46 | } | |
| 213 | |||
| 214 | 21 | void softmax_backward(const Tensor& probs, const Tensor& dProbs, Tensor& dLogits) { | |
| 215 | 21 | const auto& v = detail::dispatch(probs, dProbs, dLogits); | |
| 216 |
1/2✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
|
21 | if (!v.softmax_backward) detail::throw_not_implemented("softmax_backward", probs.device); |
| 217 | 21 | detail::adopt_output(dLogits, probs.device); | |
| 218 | 21 | v.softmax_backward(probs, dProbs, dLogits); | |
| 219 | 21 | } | |
| 220 | |||
| 221 | 8 | void layernorm_forward(const Tensor& x, const Tensor& gamma, const Tensor& beta, | |
| 222 | Tensor& y, Tensor& xhat, | ||
| 223 | float& mean_out, float& rstd_out, float eps) { | ||
| 224 | 8 | const auto& v = detail::dispatch(x, gamma, beta, y, xhat); | |
| 225 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | if (!v.layernorm_forward) detail::throw_not_implemented("layernorm_forward", x.device); |
| 226 | 8 | detail::adopt_output(y, x.device); | |
| 227 | 8 | detail::adopt_output(xhat, x.device); | |
| 228 | 8 | v.layernorm_forward(x, gamma, beta, y, xhat, mean_out, rstd_out, eps); | |
| 229 | 8 | } | |
| 230 | |||
| 231 | 14 | void layernorm_backward(const Tensor& dY, const Tensor& xhat, | |
| 232 | const Tensor& gamma, float rstd, | ||
| 233 | Tensor& dX, Tensor& dGamma, Tensor& dBeta) { | ||
| 234 | 14 | const auto& v = detail::dispatch(dY, xhat, gamma, dX, dGamma, dBeta); | |
| 235 |
1/2✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
|
14 | if (!v.layernorm_backward) detail::throw_not_implemented("layernorm_backward", dY.device); |
| 236 | 14 | detail::adopt_output(dX, dY.device); | |
| 237 | 14 | detail::adopt_output(dGamma, dY.device); | |
| 238 | 14 | detail::adopt_output(dBeta, dY.device); | |
| 239 | 14 | v.layernorm_backward(dY, xhat, gamma, rstd, dX, dGamma, dBeta); | |
| 240 | 14 | } | |
| 241 | |||
| 242 | 20 | void attention_forward(const Tensor& X, | |
| 243 | const Tensor& Wq, const Tensor& Wk, | ||
| 244 | const Tensor& Wv, const Tensor& Wo, | ||
| 245 | const float* d_mask, | ||
| 246 | Tensor& Q, Tensor& K, Tensor& V, | ||
| 247 | Tensor& Attn, Tensor& Y_pre_Wo, Tensor& O) { | ||
| 248 | 20 | const auto& v = detail::dispatch(X, Wq, Wk, Wv, Wo, Q, K, V); | |
| 249 |
1/2✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
|
20 | if (!v.attention_forward) detail::throw_not_implemented("attention_forward", X.device); |
| 250 | 20 | detail::adopt_output(Q, X.device); | |
| 251 | 20 | detail::adopt_output(K, X.device); | |
| 252 | 20 | detail::adopt_output(V, X.device); | |
| 253 | 20 | detail::adopt_output(Attn, X.device); | |
| 254 | 20 | detail::adopt_output(Y_pre_Wo, X.device); | |
| 255 | 20 | detail::adopt_output(O, X.device); | |
| 256 | 20 | v.attention_forward(X, Wq, Wk, Wv, Wo, d_mask, Q, K, V, Attn, Y_pre_Wo, O); | |
| 257 | 20 | } | |
| 258 | |||
| 259 | 16 | void attention_backward(const Tensor& dO, const Tensor& X, | |
| 260 | const Tensor& Q, const Tensor& K, | ||
| 261 | const Tensor& V, const Tensor& Attn, | ||
| 262 | const Tensor& Y_pre_Wo, | ||
| 263 | const Tensor& Wq, const Tensor& Wk, | ||
| 264 | const Tensor& Wv, const Tensor& Wo, | ||
| 265 | const float* d_mask, | ||
| 266 | Tensor& dX, | ||
| 267 | Tensor& dWq, Tensor& dWk, | ||
| 268 | Tensor& dWv, Tensor& dWo) { | ||
| 269 | 16 | const auto& v = detail::dispatch(dO, X, Q, K, V, Attn, Y_pre_Wo); | |
| 270 |
1/2✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
|
16 | if (!v.attention_backward) detail::throw_not_implemented("attention_backward", dO.device); |
| 271 | 16 | detail::adopt_output(dX, dO.device); | |
| 272 | 16 | detail::adopt_output(dWq, dO.device); | |
| 273 | 16 | detail::adopt_output(dWk, dO.device); | |
| 274 | 16 | detail::adopt_output(dWv, dO.device); | |
| 275 | 16 | detail::adopt_output(dWo, dO.device); | |
| 276 | 32 | v.attention_backward(dO, X, Q, K, V, Attn, Y_pre_Wo, | |
| 277 | 16 | Wq, Wk, Wv, Wo, d_mask, | |
| 278 | 16 | dX, dWq, dWk, dWv, dWo); | |
| 279 | 16 | } | |
| 280 | |||
| 281 | 32 | void mha_forward(const Tensor& X, | |
| 282 | const Tensor& Wq, const Tensor& Wk, | ||
| 283 | const Tensor& Wv, const Tensor& Wo, | ||
| 284 | const Tensor* bq, const Tensor* bk, | ||
| 285 | const Tensor* bv, const Tensor* bo, | ||
| 286 | const float* d_mask, int num_heads, | ||
| 287 | Tensor& Qh, Tensor& Kh, Tensor& Vh, | ||
| 288 | Tensor& Attnh, Tensor& Yconcat, Tensor& O) { | ||
| 289 | 32 | const auto& v = detail::dispatch_with_opts( | |
| 290 | 32 | X, Wq, {&Wk, &Wv, &Wo, bq, bk, bv, bo, &Qh, &Kh, &Vh}); | |
| 291 |
1/2✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
|
32 | if (!v.mha_forward) detail::throw_not_implemented("mha_forward", X.device); |
| 292 | 32 | detail::adopt_output(Qh, X.device); | |
| 293 | 32 | detail::adopt_output(Kh, X.device); | |
| 294 | 32 | detail::adopt_output(Vh, X.device); | |
| 295 | 32 | detail::adopt_output(Attnh, X.device); | |
| 296 | 32 | detail::adopt_output(Yconcat, X.device); | |
| 297 | 32 | detail::adopt_output(O, X.device); | |
| 298 | 64 | v.mha_forward(X, Wq, Wk, Wv, Wo, bq, bk, bv, bo, d_mask, num_heads, | |
| 299 | 32 | Qh, Kh, Vh, Attnh, Yconcat, O); | |
| 300 | 32 | } | |
| 301 | |||
| 302 | 30 | void mha_backward(const Tensor& dO, const Tensor& X, | |
| 303 | const Tensor& Qh, const Tensor& Kh, | ||
| 304 | const Tensor& Vh, const Tensor& Attnh, | ||
| 305 | const Tensor& Yconcat, | ||
| 306 | const Tensor& Wq, const Tensor& Wk, | ||
| 307 | const Tensor& Wv, const Tensor& Wo, | ||
| 308 | const float* d_mask, int num_heads, | ||
| 309 | Tensor& dX, | ||
| 310 | Tensor& dWq, Tensor& dWk, | ||
| 311 | Tensor& dWv, Tensor& dWo, | ||
| 312 | Tensor* dbq, Tensor* dbk, | ||
| 313 | Tensor* dbv, Tensor* dbo) { | ||
| 314 | 30 | const auto& v = detail::dispatch_with_opts( | |
| 315 | 30 | dO, X, {&Qh, &Kh, &Vh, &Attnh, &Yconcat, dbq, dbk, dbv, dbo}); | |
| 316 |
1/2✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
|
30 | if (!v.mha_backward) detail::throw_not_implemented("mha_backward", dO.device); |
| 317 | 30 | detail::adopt_output(dX, dO.device); | |
| 318 | 30 | detail::adopt_output(dWq, dO.device); | |
| 319 | 30 | detail::adopt_output(dWk, dO.device); | |
| 320 | 30 | detail::adopt_output(dWv, dO.device); | |
| 321 | 30 | detail::adopt_output(dWo, dO.device); | |
| 322 |
2/2✓ Branch 0 taken 22 times.
✓ Branch 1 taken 8 times.
|
30 | if (dbq) detail::adopt_output(*dbq, dO.device); |
| 323 |
2/2✓ Branch 0 taken 22 times.
✓ Branch 1 taken 8 times.
|
30 | if (dbk) detail::adopt_output(*dbk, dO.device); |
| 324 |
2/2✓ Branch 0 taken 22 times.
✓ Branch 1 taken 8 times.
|
30 | if (dbv) detail::adopt_output(*dbv, dO.device); |
| 325 |
2/2✓ Branch 0 taken 22 times.
✓ Branch 1 taken 8 times.
|
30 | if (dbo) detail::adopt_output(*dbo, dO.device); |
| 326 | 60 | v.mha_backward(dO, X, Qh, Kh, Vh, Attnh, Yconcat, | |
| 327 | 30 | Wq, Wk, Wv, Wo, d_mask, num_heads, | |
| 328 | 30 | dX, dWq, dWk, dWv, dWo, | |
| 329 | 30 | dbq, dbk, dbv, dbo); | |
| 330 | 30 | } | |
| 331 | |||
| 332 | // ─── Pooling / losses / embedding / concat ───────────────────────────────── | ||
| 333 | |||
| 334 | 22 | void masked_mean_pool_forward(const Tensor& X, const float* d_mask, Tensor& y) { | |
| 335 | 22 | const auto& v = detail::dispatch(X, y); | |
| 336 |
1/2✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
|
22 | if (!v.masked_mean_pool_forward) detail::throw_not_implemented("masked_mean_pool_forward", X.device); |
| 337 | 22 | detail::adopt_output(y, X.device); | |
| 338 | 22 | v.masked_mean_pool_forward(X, d_mask, y); | |
| 339 | 22 | } | |
| 340 | |||
| 341 | 16 | void masked_mean_pool_backward(const Tensor& dY, const float* d_mask, | |
| 342 | int K, Tensor& dX) { | ||
| 343 | 16 | const auto& v = detail::dispatch(dY, dX); | |
| 344 |
1/2✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
|
16 | if (!v.masked_mean_pool_backward) detail::throw_not_implemented("masked_mean_pool_backward", dY.device); |
| 345 | 16 | detail::adopt_output(dX, dY.device); | |
| 346 | 16 | v.masked_mean_pool_backward(dY, d_mask, K, dX); | |
| 347 | 16 | } | |
| 348 | |||
| 349 | 9 | float mse_vec_forward(const Tensor& pred, const Tensor& target) { | |
| 350 | 9 | const auto& v = detail::dispatch(pred, target); | |
| 351 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | if (!v.mse_vec_forward) detail::throw_not_implemented("mse_vec_forward", pred.device); |
| 352 | 9 | return v.mse_vec_forward(pred, target); | |
| 353 | } | ||
| 354 | |||
| 355 | 9 | void mse_vec_backward(const Tensor& pred, const Tensor& target, Tensor& dPred) { | |
| 356 | 9 | const auto& v = detail::dispatch(pred, target, dPred); | |
| 357 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | if (!v.mse_vec_backward) detail::throw_not_implemented("mse_vec_backward", pred.device); |
| 358 | 9 | detail::adopt_output(dPred, pred.device); | |
| 359 | 9 | v.mse_vec_backward(pred, target, dPred); | |
| 360 | 9 | } | |
| 361 | |||
| 362 | 7 | float mse_scalar(float pred, float target, float& dPred) { | |
| 363 | 7 | const auto& v = detail::ops_for(Device::CPU); | |
| 364 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | if (!v.mse_scalar) detail::throw_not_implemented("mse_scalar", Device::CPU); |
| 365 | 7 | return v.mse_scalar(pred, target, dPred); | |
| 366 | } | ||
| 367 | |||
| 368 | 7 | float softmax_xent(const Tensor& logits, const Tensor& target, | |
| 369 | Tensor& probs, Tensor& dLogits, const float* mask) { | ||
| 370 | 7 | const auto& v = detail::dispatch(logits, target, probs, dLogits); | |
| 371 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | if (!v.softmax_xent) detail::throw_not_implemented("softmax_xent", logits.device); |
| 372 | 7 | detail::adopt_output(probs, logits.device); | |
| 373 | 7 | detail::adopt_output(dLogits, logits.device); | |
| 374 | 7 | return v.softmax_xent(logits, target, probs, dLogits, mask); | |
| 375 | } | ||
| 376 | |||
| 377 | 11 | float softmax_xent_segment(const float* logits, const float* target, | |
| 378 | float* probs, float* dLogits, | ||
| 379 | int n, const float* mask) { | ||
| 380 | 11 | const auto& v = detail::ops_for(Device::CPU); | |
| 381 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | if (!v.softmax_xent_segment) detail::throw_not_implemented("softmax_xent_segment", Device::CPU); |
| 382 | 11 | return v.softmax_xent_segment(logits, target, probs, dLogits, n, mask); | |
| 383 | } | ||
| 384 | |||
| 385 | 19 | float softmax_xent_fused(const Tensor& logits, const Tensor& target, | |
| 386 | const float* d_mask, | ||
| 387 | Tensor& probs, Tensor& dLogits) { | ||
| 388 | 19 | const auto& v = detail::dispatch(logits, target, probs, dLogits); | |
| 389 |
1/2✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
|
19 | if (!v.softmax_xent_fused) detail::throw_not_implemented("softmax_xent_fused", logits.device); |
| 390 | 19 | detail::adopt_output(probs, logits.device); | |
| 391 | 19 | detail::adopt_output(dLogits, logits.device); | |
| 392 | 19 | return v.softmax_xent_fused(logits, target, d_mask, probs, dLogits); | |
| 393 | } | ||
| 394 | |||
| 395 | 39 | void embedding_lookup_forward(const Tensor& table, const int32_t* d_idx, | |
| 396 | int B, Tensor& out) { | ||
| 397 | 39 | const auto& v = detail::dispatch(table, out); | |
| 398 |
1/2✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
|
39 | if (!v.embedding_lookup_forward) detail::throw_not_implemented("embedding_lookup_forward", table.device); |
| 399 | 39 | detail::adopt_output(out, table.device); | |
| 400 | 39 | v.embedding_lookup_forward(table, d_idx, B, out); | |
| 401 | 39 | } | |
| 402 | |||
| 403 | 27 | void embedding_lookup_backward(const Tensor& dOut, const int32_t* d_idx, | |
| 404 | int B, Tensor& dTable) { | ||
| 405 | 27 | const auto& v = detail::dispatch(dOut, dTable); | |
| 406 |
1/2✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
|
27 | if (!v.embedding_lookup_backward) detail::throw_not_implemented("embedding_lookup_backward", dOut.device); |
| 407 | 27 | detail::adopt_output(dTable, dOut.device); | |
| 408 | 27 | v.embedding_lookup_backward(dOut, d_idx, B, dTable); | |
| 409 | 27 | } | |
| 410 | |||
| 411 | 17 | void concat_rows(const std::vector<const Tensor*>& parts, Tensor& out) { | |
| 412 | 17 | const auto& v = vtable_from_parts(parts, "concat_rows"); | |
| 413 |
3/4✓ Branch 0 taken 2 times.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
17 | if (out.data != nullptr && parts[0]->device != out.device) { |
| 414 | ✗ | throw std::runtime_error("brotensor: concat_rows: out on different device"); | |
| 415 | } | ||
| 416 | 17 | detail::adopt_output(out, parts[0]->device); | |
| 417 |
1/2✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
|
17 | if (!v.concat_rows) detail::throw_not_implemented("concat_rows", parts[0]->device); |
| 418 | 17 | v.concat_rows(parts, out); | |
| 419 | 17 | } | |
| 420 | |||
| 421 | 12 | void split_rows(const Tensor& in, const std::vector<Tensor*>& parts) { | |
| 422 | 12 | Device d = in.device; | |
| 423 |
2/2✓ Branch 0 taken 40 times.
✓ Branch 1 taken 12 times.
|
52 | for (std::size_t i = 0; i < parts.size(); ++i) { |
| 424 |
3/6✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 40 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 40 times.
✗ Branch 5 not taken.
|
40 | if (parts[i] && parts[i]->data != nullptr && parts[i]->device != d) { |
| 425 | ✗ | throw std::runtime_error("brotensor: split_rows: parts on different device"); | |
| 426 | } | ||
| 427 | 40 | } | |
| 428 | 12 | const auto& v = detail::ops_for(d); | |
| 429 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (!v.split_rows) detail::throw_not_implemented("split_rows", d); |
| 430 |
3/4✓ Branch 0 taken 40 times.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 40 times.
|
52 | for (Tensor* _p : parts) if (_p) detail::adopt_output(*_p, d); |
| 431 | 12 | v.split_rows(in, parts); | |
| 432 | 12 | } | |
| 433 | |||
| 434 | 23 | void concat_batched_rows(const std::vector<const Tensor*>& parts, Tensor& out) { | |
| 435 | 23 | const auto& v = vtable_from_parts(parts, "concat_batched_rows"); | |
| 436 |
3/4✓ Branch 0 taken 2 times.
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
23 | if (out.data != nullptr && parts[0]->device != out.device) { |
| 437 | ✗ | throw std::runtime_error("brotensor: concat_batched_rows: out on different device"); | |
| 438 | } | ||
| 439 | 23 | detail::adopt_output(out, parts[0]->device); | |
| 440 |
1/2✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
|
23 | if (!v.concat_batched_rows) detail::throw_not_implemented("concat_batched_rows", parts[0]->device); |
| 441 | 23 | v.concat_batched_rows(parts, out); | |
| 442 | 23 | } | |
| 443 | |||
| 444 | 27 | void concat_nchw_channels(const std::vector<const Tensor*>& parts, | |
| 445 | int N, int H, int W, | ||
| 446 | const std::vector<int>& C_per_part, | ||
| 447 | Tensor& out) { | ||
| 448 | 27 | const auto& v = vtable_from_parts(parts, "concat_nchw_channels"); | |
| 449 |
3/4✓ Branch 0 taken 2 times.
✓ Branch 1 taken 25 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
27 | if (out.data != nullptr && parts[0]->device != out.device) { |
| 450 | ✗ | throw std::runtime_error("brotensor: concat_nchw_channels: out on different device"); | |
| 451 | } | ||
| 452 | 27 | detail::adopt_output(out, parts[0]->device); | |
| 453 |
1/2✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
|
27 | if (!v.concat_nchw_channels) detail::throw_not_implemented("concat_nchw_channels", parts[0]->device); |
| 454 | 27 | v.concat_nchw_channels(parts, N, H, W, C_per_part, out); | |
| 455 | 27 | } | |
| 456 | |||
| 457 | 24 | void concat_nchw_channels_backward(const Tensor& dY, int N, int H, int W, | |
| 458 | const std::vector<int>& C_per_part, | ||
| 459 | const std::vector<Tensor*>& parts) { | ||
| 460 | 24 | Device d = dY.device; | |
| 461 |
2/2✓ Branch 0 taken 48 times.
✓ Branch 1 taken 24 times.
|
72 | for (std::size_t i = 0; i < parts.size(); ++i) { |
| 462 |
4/6✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 44 times.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
|
48 | if (parts[i] && parts[i]->data != nullptr && parts[i]->device != d) { |
| 463 | ✗ | throw std::runtime_error("brotensor: concat_nchw_channels_backward: parts on different device"); | |
| 464 | } | ||
| 465 | 48 | } | |
| 466 | 24 | const auto& v = detail::ops_for(d); | |
| 467 |
1/2✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
|
24 | if (!v.concat_nchw_channels_backward) detail::throw_not_implemented("concat_nchw_channels_backward", d); |
| 468 |
3/4✓ Branch 0 taken 48 times.
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 48 times.
|
72 | for (Tensor* _p : parts) if (_p) detail::adopt_output(*_p, d); |
| 469 | 24 | v.concat_nchw_channels_backward(dY, N, H, W, C_per_part, parts); | |
| 470 | 24 | } | |
| 471 | |||
| 472 | 36 | void copy_d2d(const Tensor& src, int src_off, Tensor& dst, int dst_off, int n) { | |
| 473 | 36 | const auto& v = detail::dispatch(src, dst); | |
| 474 |
1/2✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
|
36 | if (!v.copy_d2d) detail::throw_not_implemented("copy_d2d", src.device); |
| 475 | 36 | detail::adopt_output(dst, src.device); | |
| 476 | 36 | v.copy_d2d(src, src_off, dst, dst_off, n); | |
| 477 | 36 | } | |
| 478 | |||
| 479 | 10 | void copy_d2d_strided(const Tensor& src, int src_off, int src_pitch, | |
| 480 | Tensor& dst, int dst_off, int dst_pitch, | ||
| 481 | int width, int height) { | ||
| 482 |
1/2✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
|
10 | if (width > src_pitch || width > dst_pitch) { |
| 483 | ✗ | throw std::runtime_error("brotensor: copy_d2d_strided: width exceeds pitch"); | |
| 484 | } | ||
| 485 | 10 | const auto& v = detail::dispatch(src, dst); | |
| 486 |
1/2✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
|
10 | if (!v.copy_d2d_strided) detail::throw_not_implemented("copy_d2d_strided", src.device); |
| 487 | 10 | detail::adopt_output(dst, src.device); | |
| 488 | 10 | v.copy_d2d_strided(src, src_off, src_pitch, dst, dst_off, dst_pitch, width, height); | |
| 489 | 10 | } | |
| 490 | |||
| 491 | 230 | void cast(const Tensor& src, Tensor& dst, Dtype out_dtype) { | |
| 492 |
4/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 227 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 time.
|
230 | if (detail::jit_cast_out_hook && detail::jit_cast_out_hook(src, dst, out_dtype)) { |
| 493 | 1 | return; | |
| 494 | } | ||
| 495 | 229 | const auto& v = detail::dispatch(src, dst); | |
| 496 |
1/2✓ Branch 0 taken 229 times.
✗ Branch 1 not taken.
|
229 | if (!v.cast) detail::throw_not_implemented("cast", src.device); |
| 497 | 229 | detail::adopt_output(dst, src.device); | |
| 498 | 229 | v.cast(src, dst, out_dtype); | |
| 499 | 230 | } | |
| 500 | |||
| 501 | // ─── Inference batched + optim ───────────────────────────────────────────── | ||
| 502 | |||
| 503 | 758 | void layernorm_forward_inference_batched(const Tensor& X_RD, | |
| 504 | const Tensor& gamma, | ||
| 505 | const Tensor& beta, | ||
| 506 | Tensor& Y_RD, float eps) { | ||
| 507 | 758 | const auto& v = detail::dispatch(X_RD, gamma, beta, Y_RD); | |
| 508 |
1/2✓ Branch 0 taken 758 times.
✗ Branch 1 not taken.
|
758 | if (!v.layernorm_forward_inference_batched) |
| 509 | ✗ | detail::throw_not_implemented("layernorm_forward_inference_batched", X_RD.device); | |
| 510 | 758 | detail::adopt_output(Y_RD, X_RD.device); | |
| 511 | 758 | v.layernorm_forward_inference_batched(X_RD, gamma, &beta, Y_RD, eps); | |
| 512 | 758 | } | |
| 513 | |||
| 514 | 7 | void layernorm_forward_inference_batched(const Tensor& X_RD, | |
| 515 | const Tensor& gamma, | ||
| 516 | Tensor& Y_RD, float eps) { | ||
| 517 | 7 | const auto& v = detail::dispatch(X_RD, gamma, Y_RD); | |
| 518 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | if (!v.layernorm_forward_inference_batched) |
| 519 | ✗ | detail::throw_not_implemented("layernorm_forward_inference_batched", X_RD.device); | |
| 520 | 7 | detail::adopt_output(Y_RD, X_RD.device); | |
| 521 | 7 | v.layernorm_forward_inference_batched(X_RD, gamma, nullptr, Y_RD, eps); | |
| 522 | 7 | } | |
| 523 | |||
| 524 | 18 | void layernorm_forward_batched_with_caches(const Tensor& X_RD, | |
| 525 | const Tensor& gamma, | ||
| 526 | const Tensor& beta, | ||
| 527 | Tensor& Y_RD, Tensor& Xhat_RD, | ||
| 528 | Tensor& Mean_R, Tensor& Rstd_R, | ||
| 529 | float eps) { | ||
| 530 | 18 | const auto& v = detail::dispatch(X_RD, gamma, beta, Y_RD, Xhat_RD, Mean_R, Rstd_R); | |
| 531 |
1/2✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
|
18 | if (!v.layernorm_forward_batched_with_caches) |
| 532 | ✗ | detail::throw_not_implemented("layernorm_forward_batched_with_caches", X_RD.device); | |
| 533 | 18 | detail::adopt_output(Y_RD, X_RD.device); | |
| 534 | 18 | detail::adopt_output(Xhat_RD, X_RD.device); | |
| 535 | 18 | detail::adopt_output(Mean_R, X_RD.device); | |
| 536 | 18 | detail::adopt_output(Rstd_R, X_RD.device); | |
| 537 | 36 | v.layernorm_forward_batched_with_caches(X_RD, gamma, beta, Y_RD, Xhat_RD, | |
| 538 | 18 | Mean_R, Rstd_R, eps); | |
| 539 | 18 | } | |
| 540 | |||
| 541 | 18 | void layernorm_backward_batched_with_caches(const Tensor& dY_RD, | |
| 542 | const Tensor& Xhat_RD, | ||
| 543 | const Tensor& gamma, | ||
| 544 | const Tensor& Rstd_R, | ||
| 545 | Tensor& dX_RD, | ||
| 546 | Tensor& dGamma, Tensor& dBeta) { | ||
| 547 | 18 | const auto& v = detail::dispatch(dY_RD, Xhat_RD, gamma, Rstd_R, dX_RD, dGamma, dBeta); | |
| 548 |
1/2✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
|
18 | if (!v.layernorm_backward_batched_with_caches) |
| 549 | ✗ | detail::throw_not_implemented("layernorm_backward_batched_with_caches", dY_RD.device); | |
| 550 | 18 | detail::adopt_output(dX_RD, dY_RD.device); | |
| 551 | 18 | detail::adopt_output(dGamma, dY_RD.device); | |
| 552 | 18 | detail::adopt_output(dBeta, dY_RD.device); | |
| 553 | 36 | v.layernorm_backward_batched_with_caches(dY_RD, Xhat_RD, gamma, Rstd_R, | |
| 554 | 18 | dX_RD, dGamma, dBeta); | |
| 555 | 18 | } | |
| 556 | |||
| 557 | 5 | void sgd_step(Tensor& param, Tensor& grad, Tensor& velocity, | |
| 558 | float lr, float momentum) { | ||
| 559 | 5 | const auto& v = detail::dispatch(param, grad, velocity); | |
| 560 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | if (!v.sgd_step) detail::throw_not_implemented("sgd_step", param.device); |
| 561 | 5 | detail::adopt_output(param, param.device); | |
| 562 | 5 | detail::adopt_output(grad, param.device); | |
| 563 | 5 | detail::adopt_output(velocity, param.device); | |
| 564 | 5 | v.sgd_step(param, grad, velocity, lr, momentum); | |
| 565 | 5 | } | |
| 566 | |||
| 567 | 50 | void adam_step(Tensor& param, const Tensor& grad, | |
| 568 | Tensor& m, Tensor& v_buf, | ||
| 569 | float lr, float beta1, float beta2, float eps, int step) { | ||
| 570 | 50 | const auto& v = detail::dispatch(param, grad, m, v_buf); | |
| 571 |
1/2✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
|
50 | if (!v.adam_step) detail::throw_not_implemented("adam_step", param.device); |
| 572 | 50 | detail::adopt_output(param, param.device); | |
| 573 | 50 | detail::adopt_output(m, param.device); | |
| 574 | 50 | detail::adopt_output(v_buf, param.device); | |
| 575 | 50 | v.adam_step(param, grad, m, v_buf, lr, beta1, beta2, eps, step); | |
| 576 | 50 | } | |
| 577 | |||
| 578 | 3 | void xavier_init(Tensor& W, uint64_t& rng_state) { | |
| 579 | 3 | const auto& v = detail::dispatch(W); | |
| 580 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if (!v.xavier_init) detail::throw_not_implemented("xavier_init", W.device); |
| 581 | 3 | detail::adopt_output(W, W.device); | |
| 582 | 3 | v.xavier_init(W, rng_state); | |
| 583 | 3 | } | |
| 584 | |||
| 585 | // ─── Batched inference variants ──────────────────────────────────────────── | ||
| 586 | |||
| 587 | 97 | void linear_forward_batched(const Tensor& W, const Tensor& bias, | |
| 588 | const Tensor& X_BD, Tensor& Y_BD) { | ||
| 589 | 97 | const auto& v = detail::dispatch(W, bias, X_BD, Y_BD); | |
| 590 |
1/2✓ Branch 0 taken 97 times.
✗ Branch 1 not taken.
|
97 | if (!v.linear_forward_batched) detail::throw_not_implemented("linear_forward_batched", W.device); |
| 591 | 97 | detail::adopt_output(Y_BD, W.device); | |
| 592 | 97 | v.linear_forward_batched(W, bias, X_BD, Y_BD); | |
| 593 | 97 | } | |
| 594 | |||
| 595 | 5 | void relu_forward_batched(const Tensor& X_BD, Tensor& Y_BD) { | |
| 596 | 5 | const auto& v = detail::dispatch(X_BD, Y_BD); | |
| 597 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | if (!v.relu_forward_batched) detail::throw_not_implemented("relu_forward_batched", X_BD.device); |
| 598 | 5 | detail::adopt_output(Y_BD, X_BD.device); | |
| 599 | 5 | v.relu_forward_batched(X_BD, Y_BD); | |
| 600 | 5 | } | |
| 601 | |||
| 602 | 11 | void tanh_forward_batched(const Tensor& X_BD, Tensor& Y_BD) { | |
| 603 | 11 | const auto& v = detail::dispatch(X_BD, Y_BD); | |
| 604 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | if (!v.tanh_forward_batched) detail::throw_not_implemented("tanh_forward_batched", X_BD.device); |
| 605 | 11 | detail::adopt_output(Y_BD, X_BD.device); | |
| 606 | 11 | v.tanh_forward_batched(X_BD, Y_BD); | |
| 607 | 11 | } | |
| 608 | |||
| 609 | 5 | void add_inplace_batched(Tensor& Y_BD, const Tensor& X_BD) { | |
| 610 | 5 | const auto& v = detail::dispatch(Y_BD, X_BD); | |
| 611 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | if (!v.add_inplace_batched) detail::throw_not_implemented("add_inplace_batched", Y_BD.device); |
| 612 | 5 | detail::adopt_output(Y_BD, Y_BD.device); | |
| 613 | 5 | v.add_inplace_batched(Y_BD, X_BD); | |
| 614 | 5 | } | |
| 615 | |||
| 616 | // ─── Batched backward variants ───────────────────────────────────────────── | ||
| 617 | |||
| 618 | 55 | void linear_backward_batched(const Tensor& W, const Tensor& X_BD, | |
| 619 | const Tensor& dY_BD, | ||
| 620 | Tensor& dX_BD, Tensor& dW, Tensor& dB) { | ||
| 621 | 55 | const auto& v = detail::dispatch(W, X_BD, dY_BD, dX_BD, dW, dB); | |
| 622 |
1/2✓ Branch 0 taken 55 times.
✗ Branch 1 not taken.
|
55 | if (!v.linear_backward_batched) detail::throw_not_implemented("linear_backward_batched", W.device); |
| 623 | 55 | detail::adopt_output(dX_BD, W.device); | |
| 624 | 55 | detail::adopt_output(dW, W.device); | |
| 625 | 55 | detail::adopt_output(dB, W.device); | |
| 626 | 55 | v.linear_backward_batched(W, X_BD, dY_BD, dX_BD, dW, dB); | |
| 627 | 55 | } | |
| 628 | |||
| 629 | 8 | void relu_backward_batched(const Tensor& X_BD, const Tensor& dY_BD, Tensor& dX_BD) { | |
| 630 | 8 | const auto& v = detail::dispatch(X_BD, dY_BD, dX_BD); | |
| 631 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | if (!v.relu_backward_batched) detail::throw_not_implemented("relu_backward_batched", X_BD.device); |
| 632 | 8 | detail::adopt_output(dX_BD, X_BD.device); | |
| 633 | 8 | v.relu_backward_batched(X_BD, dY_BD, dX_BD); | |
| 634 | 8 | } | |
| 635 | |||
| 636 | 8 | void tanh_backward_batched(const Tensor& Y_BD, const Tensor& dY_BD, Tensor& dX_BD) { | |
| 637 | 8 | const auto& v = detail::dispatch(Y_BD, dY_BD, dX_BD); | |
| 638 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | if (!v.tanh_backward_batched) detail::throw_not_implemented("tanh_backward_batched", Y_BD.device); |
| 639 | 8 | detail::adopt_output(dX_BD, Y_BD.device); | |
| 640 | 8 | v.tanh_backward_batched(Y_BD, dY_BD, dX_BD); | |
| 641 | 8 | } | |
| 642 | |||
| 643 | // ─── Batched per-sample losses ───────────────────────────────────────────── | ||
| 644 | |||
| 645 | 6 | void mse_vec_per_sample(const Tensor& pred, const Tensor& target, | |
| 646 | Tensor& dPred, Tensor& loss_per_sample) { | ||
| 647 | 6 | const auto& v = detail::dispatch(pred, target, dPred, loss_per_sample); | |
| 648 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if (!v.mse_vec_per_sample) detail::throw_not_implemented("mse_vec_per_sample", pred.device); |
| 649 | 6 | detail::adopt_output(dPred, pred.device); | |
| 650 | 6 | detail::adopt_output(loss_per_sample, pred.device); | |
| 651 | 6 | v.mse_vec_per_sample(pred, target, dPred, loss_per_sample); | |
| 652 | 6 | } | |
| 653 | |||
| 654 | 12 | void softmax_xent_fused_batched(const Tensor& logits_BL, const Tensor& target_BL, | |
| 655 | const float* d_mask_BL, | ||
| 656 | const int* d_head_offsets, int n_heads, | ||
| 657 | Tensor& probs_BL, Tensor& dLogits_BL, | ||
| 658 | Tensor& loss_per_sample) { | ||
| 659 | 12 | const auto& v = detail::dispatch(logits_BL, target_BL, probs_BL, dLogits_BL, loss_per_sample); | |
| 660 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (!v.softmax_xent_fused_batched) |
| 661 | ✗ | detail::throw_not_implemented("softmax_xent_fused_batched", logits_BL.device); | |
| 662 | 12 | detail::adopt_output(probs_BL, logits_BL.device); | |
| 663 | 12 | detail::adopt_output(dLogits_BL, logits_BL.device); | |
| 664 | 12 | detail::adopt_output(loss_per_sample, logits_BL.device); | |
| 665 | 24 | v.softmax_xent_fused_batched(logits_BL, target_BL, d_mask_BL, | |
| 666 | 12 | d_head_offsets, n_heads, | |
| 667 | 12 | probs_BL, dLogits_BL, loss_per_sample); | |
| 668 | 12 | } | |
| 669 | |||
| 670 | 17 | void bce_with_logits_fused_batched(const Tensor& logits_BL, const Tensor& target_BL, | |
| 671 | const float* d_mask_BL, | ||
| 672 | float pos_weight, | ||
| 673 | Tensor& probs_BL, Tensor& dLogits_BL, | ||
| 674 | Tensor& loss_per_sample) { | ||
| 675 | 17 | const auto& v = detail::dispatch(logits_BL, target_BL, probs_BL, dLogits_BL, loss_per_sample); | |
| 676 |
1/2✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
|
17 | if (!v.bce_with_logits_fused_batched) |
| 677 | ✗ | detail::throw_not_implemented("bce_with_logits_fused_batched", logits_BL.device); | |
| 678 | 17 | detail::adopt_output(probs_BL, logits_BL.device); | |
| 679 | 17 | detail::adopt_output(dLogits_BL, logits_BL.device); | |
| 680 | 17 | detail::adopt_output(loss_per_sample, logits_BL.device); | |
| 681 | 34 | v.bce_with_logits_fused_batched(logits_BL, target_BL, d_mask_BL, | |
| 682 | 17 | pos_weight, | |
| 683 | 17 | probs_BL, dLogits_BL, loss_per_sample); | |
| 684 | 17 | } | |
| 685 | |||
| 686 | } // namespace brotensor | ||
| 687 |