GCC Code Coverage Report


Directory: brotensor/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 39.4% 179 / 0 / 454
Functions: 27.2% 34 / 0 / 125
Branches: 9.2% 108 / 0 / 1180

src/api/native_tensor_ops.cpp
Line Branch Exec Source
1 #include "native_tensor_decl.h"
2 #include "api_internal.h"
3 #include "brotensor/detail/dispatch.h"
4
5 #include <cstring>
6
7 using namespace brotensor::api;
8 using brotensor::Tensor;
9
10 // Every body checks its operands against the dims before brotensor sees them
11 // (api_internal.h "input-size contract") and runs under BROTENSOR_API_TRY:
12 // an op that throws records the message for the wrapper's chk().
13 //
14 // Outputs an op resizes are not checked; outputs it ACCUMULATES into (the
15 // dW / dB of a backward, "caller zeros") are, because the op writes them at
16 // the size the dims say.
17
18 namespace {
19
20 // A unary map: y = f(x). The op resizes y.
21 template <class Op>
22 10 void unary(const char* L, void* x, void* y, Op op) {
23 10 auto* xt = toTensor(x);
24 10 auto* yt = toTensor(y);
25
3/20
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 6 times.
10 if (!need(L, {xt, yt})) return;
26 BROTENSOR_API_TRY
27
3/20
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 6 times.
✗ Branch 19 not taken.
10 op(*xt, *yt);
28
0/120
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 57 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 66 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
✗ Branch 72 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 75 not taken.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✗ Branch 78 not taken.
✗ Branch 79 not taken.
✗ Branch 80 not taken.
✗ Branch 81 not taken.
✗ Branch 82 not taken.
✗ Branch 83 not taken.
✗ Branch 84 not taken.
✗ Branch 85 not taken.
✗ Branch 86 not taken.
✗ Branch 87 not taken.
✗ Branch 88 not taken.
✗ Branch 89 not taken.
✗ Branch 90 not taken.
✗ Branch 91 not taken.
✗ Branch 92 not taken.
✗ Branch 93 not taken.
✗ Branch 94 not taken.
✗ Branch 95 not taken.
✗ Branch 96 not taken.
✗ Branch 97 not taken.
✗ Branch 98 not taken.
✗ Branch 99 not taken.
✗ Branch 100 not taken.
✗ Branch 101 not taken.
✗ Branch 102 not taken.
✗ Branch 103 not taken.
✗ Branch 104 not taken.
✗ Branch 105 not taken.
✗ Branch 106 not taken.
✗ Branch 107 not taken.
✗ Branch 108 not taken.
✗ Branch 109 not taken.
✗ Branch 110 not taken.
✗ Branch 111 not taken.
✗ Branch 112 not taken.
✗ Branch 113 not taken.
✗ Branch 114 not taken.
✗ Branch 115 not taken.
✗ Branch 116 not taken.
✗ Branch 117 not taken.
✗ Branch 118 not taken.
✗ Branch 119 not taken.
10 BROTENSOR_API_CATCH(L)
29 10 }
30
31 // Its backward: dX = f'(a) * dY, `a` being the forward input or output.
32 template <class Op>
33 8 void unaryBackward(const char* L, void* a, void* dY, void* dX, Op op) {
34 8 auto* at = toTensor(a);
35 8 auto* dyt = toTensor(dY);
36 8 auto* dxt = toTensor(dX);
37
4/16
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 2 times.
8 if (!need(L, {at, dyt, dxt})) return;
38
4/16
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 2 times.
8 if (!needPair(L, "dY", dyt, at)) return;
39 BROTENSOR_API_TRY
40 ✗ op(*at, *dyt, *dxt);
41 ✗ BROTENSOR_API_CATCH(L)
42 8 }
43
44 // A gated unit's input is (B, 2*D): an even last dim.
45 ✗ bool needGatedInput(const char* L, const Tensor* xt) {
46 ✗ if (xt->cols % 2 != 0) {
47 ✗ setError(std::string(L) + ": X must be (B, 2*D) — an even column count");
48 ✗ return false;
49 }
50 ✗ return true;
51 ✗ }
52
53 template <class Op>
54 ✗ void gatedForward(const char* L, void* X, void* Y, Op op) {
55 ✗ auto* xt = toTensor(X);
56 ✗ auto* yt = toTensor(Y);
57 ✗ if (!need(L, {xt, yt})) return;
58 ✗ if (!needGatedInput(L, xt)) return;
59 BROTENSOR_API_TRY
60 ✗ op(*xt, *yt);
61 ✗ BROTENSOR_API_CATCH(L)
62 ✗ }
63
64 template <class Op>
65 ✗ void gatedBackward(const char* L, void* X, void* dY, void* dX, Op op) {
66 ✗ auto* xt = toTensor(X);
67 ✗ auto* dyt = toTensor(dY);
68 ✗ auto* dxt = toTensor(dX);
69 ✗ if (!need(L, {xt, dyt, dxt})) return;
70 ✗ if (!needGatedInput(L, xt)) return;
71 ✗ if (!needElems(L, "dY", dyt, elems({xt->rows, xt->cols / 2}))) return;
72 ✗ if (!needSameDtype(L, "dY", dyt, xt)) return;
73 BROTENSOR_API_TRY
74 ✗ op(*xt, *dyt, *dxt);
75 ✗ BROTENSOR_API_CATCH(L)
76 ✗ }
77
78 // An NCHW resample X:(N, C*H*W) -> Y. The op resizes Y.
79 template <class Op>
80 4 void nchwMap(const char* L, void* X, int32_t N, int32_t C, int32_t H, int32_t W, void* Y, Op op) {
81 4 auto* xt = toTensor(X);
82 4 auto* yt = toTensor(Y);
83
2/20
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 2 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
4 if (!need(L, {xt, yt})) return;
84
2/20
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
4 if (!needElems(L, "X", xt, elems({N, C, H, W}))) return;
85 BROTENSOR_API_TRY
86 ✗ op(*xt, *yt);
87 ✗ BROTENSOR_API_CATCH(L)
88 4 }
89
90 // downsample_avg_2x wants an even plane.
91 2 bool needEvenPlane(const char* L, int32_t H, int32_t W) {
92
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 if (H % 2 != 0 || W % 2 != 0) {
93
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 setError(std::string(L) + ": H and W must be even");
94 2 return false;
95 }
96 ✗ return true;
97 2 }
98
99 } // namespace
100
101 extern "C" {
102
103 4 void bro_tensor_linearForward(void* W, void* b, void* x, void* y) {
104 4 const char* L = "linearForward";
105 4 auto* wt = toTensor(W);
106 4 auto* bt = toTensor(b);
107 4 auto* xt = toTensor(x);
108 4 auto* yt = toTensor(y);
109
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (!need(L, {wt, bt, xt, yt})) return;
110
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 if (!needElems(L, "b", bt, wt->rows) || !needElems(L, "x", xt, wt->cols)) return;
111 ✗ if (!needSameDtype(L, "b", bt, wt) || !needSameDtype(L, "x", xt, wt)) return;
112 BROTENSOR_API_TRY
113 ✗ brotensor::linear_forward(*wt, *bt, *xt, *yt);
114 ✗ BROTENSOR_API_CATCH(L)
115 4 }
116
117 2 void bro_tensor_linearBackward(void* W, void* x, void* dY, void* dX, void* dW, void* dB) {
118 2 const char* L = "linearBackward";
119 2 auto* wt = toTensor(W);
120 2 auto* xt = toTensor(x);
121 2 auto* dyt = toTensor(dY);
122 2 auto* dxt = toTensor(dX);
123 2 auto* dwt = toTensor(dW);
124 2 auto* dbt = toTensor(dB);
125
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!need(L, {wt, xt, dyt, dxt, dwt, dbt})) return;
126
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!needElems(L, "x", xt, wt->cols) || !needElems(L, "dY", dyt, wt->rows)) return;
127 // dW / dB accumulate at the forward's size — "caller zeros".
128
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if (!needElems(L, "dW", dwt, elems({wt->rows, wt->cols})) || !needElems(L, "dB", dbt, wt->rows)) return;
129 ✗ for (const Tensor* t : {xt, dyt, dwt, dbt}) {
130 ✗ if (!needSameDtype(L, "every operand", t, wt)) return;
131 }
132 BROTENSOR_API_TRY
133 ✗ brotensor::linear_backward(*wt, *xt, *dyt, *dxt, *dwt, *dbt);
134 ✗ BROTENSOR_API_CATCH(L)
135 2 }
136
137 2 void bro_tensor_reluForward(void* x, void* y) {
138 4 unary("reluForward", x, y, [](const Tensor& a, Tensor& b) { brotensor::relu_forward(a, b); });
139 2 }
140
141 2 void bro_tensor_reluBackward(void* x, void* dY, void* dX) {
142 2 unaryBackward("reluBackward", x, dY, dX,
143 ✗ [](const Tensor& a, const Tensor& g, Tensor& o) { brotensor::relu_backward(a, g, o); });
144 2 }
145
146 2 void bro_tensor_tanhForward(void* x, void* y) {
147 4 unary("tanhForward", x, y, [](const Tensor& a, Tensor& b) { brotensor::tanh_forward(a, b); });
148 2 }
149
150 2 void bro_tensor_tanhBackward(void* y, void* dY, void* dX) {
151 2 unaryBackward("tanhBackward", y, dY, dX,
152 ✗ [](const Tensor& a, const Tensor& g, Tensor& o) { brotensor::tanh_backward(a, g, o); });
153 2 }
154
155 ✗ void bro_tensor_sigmoidForward(void* x, void* y) {
156 ✗ unary("sigmoidForward", x, y, [](const Tensor& a, Tensor& b) { brotensor::sigmoid_forward(a, b); });
157 ✗ }
158
159 ✗ void bro_tensor_sigmoidBackward(void* y, void* dY, void* dX) {
160 ✗ unaryBackward("sigmoidBackward", y, dY, dX,
161 ✗ [](const Tensor& a, const Tensor& g, Tensor& o) { brotensor::sigmoid_backward(a, g, o); });
162 ✗ }
163
164 6 void bro_tensor_addInplace(void* y, void* x) {
165 6 const char* L = "addInplace";
166 6 auto* yt = toTensor(y);
167 6 auto* xt = toTensor(x);
168
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (!need(L, {yt, xt})) return;
169
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
6 if (!needPair(L, "x", xt, yt)) return;
170 BROTENSOR_API_TRY
171
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 brotensor::add_inplace(*yt, *xt);
172
0/12
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
2 BROTENSOR_API_CATCH(L)
173 6 }
174
175 ✗ void bro_tensor_addScalarInplace(void* y, double s) {
176 ✗ const char* L = "addScalarInplace";
177 ✗ auto* yt = toTensor(y);
178 ✗ if (!need(L, {yt})) return;
179 BROTENSOR_API_TRY
180 ✗ brotensor::add_scalar_inplace(*yt, static_cast<float>(s));
181 ✗ BROTENSOR_API_CATCH(L)
182 ✗ }
183
184 2 void bro_tensor_scaleInplace(void* y, double s) {
185 2 const char* L = "scaleInplace";
186 2 auto* yt = toTensor(y);
187
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!need(L, {yt})) return;
188 BROTENSOR_API_TRY
189
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 brotensor::scale_inplace(*yt, static_cast<float>(s));
190
0/12
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
2 BROTENSOR_API_CATCH(L)
191 2 }
192
193 2 void bro_tensor_mulInplace(void* y, void* x) {
194 2 const char* L = "mulInplace";
195 2 auto* yt = toTensor(y);
196 2 auto* xt = toTensor(x);
197
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!need(L, {yt, xt})) return;
198
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!needPair(L, "x", xt, yt)) return;
199 BROTENSOR_API_TRY
200 ✗ brotensor::mul_inplace(*yt, *xt);
201 ✗ BROTENSOR_API_CATCH(L)
202 2 }
203
204 2 void bro_tensor_clamp(void* y, double lo, double hi) {
205 2 const char* L = "clamp";
206 2 auto* yt = toTensor(y);
207
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!need(L, {yt})) return;
208 BROTENSOR_API_TRY
209
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 brotensor::clamp(*yt, static_cast<float>(lo), static_cast<float>(hi));
210
0/12
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
2 BROTENSOR_API_CATCH(L)
211 2 }
212
213 ✗ void bro_tensor_siluForward(void* x, void* y) {
214 ✗ unary("siluForward", x, y, [](const Tensor& a, Tensor& b) { brotensor::silu_forward(a, b); });
215 ✗ }
216
217 2 void bro_tensor_siluBackward(void* x, void* dY, void* dX) {
218 2 unaryBackward("siluBackward", x, dY, dX,
219 ✗ [](const Tensor& a, const Tensor& g, Tensor& o) { brotensor::silu_backward(a, g, o); });
220 2 }
221
222 ✗ void bro_tensor_geluForward(void* x, void* y) {
223 ✗ unary("geluForward", x, y, [](const Tensor& a, Tensor& b) { brotensor::gelu_forward(a, b); });
224 ✗ }
225
226 ✗ void bro_tensor_geluBackward(void* x, void* dY, void* dX) {
227 ✗ unaryBackward("geluBackward", x, dY, dX,
228 ✗ [](const Tensor& a, const Tensor& g, Tensor& o) { brotensor::gelu_backward(a, g, o); });
229 ✗ }
230
231 ✗ void bro_tensor_geluExactForward(void* x, void* y) {
232 ✗ unary("geluExactForward", x, y, [](const Tensor& a, Tensor& b) { brotensor::gelu_exact_forward(a, b); });
233 ✗ }
234
235 ✗ void bro_tensor_geluExactBackward(void* x, void* dY, void* dX) {
236 ✗ unaryBackward("geluExactBackward", x, dY, dX,
237 ✗ [](const Tensor& a, const Tensor& g, Tensor& o) { brotensor::gelu_exact_backward(a, g, o); });
238 ✗ }
239
240 ✗ void bro_tensor_quickGeluForward(void* x, void* y) {
241 ✗ unary("quickGeluForward", x, y, [](const Tensor& a, Tensor& b) { brotensor::quick_gelu_forward(a, b); });
242 ✗ }
243
244 ✗ void bro_tensor_quickGeluBackward(void* x, void* dY, void* dX) {
245 ✗ unaryBackward("quickGeluBackward", x, dY, dX,
246 ✗ [](const Tensor& a, const Tensor& g, Tensor& o) { brotensor::quick_gelu_backward(a, g, o); });
247 ✗ }
248
249 ✗ void bro_tensor_swigluForward(void* X, void* Y) {
250 ✗ gatedForward("swigluForward", X, Y, [](const Tensor& a, Tensor& b) { brotensor::swiglu_forward(a, b); });
251 ✗ }
252
253 ✗ void bro_tensor_swigluBackward(void* X, void* dY, void* dX) {
254 ✗ gatedBackward("swigluBackward", X, dY, dX,
255 ✗ [](const Tensor& a, const Tensor& g, Tensor& o) { brotensor::swiglu_backward(a, g, o); });
256 ✗ }
257
258 ✗ void bro_tensor_gegluForward(void* X, void* Y) {
259 ✗ gatedForward("gegluForward", X, Y, [](const Tensor& a, Tensor& b) { brotensor::geglu_forward(a, b); });
260 ✗ }
261
262 ✗ void bro_tensor_gegluBackward(void* X, void* dY, void* dX) {
263 ✗ gatedBackward("gegluBackward", X, dY, dX,
264 ✗ [](const Tensor& a, const Tensor& g, Tensor& o) { brotensor::geglu_backward(a, g, o); });
265 ✗ }
266
267 ✗ void bro_tensor_gegluExactForward(void* X, void* Y) {
268 ✗ gatedForward("gegluExactForward", X, Y, [](const Tensor& a, Tensor& b) { brotensor::geglu_exact_forward(a, b); });
269 ✗ }
270
271 ✗ void bro_tensor_gegluExactBackward(void* X, void* dY, void* dX) {
272 ✗ gatedBackward("gegluExactBackward", X, dY, dX,
273 ✗ [](const Tensor& a, const Tensor& g, Tensor& o) { brotensor::geglu_exact_backward(a, g, o); });
274 ✗ }
275
276 ✗ void bro_tensor_softmaxForward(void* logits, void* probs, double temp) {
277 ✗ const char* L = "softmaxForward";
278 ✗ auto* lt = toTensor(logits);
279 ✗ auto* pt = toTensor(probs);
280 ✗ if (!need(L, {lt, pt})) return;
281 BROTENSOR_API_TRY
282 ✗ if (temp != 1.0 && temp > 0.0) {
283 ✗ auto scaled = lt->clone();
284 ✗ brotensor::scale_inplace(scaled, static_cast<float>(1.0 / temp));
285 ✗ brotensor::softmax_forward(scaled, *pt);
286 ✗ } else {
287 ✗ brotensor::softmax_forward(*lt, *pt);
288 }
289 ✗ BROTENSOR_API_CATCH(L)
290 ✗ }
291
292 2 void bro_tensor_softmaxBackward(void* probs, void* dProbs, void* dLogits) {
293 2 unaryBackward("softmaxBackward", probs, dProbs, dLogits,
294 ✗ [](const Tensor& a, const Tensor& g, Tensor& o) { brotensor::softmax_backward(a, g, o); });
295 2 }
296
297 4 void bro_tensor_matmul(void* A, void* B, void* C) {
298 4 const char* L = "matmul";
299 4 auto* at = toTensor(A);
300 4 auto* bt = toTensor(B);
301 4 auto* ct = toTensor(C);
302
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (!need(L, {at, bt, ct})) return;
303
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (at->cols != bt->rows) {
304
6/12
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
4 setError("matmul: A is (" + std::to_string(at->rows) + "," + std::to_string(at->cols) +
305
6/12
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
2 "), B is (" + std::to_string(bt->rows) + "," + std::to_string(bt->cols) + "); A.cols must equal B.rows");
306 2 return;
307 }
308
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!needSameDtype(L, "B", bt, at)) return;
309 BROTENSOR_API_TRY
310
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 brotensor::matmul(*at, *bt, *ct);
311
0/12
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
2 BROTENSOR_API_CATCH(L)
312 4 }
313
314 ✗ void bro_tensor_matmulBackward(void* A, void* B, void* dC, void* dA, void* dB) {
315 ✗ const char* L = "matmulBackward";
316 ✗ auto* at = toTensor(A);
317 ✗ auto* bt = toTensor(B);
318 ✗ auto* dct = toTensor(dC);
319 ✗ auto* dat = toTensor(dA);
320 ✗ auto* dbt = toTensor(dB);
321 ✗ if (!need(L, {at, bt, dct, dat, dbt})) return;
322 ✗ if (at->cols != bt->rows) {
323 ✗ setError("matmulBackward: A.cols must equal B.rows");
324 ✗ return;
325 }
326 ✗ if (!needElems(L, "dC", dct, elems({at->rows, bt->cols}))) return;
327 // dA / dB accumulate — "caller pre-sizes and zeros".
328 ✗ if (!needSameSize(L, "dA", dat, at) || !needSameSize(L, "dB", dbt, bt)) return;
329 ✗ for (const Tensor* t : {bt, dct, dat, dbt}) {
330 ✗ if (!needSameDtype(L, "every operand", t, at)) return;
331 }
332 BROTENSOR_API_TRY
333 ✗ brotensor::matmul_backward(*at, *bt, *dct, *dat, *dbt);
334 ✗ BROTENSOR_API_CATCH(L)
335 ✗ }
336
337 8 void bro_tensor_conv2dForward(void* X, void* Wt, uint64_t bias_bits, int32_t N, int32_t C_in, int32_t H, int32_t W, int32_t C_out, int32_t kH, int32_t kW, int32_t sH, int32_t sW, int32_t pH, int32_t pW, int32_t dH, int32_t dW, int32_t groups, void* Y) {
338 8 const char* L = "conv2dForward";
339 8 auto* xt = toTensor(X);
340 8 auto* wt = toTensor(Wt);
341 8 auto* yt = toTensor(Y);
342
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (!need(L, {xt, wt, yt})) return;
343 8 const Tensor* bias_t = tensorFromValue(bias_bits);
344 8 int64_t H_out = 0, W_out = 0;
345
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 if (!convGeom2d(L, C_in, C_out, H, W, kH, kW, sH, sW, pH, pW, dH, dW, groups, H_out, W_out)) return;
346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (!needNonNegative(L, {N})) return;
347
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (!needElems(L, "X", xt, elems({N, C_in, H, W}))) return;
348
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!needElems(L, "Wt", wt, elems({C_out, C_in / groups, kH, kW}))) return;
349 ✗ if (!needSameDtype(L, "Wt", wt, xt)) return;
350 ✗ if (bias_t && (!needElems(L, "bias", bias_t, C_out) || !needSameDtype(L, "bias", bias_t, xt))) return;
351 BROTENSOR_API_TRY
352 ✗ brotensor::conv2d_forward(*xt, *wt, bias_t, N, C_in, H, W, C_out, kH, kW, sH, sW, pH, pW, dH, dW, groups, *yt);
353 ✗ BROTENSOR_API_CATCH(L)
354 8 }
355
356 ✗ void bro_tensor_conv2dBackwardInput(void* Wt, void* dY, int32_t N, int32_t C_in, int32_t H, int32_t W, int32_t C_out, int32_t kH, int32_t kW, int32_t sH, int32_t sW, int32_t pH, int32_t pW, int32_t dH, int32_t dW, int32_t groups, void* dX) {
357 ✗ const char* L = "conv2dBackwardInput";
358 ✗ auto* wt = toTensor(Wt);
359 ✗ auto* dyt = toTensor(dY);
360 ✗ auto* dxt = toTensor(dX);
361 ✗ if (!need(L, {wt, dyt, dxt})) return;
362 ✗ int64_t H_out = 0, W_out = 0;
363 ✗ if (!convGeom2d(L, C_in, C_out, H, W, kH, kW, sH, sW, pH, pW, dH, dW, groups, H_out, W_out)) return;
364 ✗ if (!needNonNegative(L, {N})) return;
365 ✗ if (!needElems(L, "Wt", wt, elems({C_out, C_in / groups, kH, kW}))) return;
366 ✗ if (!needElems(L, "dY", dyt, elems({N, C_out, H_out, W_out}))) return;
367 ✗ if (!needSameDtype(L, "dY", dyt, wt)) return;
368 BROTENSOR_API_TRY
369 ✗ brotensor::conv2d_backward_input(*wt, *dyt, N, C_in, H, W, C_out, kH, kW, sH, sW, pH, pW, dH, dW, groups, *dxt);
370 ✗ BROTENSOR_API_CATCH(L)
371 ✗ }
372
373 ✗ void bro_tensor_conv2dBackwardWeight(void* X, void* dY, int32_t N, int32_t C_in, int32_t H, int32_t W, int32_t C_out, int32_t kH, int32_t kW, int32_t sH, int32_t sW, int32_t pH, int32_t pW, int32_t dH, int32_t dW, int32_t groups, void* dWt) {
374 ✗ const char* L = "conv2dBackwardWeight";
375 ✗ auto* xt = toTensor(X);
376 ✗ auto* dyt = toTensor(dY);
377 ✗ auto* dwt = toTensor(dWt);
378 ✗ if (!need(L, {xt, dyt, dwt})) return;
379 ✗ int64_t H_out = 0, W_out = 0;
380 ✗ if (!convGeom2d(L, C_in, C_out, H, W, kH, kW, sH, sW, pH, pW, dH, dW, groups, H_out, W_out)) return;
381 ✗ if (!needNonNegative(L, {N})) return;
382 ✗ if (!needElems(L, "X", xt, elems({N, C_in, H, W}))) return;
383 ✗ if (!needElems(L, "dY", dyt, elems({N, C_out, H_out, W_out}))) return;
384 // dWt accumulates — "caller zeros".
385 ✗ if (!needElems(L, "dWt", dwt, elems({C_out, C_in / groups, kH, kW}))) return;
386 ✗ if (!needSameDtype(L, "dY", dyt, xt) || !needSameDtype(L, "dWt", dwt, xt)) return;
387 BROTENSOR_API_TRY
388 ✗ brotensor::conv2d_backward_weight(*xt, *dyt, N, C_in, H, W, C_out, kH, kW, sH, sW, pH, pW, dH, dW, groups, *dwt);
389 ✗ BROTENSOR_API_CATCH(L)
390 ✗ }
391
392 ✗ void bro_tensor_conv2dBackwardBias(void* dY, int32_t N, int32_t C_out, int32_t H_out, int32_t W_out, void* dB) {
393 ✗ const char* L = "conv2dBackwardBias";
394 ✗ auto* dyt = toTensor(dY);
395 ✗ auto* dbt = toTensor(dB);
396 ✗ if (!need(L, {dyt, dbt})) return;
397 ✗ if (!needElems(L, "dY", dyt, elems({N, C_out, H_out, W_out}))) return;
398 ✗ if (!needElems(L, "dB", dbt, C_out) || !needSameDtype(L, "dB", dbt, dyt)) return;
399 BROTENSOR_API_TRY
400 ✗ brotensor::conv2d_backward_bias(*dyt, N, C_out, H_out, W_out, *dbt);
401 ✗ BROTENSOR_API_CATCH(L)
402 ✗ }
403
404 2 void bro_tensor_upsampleNearest2xForward(void* X, int32_t N, int32_t C, int32_t H, int32_t W, void* Y) {
405 4 nchwMap("upsampleNearest2xForward", X, N, C, H, W, Y,
406 2 [&](const Tensor& x, Tensor& y) { brotensor::upsample_nearest_2x(x, N, C, H, W, y); });
407 2 }
408
409 // The 2x backwards take the INPUT dims; dY is the upsampled (N, C*2H*2W).
410 ✗ void bro_tensor_upsampleNearest2xBackward(void* dY, int32_t N, int32_t C, int32_t H, int32_t W, void* dX) {
411 ✗ const char* L = "upsampleNearest2xBackward";
412 ✗ auto* dyt = toTensor(dY);
413 ✗ auto* dxt = toTensor(dX);
414 ✗ if (!need(L, {dyt, dxt})) return;
415 ✗ if (!needNonNegative(L, {N, C, H, W})) return;
416 ✗ if (!needElems(L, "dY", dyt, elems({N, C, 2 * static_cast<int64_t>(H), 2 * static_cast<int64_t>(W)}))) return;
417 BROTENSOR_API_TRY
418 ✗ brotensor::upsample_nearest_2x_backward(*dyt, N, C, H, W, *dxt);
419 ✗ BROTENSOR_API_CATCH(L)
420 ✗ }
421
422 ✗ void bro_tensor_upsampleBilinear2xForward(void* X, int32_t N, int32_t C, int32_t H, int32_t W, void* Y) {
423 ✗ nchwMap("upsampleBilinear2xForward", X, N, C, H, W, Y,
424 ✗ [&](const Tensor& x, Tensor& y) { brotensor::upsample_bilinear_2x(x, N, C, H, W, y); });
425 ✗ }
426
427 2 void bro_tensor_upsampleBilinear2xBackward(void* dY, int32_t N, int32_t C, int32_t H, int32_t W, void* dX) {
428 2 const char* L = "upsampleBilinear2xBackward";
429 2 auto* dyt = toTensor(dY);
430 2 auto* dxt = toTensor(dX);
431
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!need(L, {dyt, dxt})) return;
432
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!needNonNegative(L, {N, C, H, W})) return;
433
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!needElems(L, "dY", dyt, elems({N, C, 2 * static_cast<int64_t>(H), 2 * static_cast<int64_t>(W)}))) return;
434 BROTENSOR_API_TRY
435 ✗ brotensor::upsample_bilinear_2x_backward(*dyt, N, C, H, W, *dxt);
436 ✗ BROTENSOR_API_CATCH(L)
437 2 }
438
439 2 void bro_tensor_downsampleAvg2xForward(void* X, int32_t N, int32_t C, int32_t H, int32_t W, void* Y) {
440
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!needEvenPlane("downsampleAvg2xForward", H, W)) return;
441 ✗ nchwMap("downsampleAvg2xForward", X, N, C, H, W, Y,
442 ✗ [&](const Tensor& x, Tensor& y) { brotensor::downsample_avg_2x(x, N, C, H, W, y); });
443 2 }
444
445 ✗ void bro_tensor_downsampleAvg2xBackward(void* dY, int32_t N, int32_t C, int32_t H, int32_t W, void* dX) {
446 ✗ const char* L = "downsampleAvg2xBackward";
447 ✗ auto* dyt = toTensor(dY);
448 ✗ auto* dxt = toTensor(dX);
449 ✗ if (!need(L, {dyt, dxt})) return;
450 ✗ if (!needNonNegative(L, {N, C, H, W}) || !needEvenPlane(L, H, W)) return;
451 ✗ if (!needElems(L, "dY", dyt, elems({N, C, H / 2, W / 2}))) return;
452 BROTENSOR_API_TRY
453 ✗ brotensor::downsample_avg_2x_backward(*dyt, N, C, H, W, *dxt);
454 ✗ BROTENSOR_API_CATCH(L)
455 ✗ }
456
457 ✗ void bro_tensor_sumRows(void* X, void* Y) {
458 ✗ unary("sumRows", X, Y, [](const Tensor& a, Tensor& b) { brotensor::sum_rows(a, b); });
459 ✗ }
460
461 ✗ void bro_tensor_sumCols(void* X, void* Y) {
462 ✗ unary("sumCols", X, Y, [](const Tensor& a, Tensor& b) { brotensor::sum_cols(a, b); });
463 ✗ }
464
465 6 void bro_tensor_argmaxRows(void* X, void* Idx) {
466 12 unary("argmaxRows", X, Idx, [](const Tensor& a, Tensor& b) { brotensor::argmax_rows(a, b); });
467 6 }
468
469 // A flat element copy between two tensors of one dtype on one device. Both
470 // ranges are checked against their tensor: this is a raw memcpy.
471 10 void bro_tensor_copyD2D(void* src, int32_t srcOff, void* dst, int32_t dstOff, int32_t n) {
472 10 const char* L = "copyD2D";
473 10 auto* s = toTensor(src);
474 10 auto* d = toTensor(dst);
475
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (!need(L, {s, d})) return;
476
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 8 times.
10 if (!needNonNegative(L, {srcOff, dstOff, n})) return;
477
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (n == 0) return;
478
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 if (!needSameDtype(L, "dst", d, s)) return;
479
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (s->device != d->device) {
480 ✗ setError("copyD2D: src and dst live on different devices");
481 ✗ return;
482 }
483 6 const int64_t srcEnd = static_cast<int64_t>(srcOff) + n;
484 6 const int64_t dstEnd = static_cast<int64_t>(dstOff) + n;
485
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
6 if (srcEnd > static_cast<int64_t>(s->size()) || dstEnd > static_cast<int64_t>(d->size())) {
486
7/14
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 4 times.
✗ Branch 13 not taken.
12 setError("copyD2D: [" + std::to_string(srcOff) + ", " + std::to_string(srcEnd) + ") of a " +
487
7/14
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 4 times.
✗ Branch 13 not taken.
12 std::to_string(s->size()) + "-element src into [" + std::to_string(dstOff) + ", " +
488
6/12
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
8 std::to_string(dstEnd) + ") of a " + std::to_string(d->size()) + "-element dst is out of range");
489 4 return;
490 }
491 2 int elemSize = brotensor::dtype_size_bytes(s->dtype);
492
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (elemSize <= 0) elemSize = 4;
493 2 const std::size_t bytes = static_cast<std::size_t>(n) * elemSize;
494 2 const std::size_t s_byte_offset = static_cast<std::size_t>(srcOff) * elemSize;
495 2 const std::size_t d_byte_offset = static_cast<std::size_t>(dstOff) * elemSize;
496 BROTENSOR_API_TRY
497
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if (s->is_host()) {
498 ✗ std::memmove(reinterpret_cast<char*>(d->data) + d_byte_offset,
499 ✗ reinterpret_cast<const char*>(s->data) + s_byte_offset,
500 ✗ bytes);
501 ✗ } else {
502
3/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 brotensor::detail::alloc_for(d->device).memcpy_d2d(
503 2 reinterpret_cast<char*>(d->data) + d_byte_offset,
504 2 reinterpret_cast<const char*>(s->data) + s_byte_offset,
505 2 bytes, d->device.index);
506 }
507
0/12
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
2 BROTENSOR_API_CATCH(L)
508 10 }
509
510 ✗ void bro_tensor_nchwToSequence(void* X, int32_t N, int32_t C, int32_t H, int32_t W, void* Y) {
511 ✗ nchwMap("nchwToSequence", X, N, C, H, W, Y,
512 ✗ [&](const Tensor& x, Tensor& y) { brotensor::nchw_to_sequence(x, N, C, H, W, y); });
513 ✗ }
514
515 ✗ void bro_tensor_sequenceToNchw(void* X, int32_t N, int32_t C, int32_t H, int32_t W, void* Y) {
516 ✗ nchwMap("sequenceToNchw", X, N, C, H, W, Y,
517 ✗ [&](const Tensor& x, Tensor& y) { brotensor::sequence_to_nchw(x, N, C, H, W, y); });
518 ✗ }
519
520 2 void bro_tensor_interp2dForward(void* X, int32_t N, int32_t C, int32_t H_in, int32_t W_in, int32_t H_out, int32_t W_out, int32_t mode, void* Y) {
521
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!needNonNegative("interp2dForward", {H_out, W_out})) return;
522 4 nchwMap("interp2dForward", X, N, C, H_in, W_in, Y,
523 2 [&](const Tensor& x, Tensor& y) { brotensor::interp2d_forward(x, N, C, H_in, W_in, H_out, W_out, mode, y); });
524 2 }
525
526 ✗ void bro_tensor_interp2dAlignCornersForward(void* X, int32_t N, int32_t C, int32_t H_in, int32_t W_in, int32_t H_out, int32_t W_out, int32_t mode, void* Y) {
527 ✗ if (!needNonNegative("interp2dAlignCornersForward", {H_out, W_out})) return;
528 ✗ nchwMap("interp2dAlignCornersForward", X, N, C, H_in, W_in, Y,
529 ✗ [&](const Tensor& x, Tensor& y) { brotensor::interp2d_align_corners_forward(x, N, C, H_in, W_in, H_out, W_out, mode, y); });
530 ✗ }
531
532 ✗ void bro_tensor_unfold2dForward(void* X, int32_t N, int32_t C, int32_t H, int32_t W, int32_t kH, int32_t kW, int32_t sH, int32_t sW, int32_t padT, int32_t padB, int32_t padL, int32_t padR, int32_t mode, void* Y) {
533 ✗ const char* L = "unfold2dForward";
534 ✗ if (!needPositive(L, {kH, kW, sH, sW}) || !needNonNegative(L, {padT, padB, padL, padR})) return;
535 ✗ nchwMap(L, X, N, C, H, W, Y,
536 ✗ [&](const Tensor& x, Tensor& y) { brotensor::unfold2d_forward(x, N, C, H, W, kH, kW, sH, sW, padT, padB, padL, padR, mode, y); });
537 ✗ }
538
539 ✗ void bro_tensor_l2NormalizeNchwForward(void* X, int32_t N, int32_t C, int32_t H, int32_t W, double eps, void* Y) {
540 ✗ nchwMap("l2NormalizeNchwForward", X, N, C, H, W, Y,
541 ✗ [&](const Tensor& x, Tensor& y) { brotensor::l2_normalize_nchw_forward(x, N, C, H, W, static_cast<float>(eps), y); });
542 ✗ }
543
544 ✗ void bro_tensor_convexUpsampleForward(void* X, void* Mask, int32_t N, int32_t C, int32_t H, int32_t W, int32_t scale, void* Y) {
545 ✗ const char* L = "convexUpsampleForward";
546 ✗ auto* mt = toTensor(Mask);
547 ✗ if (!need(L, {mt})) return;
548 ✗ if (!needPositive(L, {scale})) return;
549 // Mask is (N, 9*scale*scale*H*W) and shares X's dtype.
550 ✗ if (!needElems(L, "Mask", mt, elems({N, 9, scale, scale, H, W}))) return;
551 ✗ if (auto* xt = toTensor(X); xt && !needSameDtype(L, "Mask", mt, xt)) return;
552 ✗ nchwMap(L, X, N, C, H, W, Y,
553 ✗ [&](const Tensor& x, Tensor& y) { brotensor::convex_upsample_forward(x, *mt, N, C, H, W, scale, y); });
554 ✗ }
555
556 2 double bro_tensor_mseVecForward(void* pred, void* target) {
557 2 const char* L = "mseVecForward";
558 2 auto* pt = toTensor(pred);
559 2 auto* tt = toTensor(target);
560
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!need(L, {pt, tt})) return 0.0;
561
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!needPair(L, "target", tt, pt)) return 0.0;
562 BROTENSOR_API_TRY
563 ✗ return static_cast<double>(brotensor::mse_vec_forward(*pt, *tt));
564 ✗ BROTENSOR_API_CATCH(L)
565 ✗ return 0.0;
566 2 }
567
568 ✗ void bro_tensor_mseVecBackward(void* pred, void* target, void* dPred) {
569 ✗ const char* L = "mseVecBackward";
570 ✗ auto* pt = toTensor(pred);
571 ✗ auto* tt = toTensor(target);
572 ✗ auto* dpt = toTensor(dPred);
573 ✗ if (!need(L, {pt, tt, dpt})) return;
574 ✗ if (!needPair(L, "target", tt, pt)) return;
575 BROTENSOR_API_TRY
576 ✗ brotensor::mse_vec_backward(*pt, *tt, *dpt);
577 ✗ BROTENSOR_API_CATCH(L)
578 ✗ }
579
580 ✗ void bro_tensor_mseVecPerSample(void* pred, void* target, void* dPred, void* lossPerSample) {
581 ✗ const char* L = "mseVecPerSample";
582 ✗ auto* pt = toTensor(pred);
583 ✗ auto* tt = toTensor(target);
584 ✗ auto* dpt = toTensor(dPred);
585 ✗ auto* lt = toTensor(lossPerSample);
586 ✗ if (!need(L, {pt, tt, dpt, lt})) return;
587 ✗ if (!needPair(L, "target", tt, pt)) return;
588 BROTENSOR_API_TRY
589 ✗ brotensor::mse_vec_per_sample(*pt, *tt, *dpt, *lt);
590 ✗ BROTENSOR_API_CATCH(L)
591 ✗ }
592
593 } // extern "C"
594