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 |