GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 89.6% 155 / 0 / 173
Functions: 88.9% 8 / 0 / 9
Branches: 47.8% 86 / 0 / 180

src/cpu/vocoder_activations.cpp
Line Branch Exec Source
1 // ─── CPU vocoder / codec activations (brosoundml CHUNK 4, family C) ─────────
2 //
3 // FP32 scalar host implementations of the genuinely-new vocoder/codec
4 // activation ops:
5 // snake_forward / snake_backward — BigVGAN / DAC snake + snakebeta
6 // elu_forward / elu_backward — EnCodec ELU
7 // leaky_relu_forward / leaky_relu_backward — HiFi-GAN leaky ReLU
8 //
9 // ── Layout ──────────────────────────────────────────────────────────────────
10 // snake is per-channel over an NCL tensor: element (n, c, l) at flat index
11 // (n*C + c)*L + l. alpha / beta carry one scalar per channel c, broadcast
12 // across the (n, l) plane — the group_norm per-channel handling.
13 // elu / leaky_relu are plain elementwise — shape is irrelevant.
14 //
15 // ── Accumulation (matches the group_norm_backward contract) ─────────────────
16 // snake_forward — Y OVERWRITTEN.
17 // snake_backward — dX OVERWRITTEN; dAlpha / dBeta ACCUMULATE (+=).
18 // elu / leaky_relu forward — y OVERWRITTEN.
19 // elu / leaky_relu backward— dX OVERWRITTEN (no learnable params).
20 //
21 // CPU is FP32-only; alpha / beta / negative_slope arithmetic is FP32.
22
23 #include <brotensor/tensor.h>
24
25 #include <cmath>
26 #include <stdexcept>
27 #include <string>
28
29 namespace brotensor::detail::cpu {
30
31 namespace {
32
33 [[noreturn]] void fail(const char* op, const std::string& reason) {
34 throw std::runtime_error(std::string("brotensor: ") + op + ": " + reason);
35 }
36
37 591 void require_fp32(const char* op, const ::brotensor::Tensor& t,
38 const char* name) {
39
1/2
✓ Branch 0 taken 591 times.
✗ Branch 1 not taken.
591 if (t.dtype != ::brotensor::Dtype::FP32) {
40 fail(op, std::string(name) + " must be FP32 (CPU backend is FP32-only)");
41 }
42 591 }
43
44 // Sign-preserving floor on a reciprocal denominator: keeps |d| >= 1e-9 so a
45 // near-zero alpha/beta degrades gracefully instead of producing NaN/Inf.
46 794 inline float guard_denom(float d) {
47 794 constexpr float kMin = 1e-9f;
48
2/4
✓ Branch 0 taken 794 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 794 times.
794 if (d >= 0.0f) return d < kMin ? kMin : d;
49 return d > -kMin ? -kMin : d;
50 794 }
51
52 } // namespace
53
54 // ─── snake ──────────────────────────────────────────────────────────────────
55
56 120 void snake_forward(const ::brotensor::Tensor& X,
57 const ::brotensor::Tensor& alpha,
58 const ::brotensor::Tensor* beta,
59 int N, int C, int L,
60 ::brotensor::Tensor& Y) {
61 120 require_fp32("snake_forward", X, "X");
62 120 require_fp32("snake_forward", alpha, "alpha");
63
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 57 times.
120 if (beta) require_fp32("snake_forward", *beta, "beta");
64
1/2
✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
120 if (N < 0 || C < 0 || L < 0) {
65 fail("snake_forward", "N, C, L must be non-negative");
66 }
67 120 const long long cols = static_cast<long long>(C) * L;
68
1/2
✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
120 if (X.rows != N || X.cols != cols) {
69 fail("snake_forward", "X must be shaped (N, C*L)");
70 }
71
1/2
✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
120 if (alpha.size() != C) {
72 fail("snake_forward", "alpha must have C elements");
73 }
74
3/4
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 57 times.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
120 if (beta && beta->size() != C) {
75 fail("snake_forward", "beta must have C elements");
76 }
77
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
120 if (Y.rows != N || Y.cols != cols || Y.dtype != X.dtype) {
78 120 Y.resize(N, static_cast<int>(cols), X.dtype);
79 120 }
80
2/4
✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 120 times.
120 if (N == 0 || cols == 0) return;
81
82 120 const float* Xp = X.host_f32();
83 120 const float* ap = alpha.host_f32();
84
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 57 times.
120 const float* bp = beta ? beta->host_f32() : nullptr;
85 120 float* Yp = Y.host_f32_mut();
86
87
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 241 times.
361 for (int n = 0; n < N; ++n) {
88
2/2
✓ Branch 0 taken 740 times.
✓ Branch 1 taken 241 times.
981 for (int c = 0; c < C; ++c) {
89 740 const float a = ap[c];
90
2/2
✓ Branch 0 taken 396 times.
✓ Branch 1 taken 344 times.
740 const float denom = guard_denom(bp ? bp[c] : a);
91 740 const float r = 1.0f / denom;
92 740 const float* x_row = Xp + (static_cast<long long>(n) * C + c) * L;
93 740 float* y_row = Yp + (static_cast<long long>(n) * C + c) * L;
94
2/2
✓ Branch 0 taken 3272 times.
✓ Branch 1 taken 740 times.
4012 for (int l = 0; l < L; ++l) {
95 3272 const float x = x_row[l];
96 3272 const float s = std::sin(a * x);
97 3272 y_row[l] = x + r * s * s; // overwrite
98 3272 }
99 740 }
100 241 }
101 120 }
102
103 8 void snake_backward(const ::brotensor::Tensor& X,
104 const ::brotensor::Tensor& alpha,
105 const ::brotensor::Tensor* beta,
106 const ::brotensor::Tensor& dY,
107 int N, int C, int L,
108 ::brotensor::Tensor& dX,
109 ::brotensor::Tensor& dAlpha,
110 ::brotensor::Tensor* dBeta) {
111 8 require_fp32("snake_backward", X, "X");
112 8 require_fp32("snake_backward", alpha, "alpha");
113 8 require_fp32("snake_backward", dY, "dY");
114 8 require_fp32("snake_backward", dAlpha, "dAlpha");
115
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 if (beta) require_fp32("snake_backward", *beta, "beta");
116
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 if (dBeta) require_fp32("snake_backward", *dBeta, "dBeta");
117
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if ((beta == nullptr) != (dBeta == nullptr)) {
118 fail("snake_backward",
119 "dBeta must be non-null exactly when beta is non-null");
120 }
121
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (N < 0 || C < 0 || L < 0) {
122 fail("snake_backward", "N, C, L must be non-negative");
123 }
124 8 const long long cols = static_cast<long long>(C) * L;
125
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (X.rows != N || X.cols != cols) {
126 fail("snake_backward", "X must be shaped (N, C*L)");
127 }
128
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (dY.rows != N || dY.cols != cols) {
129 fail("snake_backward", "dY must be shaped (N, C*L)");
130 }
131
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (alpha.size() != C) {
132 fail("snake_backward", "alpha must have C elements");
133 }
134
3/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
8 if (beta && beta->size() != C) {
135 fail("snake_backward", "beta must have C elements");
136 }
137
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (dAlpha.rows != C || dAlpha.cols != 1) {
138 fail("snake_backward", "dAlpha must be (C, 1)");
139 }
140
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
8 if (dBeta && (dBeta->rows != C || dBeta->cols != 1)) {
141 fail("snake_backward", "dBeta must be (C, 1)");
142 }
143
4/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
8 if (dX.rows != N || dX.cols != cols || dX.dtype != X.dtype) {
144 6 dX.resize(N, static_cast<int>(cols), X.dtype);
145 6 }
146
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if (N == 0 || cols == 0) return;
147
148 8 const float* Xp = X.host_f32();
149 8 const float* ap = alpha.host_f32();
150
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 const float* bp = beta ? beta->host_f32() : nullptr;
151 8 const float* dYp = dY.host_f32();
152 8 float* dXp = dX.host_f32_mut();
153 8 float* dAp = dAlpha.host_f32_mut();
154
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 float* dBp = dBeta ? dBeta->host_f32_mut() : nullptr;
155
156
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 15 times.
23 for (int n = 0; n < N; ++n) {
157
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 15 times.
69 for (int c = 0; c < C; ++c) {
158 54 const float a = ap[c];
159
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 12 times.
54 const float denom = guard_denom(bp ? bp[c] : a);
160 54 const float r = 1.0f / denom;
161 54 const float* x_row = Xp + (static_cast<long long>(n) * C + c) * L;
162 54 const float* dy_row = dYp + (static_cast<long long>(n) * C + c) * L;
163 54 float* dx_row = dXp + (static_cast<long long>(n) * C + c) * L;
164
165 54 float dalpha_acc = 0.0f;
166 54 float dbeta_acc = 0.0f;
167
2/2
✓ Branch 0 taken 534 times.
✓ Branch 1 taken 54 times.
588 for (int l = 0; l < L; ++l) {
168 534 const float x = x_row[l];
169 534 const float dy = dy_row[l];
170 534 const float s = std::sin(a * x);
171 534 const float co = std::cos(a * x);
172 534 const float sc = s * co; // sin*cos = 0.5*sin(2ax)
173
174 // dy/dx = 1 + 2*a*r*s*c
175 534 dx_row[l] = dy * (1.0f + 2.0f * a * r * sc); // overwrite
176
177 // dy/dalpha (frequency term) = 2*r*x*s*c
178 534 dalpha_acc += dy * (2.0f * r * x * sc);
179
180
2/2
✓ Branch 0 taken 480 times.
✓ Branch 1 taken 54 times.
534 if (bp) {
181 // snakebeta: dy/dbeta = -r^2 * s^2.
182 480 dbeta_acc += dy * (-r * r * s * s);
183 480 } else {
184 // plain snake: denom == alpha, so alpha also drives the
185 // reciprocal — add the -r^2*s^2 term into dAlpha too.
186 54 dalpha_acc += dy * (-r * r * s * s);
187 }
188 534 }
189 54 dAp[c] += dalpha_acc; // accumulate
190
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 42 times.
54 if (dBp) dBp[c] += dbeta_acc; // accumulate
191 54 }
192 15 }
193 8 }
194
195 // ─── elu ────────────────────────────────────────────────────────────────────
196
197 134 void elu_forward(const ::brotensor::Tensor& x, float alpha,
198 ::brotensor::Tensor& y) {
199 134 require_fp32("elu_forward", x, "x");
200
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
134 if (y.rows != x.rows || y.cols != x.cols || y.dtype != x.dtype) {
201 134 y.resize(x.rows, x.cols, x.dtype);
202 134 }
203 134 const int n = x.size();
204
1/2
✓ Branch 0 taken 134 times.
✗ Branch 1 not taken.
134 if (n == 0) return;
205 134 const float* xp = x.host_f32();
206 134 float* yp = y.host_f32_mut();
207
2/2
✓ Branch 0 taken 134 times.
✓ Branch 1 taken 6483 times.
6617 for (int i = 0; i < n; ++i) {
208 6483 const float v = xp[i];
209
2/2
✓ Branch 0 taken 3278 times.
✓ Branch 1 taken 3205 times.
6483 yp[i] = v > 0.0f ? v : alpha * (std::exp(v) - 1.0f);
210 6483 }
211 134 }
212
213 7 void elu_backward(const ::brotensor::Tensor& x, const ::brotensor::Tensor& dY,
214 float alpha, ::brotensor::Tensor& dX) {
215 7 require_fp32("elu_backward", x, "x");
216 7 require_fp32("elu_backward", dY, "dY");
217
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
7 if (dX.rows != x.rows || dX.cols != x.cols || dX.dtype != x.dtype) {
218 7 dX.resize(x.rows, x.cols, x.dtype);
219 7 }
220 7 const int n = x.size();
221
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if (n == 0) return;
222 7 const float* xp = x.host_f32();
223 7 const float* dyp = dY.host_f32();
224 7 float* dxp = dX.host_f32_mut();
225
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 378 times.
385 for (int i = 0; i < n; ++i) {
226 378 const float v = xp[i];
227
2/2
✓ Branch 0 taken 182 times.
✓ Branch 1 taken 196 times.
378 const float g = v > 0.0f ? 1.0f : alpha * std::exp(v);
228 378 dxp[i] = dyp[i] * g; // overwrite
229 378 }
230 7 }
231
232 // ─── leaky_relu ─────────────────────────────────────────────────────────────
233
234 88 void leaky_relu_forward(const ::brotensor::Tensor& x, float negative_slope,
235 ::brotensor::Tensor& y) {
236 88 require_fp32("leaky_relu_forward", x, "x");
237
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 88 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
88 if (y.rows != x.rows || y.cols != x.cols || y.dtype != x.dtype) {
238 88 y.resize(x.rows, x.cols, x.dtype);
239 88 }
240 88 const int n = x.size();
241
1/2
✓ Branch 0 taken 88 times.
✗ Branch 1 not taken.
88 if (n == 0) return;
242 88 const float* xp = x.host_f32();
243 88 float* yp = y.host_f32_mut();
244
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 9361 times.
9449 for (int i = 0; i < n; ++i) {
245 9361 const float v = xp[i];
246
2/2
✓ Branch 0 taken 4704 times.
✓ Branch 1 taken 4657 times.
9361 yp[i] = v > 0.0f ? v : negative_slope * v;
247 9361 }
248 88 }
249
250 4 void leaky_relu_backward(const ::brotensor::Tensor& x,
251 const ::brotensor::Tensor& dY,
252 float negative_slope, ::brotensor::Tensor& dX) {
253 4 require_fp32("leaky_relu_backward", x, "x");
254 4 require_fp32("leaky_relu_backward", dY, "dY");
255
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
4 if (dX.rows != x.rows || dX.cols != x.cols || dX.dtype != x.dtype) {
256 4 dX.resize(x.rows, x.cols, x.dtype);
257 4 }
258 4 const int n = x.size();
259
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (n == 0) return;
260 4 const float* xp = x.host_f32();
261 4 const float* dyp = dY.host_f32();
262 4 float* dxp = dX.host_f32_mut();
263
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 331 times.
335 for (int i = 0; i < n; ++i) {
264
2/2
✓ Branch 0 taken 169 times.
✓ Branch 1 taken 162 times.
331 const float g = xp[i] > 0.0f ? 1.0f : negative_slope;
265 331 dxp[i] = dyp[i] * g; // overwrite
266 331 }
267 4 }
268
269 } // namespace brotensor::detail::cpu
270