diff --git a/include/aidge/aidge.hpp b/include/aidge/aidge.hpp index 32e96ac2886c4956888f0657dbcbced00d76ee78..6088cf31c91cdd1b939bb4508850a9d0f798e5d2 100644 --- a/include/aidge/aidge.hpp +++ b/include/aidge/aidge.hpp @@ -52,6 +52,7 @@ #include "aidge/operator/GenericOperator.hpp" #include "aidge/operator/GlobalAveragePooling.hpp" #include "aidge/operator/GridSample.hpp" +#include "aidge/operator/Heaviside.hpp" #include "aidge/operator/MatMul.hpp" #include "aidge/operator/MaxPooling.hpp" #include "aidge/operator/MetaOperator.hpp" diff --git a/include/aidge/operator/Heaviside.hpp b/include/aidge/operator/Heaviside.hpp new file mode 100644 index 0000000000000000000000000000000000000000..65c7f341a1c5aa12eeada2165e459cc1c933e327 --- /dev/null +++ b/include/aidge/operator/Heaviside.hpp @@ -0,0 +1,107 @@ +/******************************************************************************** + * Copyright (c) 2024 CEA-List + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + ********************************************************************************/ + +#ifndef AIDGE_CORE_OPERATOR_HEAVISIDE_H_ +#define AIDGE_CORE_OPERATOR_HEAVISIDE_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "aidge/backend/OperatorImpl.hpp" +#include "aidge/graph/Node.hpp" +#include "aidge/operator/Operator.hpp" +#include "aidge/operator/OperatorTensor.hpp" +#include "aidge/utils/Registrar.hpp" +#include "aidge/utils/StaticAttributes.hpp" +#include "aidge/utils/Types.h" + +namespace Aidge { +enum class HeavisideAttr { + /** + * @brief The value used in the output tensor when the input is 0. + */ + Value +}; + +class Heaviside_Op + : public OperatorTensor, + public Registrable< + Heaviside_Op, + std::string, + std::function<std::shared_ptr<OperatorImpl>(const Heaviside_Op &)>> { + private: + using Attributes_ = StaticAttributes<HeavisideAttr, float>; + template <HeavisideAttr e> + using attr = typename Attributes_::template attr<e>; + const std::shared_ptr<Attributes_> mAttributes; + + public: + static const std::string Type; + + /* + * Compute the Heaviside step function for each element of the first input. + * Heaviside step function is defined as : + * + * \f[ + * heaviside(input, values) = \begin{cases} + * 0 & \text{if input } < 0 \\ + * values & \text{if input } = 0 \\ + * 1 & \text{if input } > 0 + * \end{cases} + * \f] + * */ + Heaviside_Op(float value); + + Heaviside_Op(const Heaviside_Op &op); + + std::shared_ptr<Operator> clone() const override; + + void setBackend(const std::string &name, DeviceIdx_t device = 0) override; + + std::set<std::string> getAvailableBackends() const override; + + static const std::vector<std::string> getInputsName() { + return {"data_input", "data_values"}; + } + + static const std::vector<std::string> getOutputsName() { + return {"output"}; + } + + inline std::shared_ptr<Attributes> attributes() const override { + return mAttributes; + } + inline float &value() const { + return mAttributes->template getAttr<HeavisideAttr::Value>(); + } +}; + +/** + * @brief Create a Heaviside node. + * + * Initializes a Heaviside node that computes the Heaviside step function for each element + * of the input tensor, using the specified value for inputs equal to zero. + * + * @param value The value used in the output tensor when the input is 0. + * @param name Optional. The name of the node. + * + * @return A shared pointer to a Node representing the Heaviside operation. + */ +std::shared_ptr<Node> Heaviside(float value, const std::string &name = ""); +} // namespace Aidge + +namespace { +template <> +const char *const EnumStrings<Aidge::HeavisideAttr>::data[] = {"value"}; +} + +#endif /* AIDGE_CORE_OPERATOR_HEAVISIDE_H_ */ diff --git a/python_binding/operator/pybind_Heaviside.cpp b/python_binding/operator/pybind_Heaviside.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cbc2502aac018927c544a57f343a6305ee2bd86f --- /dev/null +++ b/python_binding/operator/pybind_Heaviside.cpp @@ -0,0 +1,56 @@ +/******************************************************************************** + * Copyright (c) 2024 CEA-List + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + ********************************************************************************/ + +#include <pybind11/pybind11.h> + +#include "aidge/operator/Heaviside.hpp" +#include "aidge/operator/OperatorTensor.hpp" + +namespace py = pybind11; + +namespace Aidge { + +void init_Heaviside(py::module &m) { + py::class_<Heaviside_Op, std::shared_ptr<Heaviside_Op>, OperatorTensor>( + m, + "HeavisideOp", + py::multiple_inheritance(), + R"mydelimiter( + Initialize an Heaviside node. This node will compute a heaviside step function + on each element of the input tensor. + heaviside(input, values) = { 0 if input < 0 + { values if input == 0 + { 1 if input > 0 + + :param value : The value use for the output tensor when input is 0. + :type value : float + :param name : Name of the node. + )mydelimiter") + .def(py::init<float>(), py::arg("value")) + .def_static("get_inputs_name", &Heaviside_Op::getInputsName) + .def_static("get_outputs_name", &Heaviside_Op::getOutputsName) + .def_readonly_static("Type", &Heaviside_Op::Type); + + declare_registrable<Heaviside_Op>(m, "HeavisideOp"); + m.def("Heaviside", &Heaviside, py::arg("value"), py::arg("name") = "", + R"mydelimiter( + Initialize an Heaviside node. This node will compute a heaviside step function + on each element of the input tensor. + heaviside(input, values) = { 0 if input < 0 + { values if input == 0 + { 1 if input > 0 + + :param value : The value use for the output tensor when input is 0. + :type value : float + :param name : Name of the node. + )mydelimiter"); +} +} // namespace Aidge diff --git a/python_binding/pybind_core.cpp b/python_binding/pybind_core.cpp index 2730f8e0b2ebb048c88bf8af6a80f906dcea499e..02f4b732c39ef5cbc1755eb314d32c25c96d01fd 100644 --- a/python_binding/pybind_core.cpp +++ b/python_binding/pybind_core.cpp @@ -48,6 +48,7 @@ void init_Gather(py::module&); void init_GenericOperator(py::module&); void init_GlobalAveragePooling(py::module&); void init_GridSample(py::module&); +void init_Heaviside(py::module&); void init_Identity(py::module&); void init_LeakyReLU(py::module&); void init_MatMul(py::module&); @@ -137,6 +138,7 @@ void init_Aidge(py::module& m) { init_GenericOperator(m); init_GlobalAveragePooling(m); init_GridSample(m); + init_Heaviside(m); init_Identity(m); init_LeakyReLU(m); init_MatMul(m); diff --git a/src/operator/Heaviside.cpp b/src/operator/Heaviside.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9ecb3b436d8312ef479d6bc0592cfe372235fa25 --- /dev/null +++ b/src/operator/Heaviside.cpp @@ -0,0 +1,64 @@ +/******************************************************************************** + * Copyright (c) 2024 CEA-List + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + ********************************************************************************/ + +#include <cstddef> // std::size_t +#include <memory> +#include <stdexcept> // std::runtime_error +#include <string> +#include <vector> + +#include "aidge/backend/OperatorImpl.hpp" +#include "aidge/data/Tensor.hpp" +#include "aidge/operator/Heaviside.hpp" +#include "aidge/utils/ErrorHandling.hpp" +#include "aidge/utils/Types.h" + +namespace Aidge { +// ----------------------------------------------------------- Heaviside_Op +// class + +const std::string Heaviside_Op::Type = "Heaviside"; + +Heaviside_Op::Heaviside_Op(float value) + : OperatorTensor(Type, {InputCategory::Data}, 1), + mAttributes( + std::make_shared<Attributes_>(attr<HeavisideAttr::Value>(value))) {} + +Heaviside_Op::Heaviside_Op(const Heaviside_Op &op) + : OperatorTensor(op), mAttributes(op.mAttributes) { + if (op.mImpl) { + SET_IMPL_MACRO(Heaviside_Op, *this, op.backend()); + } else { + mImpl = nullptr; + } +} + +std::shared_ptr<Aidge::Operator> Aidge::Heaviside_Op::clone() const { + return std::make_shared<Heaviside_Op>(*this); +} + +void Heaviside_Op::setBackend(const std::string &name, DeviceIdx_t device) { + SET_IMPL_MACRO(Heaviside_Op, *this, name); + mOutputs[0]->setBackend(name, device); +} + +std::set<std::string> Aidge::Heaviside_Op::getAvailableBackends() const { + return Registrar<Heaviside_Op>::getKeys(); +} + +// --------------------------------------------------------------- Free +// functions + +NodePtr Heaviside(float value, const std::string &name) { + return std::make_shared<Node>(std::make_shared<Heaviside_Op>(value), name); +} + +} // namespace Aidge diff --git a/unit_tests/operator/Test_Heaviside_Op.cpp b/unit_tests/operator/Test_Heaviside_Op.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d53268d1213730dc6202193d26b0531e781518f7 --- /dev/null +++ b/unit_tests/operator/Test_Heaviside_Op.cpp @@ -0,0 +1,70 @@ +/******************************************************************************** + * Copyright (c) 2024 CEA-List + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + ********************************************************************************/ + +#include <catch2/catch_test_macros.hpp> +#include <catch2/generators/catch_generators_random.hpp> +#include <cstddef> // std::size_t +#include <memory> +#include <random> // std::mt19937, std::uniform_int_distribution +#include <vector> + +#include "aidge/data/Tensor.hpp" +#include "aidge/operator/Heaviside.hpp" +#include "aidge/operator/OperatorTensor.hpp" + +namespace Aidge { + +TEST_CASE("[core/operator] Heaviside_Op(forwardDims)", + "[Heaviside][forwardDims]") { + + constexpr std::uint16_t NBTRIALS = 10; + + // Create a random number generator + auto rd = Catch::Generators::Detail::getSeed; + std::mt19937 gen(rd()); + std::uniform_int_distribution<std::size_t> dimsDist(1, 10); + std::uniform_int_distribution<std::size_t> nbDimsDist(1, 5); + + // Create Heaviside Operator + std::shared_ptr<Node> myHeaviside = Heaviside(0.5); + auto op = + std::static_pointer_cast<OperatorTensor>(myHeaviside->getOperator()); + + // input_0 + std::shared_ptr<Tensor> T0 = std::make_shared<Tensor>(); + op->associateInput(0, T0); + + SECTION("Scalar") { + // input 0 + T0->resize({}); + + REQUIRE_NOTHROW(op->forwardDims()); + REQUIRE((op->getOutput(0)->dims() == std::vector<std::size_t>())); + } + + SECTION("+1-D Tensor") { + + for (std::uint16_t trial = 0; trial < NBTRIALS; ++trial) { + + const std::size_t nb_dims = nbDimsDist(gen); + std::vector<std::size_t> dims(nb_dims); + + for (std::size_t i = 0; i < nb_dims; ++i) { + dims[i] = dimsDist(gen); + } + T0->resize(dims); + + REQUIRE_NOTHROW(op->forwardDims()); + REQUIRE((op->getOutput(0)->dims()) == dims); + } + } +} +} // namespace Aidge