diff --git a/tensorflow/lite/experimental/micro/kernels/BUILD b/tensorflow/lite/experimental/micro/kernels/BUILD index dc2ebe7effb..3023ed0181d 100644 --- a/tensorflow/lite/experimental/micro/kernels/BUILD +++ b/tensorflow/lite/experimental/micro/kernels/BUILD @@ -24,6 +24,7 @@ cc_library( "fully_connected.cc", "logical.cc", "maximum_minimum.cc", + "neg.cc", "pack.cc", "pooling.cc", "prelu.cc", @@ -38,8 +39,8 @@ cc_library( ], copts = tflite_copts(), deps = [ + ":micro_utils", "//tensorflow/lite/c:c_api_internal", - "//tensorflow/lite/experimental/micro/kernels:micro_utils", "//tensorflow/lite/kernels:kernel_util", "//tensorflow/lite/kernels:op_macros", "//tensorflow/lite/kernels:padding", @@ -78,6 +79,7 @@ cc_library( "fully_connected.cc", "logical.cc", "maximum_minimum.cc", + "neg.cc", "pack.cc", "pooling.cc", "portable_optimized/depthwise_conv.cc", @@ -93,8 +95,8 @@ cc_library( ], copts = tflite_copts(), deps = [ + ":micro_utils", "//tensorflow/lite/c:c_api_internal", - "//tensorflow/lite/experimental/micro/kernels:micro_utils", "//tensorflow/lite/kernels:kernel_util", "//tensorflow/lite/kernels:op_macros", "//tensorflow/lite/kernels:padding", @@ -249,6 +251,19 @@ tflite_micro_cc_test( ], ) +tflite_micro_cc_test( + name = "neg_test", + srcs = [ + "neg_test.cc", + ], + deps = [ + ":all_ops_resolver", + "//tensorflow/lite/c:c_api_internal", + "//tensorflow/lite/experimental/micro:micro_framework", + "//tensorflow/lite/experimental/micro/testing:micro_test", + ], +) + tflite_micro_cc_test( name = "maximum_minimum_test", srcs = [ @@ -269,9 +284,9 @@ tflite_micro_cc_test( ], deps = [ ":all_ops_resolver", + ":micro_utils", "//tensorflow/lite/c:c_api_internal", "//tensorflow/lite/experimental/micro:micro_framework", - "//tensorflow/lite/experimental/micro/kernels:micro_utils", "//tensorflow/lite/experimental/micro/testing:micro_test", ], ) diff --git a/tensorflow/lite/experimental/micro/kernels/all_ops_resolver.cc b/tensorflow/lite/experimental/micro/kernels/all_ops_resolver.cc index 2e0a21fe878..7e5a5999c13 100644 --- a/tensorflow/lite/experimental/micro/kernels/all_ops_resolver.cc +++ b/tensorflow/lite/experimental/micro/kernels/all_ops_resolver.cc @@ -51,6 +51,7 @@ TfLiteRegistration* Register_STRIDED_SLICE(); TfLiteRegistration* Register_PACK(); TfLiteRegistration* Register_SPLIT(); TfLiteRegistration* Register_UNPACK(); +TfLiteRegistration* Register_NEG(); AllOpsResolver::AllOpsResolver() { AddBuiltin(BuiltinOperator_DEPTHWISE_CONV_2D, Register_DEPTHWISE_CONV_2D()); @@ -92,6 +93,7 @@ AllOpsResolver::AllOpsResolver() { /* min_version */ 1, /* max_version */ 3); AddBuiltin(BuiltinOperator_UNPACK, Register_UNPACK()); + AddBuiltin(BuiltinOperator_NEG, Register_NEG()); } } // namespace micro diff --git a/tensorflow/lite/experimental/micro/kernels/neg.cc b/tensorflow/lite/experimental/micro/kernels/neg.cc new file mode 100644 index 00000000000..8d87c83e785 --- /dev/null +++ b/tensorflow/lite/experimental/micro/kernels/neg.cc @@ -0,0 +1,58 @@ +/* Copyright 2019 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "tensorflow/lite/kernels/internal/reference/neg.h" + +#include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/kernels/internal/tensor_ctypes.h" +#include "tensorflow/lite/kernels/kernel_util.h" + +namespace tflite { +namespace ops { +namespace micro { +namespace neg { + +constexpr int kInputTensor = 0; +constexpr int kOutputTensor = 0; + +TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { + const TfLiteTensor* input = GetInput(context, node, kInputTensor); + TfLiteTensor* output = GetOutput(context, node, kOutputTensor); + switch (input->type) { + // TODO(wangtz): handle for kTfLiteInt8 + case kTfLiteFloat32: + reference_ops::Negate(GetTensorShape(input), GetTensorData(input), + GetTensorShape(output), + GetTensorData(output)); + break; + default: + context->ReportError( + context, "Neg only currently supports float32, got %d.", input->type); + return kTfLiteError; + } + return kTfLiteOk; +} + +} // namespace neg + +TfLiteRegistration* Register_NEG() { + static TfLiteRegistration r = {/*init=*/nullptr, /*free=*/nullptr, + /*prepare=*/nullptr, neg::Eval}; + return &r; +} + +} // namespace micro +} // namespace ops +} // namespace tflite diff --git a/tensorflow/lite/experimental/micro/kernels/neg_test.cc b/tensorflow/lite/experimental/micro/kernels/neg_test.cc new file mode 100644 index 00000000000..f751049fbc1 --- /dev/null +++ b/tensorflow/lite/experimental/micro/kernels/neg_test.cc @@ -0,0 +1,101 @@ +/* Copyright 2019 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "tensorflow/lite/c/builtin_op_data.h" +#include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/experimental/micro/kernels/all_ops_resolver.h" +#include "tensorflow/lite/experimental/micro/simple_tensor_allocator.h" +#include "tensorflow/lite/experimental/micro/testing/micro_test.h" +#include "tensorflow/lite/experimental/micro/testing/test_utils.h" + +namespace tflite { +namespace testing { +namespace { + +void TestNegFloat(std::initializer_list input_dims_data, + std::initializer_list input_data, + std::initializer_list expected_output_data, + std::initializer_list output_dims_data, + float* output_data) { + TfLiteIntArray* input_dims = IntArrayFromInitializer(input_dims_data); + TfLiteIntArray* output_dims = IntArrayFromInitializer(output_dims_data); + const int output_dims_count = ElementCount(*output_dims); + constexpr int inputs_size = 1; + constexpr int outputs_size = 1; + constexpr int tensors_size = inputs_size + outputs_size; + TfLiteTensor tensors[tensors_size] = { + CreateFloatTensor(input_data, input_dims, "input_tensor"), + CreateFloatTensor(output_data, output_dims, "output_tensor"), + }; + + TfLiteContext context; + PopulateContext(tensors, tensors_size, &context); + ::tflite::ops::micro::AllOpsResolver resolver; + const TfLiteRegistration* registration = + resolver.FindOp(tflite::BuiltinOperator_NEG, 1); + TF_LITE_MICRO_EXPECT_NE(nullptr, registration); + + int inputs_array_data[] = {1, 0}; + TfLiteIntArray* inputs_array = IntArrayFromInts(inputs_array_data); + int outputs_array_data[] = {1, 1}; + TfLiteIntArray* outputs_array = IntArrayFromInts(outputs_array_data); + TfLiteIntArray* temporaries_array = IntArrayFromInitializer({0}); + + TfLiteNode node; + node.inputs = inputs_array; + node.outputs = outputs_array; + node.temporaries = temporaries_array; + node.user_data = nullptr; + node.builtin_data = nullptr; + node.custom_initial_data = nullptr; + node.custom_initial_data_size = 0; + node.delegate = nullptr; + + TF_LITE_MICRO_EXPECT_NE(nullptr, registration->invoke); + TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, registration->invoke(&context, &node)); + + TF_LITE_MICRO_EXPECT_EQ(expected_output_data.begin()[0], output_data[0]); + for (int i = 0; i < output_dims_count; ++i) { + TF_LITE_MICRO_EXPECT_EQ(expected_output_data.begin()[i], output_data[i]); + } +} + +} // namespace +} // namespace testing +} // namespace tflite + +TF_LITE_MICRO_TESTS_BEGIN + +TF_LITE_MICRO_TEST(NegOpSingleFloat) { + float output_data[2]; + tflite::testing::TestNegFloat(/*input_dims_data=*/{1, 2}, + /*input_data=*/{8.5f, 0.0f}, + /*expected_output_data=*/{-8.5f, 0.0f}, + /*output_dims_data*/ {1, 2}, + /*output_data=*/output_data); +} + +TF_LITE_MICRO_TEST(NegOpFloat) { + float output_data[6]; + tflite::testing::TestNegFloat(/*input_dims_data=*/{2, 2, 3}, + /*input_data=*/ + {-2.0f, -1.0f, 0.f, 1.0f, 2.0f, 3.0f}, + /*expected_output_data=*/ + {2.0f, 1.0f, -0.f, -1.0f, -2.0f, -3.0f}, + /*output_dims_data=*/{2, 2, 3}, + /*output_data=*/output_data); +} + +TF_LITE_MICRO_TESTS_END diff --git a/tensorflow/lite/experimental/micro/tools/make/Makefile b/tensorflow/lite/experimental/micro/tools/make/Makefile index e947efdf074..3d52deff8b4 100644 --- a/tensorflow/lite/experimental/micro/tools/make/Makefile +++ b/tensorflow/lite/experimental/micro/tools/make/Makefile @@ -123,6 +123,7 @@ tensorflow/lite/kernels/internal/reference/round.h \ tensorflow/lite/kernels/internal/reference/softmax.h \ tensorflow/lite/kernels/internal/reference/strided_slice.h \ tensorflow/lite/kernels/internal/reference/arg_min_max.h \ +tensorflow/lite/kernels/internal/reference/neg.h \ tensorflow/lite/kernels/internal/round.h \ tensorflow/lite/kernels/internal/strided_slice_logic.h \ tensorflow/lite/kernels/internal/tensor_ctypes.h \ diff --git a/tensorflow/lite/kernels/internal/BUILD b/tensorflow/lite/kernels/internal/BUILD index 5acf034ed0f..372c0430a8b 100644 --- a/tensorflow/lite/kernels/internal/BUILD +++ b/tensorflow/lite/kernels/internal/BUILD @@ -382,6 +382,7 @@ cc_library( "reference/integer_ops/softmax.h", "reference/integer_ops/tanh.h", "reference/maximum_minimum.h", + "reference/neg.h", "reference/pooling.h", "reference/prelu.h", "reference/process_broadcast_shapes.h", @@ -434,6 +435,7 @@ cc_library( "reference/fully_connected.h", "reference/legacy_reference_ops.h", "reference/maximum_minimum.h", + "reference/neg.h", "reference/pooling.h", "reference/prelu.h", "reference/process_broadcast_shapes.h", diff --git a/tensorflow/lite/kernels/internal/reference/neg.h b/tensorflow/lite/kernels/internal/reference/neg.h new file mode 100644 index 00000000000..e127883f9ad --- /dev/null +++ b/tensorflow/lite/kernels/internal/reference/neg.h @@ -0,0 +1,37 @@ +/* Copyright 2019 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_NEG_H_ +#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_NEG_H_ + +#include "tensorflow/lite/kernels/internal/types.h" + +namespace tflite { + +namespace reference_ops { + +template +inline void Negate(const RuntimeShape& input_shape, const T* input_data, + const RuntimeShape& output_shape, T* output_data) { + const int flat_size = MatchingFlatSize(input_shape, output_shape); + + for (int i = 0; i < flat_size; ++i) { + output_data[i] = -input_data[i]; + } +} + +} // namespace reference_ops +} // namespace tflite + +#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_NEG_H_ diff --git a/tensorflow/lite/kernels/internal/reference/reference_ops.h b/tensorflow/lite/kernels/internal/reference/reference_ops.h index b728bec4541..5f2e8331f59 100644 --- a/tensorflow/lite/kernels/internal/reference/reference_ops.h +++ b/tensorflow/lite/kernels/internal/reference/reference_ops.h @@ -41,6 +41,7 @@ limitations under the License. #include "tensorflow/lite/kernels/internal/reference/floor.h" #include "tensorflow/lite/kernels/internal/reference/fully_connected.h" #include "tensorflow/lite/kernels/internal/reference/maximum_minimum.h" +#include "tensorflow/lite/kernels/internal/reference/neg.h" #include "tensorflow/lite/kernels/internal/reference/pooling.h" #include "tensorflow/lite/kernels/internal/reference/prelu.h" #include "tensorflow/lite/kernels/internal/reference/process_broadcast_shapes.h" diff --git a/tensorflow/lite/kernels/neg.cc b/tensorflow/lite/kernels/neg.cc index e9a1aa23254..1c38f8f3ca4 100644 --- a/tensorflow/lite/kernels/neg.cc +++ b/tensorflow/lite/kernels/neg.cc @@ -13,7 +13,11 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ +#include "tensorflow/lite/kernels/internal/reference/neg.h" + #include "tensorflow/lite/c/c_api_internal.h" +#include "tensorflow/lite/kernels/internal/optimized/optimized_ops.h" +#include "tensorflow/lite/kernels/internal/tensor.h" #include "tensorflow/lite/kernels/kernel_util.h" namespace tflite { @@ -35,27 +39,24 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { TfLiteIntArrayCopy(input->dims)); } -template -void Negate(const T* in_data, int num_elements, T* out_data) { - // TODO(alanchiao): add vectorized version. - for (int i = 0; i < num_elements; ++i) { - out_data[i] = -in_data[i]; - } -} - TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { const TfLiteTensor* input = GetInput(context, node, kInputTensor); TfLiteTensor* output = GetOutput(context, node, kOutputTensor); - const int num_elements = NumElements(input); switch (input->type) { case kTfLiteInt64: - Negate(input->data.i64, num_elements, output->data.i64); + reference_ops::Negate( + GetTensorShape(input), GetTensorData(input), + GetTensorShape(output), GetTensorData(output)); break; case kTfLiteInt32: - Negate(input->data.i32, num_elements, output->data.i32); + reference_ops::Negate( + GetTensorShape(input), GetTensorData(input), + GetTensorShape(output), GetTensorData(output)); break; case kTfLiteFloat32: - Negate(input->data.f, num_elements, output->data.f); + reference_ops::Negate(GetTensorShape(input), GetTensorData(input), + GetTensorShape(output), + GetTensorData(output)); break; default: context->ReportError(