Marat Dukhan | 95e8b7a | 2020-06-03 12:46:26 -0700 | [diff] [blame] | 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 | |
| 16 | enum xnn_status xnn_define_depthwise_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 depth_multiplier, |
| 29 | size_t input_channels, |
| 30 | float output_min, |
| 31 | float output_max, |
| 32 | uint32_t input_id, |
| 33 | uint32_t filter_id, |
| 34 | uint32_t bias_id, |
| 35 | uint32_t output_id, |
| 36 | uint32_t flags) |
| 37 | { |
Marat Dukhan | 854fb6b | 2020-06-19 12:33:44 -0700 | [diff] [blame] | 38 | if ((xnn_params.init_flags & XNN_INIT_FLAG_XNNPACK) == 0) { |
Marat Dukhan | 3b59de2 | 2020-06-03 20:15:19 -0700 | [diff] [blame] | 39 | xnn_log_error("failed to define %s operator: XNNPACK is not initialized", |
| 40 | xnn_node_type_to_string(xnn_node_type_depthwise_convolution_2d)); |
Marat Dukhan | 95e8b7a | 2020-06-03 12:46:26 -0700 | [diff] [blame] | 41 | return xnn_status_uninitialized; |
| 42 | } |
| 43 | |
| 44 | if (kernel_width == 0 || kernel_height == 0) { |
| 45 | xnn_log_error( |
Marat Dukhan | 3b59de2 | 2020-06-03 20:15:19 -0700 | [diff] [blame] | 46 | "failed to define %s operator with %" PRIu32 "x%" PRIu32 " kernel: kernel dimensions must be non-zero", |
| 47 | xnn_node_type_to_string(xnn_node_type_depthwise_convolution_2d), kernel_width, kernel_height); |
Marat Dukhan | 95e8b7a | 2020-06-03 12:46:26 -0700 | [diff] [blame] | 48 | return xnn_status_invalid_parameter; |
| 49 | } |
| 50 | |
| 51 | if (subsampling_width == 0 || subsampling_height == 0) { |
| 52 | xnn_log_error( |
Marat Dukhan | 3b59de2 | 2020-06-03 20:15:19 -0700 | [diff] [blame] | 53 | "failed to define %s operator with %" PRIu32 "x%" PRIu32 " subsampling: subsampling dimensions must be non-zero", |
| 54 | xnn_node_type_to_string(xnn_node_type_depthwise_convolution_2d), subsampling_width, subsampling_height); |
Marat Dukhan | 95e8b7a | 2020-06-03 12:46:26 -0700 | [diff] [blame] | 55 | return xnn_status_invalid_parameter; |
| 56 | } |
| 57 | |
| 58 | if (dilation_width == 0 || dilation_height == 0) { |
| 59 | xnn_log_error( |
Marat Dukhan | 3b59de2 | 2020-06-03 20:15:19 -0700 | [diff] [blame] | 60 | "failed to define %s operator with %" PRIu32 "x%" PRIu32 " dilation: dilation dimensions must be non-zero", |
| 61 | xnn_node_type_to_string(xnn_node_type_depthwise_convolution_2d), dilation_width, dilation_height); |
Marat Dukhan | 95e8b7a | 2020-06-03 12:46:26 -0700 | [diff] [blame] | 62 | return xnn_status_invalid_parameter; |
| 63 | } |
| 64 | |
| 65 | if (depth_multiplier == 0) { |
| 66 | xnn_log_error( |
Marat Dukhan | 3b59de2 | 2020-06-03 20:15:19 -0700 | [diff] [blame] | 67 | "failed to define %s operator with %" PRIu32 " depth multiplier: depth multiplier must be non-zero", |
| 68 | xnn_node_type_to_string(xnn_node_type_depthwise_convolution_2d), depth_multiplier); |
Marat Dukhan | 95e8b7a | 2020-06-03 12:46:26 -0700 | [diff] [blame] | 69 | return xnn_status_invalid_parameter; |
| 70 | } |
| 71 | |
| 72 | if (input_channels == 0) { |
| 73 | xnn_log_error( |
Marat Dukhan | 3b59de2 | 2020-06-03 20:15:19 -0700 | [diff] [blame] | 74 | "failed to define %s operator with %zu input channels: number of channels must be non-zero", |
| 75 | xnn_node_type_to_string(xnn_node_type_depthwise_convolution_2d), input_channels); |
Marat Dukhan | 95e8b7a | 2020-06-03 12:46:26 -0700 | [diff] [blame] | 76 | return xnn_status_invalid_parameter; |
| 77 | } |
| 78 | |
| 79 | if (isnan(output_min)) { |
| 80 | xnn_log_error( |
Marat Dukhan | 3b59de2 | 2020-06-03 20:15:19 -0700 | [diff] [blame] | 81 | "failed to define %s operator with NaN output lower bound: lower bound must be non-NaN", |
| 82 | xnn_node_type_to_string(xnn_node_type_depthwise_convolution_2d)); |
Marat Dukhan | 95e8b7a | 2020-06-03 12:46:26 -0700 | [diff] [blame] | 83 | return xnn_status_invalid_parameter; |
| 84 | } |
| 85 | |
| 86 | if (isnan(output_max)) { |
| 87 | xnn_log_error( |
Marat Dukhan | 3b59de2 | 2020-06-03 20:15:19 -0700 | [diff] [blame] | 88 | "failed to define %s operator with NaN output upper bound: upper bound must be non-NaN", |
| 89 | xnn_node_type_to_string(xnn_node_type_depthwise_convolution_2d)); |
Marat Dukhan | 95e8b7a | 2020-06-03 12:46:26 -0700 | [diff] [blame] | 90 | return xnn_status_invalid_parameter; |
| 91 | } |
| 92 | |
| 93 | if (output_min >= output_max) { |
| 94 | xnn_log_error( |
Marat Dukhan | 3b59de2 | 2020-06-03 20:15:19 -0700 | [diff] [blame] | 95 | "failed to define %s operator with [%.7g, %.7g] output range: lower bound must be below upper bound", |
| 96 | xnn_node_type_to_string(xnn_node_type_depthwise_convolution_2d), output_min, output_max); |
Marat Dukhan | 95e8b7a | 2020-06-03 12:46:26 -0700 | [diff] [blame] | 97 | return xnn_status_invalid_parameter; |
| 98 | } |
| 99 | |
Marat Dukhan | a833be3 | 2020-06-17 02:15:09 -0700 | [diff] [blame] | 100 | const uint32_t supported_flags = XNN_FLAG_TENSORFLOW_SAME_PADDING; |
| 101 | const uint32_t invalid_flags = flags & ~supported_flags; |
| 102 | if (invalid_flags != 0) { |
| 103 | xnn_log_error( |
| 104 | "failed to define %s operator with 0x%08" PRIx32 " flags: invalid flags 0x%08" PRIx32, |
| 105 | xnn_node_type_to_string(xnn_node_type_depthwise_convolution_2d), flags, invalid_flags); |
| 106 | return xnn_status_invalid_parameter; |
| 107 | } |
| 108 | |
| 109 | const bool any_padding = (input_padding_left | input_padding_top | input_padding_right | input_padding_bottom) != 0; |
| 110 | if ((flags & XNN_FLAG_TENSORFLOW_SAME_PADDING) != 0 && any_padding) { |
| 111 | xnn_log_error( |
| 112 | "failed to define %s operator with %" PRIu32 "+%" PRIu32 "x%" PRIu32 "+%" PRIu32" padding: " |
| 113 | "TensorFlow SAME padding can't be combined with explicit padding specification", |
| 114 | xnn_node_type_to_string(xnn_node_type_convolution_2d), |
| 115 | input_padding_top, input_padding_left, input_padding_bottom, input_padding_right); |
| 116 | return xnn_status_invalid_parameter; |
| 117 | } |
| 118 | |
| 119 | // Convert TensorFlow SAME padding to explicit padding specification whenever possible |
| 120 | if ((flags & XNN_FLAG_TENSORFLOW_SAME_PADDING) != 0 && (subsampling_height | subsampling_width) == 1) { |
| 121 | flags &= ~XNN_FLAG_TENSORFLOW_SAME_PADDING; |
| 122 | const uint32_t padding_height = (kernel_height - 1) * dilation_height; |
| 123 | const uint32_t padding_width = (kernel_width - 1) * dilation_width; |
| 124 | input_padding_left = padding_width / 2; |
| 125 | input_padding_top = padding_height / 2; |
| 126 | input_padding_right = padding_width - input_padding_left; |
| 127 | input_padding_bottom = padding_height - input_padding_top; |
| 128 | } |
| 129 | |
Marat Dukhan | 95e8b7a | 2020-06-03 12:46:26 -0700 | [diff] [blame] | 130 | if (input_id >= subgraph->num_values) { |
| 131 | xnn_log_error( |
Marat Dukhan | 3b59de2 | 2020-06-03 20:15:19 -0700 | [diff] [blame] | 132 | "failed to define %s operator with input ID #%" PRIu32 ": invalid Value ID", |
| 133 | xnn_node_type_to_string(xnn_node_type_depthwise_convolution_2d), input_id); |
Marat Dukhan | 95e8b7a | 2020-06-03 12:46:26 -0700 | [diff] [blame] | 134 | return xnn_status_invalid_parameter; |
| 135 | } |
| 136 | |
| 137 | if (filter_id >= subgraph->num_values) { |
| 138 | xnn_log_error( |
Marat Dukhan | 3b59de2 | 2020-06-03 20:15:19 -0700 | [diff] [blame] | 139 | "failed to define %s operator with filter ID #%" PRIu32 ": invalid Value ID", |
| 140 | xnn_node_type_to_string(xnn_node_type_depthwise_convolution_2d), filter_id); |
Marat Dukhan | 95e8b7a | 2020-06-03 12:46:26 -0700 | [diff] [blame] | 141 | return xnn_status_invalid_parameter; |
| 142 | } |
| 143 | |
| 144 | if (bias_id >= subgraph->num_values) { |
| 145 | xnn_log_error( |
Marat Dukhan | 3b59de2 | 2020-06-03 20:15:19 -0700 | [diff] [blame] | 146 | "failed to define %s operator with bias ID #%" PRIu32 ": invalid Value ID", |
| 147 | xnn_node_type_to_string(xnn_node_type_depthwise_convolution_2d), bias_id); |
Marat Dukhan | 95e8b7a | 2020-06-03 12:46:26 -0700 | [diff] [blame] | 148 | return xnn_status_invalid_parameter; |
| 149 | } |
| 150 | |
| 151 | if (output_id >= subgraph->num_values) { |
| 152 | xnn_log_error( |
Marat Dukhan | 3b59de2 | 2020-06-03 20:15:19 -0700 | [diff] [blame] | 153 | "failed to define %s operator with output ID #%" PRIu32 ": invalid Value ID", |
| 154 | xnn_node_type_to_string(xnn_node_type_depthwise_convolution_2d), output_id); |
Marat Dukhan | 95e8b7a | 2020-06-03 12:46:26 -0700 | [diff] [blame] | 155 | return xnn_status_invalid_parameter; |
| 156 | } |
| 157 | |
| 158 | struct xnn_node* node = xnn_subgraph_new_node(subgraph); |
| 159 | if (node == NULL) { |
| 160 | return xnn_status_out_of_memory; |
| 161 | } |
| 162 | |
| 163 | node->type = xnn_node_type_depthwise_convolution_2d; |
| 164 | node->params.depthwise_convolution_2d.input_padding_top = input_padding_top; |
| 165 | node->params.depthwise_convolution_2d.input_padding_right = input_padding_right; |
| 166 | node->params.depthwise_convolution_2d.input_padding_bottom = input_padding_bottom; |
| 167 | node->params.depthwise_convolution_2d.input_padding_left = input_padding_left; |
| 168 | node->params.depthwise_convolution_2d.kernel_height = kernel_height; |
| 169 | node->params.depthwise_convolution_2d.kernel_width = kernel_width; |
| 170 | node->params.depthwise_convolution_2d.subsampling_height = subsampling_height; |
| 171 | node->params.depthwise_convolution_2d.subsampling_width = subsampling_width; |
| 172 | node->params.depthwise_convolution_2d.dilation_height = dilation_height; |
| 173 | node->params.depthwise_convolution_2d.dilation_width = dilation_width; |
| 174 | node->params.depthwise_convolution_2d.depth_multiplier = depth_multiplier; |
| 175 | node->params.depthwise_convolution_2d.input_channels = input_channels; |
| 176 | node->activation.output_min = output_min; |
| 177 | node->activation.output_max = output_max; |
| 178 | node->num_inputs = 3; |
| 179 | node->inputs[0] = input_id; |
| 180 | node->inputs[1] = filter_id; |
| 181 | node->inputs[2] = bias_id; |
| 182 | node->num_outputs = 1; |
| 183 | node->outputs[0] = output_id; |
| 184 | node->flags = flags; |
| 185 | |
| 186 | return xnn_status_success; |
| 187 | }; |