diff --git a/tensorflow/lite/delegates/gpu/cl/kernels/elementwise.cc b/tensorflow/lite/delegates/gpu/cl/kernels/elementwise.cc index 3203ec3e7fc..a81c8be9ea1 100644 --- a/tensorflow/lite/delegates/gpu/cl/kernels/elementwise.cc +++ b/tensorflow/lite/delegates/gpu/cl/kernels/elementwise.cc @@ -58,6 +58,9 @@ std::string GetOneInputCode(const OperationType& op_type, case OperationType::LOG: result = "$0 = log($0);\n"; break; + case OperationType::NEG: + result = "$0 = -($0);\n"; + break; case OperationType::RSQRT: result = "$0 = rsqrt($0);\n"; break; diff --git a/tensorflow/lite/delegates/gpu/cl/kernels/elementwise_test.cc b/tensorflow/lite/delegates/gpu/cl/kernels/elementwise_test.cc index d883a734214..b6b501bd11b 100644 --- a/tensorflow/lite/delegates/gpu/cl/kernels/elementwise_test.cc +++ b/tensorflow/lite/delegates/gpu/cl/kernels/elementwise_test.cc @@ -208,6 +208,30 @@ TEST_F(OpenCLOperationTest, Log) { } } +TEST_F(OpenCLOperationTest, Neg) { + TensorFloat32 src_tensor; + src_tensor.shape = BHWC(1, 2, 1, 2); + src_tensor.data = {1.0f, -2.0f, 0.0f, 4.0f}; + + for (auto storage : env_.GetSupportedStorages()) { + for (auto precision : env_.GetSupportedPrecisions()) { + const float eps = precision == CalculationsPrecision::F32 ? 1e-6f : 1e-3f; + OperationDef op_def; + op_def.precision = precision; + auto data_type = DeduceDataTypeFromPrecision(precision); + op_def.src_tensors.push_back({data_type, storage, Layout::HWC}); + op_def.dst_tensors.push_back({data_type, storage, Layout::HWC}); + TensorFloat32 dst_tensor; + GPUOperation operation = + CreateElementwiseOneInput(op_def, OperationType::NEG); + ASSERT_OK(ExecuteGPUOperation(src_tensor, creation_context_, &operation, + BHWC(1, 2, 1, 2), &dst_tensor)); + EXPECT_THAT(dst_tensor.data, + Pointwise(FloatNear(eps), {-1.0f, 2.0f, 0.0f, -4.0f})); + } + } +} + TEST_F(OpenCLOperationTest, Rsqrt) { TensorFloat32 src_tensor; src_tensor.shape = BHWC(1, 2, 1, 2); diff --git a/tensorflow/lite/delegates/gpu/cl/selectors/operation_selector.cc b/tensorflow/lite/delegates/gpu/cl/selectors/operation_selector.cc index 1553f35a60e..d259e989dd9 100644 --- a/tensorflow/lite/delegates/gpu/cl/selectors/operation_selector.cc +++ b/tensorflow/lite/delegates/gpu/cl/selectors/operation_selector.cc @@ -336,6 +336,7 @@ absl::Status GPUOperationFromNode(const DeviceInfo& device_info, case OperationType::EXP: case OperationType::HARD_SWISH: case OperationType::LOG: + case OperationType::NEG: case OperationType::RSQRT: case OperationType::SIGMOID: case OperationType::SIN: diff --git a/tensorflow/lite/delegates/gpu/common/model_builder.cc b/tensorflow/lite/delegates/gpu/common/model_builder.cc index 0ab9e96aa7b..598e41a0fdc 100644 --- a/tensorflow/lite/delegates/gpu/common/model_builder.cc +++ b/tensorflow/lite/delegates/gpu/common/model_builder.cc @@ -801,6 +801,7 @@ class ElementwiseOperationParser : public TFLiteOperationParser { case OperationType::ELU: case OperationType::EXP: case OperationType::LOG: + case OperationType::NEG: case OperationType::RSQRT: case OperationType::SIGMOID: case OperationType::SIN: @@ -2574,6 +2575,8 @@ std::unique_ptr NewOperationParser( return std::make_unique(/*mirror_pad=*/true); case kTfLiteBuiltinMul: return std::make_unique(); + case kTfLiteBuiltinNeg: + return std::make_unique(OperationType::NEG); case kTfLiteBuiltinPad: return std::make_unique(/*mirror_pad=*/false); case kTfLiteBuiltinPow: diff --git a/tensorflow/lite/delegates/gpu/common/operations.cc b/tensorflow/lite/delegates/gpu/common/operations.cc index 94e1f0ad5e6..19d7bd919c5 100644 --- a/tensorflow/lite/delegates/gpu/common/operations.cc +++ b/tensorflow/lite/delegates/gpu/common/operations.cc @@ -134,6 +134,8 @@ std::string ToString(enum OperationType op) { return "minimum"; case OperationType::MUL: return "mul"; + case OperationType::NEG: + return "neg"; case OperationType::NOT_EQUAL: return "not_equal"; case OperationType::PAD: @@ -224,6 +226,7 @@ OperationType OperationTypeFromString(const std::string& name) { OperationType::MEAN_STDDEV_NORMALIZATION}, {"minimum", OperationType::MINIMUM}, {"mul", OperationType::MUL}, + {"neg", OperationType::NEG}, {"not_equal", OperationType::NOT_EQUAL}, {"pad", OperationType::PAD}, {"pooling_2d", OperationType::POOLING_2D}, diff --git a/tensorflow/lite/delegates/gpu/common/operations.h b/tensorflow/lite/delegates/gpu/common/operations.h index 968f75e155b..a93f63a02b7 100644 --- a/tensorflow/lite/delegates/gpu/common/operations.h +++ b/tensorflow/lite/delegates/gpu/common/operations.h @@ -63,6 +63,7 @@ enum class OperationType { MEAN_STDDEV_NORMALIZATION, MINIMUM, MUL, + NEG, NOT_EQUAL, PAD, POOLING_2D, diff --git a/tensorflow/lite/delegates/gpu/gl/kernels/elementwise.cc b/tensorflow/lite/delegates/gpu/gl/kernels/elementwise.cc index 5d50fcc0118..42dafe7597e 100644 --- a/tensorflow/lite/delegates/gpu/gl/kernels/elementwise.cc +++ b/tensorflow/lite/delegates/gpu/gl/kernels/elementwise.cc @@ -69,6 +69,9 @@ class ElementwiseOneArgument : public NodeShader { value_0.w = value_0.w > 0.0 ? log(value_0.w) : nan; )"; break; + case OperationType::NEG: + source = "value_0 = -(value_0);"; + break; case OperationType::RSQRT: source = R"( const float nan = normalize(vec4(0, 0, 0, 0)).x; diff --git a/tensorflow/lite/delegates/gpu/gl/kernels/elementwise_test.cc b/tensorflow/lite/delegates/gpu/gl/kernels/elementwise_test.cc index a32a4ea9f76..5ff7bfc9ed7 100644 --- a/tensorflow/lite/delegates/gpu/gl/kernels/elementwise_test.cc +++ b/tensorflow/lite/delegates/gpu/gl/kernels/elementwise_test.cc @@ -129,6 +129,18 @@ TEST(ElementwiseOneArgumentTest, Log) { Pointwise(FloatNear(1e-6), {0.0, 1.14473, 0.0, 0.0})); } +TEST(ElementwiseOneArgumentTest, Neg) { + OperationType op_type = OperationType::NEG; + const BHWC shape(1, 2, 2, 1); + SingleOpModel model({/*type=*/ToString(op_type), /*attributes=*/{}}, + /*inputs=*/{GetTensorRef(0, shape)}, + /*outputs=*/{GetTensorRef(1, shape)}); + ASSERT_TRUE(model.PopulateTensor(0, {1.0, -3.1415926, 0.0, 1.0})); + ASSERT_OK(model.Invoke(*NewElementwiseNodeShader(op_type))); + EXPECT_THAT(model.GetOutput(0), + Pointwise(FloatNear(1e-6), {-1.0, 3.1415926, 0.0, -1.0})); +} + TEST(ElementwiseOneArgumentTest, Rsqrt) { OperationType op_type = OperationType::RSQRT; const BHWC shape(1, 2, 2, 1); diff --git a/tensorflow/lite/delegates/gpu/gl/kernels/registry.cc b/tensorflow/lite/delegates/gpu/gl/kernels/registry.cc index 645e5b6c728..e6cd539500e 100644 --- a/tensorflow/lite/delegates/gpu/gl/kernels/registry.cc +++ b/tensorflow/lite/delegates/gpu/gl/kernels/registry.cc @@ -103,6 +103,7 @@ class Registry : public NodeShader { insert_elementwise_op(Type::EXP); insert_elementwise_op(Type::HARD_SWISH); insert_elementwise_op(Type::LOG); + insert_elementwise_op(Type::NEG); insert_elementwise_op(Type::MAXIMUM); insert_elementwise_op(Type::MINIMUM); insert_elementwise_op(Type::POW); diff --git a/tensorflow/lite/delegates/gpu/metal/api.cc b/tensorflow/lite/delegates/gpu/metal/api.cc index 5773457b642..3359174e9f8 100644 --- a/tensorflow/lite/delegates/gpu/metal/api.cc +++ b/tensorflow/lite/delegates/gpu/metal/api.cc @@ -371,6 +371,7 @@ absl::Status RegisterPrimaryOps(const GraphFloat32& graph, const Node* node, case OperationType::EXP: case OperationType::HARD_SWISH: case OperationType::LOG: + case OperationType::NEG: case OperationType::RSQRT: case OperationType::SIGMOID: case OperationType::SIN: diff --git a/tensorflow/lite/delegates/gpu/metal/kernels/elementwise.cc b/tensorflow/lite/delegates/gpu/metal/kernels/elementwise.cc index 9edfc884638..fb4de84be8e 100644 --- a/tensorflow/lite/delegates/gpu/metal/kernels/elementwise.cc +++ b/tensorflow/lite/delegates/gpu/metal/kernels/elementwise.cc @@ -45,6 +45,7 @@ std::string OneInputFunctor(OperationType op_type, const std::string& value) { "$0.w < FLT(0.0f) ? exp($0.w) - FLT(1.0f) : $0.w)"}, {OperationType::EXP, "exp($0)"}, {OperationType::LOG, "log($0)"}, + {OperationType::NEG, "-($0)"}, {OperationType::SQRT, "sqrt($0)"}, {OperationType::RSQRT, "1.0 / sqrt($0)"}, {OperationType::SQUARE, "$0 * $0"}, diff --git a/tensorflow/lite/delegates/gpu/metal/kernels/elementwise_test.mm b/tensorflow/lite/delegates/gpu/metal/kernels/elementwise_test.mm index 4972fdeb1a9..867ed596ed8 100644 --- a/tensorflow/lite/delegates/gpu/metal/kernels/elementwise_test.mm +++ b/tensorflow/lite/delegates/gpu/metal/kernels/elementwise_test.mm @@ -257,6 +257,19 @@ TensorRef GetTensorRef(int ref, const BHWC& shape) { XCTAssertTrue(status.ok(), @"%s", std::string(status.message()).c_str()); } +- (void)testNeg { + OperationType op_type = OperationType::NEG; + const BHWC shape(1, 2, 2, 1); + SingleOpModel model({/*type=*/ToString(op_type), /*attributes=*/{}}, + /*inputs=*/{GetTensorRef(0, shape)}, + /*outputs=*/{GetTensorRef(1, shape)}); + XCTAssertTrue(model.PopulateTensor(0, {-1.0, 3.1415926, 0.0, 1.0})); + auto status = model.Invoke(); + XCTAssertTrue(status.ok(), @"%s", std::string(status.message()).c_str()); + status = CompareVectors({1.0, -3.1415926, 0.0, -1.0}, model.GetOutput(0), 1e-6f); + XCTAssertTrue(status.ok(), @"%s", std::string(status.message()).c_str()); +} + - (void)testPow { OperationType op_type = OperationType::POW; const BHWC shape(1, 2, 2, 1);