Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/luci-interpreter/pal/linux/KernelsToBuild.lst
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ REGISTER_KERNEL(Rsqrt)
REGISTER_KERNEL(Select)
REGISTER_KERNEL(SelectV2)
REGISTER_KERNEL(Shape)
REGISTER_KERNEL(Sign)
REGISTER_KERNEL(Sin)
REGISTER_KERNEL(Slice)
REGISTER_KERNEL(Softmax)
Expand Down
79 changes: 79 additions & 0 deletions compiler/luci-interpreter/src/kernels/Sign.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2026 Samsung Electronics Co., Ltd. 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 "kernels/Sign.h"

#include "kernels/Utils.h"

#include <cmath>

namespace luci_interpreter
{
namespace kernels
{

namespace
{

template <typename T>
inline void CalcSign(const T *input_data, const size_t num_elements, T *output_data)
{
for (size_t i = 0; i < num_elements; ++i)
{
const T x = input_data[i];
if constexpr (std::is_floating_point_v<T>)
{
if (std::isnan(x))
{
output_data[i] = x; // NaN -> NaN
continue;
}
}
output_data[i] = (T(0) < x) - (x < T(0)); // -1/0/1
}
}

} // namespace

Sign::Sign(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {}

void Sign::configure()
{
LUCI_INTERPRETER_CHECK(input()->element_type() == DataType::FLOAT32);
LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
output()->resize(input()->shape());
}

void Sign::execute() const
{
switch (input()->element_type())
{
case DataType::FLOAT32:
evalFloat();
break;
default:
throw std::runtime_error("luci-intp Sign Unsupported type.");
}
}

void Sign::evalFloat() const
{
const int size = tflite::MatchingFlatSize(getTensorShape(input()), getTensorShape(output()));
CalcSign(getTensorData<float>(input()), size, getTensorData<float>(output()));
}

} // namespace kernels
} // namespace luci_interpreter
46 changes: 46 additions & 0 deletions compiler/luci-interpreter/src/kernels/Sign.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2026 Samsung Electronics Co., Ltd. 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 LUCI_INTERPRETER_KERNELS_SIGN_H
#define LUCI_INTERPRETER_KERNELS_SIGN_H

#include "core/Kernel.h"
#include "core/KernelParams.h"

namespace luci_interpreter
{
namespace kernels
{

class Sign : public Kernel
{
public:
Sign(const Tensor *input, Tensor *output);

const Tensor *input() const { return _inputs[0]; }
Tensor *output() const { return _outputs[0]; }

void configure() override;
void execute() const override;

private:
void evalFloat() const;
};

} // namespace kernels
} // namespace luci_interpreter

#endif // LUCI_INTERPRETER_KERNELS_SIGN_H
80 changes: 80 additions & 0 deletions compiler/luci-interpreter/src/kernels/Sign.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (c) 2026 Samsung Electronics Co., Ltd. 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 "kernels/Sign.h"
#include "kernels/TestUtils.h"
#include "luci_interpreter/TestMemoryManager.h"

#include <cmath>

namespace luci_interpreter
{
namespace kernels
{
namespace
{

using namespace testing;

TEST(SignTest, Float)
{
std::unique_ptr<IMemoryManager> memory_manager = std::make_unique<TestMemoryManager>();

// 0, +, -, NaN
Shape input_shape{1, 1, 4};
std::vector<float> input_data{0.0f, 2.0f, -3.0f, std::numeric_limits<float>::quiet_NaN()};

Tensor input_tensor =
makeInputTensor<DataType::FLOAT32>(input_shape, input_data, memory_manager.get());
Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);

Sign kernel(&input_tensor, &output_tensor);
kernel.configure();
memory_manager->allocate_memory(output_tensor);
kernel.execute();

// shape check
std::vector<int32_t> ref_output_shape{1, 1, 4};
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));

// data check
auto out = extractTensorData<float>(output_tensor);

// first 3 are deterministic
std::vector<float> ref_first3{0.0f, 1.0f, -1.0f};
EXPECT_THAT(std::vector<float>(out.begin(), out.begin() + 3), FloatArrayNear(ref_first3));

// NaN should stay NaN
EXPECT_TRUE(std::isnan(out[3]));
}

TEST(SignTest, InvalidDType_NEG)
{
std::unique_ptr<IMemoryManager> memory_manager = std::make_unique<TestMemoryManager>();
Shape input_shape{1, 1, 3};
std::vector<int64_t> input_data{1l, 2l, 3l};

Tensor input_tensor =
makeInputTensor<DataType::S64>(input_shape, input_data, memory_manager.get());
Tensor output_tensor = makeOutputTensor(DataType::S64);

Sign kernel(&input_tensor, &output_tensor);
EXPECT_ANY_THROW(kernel.configure());
}

} // namespace
} // namespace kernels
} // namespace luci_interpreter
36 changes: 36 additions & 0 deletions compiler/luci-interpreter/src/loader/nodes/Sign.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2026 Samsung Electronics Co., Ltd. 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 "Builders.h"

#include "kernels/Sign.h"

namespace luci_interpreter
{

std::unique_ptr<Kernel> build_kernel_CircleSign(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
const auto *node = loco::must_cast<const luci::CircleSign *>(circle_node);
assert(node->arity() == 1);

const Tensor *input = helper.getInputTensor(node->x());
Tensor *output = helper.getOutputTensor(node);

return std::make_unique<kernels::Sign>(input, output);
}

} // namespace luci_interpreter