@@ -38,7 +38,8 @@ namespace internal {
3838 padding pad_type,
3939 std::size_t input_shape_size_d4,
4040 std::size_t input_shape_height,
41- std::size_t input_shape_width)
41+ std::size_t input_shape_width,
42+ bool transposed = false )
4243 {
4344 const int filter_size_d4 = static_cast <int >(filter_shape.size_dim_4_ );
4445 const int filter_height = static_cast <int >(filter_shape.height_ );
@@ -59,9 +60,15 @@ namespace internal {
5960 out_height = fplus::ceil (static_cast <float >(in_height) / static_cast <float >(strides_y) - 0.001 );
6061 out_width = fplus::ceil (static_cast <float >(in_width) / static_cast <float >(strides_x) - 0.001 );
6162 } else {
62- out_size_d4 = fplus::ceil (static_cast <float >(in_size_d4 - filter_size_d4 + 1 ) / static_cast <float >(strides_d4) - 0.001 );
63- out_height = fplus::ceil (static_cast <float >(in_height - filter_height + 1 ) / static_cast <float >(strides_y) - 0.001 );
64- out_width = fplus::ceil (static_cast <float >(in_width - filter_width + 1 ) / static_cast <float >(strides_x) - 0.001 );
63+ if (transposed) {
64+ out_size_d4 = fplus::ceil (static_cast <float >(in_size_d4 + filter_size_d4 - 1 ) / static_cast <float >(strides_d4) - 0.001 );
65+ out_height = fplus::ceil (static_cast <float >(in_height + filter_height - 1 ) / static_cast <float >(strides_y) - 0.001 );
66+ out_width = fplus::ceil (static_cast <float >(in_width + filter_width - 1 ) / static_cast <float >(strides_x) - 0.001 );
67+ } else {
68+ out_size_d4 = fplus::ceil (static_cast <float >(in_size_d4 - filter_size_d4 + 1 ) / static_cast <float >(strides_d4) - 0.001 );
69+ out_height = fplus::ceil (static_cast <float >(in_height - filter_height + 1 ) / static_cast <float >(strides_y) - 0.001 );
70+ out_width = fplus::ceil (static_cast <float >(in_width - filter_width + 1 ) / static_cast <float >(strides_x) - 0.001 );
71+ }
6572 }
6673
6774 int pad_front = 0 ;
@@ -71,6 +78,15 @@ namespace internal {
7178 int pad_left = 0 ;
7279 int pad_right = 0 ;
7380
81+ if (transposed) {
82+ pad_front = filter_size_d4 - 1 ;
83+ pad_back = filter_size_d4 - 1 ;
84+ pad_top = filter_height - 1 ;
85+ pad_bottom = filter_height - 1 ;
86+ pad_left = filter_width - 1 ;
87+ pad_right = filter_width - 1 ;
88+ }
89+
7490 if (pad_type == padding::same) {
7591 int pad_along_d4 = 0 ;
7692 int pad_along_height = 0 ;
@@ -125,28 +141,35 @@ namespace internal {
125141 tensor filter_mats_;
126142 };
127143
128- inline tensor dilate_tensor_3d (const shape3& dilation_rate, const tensor& in)
144+ inline tensor dilate_tensor_3d (const shape3& dilation_rate, const tensor& in,
145+ bool trailing_zeros = false )
129146 {
130147 if (dilation_rate == shape3 (1 , 1 , 1 )) {
131148 return in;
132149 }
133150 assertion (in.shape ().rank () == 4 , " Invalid rank for 3d dilation" );
134151
135152 const auto in_shape = in.shape ();
153+ const std::size_t expansion_d4 = trailing_zeros ? (dilation_rate.size_dim_4_ - 1 ) : 0 ;
154+ const std::size_t expansion_y = trailing_zeros ? (dilation_rate.height_ - 1 ) : 0 ;
155+ const std::size_t expansion_x = trailing_zeros ? (dilation_rate.width_ - 1 ) : 0 ;
136156 const tensor_shape dilated_shape (
137- (in_shape.size_dim_4_ - 1 ) * dilation_rate.size_dim_4_ + 1 ,
138- (in_shape.height_ - 1 ) * dilation_rate.height_ + 1 ,
139- (in_shape.width_ - 1 ) * dilation_rate.width_ + 1 ,
157+ (in_shape.size_dim_4_ - 1 ) * dilation_rate.size_dim_4_ + 1 + expansion_d4 ,
158+ (in_shape.height_ - 1 ) * dilation_rate.height_ + 1 + expansion_y ,
159+ (in_shape.width_ - 1 ) * dilation_rate.width_ + 1 + expansion_x ,
140160 in_shape.depth_ );
161+ const std::size_t offset_d4 = expansion_d4 - expansion_d4 / 2 ;
162+ const std::size_t offset_y = expansion_y - expansion_y / 2 ;
163+ const std::size_t offset_x = expansion_x - expansion_x / 2 ;
141164 tensor result (dilated_shape, static_cast <float_type>(0 ));
142165 for (std::size_t d4 = 0 ; d4 < in_shape.size_dim_4_ ; ++d4) {
143166 for (std::size_t y = 0 ; y < in_shape.height_ ; ++y) {
144167 for (std::size_t x = 0 ; x < in_shape.width_ ; ++x) {
145168 for (std::size_t z = 0 ; z < in_shape.depth_ ; ++z) {
146169 result.set_ignore_rank (tensor_pos (
147- d4 * dilation_rate.size_dim_4_ ,
148- y * dilation_rate.height_ ,
149- x * dilation_rate.width_ ,
170+ d4 * dilation_rate.size_dim_4_ + offset_d4 ,
171+ y * dilation_rate.height_ + offset_y ,
172+ x * dilation_rate.width_ + offset_x ,
150173 z),
151174 in.get_ignore_rank (tensor_pos (d4, y, x, z)));
152175 }
@@ -156,6 +179,16 @@ namespace internal {
156179 return result;
157180 }
158181
182+ inline tensor reverse_size_dim_4_dimension (const tensor& in)
183+ {
184+ tensor out = tensor (in.shape (), static_cast <float_type>(0 ));
185+ loop_over_all_dims (in.shape (), [&in, &out](std::size_t dim5, std::size_t dim4, std::size_t y, std::size_t x, std::size_t z) {
186+ out.set_ignore_rank (tensor_pos (dim5, in.shape ().size_dim_4_ - dim4 - 1 , y, x, z),
187+ in.get_ignore_rank (tensor_pos (dim5, dim4, y, x, z)));
188+ });
189+ return out;
190+ }
191+
159192 inline filter dilate_filter_3d (const shape3& dilation_rate, const filter& undilated)
160193 {
161194 return filter (dilate_tensor_3d (dilation_rate, undilated.get_tensor ()),
@@ -165,7 +198,8 @@ namespace internal {
165198 inline filter_vec generate_filters_3d (
166199 const shape3& dilation_rate,
167200 const tensor_shape& filter_shape, std::size_t k,
168- const float_vec& weights, const float_vec& bias)
201+ const float_vec& weights, const float_vec& bias,
202+ bool transpose = false )
169203 {
170204 filter_vec filters (k, filter (tensor (filter_shape, 0 ), 0 ));
171205
@@ -186,6 +220,11 @@ namespace internal {
186220 for (auto & filt : filters) {
187221 filt.set_params (*it_filter_val, *it_filter_bias);
188222 filt = dilate_filter_3d (dilation_rate, filt);
223+ if (transpose) {
224+ filt = filter (reverse_size_dim_4_dimension (filt.get_tensor ()), filt.get_bias ());
225+ filt = filter (reverse_height_dimension (filt.get_tensor ()), filt.get_bias ());
226+ filt = filter (reverse_width_dimension (filt.get_tensor ()), filt.get_bias ());
227+ }
189228 ++it_filter_val;
190229 ++it_filter_bias;
191230 }
@@ -276,6 +315,73 @@ namespace internal {
276315 Eigen::OuterStride<>(static_cast <EigenIndex>(f_depth * strides_x)));
277316 }
278317
318+ // Special version for convolution with strides 1×1×1.
319+ // Uses fewer but larger GEMMs by collapsing the spatial output loops into one big input mapping.
320+ inline tensor convolve_accumulative_s1x1x1_3d (
321+ std::size_t out_size_d4,
322+ std::size_t out_height,
323+ std::size_t out_width,
324+ const convolution3d_filter_matrices& filter_mat,
325+ const tensor& in)
326+ {
327+ const tensor& filter_mats = filter_mat.filter_mats_ ;
328+ const auto f_size_d4 = filter_mat.filter_shape_ .size_dim_4_ ;
329+ const auto f_height = filter_mat.filter_shape_ .height_ ;
330+ const auto f_width = filter_mat.filter_shape_ .width_ ;
331+ const auto f_depth = filter_mat.filter_shape_ .depth_ ;
332+ const auto out_depth = filter_mat.filter_count_ ;
333+
334+ assertion (f_depth == in.shape ().depth_ , " filter depth does not match input" );
335+ assertion (out_height == (in.shape ().height_ - f_height) + 1 , " output height does not match" );
336+ assertion (out_width == (in.shape ().width_ - f_width) + 1 , " output width does not match" );
337+ assertion (out_size_d4 == (in.shape ().size_dim_4_ - f_size_d4) + 1 , " output d4 size does not match" );
338+ assertion (out_depth == filter_mat.biases_ .size (), " invalid bias count" );
339+
340+ tensor output = init_conv_output_tensor_3d (out_size_d4, out_height, out_width, out_depth, in.shape ().rank (), filter_mat);
341+
342+ const std::size_t out_height_temp = out_height + f_height - 1 ;
343+ const std::size_t out_width_temp = out_width + f_width - 1 ;
344+ tensor output_temp (tensor_shape_with_changed_rank (
345+ tensor_shape (out_size_d4, out_height_temp, out_width_temp, out_depth),
346+ in.shape ().rank ()),
347+ static_cast <float_type>(0 ));
348+
349+ const auto mapping_width = (out_size_d4 - 1 ) * out_height_temp * out_width_temp
350+ + (out_height - 1 ) * out_width_temp
351+ + out_width;
352+
353+ for (std::size_t d4_filt = 0 ; d4_filt < f_size_d4; ++d4_filt) {
354+ for (std::size_t y_filt = 0 ; y_filt < f_height; ++y_filt) {
355+ const Eigen::Map<ColMajorMatrixXf, Eigen::Unaligned>
356+ filter (const_cast <float_type*>(&filter_mats.get_ref_ignore_rank (tensor_pos (d4_filt, y_filt, 0 , 0 , 0 ))),
357+ static_cast <EigenIndex>(out_depth),
358+ static_cast <EigenIndex>(f_width * f_depth));
359+
360+ const auto input = get_im2col_mapping_3d (in, f_width, f_depth, 1 , mapping_width, 0 , 0 , d4_filt, y_filt);
361+
362+ Eigen::Map<ColMajorMatrixXf, Eigen::Unaligned>
363+ output_temp_map (&output_temp.get_ref_ignore_rank (tensor_pos (0 , 0 , 0 , 0 , 0 )),
364+ static_cast <EigenIndex>(out_depth),
365+ static_cast <EigenIndex>(mapping_width));
366+
367+ output_temp_map.noalias () += filter * input;
368+ }
369+ }
370+
371+ for (std::size_t d4_out = 0 ; d4_out < out_size_d4; ++d4_out) {
372+ for (std::size_t y_out = 0 ; y_out < out_height; ++y_out) {
373+ for (std::size_t x_out = 0 ; x_out < out_width; ++x_out) {
374+ for (std::size_t z_out = 0 ; z_out < out_depth; ++z_out) {
375+ output.get_ref_ignore_rank (tensor_pos (0 , d4_out, y_out, x_out, z_out))
376+ += output_temp.get_ref_ignore_rank (tensor_pos (0 , d4_out, y_out, x_out, z_out));
377+ }
378+ }
379+ }
380+ }
381+
382+ return output;
383+ }
384+
279385 inline tensor convolve_accumulative_3d (
280386 std::size_t out_size_d4,
281387 std::size_t out_height,
@@ -299,6 +405,10 @@ namespace internal {
299405 assertion (out_width == (in.shape ().width_ - f_width) / strides_x + 1 , " output width does not match" );
300406 assertion (out_depth == filter_mat.biases_ .size (), " invalid bias count" );
301407
408+ if (strides_d4 == 1 && strides_y == 1 && strides_x == 1 ) {
409+ return convolve_accumulative_s1x1x1_3d (out_size_d4, out_height, out_width, filter_mat, in);
410+ }
411+
302412 tensor output = init_conv_output_tensor_3d (out_size_d4, out_height, out_width, out_depth, in.shape ().rank (), filter_mat);
303413
304414 for (std::size_t d4_filt = 0 ; d4_filt < f_size_d4; ++d4_filt) {
@@ -358,5 +468,42 @@ namespace internal {
358468 in_padded);
359469 }
360470
471+ inline tensor convolve_transposed_3d (
472+ const shape3& strides,
473+ const padding& pad_type,
474+ const convolution3d_filter_matrices& filter_mat,
475+ const tensor& input)
476+ {
477+ assertion (filter_mat.filter_shape_ .depth_ == input.shape ().depth_ ,
478+ " invalid filter depth" );
479+
480+ const auto input_dilated = dilate_tensor_3d (strides, input, pad_type == padding::same);
481+
482+ const shape3 filter_spatial_shape (
483+ filter_mat.filter_shape_ .size_dim_4_ ,
484+ filter_mat.filter_shape_ .height_ ,
485+ filter_mat.filter_shape_ .width_ );
486+
487+ const auto conv_cfg = preprocess_convolution_3d (
488+ filter_spatial_shape,
489+ shape3 (1 , 1 , 1 ), pad_type,
490+ input_dilated.shape ().size_dim_4_ ,
491+ input_dilated.shape ().height_ ,
492+ input_dilated.shape ().width_ ,
493+ true );
494+
495+ const auto in_padded = pad_tensor (0 ,
496+ conv_cfg.pad_front_ , conv_cfg.pad_back_ ,
497+ conv_cfg.pad_top_ , conv_cfg.pad_bottom_ ,
498+ conv_cfg.pad_left_ , conv_cfg.pad_right_ ,
499+ input_dilated);
500+
501+ return convolve_accumulative_3d (
502+ conv_cfg.out_size_d4_ , conv_cfg.out_height_ , conv_cfg.out_width_ ,
503+ 1 , 1 , 1 ,
504+ filter_mat,
505+ in_padded);
506+ }
507+
361508}
362509}
0 commit comments