GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 0.0% 0 / 0 / 15
Functions: 0.0% 0 / 0 / 3
Branches: -% 0 / 0 / 0

include/brotensor/ops/conv.h
Line Branch Exec Source
1 #pragma once
2
3 // brotensor ops/conv.h — Convolution: conv2d / conv3d / conv_transpose2d (+ int8 weight variants).
4
5 #include "../tensor.h"
6 #include <cstdint>
7
8 namespace brotensor {
9
10
11 // ─── Convolution: conv2d / conv3d / conv_transpose2d ───────────────────────
12 //
13 // NCHW tensors are carried as flat (rows, cols) buffers; the (N,C,H,W) dims
14 // are passed as int args. Unless noted, these ops are dtype-dispatched on the
15 // primary input (FP32 or FP16) with FP32 internal accumulation, and resize +
16 // dtype-set their outputs to match.
17
18 // 2D convolution, NCHW. Dispatched on X.dtype (FP32/FP16); Wt, bias, Y share
19 // it. FP32 accumulation.
20 // X: (N, C_in*H*W).
21 // Wt: (C_out, (C_in/groups)*kH*kW) OIHW filter layout.
22 // bias: (C_out,1) or null.
23 // Y: (N, C_out*H_out*W_out), resized + dtype-set to match X.
24 // groups: divides C_in and C_out. Output channel c_out belongs to group
25 // g = c_out/(C_out/groups) and reads only input channels
26 // [g*(C_in/groups), (g+1)*(C_in/groups)). groups=1 is standard conv;
27 // groups==C_in==C_out is depthwise (Wt becomes (C_out, kH*kW)).
28 // H_out = (H + 2*pad_h - dil_h*(kH-1) - 1)/stride_h + 1 (W_out analogous).
29 void conv2d_forward(const Tensor& X,
30 const Tensor& Wt,
31 const Tensor* bias,
32 int N, int C_in, int H, int W,
33 int C_out, int kH, int kW,
34 int stride_h, int stride_w,
35 int pad_h, int pad_w,
36 int dil_h, int dil_w,
37 int groups,
38 Tensor& Y);
39
40 // Convenience overload: groups defaults to 1 (full convolution).
41 inline void conv2d_forward(const Tensor& X,
42 const Tensor& Wt,
43 const Tensor* bias,
44 int N, int C_in, int H, int W,
45 int C_out, int kH, int kW,
46 int stride_h, int stride_w,
47 int pad_h, int pad_w,
48 int dil_h, int dil_w,
49 Tensor& Y) {
50 conv2d_forward(X, Wt, bias, N, C_in, H, W, C_out, kH, kW,
51 stride_h, stride_w, pad_h, pad_w, dil_h, dil_w,
52 /*groups=*/1, Y);
53 }
54
55
56 // 2D convolution backward w.r.t. input. Dtype-dispatched (FP32/FP16); Wt and
57 // dY share dtype, dX matches. All conv hyperparams match the forward call.
58 // dX[n,c_in,i,j] = sum over kernel taps (kh,kw) and the group's output
59 // channels of dY[n,c_out,i_out,j_out]*Wt[c_out,c_in_local,kh,kw], where
60 // i_out = (i + pad_h - dil_h*kh)/stride_h (j_out analogous),
61 // counted only when divisible by stride and in [0,H_out)x[0,W_out).
62 // Wt: (C_out, (C_in/groups)*kH*kW) forward filter, OIHW.
63 // dY: (N, C_out*H_out*W_out) upstream gradient.
64 // dX: (N, C_in*H*W) overwritten, resized + dtype-set to dY.
65 void conv2d_backward_input(const Tensor& Wt,
66 const Tensor& dY,
67 int N, int C_in, int H, int W,
68 int C_out, int kH, int kW,
69 int stride_h, int stride_w,
70 int pad_h, int pad_w,
71 int dil_h, int dil_w,
72 int groups,
73 Tensor& dX);
74
75 // Convenience overload: groups defaults to 1.
76 inline void conv2d_backward_input(const Tensor& Wt,
77 const Tensor& dY,
78 int N, int C_in, int H, int W,
79 int C_out, int kH, int kW,
80 int stride_h, int stride_w,
81 int pad_h, int pad_w,
82 int dil_h, int dil_w,
83 Tensor& dX) {
84 conv2d_backward_input(Wt, dY, N, C_in, H, W, C_out, kH, kW,
85 stride_h, stride_w, pad_h, pad_w, dil_h, dil_w,
86 /*groups=*/1, dX);
87 }
88
89
90 // 2D convolution backward w.r.t. weights. Dtype-dispatched (FP32/FP16); X, dY,
91 // dWt share dtype. All conv hyperparams match the forward call.
92 // dWt[c_out,c_in_local,kh,kw] += sum over (n,i_out,j_out) of
93 // dY[n,c_out,i_out,j_out] *
94 // X[n,c_in, stride_h*i_out-pad_h+dil_h*kh, stride_w*j_out-pad_w+dil_w*kw]
95 // (OOB input reads treated as zero), with c_in = g*(C_in/groups)+c_in_local,
96 // g = c_out/(C_out/groups).
97 // X: (N, C_in*H*W) forward input.
98 // dY: (N, C_out*H_out*W_out) upstream gradient.
99 // dWt: (C_out, (C_in/groups)*kH*kW) accumulated — caller zeros.
100 void conv2d_backward_weight(const Tensor& X,
101 const Tensor& dY,
102 int N, int C_in, int H, int W,
103 int C_out, int kH, int kW,
104 int stride_h, int stride_w,
105 int pad_h, int pad_w,
106 int dil_h, int dil_w,
107 int groups,
108 Tensor& dWt);
109
110 // Convenience overload: groups defaults to 1.
111 inline void conv2d_backward_weight(const Tensor& X,
112 const Tensor& dY,
113 int N, int C_in, int H, int W,
114 int C_out, int kH, int kW,
115 int stride_h, int stride_w,
116 int pad_h, int pad_w,
117 int dil_h, int dil_w,
118 Tensor& dWt) {
119 conv2d_backward_weight(X, dY, N, C_in, H, W, C_out, kH, kW,
120 stride_h, stride_w, pad_h, pad_w, dil_h, dil_w,
121 /*groups=*/1, dWt);
122 }
123
124
125 // 2D convolution backward w.r.t. bias. Dtype-dispatched (FP32/FP16); dY and dB
126 // share dtype.
127 // dB[c_out] += sum over (n,i_out,j_out) of dY[n,c_out,i_out,j_out].
128 // dY: (N, C_out*H_out*W_out) upstream gradient.
129 // dB: (C_out,1) accumulated — caller zeros.
130 void conv2d_backward_bias(const Tensor& dY,
131 int N, int C_out, int H_out, int W_out,
132 Tensor& dB);
133
134
135 // Modulated deformable 2D convolution forward — torchvision `deform_conv2d`
136 // (Deformable ConvNets v2), NCHW, forward/inference only. Each output pixel's
137 // kH×kW sampling grid is shifted per-tap by a learned `offset` field and
138 // (optionally) reweighted by a learned `mask` modulator; taps are bilinearly
139 // sampled from X with ZERO padding outside the input (torchvision convention).
140 // X: (N, C_in*H*W).
141 // offset: (N, deform_groups*2*kH*kW * H_out*W_out). Channel-major within a
142 // batch row: channel = grp*(2*kH*kW) + 2*(kh*kW+kw) [+1 for the col
143 // axis], then (H_out,W_out). Channel 2*(kh*kW+kw) is the ROW (y)
144 // offset, +1 the COL (x) offset — matches an offset_conv whose output
145 // channels are laid out (deform_groups, 2, kH, kW).
146 // mask: (N, deform_groups*kH*kW * H_out*W_out) or null. null == plain
147 // deformable conv (all modulators 1). Channel grp*(kH*kW)+(kh*kW+kw).
148 // Wt: (C_out, (C_in/groups)*kH*kW) OIHW (same layout as conv2d_forward).
149 // bias: (C_out,1) or null.
150 // Y: (N, C_out*H_out*W_out), resized + dtype-set to match X.
151 // groups divides C_in and C_out (regular conv grouping); deform_groups
152 // divides C_in (offset/mask grouping). H_out/W_out follow the conv2d formula.
153 // Dispatched FP32/FP16 on X.dtype (CPU is FP32-only); FP32 accumulation. No
154 // backward (an inference op — BiRefNet's ASPP-deformable decoder).
155 void deform_conv2d_forward(const Tensor& X,
156 const Tensor& offset,
157 const Tensor* mask,
158 const Tensor& Wt,
159 const Tensor* bias,
160 int N, int C_in, int H, int W,
161 int C_out, int kH, int kW,
162 int stride_h, int stride_w,
163 int pad_h, int pad_w,
164 int dil_h, int dil_w,
165 int groups, int deform_groups,
166 Tensor& Y);
167
168
169 // 3D convolution, NCTHW (forward only). Dispatched on X.dtype (FP32/FP16/BF16
170 // on GPU; CPU is FP32-only). FP32 accumulation.
171 // X: (N, C_in*T*H*W).
172 // Wt: (C_out, (C_in/groups)*kT*kH*kW) OICTHW filter layout (grouped).
173 // bias: (C_out,1) or null.
174 // Y: (N, C_out*T_out*H_out*W_out), resized + dtype-set to match X.
175 // Per-axis stride/pad/dilation: (_t, _h, _w). `groups` divides C_in and C_out
176 // exactly as in conv2d (output channel c_out belongs to group
177 // c_out/(C_out/groups); reads input channels of that group only).
178 // T_out = (T + 2*pad_t - dil_t*(kT-1) - 1)/stride_t + 1 (H_out, W_out analogous).
179 // Y is OVERWRITTEN (the kernel stores acc directly), matching conv2d_forward.
180 void conv3d_forward(const Tensor& X,
181 const Tensor& Wt,
182 const Tensor* bias,
183 int N, int C_in, int T, int H, int W,
184 int C_out, int kT, int kH, int kW,
185 int stride_t, int stride_h, int stride_w,
186 int pad_t, int pad_h, int pad_w,
187 int dil_t, int dil_h, int dil_w,
188 int groups,
189 Tensor& Y);
190
191 // Convenience overload: groups defaults to 1 (full convolution).
192 inline void conv3d_forward(const Tensor& X,
193 const Tensor& Wt,
194 const Tensor* bias,
195 int N, int C_in, int T, int H, int W,
196 int C_out, int kT, int kH, int kW,
197 int stride_t, int stride_h, int stride_w,
198 int pad_t, int pad_h, int pad_w,
199 int dil_t, int dil_h, int dil_w,
200 Tensor& Y) {
201 conv3d_forward(X, Wt, bias, N, C_in, T, H, W, C_out, kT, kH, kW,
202 stride_t, stride_h, stride_w, pad_t, pad_h, pad_w,
203 dil_t, dil_h, dil_w, /*groups=*/1, Y);
204 }
205
206
207 // W8A16 3D convolution forward — the Qwen3-VL patch-embed variant of
208 // conv3d_forward. Same NCTHW / OICTHW layout, same per-axis stride/pad/dilation
209 // and groups semantics; X / bias FP16, W_int8 INT8 with per-output-row FP32
210 // dequant scales (analogous to conv2d_int8w_fp16_forward). GPU-only — the CPU
211 // vtable slot is left null and the dispatcher throws "not implemented on CPU".
212 // X: (N, C_in*T*H*W) FP16.
213 // W_int8: (C_out, (C_in/groups)*kT*kH*kW) INT8.
214 // scales: (C_out, 1) FP32.
215 // bias: (C_out, 1) FP16 or null.
216 // Y: (N, C_out*T_out*H_out*W_out) FP16, resized + dtype-set as needed.
217 void conv3d_int8w_fp16_forward(const Tensor& X,
218 const Tensor& W_int8,
219 const Tensor& scales,
220 const Tensor* bias,
221 int N, int C_in, int T, int H, int W,
222 int C_out, int kT, int kH, int kW,
223 int stride_t, int stride_h, int stride_w,
224 int pad_t, int pad_h, int pad_w,
225 int dil_t, int dil_h, int dil_w,
226 int groups,
227 Tensor& Y);
228
229 // Convenience overload: groups defaults to 1.
230 inline void conv3d_int8w_fp16_forward(const Tensor& X,
231 const Tensor& W_int8,
232 const Tensor& scales,
233 const Tensor* bias,
234 int N, int C_in, int T, int H, int W,
235 int C_out, int kT, int kH, int kW,
236 int stride_t, int stride_h, int stride_w,
237 int pad_t, int pad_h, int pad_w,
238 int dil_t, int dil_h, int dil_w,
239 Tensor& Y) {
240 conv3d_int8w_fp16_forward(X, W_int8, scales, bias,
241 N, C_in, T, H, W, C_out, kT, kH, kW,
242 stride_t, stride_h, stride_w,
243 pad_t, pad_h, pad_w,
244 dil_t, dil_h, dil_w, /*groups=*/1, Y);
245 }
246
247
248 // 2D transposed convolution, NCHW. The 2D counterpart of conv_transpose1d,
249 // generalising independently across H and W. Used by SAM's mask decoder
250 // (4x upsampler), DPT depth heads, and any segmentation decoder that
251 // learns the upsample. Output spatial dims (torch ConvTranspose2d formula):
252 // H_out = (H - 1)*stride_h - 2*pad_h + dilation_h*(kH-1) + output_padding_h + 1
253 // W_out = (W - 1)*stride_w - 2*pad_w + dilation_w*(kW-1) + output_padding_w + 1
254 // Weight layout is input-channel-major (transposed-conv convention):
255 // Wt: (C_in, (C_out/groups)*kH*kW) with index
256 // (c_in*(Cg_out*kH*kW) + (oc_local*kH + kh)*kW + kw)
257 // bias may be null. groups must divide both C_in and C_out (default 1).
258 // output_padding must be < stride or < dilation on each axis (matches torch).
259 // X: (N, C_in*H*W). Y: (N, C_out*H_out*W_out), resized + dtype-set.
260 // CPU backend is FP32-only; the CUDA forward is dtype-dispatched on X
261 // (FP32/FP16/BF16 — Wt and bias must match, FP32 accumulation). The three
262 // backward ops are FP32-only on both backends.
263 void conv_transpose2d_forward(const Tensor& X, const Tensor& Wt,
264 const Tensor* bias,
265 int N, int C_in, int H, int W,
266 int C_out, int kH, int kW,
267 int stride_h, int stride_w,
268 int pad_h, int pad_w,
269 int output_padding_h, int output_padding_w,
270 int dil_h, int dil_w, int groups,
271 Tensor& Y);
272
273
274 // Backward to the input. dX OVERWRITTEN — the adjoint of the forward
275 // scatter is a plain gather conv (cross-correlation in disguise).
276 // Wt: (C_in, (C_out/groups)*kH*kW). dY: (N, C_out*H_out*W_out).
277 // dX: (N, C_in*H*W), resized + dtype-set.
278 void conv_transpose2d_backward_input(const Tensor& Wt, const Tensor& dY,
279 int N, int C_in, int H, int W,
280 int C_out, int kH, int kW,
281 int stride_h, int stride_w,
282 int pad_h, int pad_w,
283 int output_padding_h, int output_padding_w,
284 int dil_h, int dil_w, int groups,
285 Tensor& dX);
286
287
288 // Backward to the weights. dWt ACCUMULATES (+=) — caller zeros it first
289 // (matches the conv2d contract).
290 // X: (N, C_in*H*W). dY: (N, C_out*H_out*W_out).
291 // dWt: (C_in, (C_out/groups)*kH*kW), pre-zeroed by caller.
292 void conv_transpose2d_backward_weight(const Tensor& X, const Tensor& dY,
293 int N, int C_in, int H, int W,
294 int C_out, int kH, int kW,
295 int stride_h, int stride_w,
296 int pad_h, int pad_w,
297 int output_padding_h, int output_padding_w,
298 int dil_h, int dil_w, int groups,
299 Tensor& dWt);
300
301
302 // Backward to the bias. dB ACCUMULATES (+=) — caller zeros first.
303 // dY: (N, C_out*H_out*W_out). dB: (C_out, 1).
304 void conv_transpose2d_backward_bias(const Tensor& dY, int N, int C_out,
305 int H_out, int W_out, Tensor& dB);
306
307
308 // W8A16 conv2d forward. Mirrors conv2d_forward; only the weight dtype differs.
309 // W_int8: (C_out, C_in/groups*kH*kW) INT8 OIHW, quantised per output channel.
310 // scales: (C_out,1) FP32 per-output-channel dequant scales.
311 // bias: (C_out,1) FP16 or null. X, Y: FP16, layout as conv2d_forward.
312 void conv2d_int8w_fp16_forward(const Tensor& X,
313 const Tensor& W_int8,
314 const Tensor& scales,
315 const Tensor* bias,
316 int N, int C_in, int H, int W,
317 int C_out, int kH, int kW,
318 int stride_h, int stride_w,
319 int pad_h, int pad_w,
320 int dil_h, int dil_w, int groups,
321 Tensor& Y);
322
323 } // namespace brotensor
324