@@ -159,11 +159,74 @@ namespace internal {
159159 return output;
160160 }
161161
162+ // Dilated variant: filter values are at positions (y_filt * dil_y, x_filt * dil_x) in the
163+ // effective receptive field. We loop over the un-dilated filter positions and do one GEMM
164+ // per (y_filt, x_filt), which avoids multiplying by the zeros of an inflated kernel.
165+ inline tensor convolve_accumulative_s1x1_dilated (
166+ std::size_t out_height,
167+ std::size_t out_width,
168+ const shape2& dilation_rate,
169+ const convolution_filter_matrices& filter_mat,
170+ const tensor& in)
171+ {
172+ const tensor& filter_mats = filter_mat.filter_mats_ ;
173+ const auto f_height = filter_mat.filter_shape_ .height_ ;
174+ const auto f_width = filter_mat.filter_shape_ .width_ ;
175+ const auto f_depth = filter_mat.filter_shape_ .depth_ ;
176+ const auto out_depth = filter_mat.filter_count_ ;
177+ const auto dil_y = dilation_rate.height_ ;
178+ const auto dil_x = dilation_rate.width_ ;
179+ const auto eff_f_width = (f_width - 1 ) * dil_x + 1 ;
180+
181+ tensor output = init_conv_output_tensor (out_height, out_width, out_depth, in.shape ().rank (), filter_mat);
182+
183+ const std::size_t out_width_temp = out_width + eff_f_width - 1 ;
184+ tensor output_temp (tensor_shape_with_changed_rank (
185+ tensor_shape (out_height, out_width_temp, out_depth),
186+ in.shape ().rank ()),
187+ static_cast <float_type>(0 ));
188+
189+ const auto mapping_width = out_width_temp * (out_height - 1 ) + out_width;
190+
191+ for (std::size_t y_filt = 0 ; y_filt < f_height; ++y_filt) {
192+ for (std::size_t x_filt = 0 ; x_filt < f_width; ++x_filt) {
193+ const Eigen::Map<ColMajorMatrixXf, Eigen::Unaligned>
194+ filter (const_cast <float_type*>(&filter_mats.get_ref_ignore_rank (tensor_pos (0 , y_filt, x_filt, 0 , 0 ))),
195+ static_cast <EigenIndex>(out_depth),
196+ static_cast <EigenIndex>(f_depth));
197+
198+ const Eigen::Map<ColMajorMatrixXf, Eigen::Unaligned, Eigen::OuterStride<>>
199+ input (const_cast <float_type*>(&in.get_ref_ignore_rank (tensor_pos (0 , 0 , y_filt * dil_y, x_filt * dil_x, 0 ))),
200+ static_cast <EigenIndex>(f_depth),
201+ static_cast <EigenIndex>(mapping_width),
202+ Eigen::OuterStride<>(static_cast <EigenIndex>(f_depth)));
203+
204+ Eigen::Map<ColMajorMatrixXf, Eigen::Unaligned>
205+ output_temp_map (&output_temp.get_ref_ignore_rank (tensor_pos (0 , 0 , 0 , 0 , 0 )),
206+ static_cast <EigenIndex>(out_depth),
207+ static_cast <EigenIndex>(mapping_width));
208+
209+ output_temp_map.noalias () += filter * input;
210+ }
211+ }
212+
213+ for (std::size_t y_out = 0 ; y_out < out_height; ++y_out) {
214+ for (std::size_t x_out = 0 ; x_out < out_width; ++x_out) {
215+ for (std::size_t z_out = 0 ; z_out < out_depth; ++z_out) {
216+ output.get_ref_ignore_rank (tensor_pos (0 , 0 , y_out, x_out, z_out)) += output_temp.get_ref_ignore_rank (tensor_pos (0 , 0 , y_out, x_out, z_out));
217+ }
218+ }
219+ }
220+
221+ return output;
222+ }
223+
162224 inline tensor convolve_accumulative (
163225 std::size_t out_height,
164226 std::size_t out_width,
165227 std::size_t strides_y,
166228 std::size_t strides_x,
229+ const shape2& dilation_rate,
167230 const convolution_filter_matrices& filter_mat,
168231 const tensor& in)
169232 {
@@ -176,31 +239,65 @@ namespace internal {
176239 const auto f_width = filter_mat.filter_shape_ .width_ ;
177240 const auto f_depth = filter_mat.filter_shape_ .depth_ ;
178241 const auto out_depth = filter_mat.filter_count_ ;
242+ const auto dil_y = dilation_rate.height_ ;
243+ const auto dil_x = dilation_rate.width_ ;
244+ const auto eff_f_height = (f_height - 1 ) * dil_y + 1 ;
245+ const auto eff_f_width = (f_width - 1 ) * dil_x + 1 ;
179246
180247 assertion (f_depth == in.shape ().depth_ , " filter depth does not match input" );
181248 assertion (filter_mats.shape ().size_dim_4_ == f_height, " incorrect number of filter levels in y direction" );
182- assertion (out_width == (in.shape ().width_ - f_width ) / strides_x + 1 , " output width does not match" );
249+ assertion (out_width == (in.shape ().width_ - eff_f_width ) / strides_x + 1 , " output width does not match" );
183250 assertion (out_depth == filter_mat.biases_ .size (), " invlid bias count" );
184251
185252 if (strides_x == 1 && strides_y == 1 ) {
186- return convolve_accumulative_s1x1 (out_height, out_width, filter_mat, in);
253+ if (dil_x == 1 && dil_y == 1 ) {
254+ return convolve_accumulative_s1x1 (out_height, out_width, filter_mat, in);
255+ }
256+ return convolve_accumulative_s1x1_dilated (out_height, out_width, dilation_rate, filter_mat, in);
187257 }
188258
189259 tensor output = init_conv_output_tensor (out_height, out_width, out_depth, in.shape ().rank (), filter_mat);
190260
191- for (std::size_t y_filt = 0 ; y_filt < f_height; ++y_filt) {
192- const Eigen::Map<ColMajorMatrixXf, Eigen::Unaligned>
193- filter (const_cast <float_type*>(&filter_mats.get_ref_ignore_rank (tensor_pos (0 , y_filt, 0 , 0 , 0 ))),
194- static_cast <EigenIndex>(out_depth),
195- static_cast <EigenIndex>(f_width * f_depth));
196- for (std::size_t y = 0 , y_out = 0 ; y < in.shape ().height_ + 1 - f_height; y += strides_y, ++y_out) {
197- const auto input = get_im2col_mapping (in, f_width, f_depth, strides_x, out_width, y, y_filt);
198- Eigen::Map<ColMajorMatrixXf, Eigen::Unaligned>
199- output_map (&output.get_ref_ignore_rank (tensor_pos (0 , 0 , y_out, 0 , 0 )),
261+ if (dil_x == 1 && dil_y == 1 ) {
262+ for (std::size_t y_filt = 0 ; y_filt < f_height; ++y_filt) {
263+ const Eigen::Map<ColMajorMatrixXf, Eigen::Unaligned>
264+ filter (const_cast <float_type*>(&filter_mats.get_ref_ignore_rank (tensor_pos (0 , y_filt, 0 , 0 , 0 ))),
200265 static_cast <EigenIndex>(out_depth),
201- static_cast <EigenIndex>(out_width));
266+ static_cast <EigenIndex>(f_width * f_depth));
267+ for (std::size_t y = 0 , y_out = 0 ; y < in.shape ().height_ + 1 - f_height; y += strides_y, ++y_out) {
268+ const auto input = get_im2col_mapping (in, f_width, f_depth, strides_x, out_width, y, y_filt);
269+ Eigen::Map<ColMajorMatrixXf, Eigen::Unaligned>
270+ output_map (&output.get_ref_ignore_rank (tensor_pos (0 , 0 , y_out, 0 , 0 )),
271+ static_cast <EigenIndex>(out_depth),
272+ static_cast <EigenIndex>(out_width));
273+
274+ output_map.noalias () += filter * input;
275+ }
276+ }
277+ return output;
278+ }
202279
203- output_map.noalias () += filter * input;
280+ // Strided + dilated: nested f_height x f_width x out_height GEMMs.
281+ for (std::size_t y_filt = 0 ; y_filt < f_height; ++y_filt) {
282+ for (std::size_t x_filt = 0 ; x_filt < f_width; ++x_filt) {
283+ const Eigen::Map<ColMajorMatrixXf, Eigen::Unaligned>
284+ filter (const_cast <float_type*>(&filter_mats.get_ref_ignore_rank (tensor_pos (0 , y_filt, x_filt, 0 , 0 ))),
285+ static_cast <EigenIndex>(out_depth),
286+ static_cast <EigenIndex>(f_depth));
287+ for (std::size_t y = 0 , y_out = 0 ; y < in.shape ().height_ + 1 - eff_f_height; y += strides_y, ++y_out) {
288+ const Eigen::Map<ColMajorMatrixXf, Eigen::Unaligned, Eigen::OuterStride<>>
289+ input (const_cast <float_type*>(&in.get_ref_ignore_rank (tensor_pos (0 , 0 , y + y_filt * dil_y, x_filt * dil_x, 0 ))),
290+ static_cast <EigenIndex>(f_depth),
291+ static_cast <EigenIndex>(out_width),
292+ Eigen::OuterStride<>(static_cast <EigenIndex>(f_depth * strides_x)));
293+
294+ Eigen::Map<ColMajorMatrixXf, Eigen::Unaligned>
295+ output_map (&output.get_ref_ignore_rank (tensor_pos (0 , 0 , y_out, 0 , 0 )),
296+ static_cast <EigenIndex>(out_depth),
297+ static_cast <EigenIndex>(out_width));
298+
299+ output_map.noalias () += filter * input;
300+ }
204301 }
205302 }
206303
@@ -301,14 +398,19 @@ namespace internal {
301398 inline tensor convolve (
302399 const shape2& strides,
303400 const padding& pad_type,
401+ const shape2& dilation_rate,
304402 const convolution_filter_matrices& filter_mat,
305403 const tensor& input)
306404 {
307405 assertion (filter_mat.filter_shape_ .depth_ == input.shape ().depth_ ,
308406 " invalid filter depth" );
309407
408+ const shape2 eff_filter_shape (
409+ (filter_mat.filter_shape_ .height_ - 1 ) * dilation_rate.height_ + 1 ,
410+ (filter_mat.filter_shape_ .width_ - 1 ) * dilation_rate.width_ + 1 );
411+
310412 const auto conv_cfg = preprocess_convolution (
311- filter_mat. filter_shape_ . without_depth () ,
413+ eff_filter_shape ,
312414 strides, pad_type, input.shape ().height_ , input.shape ().width_ , false );
313415
314416 // The padding step usually (on a VGG19 net) only takes about 1% of the overall runtime.
@@ -321,10 +423,23 @@ namespace internal {
321423 return convolve_accumulative (
322424 conv_cfg.out_height_ , conv_cfg.out_width_ ,
323425 strides.height_ , strides.width_ ,
426+ dilation_rate,
324427 filter_mat,
325428 in_padded);
326429 }
327430
431+ // Backward-compatible overload for callers (e.g., transposed conv, depthwise conv,
432+ // separable pointwise conv) where the filter has already been pre-dilated or
433+ // dilation does not apply.
434+ inline tensor convolve (
435+ const shape2& strides,
436+ const padding& pad_type,
437+ const convolution_filter_matrices& filter_mat,
438+ const tensor& input)
439+ {
440+ return convolve (strides, pad_type, shape2 (1 , 1 ), filter_mat, input);
441+ }
442+
328443 inline tensor convolve_transposed (
329444 const shape2& strides,
330445 const padding& pad_type,
@@ -348,6 +463,7 @@ namespace internal {
348463 return convolve_accumulative (
349464 conv_cfg.out_height_ , conv_cfg.out_width_ ,
350465 1 , 1 ,
466+ shape2 (1 , 1 ),
351467 filter_mat,
352468 in_padded);
353469 }
0 commit comments