• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 Google LLC
2 //
3 // This source code is licensed under the BSD-style license found in the
4 // LICENSE file in the root directory of this source tree.
5 
6 #include <math.h>
7 #include <stddef.h>
8 #include <stdint.h>
9 
10 #include <xnnpack.h>
11 #include <xnnpack/log.h>
12 #include <xnnpack/params.h>
13 #include <xnnpack/subgraph.h>
14 
15 
xnn_define_convolution_2d(xnn_subgraph_t subgraph,uint32_t input_padding_top,uint32_t input_padding_right,uint32_t input_padding_bottom,uint32_t input_padding_left,uint32_t kernel_height,uint32_t kernel_width,uint32_t subsampling_height,uint32_t subsampling_width,uint32_t dilation_height,uint32_t dilation_width,uint32_t groups,size_t group_input_channels,size_t group_output_channels,float output_min,float output_max,uint32_t input_id,uint32_t filter_id,uint32_t bias_id,uint32_t output_id,uint32_t flags)16 enum xnn_status xnn_define_convolution_2d(
17   xnn_subgraph_t subgraph,
18   uint32_t input_padding_top,
19   uint32_t input_padding_right,
20   uint32_t input_padding_bottom,
21   uint32_t input_padding_left,
22   uint32_t kernel_height,
23   uint32_t kernel_width,
24   uint32_t subsampling_height,
25   uint32_t subsampling_width,
26   uint32_t dilation_height,
27   uint32_t dilation_width,
28   uint32_t groups,
29   size_t group_input_channels,
30   size_t group_output_channels,
31   float output_min,
32   float output_max,
33   uint32_t input_id,
34   uint32_t filter_id,
35   uint32_t bias_id,
36   uint32_t output_id,
37   uint32_t flags)
38 {
39   if ((xnn_params.init_flags & XNN_INIT_FLAG_XNNPACK) == 0) {
40     xnn_log_error("failed to define %s operator: XNNPACK is not initialized",
41       xnn_node_type_to_string(xnn_node_type_convolution_2d));
42     return xnn_status_uninitialized;
43   }
44 
45   if (kernel_width == 0 || kernel_height == 0) {
46     xnn_log_error(
47       "failed to define %s operator with %" PRIu32 "x%" PRIu32 " kernel: kernel dimensions must be non-zero",
48       xnn_node_type_to_string(xnn_node_type_convolution_2d), kernel_width, kernel_height);
49     return xnn_status_invalid_parameter;
50   }
51 
52   if (subsampling_width == 0 || subsampling_height == 0) {
53     xnn_log_error(
54       "failed to define %s operator with %" PRIu32 "x%" PRIu32 " subsampling: subsampling dimensions must be non-zero",
55       xnn_node_type_to_string(xnn_node_type_convolution_2d), subsampling_width, subsampling_height);
56     return xnn_status_invalid_parameter;
57   }
58 
59   if (dilation_width == 0 || dilation_height == 0) {
60     xnn_log_error(
61       "failed to define %s operator with %" PRIu32 "x%" PRIu32 " dilation: dilation dimensions must be non-zero",
62       xnn_node_type_to_string(xnn_node_type_convolution_2d), dilation_width, dilation_height);
63     return xnn_status_invalid_parameter;
64   }
65 
66   if (groups == 0) {
67     xnn_log_error(
68       "failed to define %s operator with %" PRIu32 " groups: number of groups must be non-zero",
69       xnn_node_type_to_string(xnn_node_type_convolution_2d), groups);
70     return xnn_status_invalid_parameter;
71   }
72 
73   if (group_input_channels == 0) {
74     xnn_log_error(
75       "failed to define %s operator with %zu input channels per group: number of channels must be non-zero",
76       xnn_node_type_to_string(xnn_node_type_convolution_2d), group_input_channels);
77     return xnn_status_invalid_parameter;
78   }
79 
80   if (group_output_channels == 0) {
81     xnn_log_error(
82       "failed to define %s operator with %zu output channels per group: number of channels must be non-zero",
83       xnn_node_type_to_string(xnn_node_type_convolution_2d), group_output_channels);
84     return xnn_status_invalid_parameter;
85   }
86 
87   if (isnan(output_min)) {
88     xnn_log_error(
89       "failed to define %s operator with NaN output lower bound: lower bound must be non-NaN",
90       xnn_node_type_to_string(xnn_node_type_convolution_2d));
91     return xnn_status_invalid_parameter;
92   }
93 
94   if (isnan(output_max)) {
95     xnn_log_error(
96       "failed to define %s operator with NaN output upper bound: upper bound must be non-NaN",
97       xnn_node_type_to_string(xnn_node_type_convolution_2d));
98     return xnn_status_invalid_parameter;
99   }
100 
101   if (output_min >= output_max) {
102     xnn_log_error(
103       "failed to define %s operator with [%.7g, %.7g] output range: lower bound must be below upper bound",
104       xnn_node_type_to_string(xnn_node_type_convolution_2d), output_min, output_max);
105     return xnn_status_invalid_parameter;
106   }
107 
108   const uint32_t supported_flags = XNN_FLAG_TENSORFLOW_SAME_PADDING;
109   const uint32_t invalid_flags = flags & ~supported_flags;
110   if (invalid_flags != 0) {
111     xnn_log_error(
112       "failed to define %s operator with 0x%08" PRIx32 " flags: invalid flags 0x%08" PRIx32,
113       xnn_node_type_to_string(xnn_node_type_convolution_2d), flags, invalid_flags);
114     return xnn_status_invalid_parameter;
115   }
116 
117   const bool any_padding = (input_padding_left | input_padding_top | input_padding_right | input_padding_bottom) != 0;
118   if ((flags & XNN_FLAG_TENSORFLOW_SAME_PADDING) != 0 && any_padding) {
119     xnn_log_error(
120       "failed to define %s operator with %" PRIu32 "+%" PRIu32 "x%" PRIu32 "+%" PRIu32" padding: "
121       "TensorFlow SAME padding can't be combined with explicit padding specification",
122       xnn_node_type_to_string(xnn_node_type_convolution_2d),
123       input_padding_top, input_padding_left, input_padding_bottom, input_padding_right);
124     return xnn_status_invalid_parameter;
125   }
126 
127   // Convert TensorFlow SAME padding to explicit padding specification whenever possible
128   if ((flags & XNN_FLAG_TENSORFLOW_SAME_PADDING) != 0 && (subsampling_height | subsampling_width) == 1) {
129     flags &= ~XNN_FLAG_TENSORFLOW_SAME_PADDING;
130     const uint32_t padding_height = (kernel_height - 1) * dilation_height;
131     const uint32_t padding_width = (kernel_width - 1) * dilation_width;
132     input_padding_left = padding_width / 2;
133     input_padding_top = padding_height / 2;
134     input_padding_right = padding_width - input_padding_left;
135     input_padding_bottom = padding_height - input_padding_top;
136   }
137 
138   if (input_id >= subgraph->num_values) {
139     xnn_log_error(
140       "failed to define %s operator with input ID #%" PRIu32 ": invalid Value ID",
141       xnn_node_type_to_string(xnn_node_type_convolution_2d), input_id);
142     return xnn_status_invalid_parameter;
143   }
144 
145   if (filter_id >= subgraph->num_values) {
146     xnn_log_error(
147       "failed to define %s operator with filter ID #%" PRIu32 ": invalid Value ID",
148       xnn_node_type_to_string(xnn_node_type_convolution_2d), filter_id);
149     return xnn_status_invalid_parameter;
150   }
151 
152   if (bias_id >= subgraph->num_values) {
153     xnn_log_error(
154       "failed to define %s operator with bias ID #%" PRIu32 ": invalid Value ID",
155       xnn_node_type_to_string(xnn_node_type_convolution_2d), bias_id);
156     return xnn_status_invalid_parameter;
157   }
158 
159   if (output_id >= subgraph->num_values) {
160     xnn_log_error(
161       "failed to define %s operator with output ID #%" PRIu32 ": invalid Value ID",
162       xnn_node_type_to_string(xnn_node_type_convolution_2d), output_id);
163     return xnn_status_invalid_parameter;
164   }
165 
166   struct xnn_node* node = xnn_subgraph_new_node(subgraph);
167   if (node == NULL) {
168     return xnn_status_out_of_memory;
169   }
170 
171   node->type = xnn_node_type_convolution_2d;
172   node->params.convolution_2d.input_padding_top = input_padding_top;
173   node->params.convolution_2d.input_padding_right = input_padding_right;
174   node->params.convolution_2d.input_padding_bottom = input_padding_bottom;
175   node->params.convolution_2d.input_padding_left = input_padding_left;
176   node->params.convolution_2d.kernel_height = kernel_height;
177   node->params.convolution_2d.kernel_width = kernel_width;
178   node->params.convolution_2d.subsampling_height = subsampling_height;
179   node->params.convolution_2d.subsampling_width = subsampling_width;
180   node->params.convolution_2d.dilation_height = dilation_height;
181   node->params.convolution_2d.dilation_width = dilation_width;
182   node->params.convolution_2d.groups = groups;
183   node->params.convolution_2d.group_input_channels = group_input_channels;
184   node->params.convolution_2d.group_output_channels = group_output_channels;
185   node->activation.output_min = output_min;
186   node->activation.output_max = output_max;
187   node->num_inputs = 3;
188   node->inputs[0] = input_id;
189   node->inputs[1] = filter_id;
190   node->inputs[2] = bias_id;
191   node->num_outputs = 1;
192   node->outputs[0] = output_id;
193   node->flags = flags;
194 
195   return xnn_status_success;
196 };
197