GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 98.3% 119 / 0 / 121
Functions: 100.0% 9 / 0 / 9
Branches: 44.5% 49 / 0 / 110

src/cpu/stylegan_elementwise.cpp
Line Branch Exec Source
1 // ─── CPU StyleGAN3 synthesis-input primitives ──────────────────────────────
2 //
3 // FP32 scalar host implementations. CPU is FP32-only.
4 //
5 // sin/cos Fourier features for SynthesisInput (sin(2π·…)).
6 // rsqrt reciprocal sqrt backing modulation-demod / pixel-norm.
7 // pixel_norm RMS-over-channel normalisation for the mapping network.
8 //
9 // sin/cos/rsqrt are elementwise: outputs resized + dtype-set to match the
10 // input, x/y and dX/dY may alias, and the backward OVERWRITES dX (no learnable
11 // parameters). pixel_norm operates per row over the trailing (cols) axis.
12 //
13 // rsqrt: the caller owns the x > 0 precondition (no guard — rsqrt(0)=+inf,
14 // rsqrt(<0)=NaN), matching log/exp in log_exp_round.cpp.
15
16 #include <brotensor/tensor.h>
17
18 #include <cmath>
19 #include <stdexcept>
20 #include <string>
21
22 namespace brotensor::detail::cpu {
23
24 namespace {
25
26 163 inline void check_fp32(const ::brotensor::Tensor& t,
27 const char* op, const char* name) {
28
1/2
✓ Branch 0 taken 163 times.
✗ Branch 1 not taken.
163 if (t.dtype != Dtype::FP32) {
29 throw std::runtime_error(std::string("brotensor: ") + op + ": " +
30 name + " must be FP32 (CPU backend is "
31 "FP32-only)");
32 }
33 163 }
34
35 } // namespace
36
37 // ─── sin ─────────────────────────────────────────────────────────────────────
38
39 29 void sin_forward(const ::brotensor::Tensor& x, ::brotensor::Tensor& y) {
40 29 check_fp32(x, "sin_forward", "x");
41
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
29 if (y.rows != x.rows || y.cols != x.cols || y.dtype != Dtype::FP32) {
42 29 y.resize(x.rows, x.cols, Dtype::FP32);
43 29 }
44 29 const int n = x.size();
45
1/2
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
29 if (n == 0) return;
46 29 const float* xp = x.host_f32();
47 29 float* yp = y.host_f32_mut();
48
2/2
✓ Branch 0 taken 875 times.
✓ Branch 1 taken 29 times.
904 for (int i = 0; i < n; ++i) yp[i] = std::sin(xp[i]);
49 29 }
50
51 2 void sin_backward(const ::brotensor::Tensor& x, const ::brotensor::Tensor& dY,
52 ::brotensor::Tensor& dX) {
53 2 check_fp32(x, "sin_backward", "x");
54 2 check_fp32(dY, "sin_backward", "dY");
55
1/6
✗ 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.
2 if (dX.rows != x.rows || dX.cols != x.cols || dX.dtype != Dtype::FP32) {
56 2 dX.resize(x.rows, x.cols, Dtype::FP32);
57 2 }
58 2 const int n = x.size();
59
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (n == 0) return;
60 2 const float* xp = x.host_f32();
61 2 const float* dyp = dY.host_f32();
62 2 float* dxp = dX.host_f32_mut(); // overwrite — dX may alias dY
63
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 2 times.
49 for (int i = 0; i < n; ++i) dxp[i] = dyp[i] * std::cos(xp[i]);
64 2 }
65
66 // ─── cos ─────────────────────────────────────────────────────────────────────
67
68 29 void cos_forward(const ::brotensor::Tensor& x, ::brotensor::Tensor& y) {
69 29 check_fp32(x, "cos_forward", "x");
70
4/6
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 25 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
29 if (y.rows != x.rows || y.cols != x.cols || y.dtype != Dtype::FP32) {
71 25 y.resize(x.rows, x.cols, Dtype::FP32);
72 25 }
73 29 const int n = x.size();
74
1/2
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
29 if (n == 0) return;
75 29 const float* xp = x.host_f32();
76 29 float* yp = y.host_f32_mut();
77
2/2
✓ Branch 0 taken 875 times.
✓ Branch 1 taken 29 times.
904 for (int i = 0; i < n; ++i) yp[i] = std::cos(xp[i]);
78 29 }
79
80 2 void cos_backward(const ::brotensor::Tensor& x, const ::brotensor::Tensor& dY,
81 ::brotensor::Tensor& dX) {
82 2 check_fp32(x, "cos_backward", "x");
83 2 check_fp32(dY, "cos_backward", "dY");
84
1/6
✗ 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.
2 if (dX.rows != x.rows || dX.cols != x.cols || dX.dtype != Dtype::FP32) {
85 2 dX.resize(x.rows, x.cols, Dtype::FP32);
86 2 }
87 2 const int n = x.size();
88
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (n == 0) return;
89 2 const float* xp = x.host_f32();
90 2 const float* dyp = dY.host_f32();
91 2 float* dxp = dX.host_f32_mut(); // overwrite — dX may alias dY
92
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 2 times.
49 for (int i = 0; i < n; ++i) dxp[i] = -dyp[i] * std::sin(xp[i]);
93 2 }
94
95 // ─── rsqrt ───────────────────────────────────────────────────────────────────
96
97 29 void rsqrt_forward(const ::brotensor::Tensor& x, ::brotensor::Tensor& y) {
98 29 check_fp32(x, "rsqrt_forward", "x");
99
4/6
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 time.
29 if (y.rows != x.rows || y.cols != x.cols || y.dtype != Dtype::FP32) {
100 28 y.resize(x.rows, x.cols, Dtype::FP32);
101 28 }
102 29 const int n = x.size();
103
1/2
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
29 if (n == 0) return;
104 29 const float* xp = x.host_f32();
105 29 float* yp = y.host_f32_mut();
106
2/2
✓ Branch 0 taken 875 times.
✓ Branch 1 taken 29 times.
904 for (int i = 0; i < n; ++i) yp[i] = 1.0f / std::sqrt(xp[i]);
107 29 }
108
109 4 void rsqrt_backward(const ::brotensor::Tensor& y, const ::brotensor::Tensor& dY,
110 ::brotensor::Tensor& dX) {
111 4 check_fp32(y, "rsqrt_backward", "y");
112 4 check_fp32(dY, "rsqrt_backward", "dY");
113
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 != y.rows || dX.cols != y.cols || dX.dtype != Dtype::FP32) {
114 4 dX.resize(y.rows, y.cols, Dtype::FP32);
115 4 }
116 4 const int n = y.size();
117
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (n == 0) return;
118 4 const float* yp = y.host_f32();
119 4 const float* dyp = dY.host_f32();
120 4 float* dxp = dX.host_f32_mut(); // overwrite — dX may alias dY
121 // y = x^{-1/2} ⇒ dy/dx = -1/2 x^{-3/2} = -1/2 y^3.
122
2/2
✓ Branch 0 taken 567 times.
✓ Branch 1 taken 4 times.
571 for (int i = 0; i < n; ++i) dxp[i] = -0.5f * dyp[i] * yp[i] * yp[i] * yp[i];
123 4 }
124
125 // ─── pixel_norm ──────────────────────────────────────────────────────────────
126
127 52 void pixel_norm_forward(const ::brotensor::Tensor& X, float eps,
128 ::brotensor::Tensor& Y) {
129 52 check_fp32(X, "pixel_norm_forward", "X");
130
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
52 if (Y.rows != X.rows || Y.cols != X.cols || Y.dtype != Dtype::FP32) {
131 52 Y.resize(X.rows, X.cols, Dtype::FP32);
132 52 }
133 52 const int R = X.rows, C = X.cols;
134
2/4
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 52 times.
52 if (R == 0 || C == 0) return;
135 52 const float* xp = X.host_f32();
136 52 float* yp = Y.host_f32_mut();
137
2/2
✓ Branch 0 taken 210 times.
✓ Branch 1 taken 52 times.
262 for (int r = 0; r < R; ++r) {
138 210 const float* xr = xp + static_cast<size_t>(r) * C;
139 210 float* yr = yp + static_cast<size_t>(r) * C;
140 210 float ss = 0.0f;
141
2/2
✓ Branch 0 taken 3238 times.
✓ Branch 1 taken 210 times.
3448 for (int c = 0; c < C; ++c) ss += xr[c] * xr[c];
142 210 const float rinv = 1.0f / std::sqrt(ss / static_cast<float>(C) + eps);
143
2/2
✓ Branch 0 taken 3238 times.
✓ Branch 1 taken 210 times.
3448 for (int c = 0; c < C; ++c) yr[c] = xr[c] * rinv;
144 210 }
145 52 }
146
147 4 void pixel_norm_backward(const ::brotensor::Tensor& X,
148 const ::brotensor::Tensor& dY, float eps,
149 ::brotensor::Tensor& dX) {
150 4 check_fp32(X, "pixel_norm_backward", "X");
151 4 check_fp32(dY, "pixel_norm_backward", "dY");
152
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 != Dtype::FP32) {
153 4 dX.resize(X.rows, X.cols, Dtype::FP32);
154 4 }
155 4 const int R = X.rows, C = X.cols;
156
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 if (R == 0 || C == 0) return;
157 4 const float* xp = X.host_f32();
158 4 const float* dyp = dY.host_f32();
159 4 float* dxp = dX.host_f32_mut(); // overwrite — dX may alias dY
160 4 const float invC = 1.0f / static_cast<float>(C);
161
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 4 times.
22 for (int r = 0; r < R; ++r) {
162 18 const float* xr = xp + static_cast<size_t>(r) * C;
163 18 const float* dyr = dyp + static_cast<size_t>(r) * C;
164 18 float* dxr = dxp + static_cast<size_t>(r) * C;
165 18 float ss = 0.0f, s = 0.0f;
166
2/2
✓ Branch 0 taken 2086 times.
✓ Branch 1 taken 18 times.
2104 for (int c = 0; c < C; ++c) {
167 2086 ss += xr[c] * xr[c];
168 2086 s += dyr[c] * xr[c];
169 2086 }
170 18 const float rinv = 1.0f / std::sqrt(ss * invC + eps);
171 18 const float r3s = rinv * rinv * rinv * s * invC;
172 // Read s/ss before writing so an in-place dX==dY alias is safe.
173
2/2
✓ Branch 0 taken 2086 times.
✓ Branch 1 taken 18 times.
2104 for (int c = 0; c < C; ++c) dxr[c] = rinv * dyr[c] - r3s * xr[c];
174 18 }
175 4 }
176
177 } // namespace brotensor::detail::cpu
178