diff --git a/CMakeLists.txt b/CMakeLists.txt
index eef0e63bf398cffb2c15b3af56ec0bf02d6590a9..3574e25cec5977bc2249c7d756041c09650f9b11 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.15)
+cmake_minimum_required(VERSION 3.18)
 set(CXX_STANDARD 14)
 
 file(STRINGS "${CMAKE_SOURCE_DIR}/version.txt" version)
@@ -24,6 +24,7 @@ add_definitions(-DGIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
 
 # Note : project name is ${CMAKE_PROJECT_NAME} and python module name is also ${CMAKE_PROJECT_NAME}
 set(module_name _${CMAKE_PROJECT_NAME}) # target name
+set(pybind_module_name ${CMAKE_PROJECT_NAME}) # name of submodule for python bindings
 
 ##############################################
 # Define options
@@ -69,16 +70,12 @@ set_property(TARGET ${module_name} PROPERTY POSITION_INDEPENDENT_CODE ON)
 
 # PYTHON BINDING
 if (PYBIND)
-    # Handles Python + pybind11 headers dependencies
-    include(PybindModuleCreation)
-    generate_python_binding(${CMAKE_PROJECT_NAME} ${module_name})
+    # Python binding lib is by default installed in <prefix>/python_packages/<package>/
+    # When installed from python, setup.py should set it to the python package dir
+    set(PYBIND_INSTALL_PREFIX python_packages/${pybind_module_name} CACHE PATH "Python package install prefix")
 
-    target_link_libraries(${module_name}
-        PUBLIC
-            pybind11::pybind11
-        PRIVATE
-            Python::Module
-        )
+    include(PybindModuleCreation)
+    generate_python_binding(${pybind_module_name} ${module_name})
 endif()
 
 if( ${ENABLE_ASAN} )
@@ -102,7 +99,6 @@ target_include_directories(${module_name}
         ${CMAKE_CURRENT_SOURCE_DIR}/src
 )
 
-target_link_libraries(${module_name} PUBLIC fmt::fmt)
 target_compile_features(${module_name} PRIVATE cxx_std_14)
 
 target_compile_options(${module_name} PRIVATE
@@ -128,6 +124,12 @@ install(TARGETS ${module_name} EXPORT ${CMAKE_PROJECT_NAME}-targets
 )
 install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
 
+if (PYBIND)
+    install(TARGETS ${pybind_module_name}
+        DESTINATION ${PYBIND_INSTALL_PREFIX}
+    )
+endif()
+
 #Export the targets to a script
 install(EXPORT ${CMAKE_PROJECT_NAME}-targets
  FILE "${CMAKE_PROJECT_NAME}-targets.cmake"
@@ -159,15 +161,16 @@ install(FILES
 ## Exporting from the build tree
 message(STATUS "Exporting created targets to use them in another build")
 export(EXPORT ${CMAKE_PROJECT_NAME}-targets
-    FILE "${CMAKE_CURRENT_BINARY_DIR}/${project}-targets.cmake")
+    FILE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}-targets.cmake")
 
 
 ##############################################
 ## Add test
 if(TEST)
-    if(PYBIND)
-        message(FATAL_ERROR "PYBIND and TEST are both enabled. But cannot compile with catch_2.\nChoose between pybind and Catch2 for compilation.")
+    if (AIDGE_REQUIRES_PYTHON AND NOT AIDGE_PYTHON_HAS_EMBED)
+        message(WARNING "Skipping compilation of tests: missing Python embedded interpreter")
+    else()
+        enable_testing()
+        add_subdirectory(unit_tests)
     endif()
-    enable_testing()
-    add_subdirectory(unit_tests)
 endif()
diff --git a/README.md b/README.md
index ed44c2eaaee074f17c8d7edad059c0891473f272..96283603759f03415b7dc1b99f3905550427f633 100644
--- a/README.md
+++ b/README.md
@@ -23,9 +23,23 @@ Those operators can be used on any machine with an Linux OS.
 pip install . -v
 ```
 > **TIPS :** Use environment variables to change compilation options :
-> - `AIDGE_INSTALL` : to set the installation folder. Defaults to /usr/local/lib. :warning: This path must be identical to aidge_core install path.
-> - `AIDGE_PYTHON_BUILD_TYPE` : to set the compilation mode to **Debug** or **Release** 
-> - `AIDGE_BUILD_GEN` : to set the build backend with 
+> - `AIDGE_INSTALL` : to set the installation folder. Defaults to `<python_prefix>/lib/libAidge`. :warning: This path must be identical to aidge_core install path.
+> - `AIDGE_PYTHON_BUILD_TYPE` : to set the compilation mode to **Debug** or **Release** or "" (for default flags). Defaults to **Release**.
+> - `AIDGE_BUILD_GEN` : to set the build backend (for development mode) or "" for the cmake default. Default to "".
+
+## Pip installation for development
+
+To setup using pip in development (or editable mode), use the `--no-build-isolation -e` options to pip.
+
+For instance run the following command in your python environnement for a typical setup :
+``` bash
+export AIDGE_PYTHON_BUILD_TYPE=         # default flags (no debug info but fastest build time)
+export AIDGE_PYTHON_BUILD_TYPE=Debug    # or if one really need to debug the C++ code
+pip install -U pip setuptools setuptools_scm[toml] cmake   # Pre-install build requirements (refer to the pyproject.toml [build-system] section)
+pip install -v --no-build-isolation -e .
+```
+
+Refer to `aidge_core/README.md` for more details on development build options.
 
 ### Standard C++ Compilation
 
diff --git a/aidge_backend_cpu-config.cmake.in b/aidge_backend_cpu-config.cmake.in
index f3604be11c27d86caf1ad8a48b333b9bd8f30625..d8e1372bc8a7b79bd09c79b654af4291c995ac58 100644
--- a/aidge_backend_cpu-config.cmake.in
+++ b/aidge_backend_cpu-config.cmake.in
@@ -1,3 +1,10 @@
+@PACKAGE_INIT@
+
+include(CMakeFindDependencyMacro)
+find_dependency(aidge_core)
+
+include(CMakeFindDependencyMacro)
+
 include(${CMAKE_CURRENT_LIST_DIR}/aidge_backend_cpu-config-version.cmake)
 
 include(${CMAKE_CURRENT_LIST_DIR}/aidge_backend_cpu-targets.cmake)
diff --git a/cmake/PybindModuleCreation.cmake b/cmake/PybindModuleCreation.cmake
index 8f386bef59ed86dfa366eca5d4fccae24b28d24e..a520039f6505a7178acefaca076fa3f659e41bcb 100644
--- a/cmake/PybindModuleCreation.cmake
+++ b/cmake/PybindModuleCreation.cmake
@@ -1,9 +1,10 @@
 function(generate_python_binding pybind_module_name target_to_bind) 
-    add_definitions(-DPYBIND)
+
+    find_package(Python COMPONENTS Interpreter Development.Module)
+
     Include(FetchContent)
 
     set(PYBIND_VERSION v2.10.4)
-    set(PYBIND11_FINDPYTHON ON)
     message(STATUS "Retrieving pybind ${PYBIND_VERSION} from git")
 
     FetchContent_Declare(
@@ -12,14 +13,12 @@ function(generate_python_binding pybind_module_name target_to_bind)
         GIT_TAG        ${PYBIND_VERSION} # or a later release
     )
 
-    # Use the New FindPython mode, recommanded. Requires CMake 3.15+
-    find_package(Python COMPONENTS Interpreter Development.Module)
     FetchContent_MakeAvailable(PyBind11)
 
     message(STATUS "Creating binding for module ${pybind_module_name}")
     file(GLOB_RECURSE pybind_src_files "python_binding/*.cpp")
 
     pybind11_add_module(${pybind_module_name} MODULE ${pybind_src_files} "NO_EXTRAS") # NO EXTRA recquired for pip install
-    target_include_directories(${pybind_module_name} PUBLIC "python_binding")
-    target_link_libraries(${pybind_module_name} PUBLIC ${target_to_bind})
+    target_include_directories(${pybind_module_name} PRIVATE "python_binding")
+    target_link_libraries(${pybind_module_name} PRIVATE ${target_to_bind})
 endfunction()
diff --git a/include/aidge/backend/cpu/operator/AbsImpl.hpp b/include/aidge/backend/cpu/operator/AbsImpl.hpp
index faba3ef69ff27fbfb92393e1e0dacaebd5d69b07..8233d47c4d1e2dc7bf724600ec083bcaa0d667e9 100644
--- a/include/aidge/backend/cpu/operator/AbsImpl.hpp
+++ b/include/aidge/backend/cpu/operator/AbsImpl.hpp
@@ -12,7 +12,7 @@
 #ifndef AIDGE_CPU_OPERATOR_ABSIMPL_H_
 #define AIDGE_CPU_OPERATOR_ABSIMPL_H_
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/Abs.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
@@ -20,31 +20,12 @@
 #include <vector>
 
 namespace Aidge {
-// class Abs_Op;
+// Operator implementation entry point for the backend
+using AbsImpl_cpu = OperatorImpl_cpu<Abs_Op,
+    void(const std::size_t, const void*, void*)>;
 
-// compute kernel registry for forward and backward
-class AbsImplForward_cpu
-    : public Registrable<AbsImplForward_cpu, std::tuple<DataType, DataType>, void(const std::size_t, const void*, void*)> {
-};
-class AbsImplBackward_cpu
-    : public Registrable<AbsImplBackward_cpu, std::tuple<DataType, DataType>, void(const std::size_t, const void*, void*)> {
-};
-
-class AbsImpl_cpu : public OperatorImpl {
-public:
-    AbsImpl_cpu(const Abs_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<AbsImpl_cpu> create(const Abs_Op& op) {
-        return std::make_unique<AbsImpl_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-    void forward() override;
-};
-
-namespace {
-static Registrar<Abs_Op> registrarAbsImpl_cpu("cpu", Aidge::AbsImpl_cpu::create);
-}
+// Implementation entry point registration to Operator
+REGISTRAR(Abs_Op, "cpu", Aidge::AbsImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_ABSIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/AbsImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/AbsImpl_kernels.hpp
similarity index 57%
rename from include/aidge/backend/cpu/operator/AbsImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/AbsImpl_kernels.hpp
index 4922d6273b681515a4682b86c720b42381598042..16e5f9dee26a6f8b760e14a1ad66a40d8f0f7e93 100644
--- a/include/aidge/backend/cpu/operator/AbsImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/AbsImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_ABSIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_ABSIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_ABSIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_ABSIMPL_KERNELS_H_
 
 #include <cmath>
 
@@ -32,14 +32,16 @@ void AbsImpl_cpu_forward_kernel(std::size_t inputLenght,
     }
 }
 
-namespace {
-static Registrar<AbsImplForward_cpu> registrarAbsImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32}, Aidge::AbsImpl_cpu_forward_kernel<float, float>);
-static Registrar<AbsImplForward_cpu> registrarAbsImplForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32}, Aidge::AbsImpl_cpu_forward_kernel<int, int>);
-static Registrar<AbsImplForward_cpu> registrarAbsImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64}, Aidge::AbsImpl_cpu_forward_kernel<double, double>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(AbsImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::inPlaceModel, Aidge::AbsImpl_cpu_forward_kernel<float, float>, nullptr});
+REGISTRAR(AbsImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::inPlaceModel, Aidge::AbsImpl_cpu_forward_kernel<double, double>, nullptr});
+REGISTRAR(AbsImpl_cpu,
+    {DataType::Int32},
+    {ProdConso::inPlaceModel, Aidge::AbsImpl_cpu_forward_kernel<std::int32_t, std::int32_t>, nullptr});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_ABSIMPL_FORWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_ABSIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/AddImpl.hpp b/include/aidge/backend/cpu/operator/AddImpl.hpp
index 7a1497a2f4a2ae0e6005897ae504502505bbe60a..5e795922a67be178dde588e8e5e346ec268efe86 100644
--- a/include/aidge/backend/cpu/operator/AddImpl.hpp
+++ b/include/aidge/backend/cpu/operator/AddImpl.hpp
@@ -17,36 +17,18 @@
 #include <string>
 #include <vector>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/Add.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 
 namespace Aidge {
+// Operator implementation entry point for the backend
+using AddImpl_cpu = OperatorImpl_cpu<Add_Op,
+    void(const std::vector<const void*>, const std::vector<std::vector<std::size_t>>&, const std::size_t, const std::vector<std::size_t>&, void*)>;
 
-// compute kernel registry for forward and backward
-class AddImplForward_cpu
-    : public Registrable<AddImplForward_cpu, std::tuple<DataType, DataType>, void(const std::vector<const void*>, const std::vector<std::vector<std::size_t>>&, const std::size_t, const std::vector<std::size_t>&, void*)> {};
-
-class AddImplBackward_cpu
-    : public Registrable<AddImplBackward_cpu, std::tuple<DataType, DataType>, void(const std::vector<const void*>, const std::vector<std::vector<std::size_t>>&, const std::size_t, const std::vector<std::size_t>&, void*)> {};
-
-
-class AddImpl_cpu : public OperatorImpl {
-public:
-    AddImpl_cpu(const Add_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<AddImpl_cpu> create(const Add_Op& op) {
-        return std::make_unique<AddImpl_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t /*inputIdx*/) const override final;
-    void forward() override;
-};
-
-namespace {
-static Registrar<Add_Op> registrarAddImpl_cpu("cpu", Aidge::AddImpl_cpu::create);
-}  // namespace
+// Implementation entry point registration to Operator
+REGISTRAR(Add_Op, "cpu", Aidge::AddImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_ADDIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/AddImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/AddImpl_kernels.hpp
similarity index 60%
rename from include/aidge/backend/cpu/operator/AddImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/AddImpl_kernels.hpp
index 94b22dcc7fc8251f8ca907ab0b060b0275309c9d..4a4ba2a8999c4dc33fc743b5a3a7dad023f9e0dd 100644
--- a/include/aidge/backend/cpu/operator/AddImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/AddImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_ADDIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_ADDIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_ADDIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_ADDIMPL_KERNELS_H_
 
 #include "aidge/utils/Registrar.hpp"
 
@@ -41,16 +41,19 @@ void AddImpl_cpu_forward_kernel(const std::vector<const void*> inputs_, const st
 	}
 }
 
-namespace {
-static Registrar<AddImplForward_cpu> registrarAddImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32}, Aidge::AddImpl_cpu_forward_kernel<float, float>);
-static Registrar<AddImplForward_cpu> registrarAddImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64}, Aidge::AddImpl_cpu_forward_kernel<double, double>);
-static Registrar<AddImplForward_cpu> registrarAddImplForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32}, Aidge::AddImpl_cpu_forward_kernel<std::int32_t, std::int32_t>);
-static Registrar<AddImplForward_cpu> registrarAddImplForward_cpu_Int64(
-        {DataType::Int64, DataType::Int64}, Aidge::AddImpl_cpu_forward_kernel<std::int64_t, std::int64_t>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(AddImpl_cpu,
+    {ImplSpec::IOSpec{DataType::Any}, ImplSpec::IOSpec{DataType::Float32}},
+    {ProdConso::inPlaceModel, Aidge::AddImpl_cpu_forward_kernel<float, float>, nullptr});
+REGISTRAR(AddImpl_cpu,
+    {ImplSpec::IOSpec{DataType::Any}, ImplSpec::IOSpec{DataType::Float64}},
+    {ProdConso::inPlaceModel, Aidge::AddImpl_cpu_forward_kernel<double, double>, nullptr});
+REGISTRAR(AddImpl_cpu,
+    {ImplSpec::IOSpec{DataType::Any}, ImplSpec::IOSpec{DataType::Int32}},
+    {ProdConso::inPlaceModel, Aidge::AddImpl_cpu_forward_kernel<std::int32_t, std::int32_t>, nullptr});
+REGISTRAR(AddImpl_cpu,
+    {ImplSpec::IOSpec{DataType::Any}, ImplSpec::IOSpec{DataType::Int64}},
+    {ProdConso::inPlaceModel, Aidge::AddImpl_cpu_forward_kernel<std::int64_t, std::int64_t>, nullptr});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_ADDIMPL_CPU_FORWARD_KERNEL_H_ */
\ No newline at end of file
+#endif /* AIDGE_CPU_OPERATOR_ADDIMPL_CPU_KERNELS_H_ */
\ No newline at end of file
diff --git a/include/aidge/backend/cpu/operator/AndImpl.hpp b/include/aidge/backend/cpu/operator/AndImpl.hpp
index 139b1f08e4c4e2900e07d2bb470cb27fb878807f..316a2fb922596642088d133a7fec49c988739bb7 100644
--- a/include/aidge/backend/cpu/operator/AndImpl.hpp
+++ b/include/aidge/backend/cpu/operator/AndImpl.hpp
@@ -12,7 +12,7 @@
 #ifndef AIDGE_CPU_OPERATOR_ANDIMPL_H_
 #define AIDGE_CPU_OPERATOR_ANDIMPL_H_
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/And.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
@@ -21,29 +21,12 @@
 #include <vector>
 
 namespace Aidge {
-// compute kernel registry for forward and backward
-class AndImplForward_cpu
-    : public Registrable<AndImplForward_cpu, std::tuple<DataType, DataType, DataType>, void(const std::vector<std::size_t>&, const std::vector<std::size_t>&, const std::vector<std::size_t>&, const void*, const void*,void*)> {
-};
-class AndImplBackward_cpu
-    : public Registrable<AndImplBackward_cpu, std::tuple<DataType, DataType, DataType>, void(const std::vector<std::size_t>&, const std::vector<std::size_t>&, const std::vector<std::size_t>&, const void*, const void*, void*)> {
-};
+// Operator implementation entry point for the backend
+using AndImpl_cpu = OperatorImpl_cpu<And_Op,
+    void(const std::vector<std::size_t>&, const std::vector<std::size_t>&, const std::vector<std::size_t>&, const void*, const void*,void*)>;
 
-class AndImpl_cpu : public OperatorImpl {
-public:
-    AndImpl_cpu(const And_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<AndImpl_cpu> create(const And_Op& op) {
-        return std::make_unique<AndImpl_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-    void forward() override;
-};
-
-namespace {
-static Registrar<And_Op> registrarAndImpl_cpu("cpu", Aidge::AndImpl_cpu::create);
-}
+// Implementation entry point registration to Operator
+REGISTRAR(And_Op, "cpu", Aidge::AndImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_ANDIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/AndImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/AndImpl_kernels.hpp
similarity index 61%
rename from include/aidge/backend/cpu/operator/AndImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/AndImpl_kernels.hpp
index c537863c83fcdd2f766911386467da8e74138253..197e829f3527ce2f36c3ef5ee812a26477633703 100644
--- a/include/aidge/backend/cpu/operator/AndImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/AndImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_ANDIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_ANDIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_ANDIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_ANDIMPL_KERNELS_H_
 
 #include "aidge/backend/cpu/data/Broadcasting.hpp"
 #include "aidge/backend/cpu/operator/AndImpl.hpp"
@@ -45,20 +45,19 @@ void AndImpl_cpu_forward_kernel(const std::vector<std::size_t>& input1Dims,
     }
 }
 
-namespace {
-static Registrar<AndImplForward_cpu> registrarAndImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32, DataType::Float32},
-        Aidge::AndImpl_cpu_forward_kernel<float, float, float>);
-static Registrar<AndImplForward_cpu> registrarAndImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64, DataType::Float64},
-        Aidge::AndImpl_cpu_forward_kernel<double, double, double>);
-static Registrar<AndImplForward_cpu> registrarAndImplForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32, DataType::Int32},
-        Aidge::AndImpl_cpu_forward_kernel<std::int32_t, std::int32_t, std::int32_t>);
-static Registrar<AndImplForward_cpu> registrarAndImplForward_cpu_Int64(
-        {DataType::Int64, DataType::Int64, DataType::Int64},
-        Aidge::AndImpl_cpu_forward_kernel<std::int64_t, std::int64_t, std::int64_t>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(AndImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::inPlaceModel, Aidge::AndImpl_cpu_forward_kernel<float, float, float>, nullptr});
+REGISTRAR(AndImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::inPlaceModel, Aidge::AndImpl_cpu_forward_kernel<double, double, double>, nullptr});
+REGISTRAR(AndImpl_cpu,
+    {DataType::Int32},
+    {ProdConso::inPlaceModel, Aidge::AndImpl_cpu_forward_kernel<std::int32_t, std::int32_t, std::int32_t>, nullptr});
+REGISTRAR(AndImpl_cpu,
+    {DataType::Int64},
+    {ProdConso::inPlaceModel, Aidge::AndImpl_cpu_forward_kernel<std::int64_t, std::int64_t, std::int64_t>, nullptr});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_ANDIMPL_FORWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_ANDIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/ArgMaxImpl.hpp b/include/aidge/backend/cpu/operator/ArgMaxImpl.hpp
index f93abbbca9630a8b60290bae7660f6428b41b0b3..b1a2d5168013e4f9595f4275b98143cfc3509629 100644
--- a/include/aidge/backend/cpu/operator/ArgMaxImpl.hpp
+++ b/include/aidge/backend/cpu/operator/ArgMaxImpl.hpp
@@ -17,44 +17,22 @@
 #include <tuple>
 #include <vector>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/ArgMax.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 
 namespace Aidge {
-class ArgMaxImplForward_cpu
-    : public Registrable<ArgMaxImplForward_cpu,
-                        std::tuple<DataType, DataType>,
-                        void(std::int32_t,
-                            DimSize_t,
-                            const std::vector<DimSize_t>&,
-                            const void *,
-                            void *)> {};
-class ArgMaxImplBackward_cpu
-    : public Registrable<ArgMaxImplBackward_cpu,
-                        std::tuple<DataType, DataType>,
-                        void(std::int32_t,
-                            DimSize_t,
-                            const std::vector<DimSize_t>&,
-                            const void *,
-                            void *)> {};
-
-class ArgMaxImpl_cpu : public OperatorImpl {
-   public:
-    ArgMaxImpl_cpu(const ArgMax_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<ArgMaxImpl_cpu> create(const ArgMax_Op &op) {
-        return std::make_unique<ArgMaxImpl_cpu>(op);
-    }
-
-   public:
-    void forward() override;
-};
-
-namespace {
-static Registrar<ArgMax_Op> registrarArgMaxImpl_cpu("cpu", Aidge::ArgMaxImpl_cpu::create);
-}  // namespace
+// Operator implementation entry point for the backend
+using ArgMaxImpl_cpu = OperatorImpl_cpu<ArgMax_Op,
+    void(std::int32_t,
+        DimSize_t,
+        const std::vector<DimSize_t>&,
+        const void *,
+        void *)>;
+
+// Implementation entry point registration to Operator
+REGISTRAR(ArgMax_Op, "cpu", Aidge::ArgMaxImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_ARGMAXIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/ArgMaxImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/ArgMaxImpl_kernels.hpp
similarity index 77%
rename from include/aidge/backend/cpu/operator/ArgMaxImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/ArgMaxImpl_kernels.hpp
index cea7d973fce947d980ffe497581c56cd6fe59a8e..1bedec701766fc59fac233a1c400df1042369c5a 100644
--- a/include/aidge/backend/cpu/operator/ArgMaxImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/ArgMaxImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_ARGMAXIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_ARGMAXIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_ARGMAXIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_ARGMAXIMPL_KERNELS_H_
 
 #include <algorithm>   // std::for_each
 #include <cstddef>     // std::size_t
@@ -72,14 +72,16 @@ void ArgMaxImpl_cpu_forward_kernel(std::int32_t axis_,
 
 }
 
-namespace {
-static Registrar<ArgMaxImplForward_cpu> registrarArgMaxImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32}, Aidge::ArgMaxImpl_cpu_forward_kernel<float, float>);
-static Registrar<ArgMaxImplForward_cpu> registrarArgMaxImplForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32}, Aidge::ArgMaxImpl_cpu_forward_kernel<int, int>);
-static Registrar<ArgMaxImplForward_cpu> registrarArgMaxImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64}, Aidge::ArgMaxImpl_cpu_forward_kernel<double, double>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(ArgMaxImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::defaultModel, Aidge::ArgMaxImpl_cpu_forward_kernel<float, float>, nullptr});
+REGISTRAR(ArgMaxImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::defaultModel, Aidge::ArgMaxImpl_cpu_forward_kernel<double, double>, nullptr});
+REGISTRAR(ArgMaxImpl_cpu,
+    {DataType::Int32},
+    {ProdConso::defaultModel, Aidge::ArgMaxImpl_cpu_forward_kernel<std::int32_t, std::int32_t>, nullptr});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_ARGMAXIMPL_FORWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_ARGMAXIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/AvgPoolingImpl.hpp b/include/aidge/backend/cpu/operator/AvgPoolingImpl.hpp
index 12a5dc334619c16e6ad3a77f0cd76f4db7a87b77..adea96ca43a1ad9d2a49777426913ca4676e4f32 100644
--- a/include/aidge/backend/cpu/operator/AvgPoolingImpl.hpp
+++ b/include/aidge/backend/cpu/operator/AvgPoolingImpl.hpp
@@ -17,49 +17,24 @@
 #include <tuple>
 #include <vector>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/AvgPooling.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 
 namespace Aidge {
-// class AvgPooling_Op;
-
-// compute kernel registry for forward and backward
-class AvgPoolingImpl2DForward_cpu
-    : public Registrable<AvgPoolingImpl2DForward_cpu,
-                        std::tuple<DataType, DataType>,
-                        void(const std::array<DimSize_t, 2>&,
-                            const std::array<DimSize_t, 2>&,
-                            const std::array<DimSize_t, 4>&,
-                            const void *,
-                            void *)> {};
-class AvgPoolingImpl2DBackward_cpu
-    : public Registrable<AvgPoolingImpl2DBackward_cpu,
-                        std::tuple<DataType, DataType>,
-                        void(const std::array<DimSize_t, 2>&,
-                            const std::array<DimSize_t, 2>&,
-                            const std::array<DimSize_t, 4>&,
-                            const void *,
-                            void *)> {};
-
-class AvgPoolingImpl2D_cpu : public OperatorImpl {
-public:
-    AvgPoolingImpl2D_cpu(const AvgPooling_Op<2> &op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<AvgPoolingImpl2D_cpu> create(const AvgPooling_Op<2> &op) {
-        return std::make_unique<AvgPoolingImpl2D_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-    void forward() override;
-};
-
-namespace {
-// add cpu backend to AvgPooling_Op<2> implementation registry
-static Registrar<AvgPooling_Op<2>> registrarAvgPoolingImpl2D_cpu("cpu", Aidge::AvgPoolingImpl2D_cpu::create);
-}  // namespace
+// Operator implementation entry point for the backend
+using AvgPooling2D_Op = AvgPooling_Op<2>;
+using AvgPoolingImpl2D_cpu = OperatorImpl_cpu<AvgPooling_Op<2>,
+    void(const std::array<DimSize_t, 2>&,
+        const std::array<DimSize_t, 2>&,
+        const std::array<DimSize_t, 4>&,
+        const void *,
+        void *)>;
+
+// Implementation entry point registration to Operator
+REGISTRAR(AvgPooling2D_Op, "cpu", Aidge::AvgPoolingImpl2D_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_AVGPOOLINGIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/AvgPoolingImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/AvgPoolingImpl_kernels.hpp
similarity index 85%
rename from include/aidge/backend/cpu/operator/AvgPoolingImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/AvgPoolingImpl_kernels.hpp
index c7d9f86235c3bf1d7d01cf429cab29d156592fb5..f6da9dcb026101b93de862499d42ae8734532d52 100644
--- a/include/aidge/backend/cpu/operator/AvgPoolingImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/AvgPoolingImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_AVGPOOLINGIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_AVGPOOLINGIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_AVGPOOLINGIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_AVGPOOLINGIMPL_KERNELS_H_
 
 #include <array>
 #include <tuple>
@@ -101,17 +101,16 @@ void AvgPoolingImpl2D_cpu_forward_kernel(const std::array<DimSize_t, 2>& strideD
     }
 }
 
-namespace {
-static Registrar<AvgPoolingImpl2DForward_cpu> registrarAvgPoolingImpl2DForward_cpu_Float32(
-        std::tuple<DataType, DataType>({DataType::Float32, DataType::Float32}),
-        Aidge::AvgPoolingImpl2D_cpu_forward_kernel<float, float>);
-static Registrar<AvgPoolingImpl2DForward_cpu> registrarAvgPoolingImpl2DForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32},
-        Aidge::AvgPoolingImpl2D_cpu_forward_kernel<int, int>);
-static Registrar<AvgPoolingImpl2DForward_cpu> registrarAvgPoolingImpl2DForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64},
-        Aidge::AvgPoolingImpl2D_cpu_forward_kernel<double, double>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(AvgPoolingImpl2D_cpu,
+    {{DataType::Float32, DataFormat::NCHW}, {DataType::Float32, DataFormat::NCHW}},
+    {ProdConso::inPlaceModel, Aidge::AvgPoolingImpl2D_cpu_forward_kernel<float, float>, nullptr});
+REGISTRAR(AvgPoolingImpl2D_cpu,
+    {{DataType::Int32, DataFormat::NCHW}, {DataType::Int32, DataFormat::NCHW}},
+    {ProdConso::inPlaceModel, Aidge::AvgPoolingImpl2D_cpu_forward_kernel<std::int32_t, std::int32_t>, nullptr});
+REGISTRAR(AvgPoolingImpl2D_cpu,
+    {{DataType::Float64, DataFormat::NCHW}, {DataType::Float64, DataFormat::NCHW}},
+    {ProdConso::inPlaceModel, Aidge::AvgPoolingImpl2D_cpu_forward_kernel<double, double>, nullptr});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_AVGPOOLINGIMPL_FORWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_AVGPOOLINGIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/BatchNormImpl.hpp b/include/aidge/backend/cpu/operator/BatchNormImpl.hpp
index 93bdab2d3f37e3bd8dc1e68ab68a05de8c8015ed..36a100b21edc6cd63a0176c89f2f1e57c10001c7 100644
--- a/include/aidge/backend/cpu/operator/BatchNormImpl.hpp
+++ b/include/aidge/backend/cpu/operator/BatchNormImpl.hpp
@@ -17,58 +17,29 @@
 #include <tuple>
 #include <vector>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/BatchNorm.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 
 namespace Aidge {
-// class BatchNorm_Op;
-
-// compute kernel registry for forward and backward
-class BatchNormImpl2DForward_cpu
-    : public Registrable<BatchNormImpl2DForward_cpu,
-                         std::tuple<DataType, DataType, DataType>,
-                         void(float,
-                            float,
-                            const std::array<DimSize_t, 4> &,
-                            const void *,
-                            const void *,
-                            const void *,
-                            void *,
-                            void *,
-                            void *,
-                            const bool)> {};
-class BatchNormImpl2DBackward_cpu
-    : public Registrable<BatchNormImpl2DBackward_cpu,
-                         std::tuple<DataType, DataType, DataType>,
-                         void(float,
-                            float,
-                            const std::array<DimSize_t, 4> &,
-                            const void *,
-                            const void *,
-                            const void *,
-                            void *,
-                            void *,
-                            void *)> {};
-
-class BatchNormImpl2D_cpu : public OperatorImpl {
-public:
-    BatchNormImpl2D_cpu(const BatchNorm_Op<2> &op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<BatchNormImpl2D_cpu> create(const BatchNorm_Op<2> &op) {
-        return std::make_unique<BatchNormImpl2D_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-    void forward() override;
-};
-
-namespace {
-// add cpu backend to BatchNorm_Op<2> implementation registry
-static Registrar<BatchNorm_Op<2>> registrarBatchNormImpl2D_cpu("cpu", Aidge::BatchNormImpl2D_cpu::create);
-}  // namespace
+// Operator implementation entry point for the backend
+using BatchNorm2D_Op = BatchNorm_Op<2>;
+using BatchNormImpl2D_cpu = OperatorImpl_cpu<BatchNorm_Op<2>,
+    void(float,
+        float,
+        const std::array<DimSize_t, 4> &,
+        const void *,
+        const void *,
+        const void *,
+        void *,
+        void *,
+        void *,
+        const bool)>;
+
+// Implementation entry point registration to Operator
+REGISTRAR(BatchNorm2D_Op, "cpu", Aidge::BatchNormImpl2D_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_BATCHNORMIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/BatchNormImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/BatchNormImpl_kernels.hpp
similarity index 90%
rename from include/aidge/backend/cpu/operator/BatchNormImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/BatchNormImpl_kernels.hpp
index 19f232a783bccf0a800d41f2bc566ccf6e04f05e..ec71e3b8e37e344c551fd643dc7b3957bdddcb67 100644
--- a/include/aidge/backend/cpu/operator/BatchNormImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/BatchNormImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_BATCHNORMIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_BATCHNORMIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_BATCHNORMIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_BATCHNORMIMPL_KERNELS_H_
 
 #include "aidge/utils/Registrar.hpp"
 
@@ -96,15 +96,10 @@ void BatchNormImpl2D_cpu_forward_kernel(float epsilon, float momentum, const std
     }
 }
 
-
-
-
-
-namespace {
-static Registrar<BatchNormImpl2DForward_cpu> registrarBatchNormImpl2DForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32, DataType::Float32},
-        Aidge::BatchNormImpl2D_cpu_forward_kernel<float, float, float>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(BatchNormImpl2D_cpu,
+    {{DataType::Float32, DataFormat::NCHW}, {DataType::Float32, DataFormat::NCHW}},
+    {ProdConso::inPlaceModel, Aidge::BatchNormImpl2D_cpu_forward_kernel<float, float, float>, nullptr});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_BATCHNORMIMPL_FORWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_BATCHNORMIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/ConstantOfShapeImpl.hpp b/include/aidge/backend/cpu/operator/ConstantOfShapeImpl.hpp
index 80efb31d5cc9b99a6678d9010903d99245a3cee6..83e7e030f526e0db3cff4741eabe39e287130562 100644
--- a/include/aidge/backend/cpu/operator/ConstantOfShapeImpl.hpp
+++ b/include/aidge/backend/cpu/operator/ConstantOfShapeImpl.hpp
@@ -16,36 +16,18 @@
 #include <memory>
 #include <vector>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/ConstantOfShape.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 
 namespace Aidge {
-// class ConstantOfShape_op;
+// Operator implementation entry point for the backend
+using ConstantOfShapeImpl_cpu = OperatorImpl_cpu<ConstantOfShape_Op,
+    void(const std::vector<DimSize_t>, const Tensor&, void *)>;
 
-class ConstantOfShapeImplForward_cpu
-    : public Registrable<
-          ConstantOfShapeImplForward_cpu, std::tuple<DataType>,
-          void(const std::vector<DimSize_t>, const Tensor&, void *)> {};
-
-class ConstantOfShapeImpl_cpu : public OperatorImpl {
-public:
-  ConstantOfShapeImpl_cpu(const ConstantOfShape_Op &op)
-      : OperatorImpl(op, "cpu") {}
-
-  static std::unique_ptr<ConstantOfShapeImpl_cpu>
-  create(const ConstantOfShapeImpl_cpu &op) {
-    return std::make_unique<ConstantOfShapeImpl_cpu>(op);
-  }
-
-  void forward() override;
-};
-
-namespace {
-static Registrar<ConstantOfShape_Op> registrarConstantOfShapeImpl_cpu(
-    "cpu", Aidge::ConstantOfShapeImpl_cpu::create);
-}
+// Implementation entry point registration to Operator
+REGISTRAR(ConstantOfShape_Op, "cpu", Aidge::ConstantOfShapeImpl_cpu::create);
 } // namespace Aidge
 
 #endif /* _AIDGE_CPU_OPERATOR_CONSTANTOFSHAPEIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/ConstantOfShapeImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/ConstantOfShapeImpl_forward_kernels.hpp
deleted file mode 100644
index 59a3475ec0f8f33cbeca76688a5f2e395d268ad7..0000000000000000000000000000000000000000
--- a/include/aidge/backend/cpu/operator/ConstantOfShapeImpl_forward_kernels.hpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2023 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_CPU_OPERATOR_CONSTANTOFSHAPEIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_CONSTANTOFSHAPEIMPL_FORWARD_KERNEL_H_
-
-#include <aidge/data/Tensor.hpp>
-#include <aidge/data/half.hpp>
-#include <algorithm>
-#include <cstddef>
-#include <cstdint>
-#include <functional> // std::multiplies
-#include <numeric>    // std::accumulate
-#include <vector>
-
-#include "aidge/backend/cpu/operator/ConstantOfShapeImpl.hpp"
-#include "aidge/data/Data.hpp"
-#include "aidge/utils/ErrorHandling.hpp"
-#include "aidge/utils/Registrar.hpp"
-#include "aidge/utils/Types.h"
-
-namespace Aidge {
-template <class O>
-void ConstantOfShapeimpl_cpu_forward_kernel(
-    const std::vector<DimSize_t> output_dims, const Tensor &value,
-    void *output_) {
-
-  O *output = static_cast<O *>(output_);
-  O val;
-  std::copy(static_cast<O *>(value.getImpl()->hostPtr()),
-            static_cast<O *>(value.getImpl()->hostPtr()) +
-                static_cast<NbElts_t>(1),
-            &val);
-  const size_t output_size = std::accumulate(
-      output_dims.begin(), output_dims.end(), 1, std::multiplies<DimSize_t>());
-  for (size_t i = 0; i < output_size; ++i) {
-    output[i] = val;
-  }
-}
-
-// Then we add the Registrar declaration for different input/output types
-namespace {
-static Registrar<ConstantOfShapeImplForward_cpu>
-    registrarConstantOfShapeImplForward_cpu_Float16(
-        {DataType::Float16},
-        Aidge::ConstantOfShapeimpl_cpu_forward_kernel<half_float::half>);
-static Registrar<ConstantOfShapeImplForward_cpu>
-    registrarConstantOfShapeImplForward_cpu_Float32(
-        {DataType::Float32},
-        Aidge::ConstantOfShapeimpl_cpu_forward_kernel<float>);
-static Registrar<ConstantOfShapeImplForward_cpu>
-    registrarConstantOfShapeImplForward_cpu_Float64(
-        {DataType::Float64},
-        Aidge::ConstantOfShapeimpl_cpu_forward_kernel<double>);
-static Registrar<ConstantOfShapeImplForward_cpu>
-    registrarConstantOfShapeImplForward_cpu_Int16(
-        {DataType::Int16},
-        Aidge::ConstantOfShapeimpl_cpu_forward_kernel<std::int16_t>);
-static Registrar<ConstantOfShapeImplForward_cpu>
-    registrarConstantOfShapeImplForward_cpu_Int32(
-        {DataType::Int32},
-        Aidge::ConstantOfShapeimpl_cpu_forward_kernel<std::int32_t>);
-static Registrar<ConstantOfShapeImplForward_cpu>
-    registrarConstantOfShapeImplForward_cpu_Int64(
-        {DataType::Int64}, Aidge::ConstantOfShapeimpl_cpu_forward_kernel <std::int64_t>);
-} // namespace
-} // namespace Aidge
-
-#endif /* AIDGE_CPU_OPERATOR_CONSTANTOFSHAPEIMPL_FORWARD_KERNEL_H_ */
-
diff --git a/include/aidge/backend/cpu/operator/ConstantOfShapeImpl_kernels.hpp b/include/aidge/backend/cpu/operator/ConstantOfShapeImpl_kernels.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..18ab9c0a77c4545c955fc4fe1f1fc1cbcb763bf7
--- /dev/null
+++ b/include/aidge/backend/cpu/operator/ConstantOfShapeImpl_kernels.hpp
@@ -0,0 +1,71 @@
+/********************************************************************************
+ * Copyright (c) 2023 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_CPU_OPERATOR_CONSTANTOFSHAPEIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_CONSTANTOFSHAPEIMPL_KERNELS_H_
+
+#include <aidge/data/Tensor.hpp>
+#include <aidge/data/half.hpp>
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <functional> // std::multiplies
+#include <numeric>    // std::accumulate
+#include <vector>
+
+#include "aidge/backend/cpu/operator/ConstantOfShapeImpl.hpp"
+#include "aidge/data/Data.hpp"
+#include "aidge/utils/ErrorHandling.hpp"
+#include "aidge/utils/Registrar.hpp"
+#include "aidge/utils/Types.h"
+
+namespace Aidge {
+template <class O>
+void ConstantOfShapeimpl_cpu_forward_kernel(
+    const std::vector<DimSize_t> output_dims, const Tensor &value,
+    void *output_) {
+
+  O *output = static_cast<O *>(output_);
+  O val;
+  std::copy(static_cast<O *>(value.getImpl()->hostPtr()),
+            static_cast<O *>(value.getImpl()->hostPtr()) +
+                static_cast<NbElts_t>(1),
+            &val);
+  const size_t output_size = std::accumulate(
+      output_dims.begin(), output_dims.end(), 1, std::multiplies<DimSize_t>());
+  for (size_t i = 0; i < output_size; ++i) {
+    output[i] = val;
+  }
+}
+
+// Kernels registration to implementation entry point
+REGISTRAR(ConstantOfShapeImpl_cpu,
+    {ImplSpec::IOSpec{DataType::Int64}, ImplSpec::IOSpec{DataType::Float16}},
+    {ProdConso::defaultModel, Aidge::ConstantOfShapeimpl_cpu_forward_kernel<half_float::half>, nullptr});
+REGISTRAR(ConstantOfShapeImpl_cpu,
+    {ImplSpec::IOSpec{DataType::Int64}, ImplSpec::IOSpec{DataType::Float32}},
+    {ProdConso::defaultModel, Aidge::ConstantOfShapeimpl_cpu_forward_kernel<float>, nullptr});
+REGISTRAR(ConstantOfShapeImpl_cpu,
+    {ImplSpec::IOSpec{DataType::Int64}, ImplSpec::IOSpec{DataType::Float64}},
+    {ProdConso::defaultModel, Aidge::ConstantOfShapeimpl_cpu_forward_kernel<double>, nullptr});
+REGISTRAR(ConstantOfShapeImpl_cpu,
+    {ImplSpec::IOSpec{DataType::Int64}, ImplSpec::IOSpec{DataType::Int16}},
+    {ProdConso::defaultModel, Aidge::ConstantOfShapeimpl_cpu_forward_kernel<std::int16_t>, nullptr});
+REGISTRAR(ConstantOfShapeImpl_cpu,
+    {ImplSpec::IOSpec{DataType::Int64}, ImplSpec::IOSpec{DataType::Int32}},
+    {ProdConso::defaultModel, Aidge::ConstantOfShapeimpl_cpu_forward_kernel<std::int32_t>, nullptr});
+REGISTRAR(ConstantOfShapeImpl_cpu,
+    {ImplSpec::IOSpec{DataType::Int64}, ImplSpec::IOSpec{DataType::Int64}},
+    {ProdConso::defaultModel, Aidge::ConstantOfShapeimpl_cpu_forward_kernel<std::int64_t>, nullptr});
+} // namespace Aidge
+
+#endif /* AIDGE_CPU_OPERATOR_CONSTANTOFSHAPEIMPL_KERNELS_H_ */
+
diff --git a/include/aidge/backend/cpu/operator/ConvDepthWiseImpl.hpp b/include/aidge/backend/cpu/operator/ConvDepthWiseImpl.hpp
index ec886a310dd2edc616ced6ee447665eab3ce301a..5b985accfb7b9778993b557524de7b60060ad437 100644
--- a/include/aidge/backend/cpu/operator/ConvDepthWiseImpl.hpp
+++ b/include/aidge/backend/cpu/operator/ConvDepthWiseImpl.hpp
@@ -17,85 +17,39 @@
 #include <tuple>
 #include <vector>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/ConvDepthWise.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 
 namespace Aidge {
-// class ConvDepthWise_Op;
-// compute kernel registry for forward and backward
-class ConvDepthWiseImpl1DForward_cpu
-    : public Registrable<ConvDepthWiseImpl1DForward_cpu,
-                         std::tuple<DataType, DataType, DataType, DataType>,
-                         void(const std::array<DimSize_t, 1>&,
-                            const std::array<DimSize_t, 1>&,
-                            const std::array<DimSize_t, 1>&,
-                            const std::array<DimSize_t, 3>&,
-                            const void *,
-                            const void *,
-                            const void *,
-                            void *)> {};
-
-class ConvDepthWiseImpl1D_cpu : public OperatorImpl {
-public:
-    ConvDepthWiseImpl1D_cpu(const ConvDepthWise_Op<1> &op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<ConvDepthWiseImpl1D_cpu> create(const ConvDepthWise_Op<1> &op) {
-        return std::make_unique<ConvDepthWiseImpl1D_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-    void forward() override;
-};
-
-namespace {
-// add cpu backend to ConvDepthWise_Op<1> implementation registry
-static Registrar<ConvDepthWise_Op<1>> registrarConvDepthWiseImpl1D_cpu("cpu", Aidge::ConvDepthWiseImpl1D_cpu::create);
-}  // namespace
-
-// compute kernel registry for forward and backward
-class ConvDepthWiseImpl2DForward_cpu
-    : public Registrable<ConvDepthWiseImpl2DForward_cpu,
-                         std::tuple<DataType, DataType, DataType, DataType>,
-                         void(const std::array<DimSize_t, 2>&,
-                            const std::array<DimSize_t, 2>&,
-                            const std::array<DimSize_t, 2>&,
-                            const std::array<DimSize_t, 4> &,
-                            const void *,
-                            const void *,
-                            const void *,
-                            void *)> {};
-class ConvDepthWiseImpl2DBackward_cpu
-    : public Registrable<ConvDepthWiseImpl2DBackward_cpu,
-                         std::tuple<DataType, DataType, DataType, DataType>,
-                         void(const std::array<DimSize_t, 2>&,
-                            const std::array<DimSize_t, 2>&,
-                            const std::array<DimSize_t, 2>&,
-                            bool,
-                            const std::array<DimSize_t, 4> &,
-                            const void *,
-                            const void *,
-                            const void *,
-                            void *)> {};
-
-class ConvDepthWiseImpl2D_cpu : public OperatorImpl {
-public:
-    ConvDepthWiseImpl2D_cpu(const ConvDepthWise_Op<2> &op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<ConvDepthWiseImpl2D_cpu> create(const ConvDepthWise_Op<2> &op) {
-        return std::make_unique<ConvDepthWiseImpl2D_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-    void forward() override;
-};
-
-namespace {
-// add cpu backend to ConvDepthWise_Op<2> implementation registry
-static Registrar<ConvDepthWise_Op<2>> registrarConvDepthWiseImpl2D_cpu("cpu", Aidge::ConvDepthWiseImpl2D_cpu::create);
-}  // namespace
+// Operator implementation entry point for the backend
+using ConvDepthWise1D_Op = ConvDepthWise_Op<1>;
+using ConvDepthWiseImpl1D_cpu = OperatorImpl_cpu<ConvDepthWise_Op<1>,
+    void(const std::array<DimSize_t, 1>&,
+        const std::array<DimSize_t, 1>&,
+        const std::array<DimSize_t, 1>&,
+        const std::array<DimSize_t, 3>&,
+        const void *,
+        const void *,
+        const void *,
+        void *)>;
+
+using ConvDepthWise2D_Op = ConvDepthWise_Op<2>;
+using ConvDepthWiseImpl2D_cpu = OperatorImpl_cpu<ConvDepthWise_Op<2>,
+    void(const std::array<DimSize_t, 2>&,
+        const std::array<DimSize_t, 2>&,
+        const std::array<DimSize_t, 2>&,
+        const std::array<DimSize_t, 4> &,
+        const void *,
+        const void *,
+        const void *,
+        void *)>;
+
+// Implementation entry point registration to Operator
+REGISTRAR(ConvDepthWise1D_Op, "cpu", Aidge::ConvDepthWiseImpl1D_cpu::create);
+REGISTRAR(ConvDepthWise2D_Op, "cpu", Aidge::ConvDepthWiseImpl2D_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_CONVDEPTHWISEIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/ConvDepthWiseImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/ConvDepthWiseImpl_kernels.hpp
similarity index 83%
rename from include/aidge/backend/cpu/operator/ConvDepthWiseImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/ConvDepthWiseImpl_kernels.hpp
index a02aa672b92f089790ef1903af8b804f816f3baa..ff9bb148fa68d75e2d4b00804e13f063e3ca2cc0 100644
--- a/include/aidge/backend/cpu/operator/ConvDepthWiseImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/ConvDepthWiseImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_CONVDEPTHWISEIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_CONVDEPTHWISEIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_CONVDEPTHWISEIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_CONVDEPTHWISEIMPL_KERNELS_H_
 
 #include <algorithm>
 #include <array>
@@ -86,17 +86,16 @@ void ConvDepthWiseImpl1D_cpu_forward_kernel(const std::array<DimSize_t, 1>& stri
     }
 }
 
-namespace {
-static Registrar<ConvDepthWiseImpl1DForward_cpu> registrarConvDepthWiseImpl1DForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32, DataType::Float32, DataType::Float32},
-        Aidge::ConvDepthWiseImpl1D_cpu_forward_kernel<float, float, float, float>);
-static Registrar<ConvDepthWiseImpl1DForward_cpu> registrarConvDepthWiseImpl1DForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32, DataType::Int32, DataType::Int32},
-        Aidge::ConvDepthWiseImpl1D_cpu_forward_kernel<std::int32_t, std::int32_t, std::int32_t, std::int32_t>);
-static Registrar<ConvDepthWiseImpl1DForward_cpu> registrarConvDepthWiseImpl1DForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64, DataType::Float64, DataType::Float64},
-        Aidge::ConvDepthWiseImpl1D_cpu_forward_kernel<double, double, double, double>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(ConvDepthWiseImpl1D_cpu,
+    {{DataType::Any, DataFormat::NCHW}, {DataType::Float32, DataFormat::NCHW}},
+    {ProdConso::inPlaceModel, Aidge::ConvDepthWiseImpl1D_cpu_forward_kernel<float, float, float, float>, nullptr});
+REGISTRAR(ConvDepthWiseImpl1D_cpu,
+    {{DataType::Any, DataFormat::NCHW}, {DataType::Int32, DataFormat::NCHW}},
+    {ProdConso::inPlaceModel, Aidge::ConvDepthWiseImpl1D_cpu_forward_kernel<std::int32_t, std::int32_t, std::int32_t, std::int32_t>, nullptr});
+REGISTRAR(ConvDepthWiseImpl1D_cpu,
+    {{DataType::Any, DataFormat::NCHW}, {DataType::Float64, DataFormat::NCHW}},
+    {ProdConso::inPlaceModel, Aidge::ConvDepthWiseImpl1D_cpu_forward_kernel<double, double, double, double>, nullptr});
 
 
 /**
@@ -187,17 +186,16 @@ void ConvDepthWiseImpl2D_cpu_forward_kernel(const std::array<DimSize_t, 2>& stri
     }
 }
 
-namespace {
-static Registrar<ConvDepthWiseImpl2DForward_cpu> registrarConvDepthWiseImpl2DForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32, DataType::Float32, DataType::Float32},
-        Aidge::ConvDepthWiseImpl2D_cpu_forward_kernel<float, float, float, float>);
-static Registrar<ConvDepthWiseImpl2DForward_cpu> registrarConvDepthWiseImpl2DForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32, DataType::Int32, DataType::Int32},
-        Aidge::ConvDepthWiseImpl2D_cpu_forward_kernel<std::int32_t, std::int32_t, std::int32_t, std::int32_t>);
-static Registrar<ConvDepthWiseImpl2DForward_cpu> registrarConvDepthWiseImpl2DForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64, DataType::Float64, DataType::Float64},
-        Aidge::ConvDepthWiseImpl2D_cpu_forward_kernel<double, double, double, double>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(ConvDepthWiseImpl2D_cpu,
+    {{DataType::Any, DataFormat::NCHW}, {DataType::Float32, DataFormat::NCHW}},
+    {ProdConso::inPlaceModel, Aidge::ConvDepthWiseImpl2D_cpu_forward_kernel<float, float, float, float>, nullptr});
+REGISTRAR(ConvDepthWiseImpl2D_cpu,
+    {{DataType::Any, DataFormat::NCHW}, {DataType::Int32, DataFormat::NCHW}},
+    {ProdConso::inPlaceModel, Aidge::ConvDepthWiseImpl2D_cpu_forward_kernel<std::int32_t, std::int32_t, std::int32_t, std::int32_t>, nullptr});
+REGISTRAR(ConvDepthWiseImpl2D_cpu,
+    {{DataType::Any, DataFormat::NCHW}, {DataType::Float64, DataFormat::NCHW}},
+    {ProdConso::inPlaceModel, Aidge::ConvDepthWiseImpl2D_cpu_forward_kernel<double, double, double, double>, nullptr});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_CONVDEPTHWISEIMPL_FORWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_CONVDEPTHWISEIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/ConvImpl.hpp b/include/aidge/backend/cpu/operator/ConvImpl.hpp
index d7be46c251a82d1b631f4ad50e7175fa2f896d03..c06d0912f419909013f930867ce3c3238c1a5555 100644
--- a/include/aidge/backend/cpu/operator/ConvImpl.hpp
+++ b/include/aidge/backend/cpu/operator/ConvImpl.hpp
@@ -17,91 +17,41 @@
 #include <tuple>
 #include <vector>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/Conv.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 
 namespace Aidge {
-// class Conv_Op;
-
-// compute kernel registry for forward and backward
-// Conv 1D
-class ConvImpl1DForward_cpu
-    : public Registrable<ConvImpl1DForward_cpu,
-                         std::tuple<DataType, DataType, DataType, DataType>,
-                         void(const std::array<DimSize_t, 1>&,
-                            const std::array<DimSize_t, 1>&,
-                            const std::array<DimSize_t, 1>&,
-                            const std::array<DimSize_t, 3> &,
-                            DimSize_t,
-                            const void *,
-                            const void *,
-                            const void *,
-                            void *)> {};
-
-class ConvImpl1D_cpu : public OperatorImpl {
-   public:
-    ConvImpl1D_cpu(const Conv_Op<1>& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<ConvImpl1D_cpu> create(const Conv_Op<1> &op) {
-        return std::make_unique<ConvImpl1D_cpu>(op);
-    }
-
-   public:
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-    void forward() override;
-};
-
-namespace {
-// add cpu backend to Conv_Op<1> implementation registry
-static Registrar<Conv_Op<1>> registrarConvImpl1D_cpu("cpu", Aidge::ConvImpl1D_cpu::create);
-}  // namespace
-
-// Conv 2D
-class ConvImpl2DForward_cpu
-    : public Registrable<ConvImpl2DForward_cpu,
-                         std::tuple<DataType, DataType, DataType, DataType>,
-                         void(const std::array<DimSize_t, 2>&,
-                            const std::array<DimSize_t, 2>&,
-                            const std::array<DimSize_t, 2>&,
-                            const std::array<DimSize_t, 4> &,
-                            DimSize_t,
-                            const void *,
-                            const void *,
-                            const void *,
-                            void *)> {};
-class ConvImpl2DBackward_cpu
-    : public Registrable<ConvImpl2DBackward_cpu,
-                         std::tuple<DataType, DataType, DataType, DataType>,
-                         void(const std::array<DimSize_t, 2>&,
-                            const std::array<DimSize_t, 2>&,
-                            const std::array<DimSize_t, 2>&,
-                            bool,
-                            const std::array<DimSize_t, 4> &,
-                            const void *,
-                            const void *,
-                            const void *,
-                            void *)> {};
-
-class ConvImpl2D_cpu : public OperatorImpl {
-   public:
-    ConvImpl2D_cpu(const Conv_Op<2>& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<ConvImpl2D_cpu> create(const Conv_Op<2> &op) {
-        return std::make_unique<ConvImpl2D_cpu>(op);
-    }
-
-   public:
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-    void forward() override;
-};
-
-namespace {
-// add cpu backend to Conv_Op<2> implementation registry
-static Registrar<Conv_Op<2>> registrarConvImpl2D_cpu("cpu", Aidge::ConvImpl2D_cpu::create);
-}  // namespace
+// Operator implementation entry point for the backend
+using Conv1D_Op = Conv_Op<1>;
+using ConvImpl1D_cpu = OperatorImpl_cpu<Conv_Op<1>,
+    void(const std::array<DimSize_t, 1>&,
+        const std::array<DimSize_t, 1>&,
+        const std::array<DimSize_t, 1>&,
+        const std::array<DimSize_t, 3> &,
+        DimSize_t,
+        const void *,
+        const void *,
+        const void *,
+        void *)>;
+
+using Conv2D_Op = Conv_Op<2>;
+using ConvImpl2D_cpu = OperatorImpl_cpu<Conv_Op<2>,
+    void(const std::array<DimSize_t, 2>&,
+        const std::array<DimSize_t, 2>&,
+        const std::array<DimSize_t, 2>&,
+        const std::array<DimSize_t, 4> &,
+        DimSize_t,
+        const void *,
+        const void *,
+        const void *,
+        void *)>;
+
+// Implementation entry point registration to Operator
+REGISTRAR(Conv1D_Op, "cpu", Aidge::ConvImpl1D_cpu::create);
+REGISTRAR(Conv2D_Op, "cpu", Aidge::ConvImpl2D_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_CONVIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/ConvImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/ConvImpl_kernels.hpp
similarity index 70%
rename from include/aidge/backend/cpu/operator/ConvImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/ConvImpl_kernels.hpp
index 88a71c47244788f2da5e576c8ad5170a92561909..cc3bd57cb17f2a0feb6a79af2c291e6f960467d8 100644
--- a/include/aidge/backend/cpu/operator/ConvImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/ConvImpl_kernels.hpp
@@ -9,18 +9,20 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_CONVIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_CONVIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_CONVIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_CONVIMPL_KERNELS_H_
 
-#include <algorithm>
 #include <array>
-#include <cmath>
+#include <memory>
+#include <tuple>
+#include <vector>
 
-#include "aidge/backend/cpu/data/GetCPUPtr.h"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/backend/cpu/operator/ConvImpl.hpp"
-#include "aidge/data/half.hpp"
+#include "aidge/operator/Conv.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
+#include "aidge/backend/cpu/data/GetCPUPtr.h"
 
 namespace Aidge {
 /**
@@ -90,20 +92,19 @@ void ConvImpl1D_cpu_forward_kernel(const std::array<DimSize_t, 1>& strideDims,
     }
 }
 
-namespace {
-static Registrar<ConvImpl1DForward_cpu> registrarConvImpl1DForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32, DataType::Float32, DataType::Float32},
-        Aidge::ConvImpl1D_cpu_forward_kernel<float, float, float, float>);
-static Registrar<ConvImpl1DForward_cpu> registrarConvImpl1DForward_cpu_Float16(
-        {DataType::Float16, DataType::Float16, DataType::Float16, DataType::Float16},
-        Aidge::ConvImpl1D_cpu_forward_kernel<half_float::half, half_float::half, half_float::half, half_float::half>);
-static Registrar<ConvImpl1DForward_cpu> registrarConvImpl1DForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32, DataType::Int32, DataType::Int32},
-        Aidge::ConvImpl1D_cpu_forward_kernel<int, int, int, int>);
-static Registrar<ConvImpl1DForward_cpu> registrarConvImpl1DForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64, DataType::Float64, DataType::Float64},
-        Aidge::ConvImpl1D_cpu_forward_kernel<double, double, double, double>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(ConvImpl1D_cpu,
+    {{DataType::Any, DataFormat::NCHW}, {DataType::Float32, DataFormat::NCHW}},
+    {ProdConso::inPlaceModel, Aidge::ConvImpl1D_cpu_forward_kernel<float, float, float, float>, nullptr});
+REGISTRAR(ConvImpl1D_cpu,
+    {{DataType::Any, DataFormat::NCHW}, {DataType::Float16, DataFormat::NCHW}},
+    {ProdConso::inPlaceModel, Aidge::ConvImpl1D_cpu_forward_kernel<half_float::half, half_float::half, half_float::half, half_float::half>, nullptr});
+REGISTRAR(ConvImpl1D_cpu,
+    {{DataType::Any, DataFormat::NCHW}, {DataType::Int32, DataFormat::NCHW}},
+    {ProdConso::inPlaceModel, Aidge::ConvImpl1D_cpu_forward_kernel<int32_t, int32_t, int32_t, int32_t>, nullptr});
+REGISTRAR(ConvImpl1D_cpu,
+    {{DataType::Any, DataFormat::NCHW}, {DataType::Float64, DataFormat::NCHW}},
+    {ProdConso::inPlaceModel, Aidge::ConvImpl1D_cpu_forward_kernel<double, double, double, double>, nullptr});
 
 
 /**
@@ -135,49 +136,6 @@ void ConvImpl2D_cpu_forward_kernel(const std::array<DimSize_t, 2>& strideDims,
     const W *weights = static_cast<const W *>(weights_);
     const B *biases = static_cast<const B *>(biases_);
     O *output = static_cast<O *>(output_);
-/*
-    // output H size
-    const std::size_t oxSize =
-            static_cast<std::size_t>(static_cast<float>(inputDims[0] - kernelDims[0] + strideDims[0]) /
-                                static_cast<float>(strideDims[0]));
-    // output W size
-    const std::size_t oySize =
-            static_cast<std::size_t>(static_cast<float>(inputDims[1] - kernelDims[1] + strideDims[1]) /
-                                static_cast<float>(strideDims[1]));
-
-    // TODO: kernel computation
-    // output (Xout, Yout, outCh, batch)
-    // input  (Xin, Yin, inCh, batch)
-    // weight (kernelX, kernelY, inCh, outCh)
-    // does not take Dilation attribute into account
-    for (std::size_t ox = 0; ox < oxSize; ++ox) {
-        for (std::size_t oy = 0; oy < oySize; ++oy) {
-            const std::size_t ix = ox * strideDims[0];
-            const std::size_t iy = oy * strideDims[1];
-
-            for (std::size_t outCh = 0; outCh < outChannels; ++outCh) {
-                const std::size_t oIndex = inputDims[3] * (outCh + outChannels * (oy + oySize * ox));
-                B biasVal = (biases != nullptr) ? biases[outCh] : B(0);
-                for (std::size_t batch = 0; batch < inputDims[3]; ++batch) {
-                    output[oIndex + batch] = biasVal;
-                }
-                for (std::size_t inCh = 0; inCh < inputDims[2]; ++inCh) {
-                    for (std::size_t sx = 0; sx < kernelDims[0]; ++sx) {
-                        for (std::size_t sy = 0; sy < kernelDims[1]; ++sy) {
-                            const std::size_t wIndex =
-                                    outCh + outChannels * (inCh + inputDims[2] * (sy + kernelDims[1] * sx));
-                            std::size_t iIndex = inputDims[3] * (inCh + inputDims[2] * ((iy + sy) + inputDims[1] * (ix + sx)));
-                            for (std::size_t batch = 0; batch < inputDims[3]; ++batch) {
-                                output[oIndex + batch] += weights[wIndex] * input[iIndex + batch];
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-*/
-
 
     // output H size
     const std::size_t oxSize =
@@ -240,20 +198,19 @@ void ConvImpl2D_cpu_forward_kernel(const std::array<DimSize_t, 2>& strideDims,
     }
 }
 
-namespace {
-static Registrar<ConvImpl2DForward_cpu> registrarConvImpl2DForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32, DataType::Float32, DataType::Float32},
-        Aidge::ConvImpl2D_cpu_forward_kernel<float, float, float, float>);
-static Registrar<ConvImpl2DForward_cpu> registrarConvImpl2DForward_cpu_Float16(
-        {DataType::Float16, DataType::Float16, DataType::Float16, DataType::Float16},
-        Aidge::ConvImpl2D_cpu_forward_kernel<half_float::half, half_float::half, half_float::half, half_float::half>);
-static Registrar<ConvImpl2DForward_cpu> registrarConvImpl2DForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32, DataType::Int32, DataType::Int32},
-        Aidge::ConvImpl2D_cpu_forward_kernel<int, int, int, int>);
-static Registrar<ConvImpl2DForward_cpu> registrarConvImpl2DForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64, DataType::Float64, DataType::Float64},
-        Aidge::ConvImpl2D_cpu_forward_kernel<double, double, double, double>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(ConvImpl2D_cpu,
+    {{DataType::Any, DataFormat::NCHW}, {DataType::Float32, DataFormat::NCHW}},
+    {ProdConso::inPlaceModel, Aidge::ConvImpl2D_cpu_forward_kernel<float, float, float, float>, nullptr});
+REGISTRAR(ConvImpl2D_cpu,
+    {{DataType::Any, DataFormat::NCHW}, {DataType::Float16, DataFormat::NCHW}},
+    {ProdConso::inPlaceModel, Aidge::ConvImpl2D_cpu_forward_kernel<half_float::half, half_float::half, half_float::half, half_float::half>, nullptr});
+REGISTRAR(ConvImpl2D_cpu,
+    {{DataType::Any, DataFormat::NCHW}, {DataType::Int32, DataFormat::NCHW}},
+    {ProdConso::inPlaceModel, Aidge::ConvImpl2D_cpu_forward_kernel<int32_t, int32_t, int32_t, int32_t>, nullptr});
+REGISTRAR(ConvImpl2D_cpu,
+    {{DataType::Any, DataFormat::NCHW}, {DataType::Float64, DataFormat::NCHW}},
+    {ProdConso::inPlaceModel, Aidge::ConvImpl2D_cpu_forward_kernel<double, double, double, double>, nullptr});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_CONVIMPL_FORWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_CONVIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/DivImpl.hpp b/include/aidge/backend/cpu/operator/DivImpl.hpp
index 3a19d7303464e3543bd1ce83e334c4a6bdb713a2..40c1b678a78713d6c3b27629ae898c715797b9b2 100644
--- a/include/aidge/backend/cpu/operator/DivImpl.hpp
+++ b/include/aidge/backend/cpu/operator/DivImpl.hpp
@@ -16,38 +16,18 @@
 #include <tuple>
 #include <vector>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/Div.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 
 namespace Aidge {
+// Operator implementation entry point for the backend
+using DivImpl_cpu = OperatorImpl_cpu<Div_Op,
+    void(const std::size_t, const std::size_t, const std::size_t, const void*, const void*,void*)>;
 
-// compute kernel registry for forward and backward
-class DivImplForward_cpu
-    // : public Registrable<DivImplForward_cpu, std::tuple<DataType, DataType, DataType>, void(const std::vector<std::size_t>&, const std::vector<std::size_t>&, const std::vector<std::size_t>&, const void*, const void*,void*)> {
-    : public Registrable<DivImplForward_cpu, std::tuple<DataType, DataType, DataType>, void(const std::size_t, const std::size_t, const std::size_t, const void*, const void*,void*)> {
-};
-class DivImplBackward_cpu
-    : public Registrable<DivImplBackward_cpu, std::tuple<DataType, DataType, DataType>, void(const std::vector<std::size_t>&, const std::vector<std::size_t>&, const std::vector<std::size_t>&, const void*, const void*, void*)> {
-};
-
-class DivImpl_cpu : public OperatorImpl {
-public:
-    DivImpl_cpu(const Div_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<DivImpl_cpu> create(const Div_Op& op) {
-        return std::make_unique<DivImpl_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-
-    void forward() override final;
-};
-
-namespace {
-static Registrar<Div_Op> registrarDivImpl_cpu("cpu", Aidge::DivImpl_cpu::create);
-}
+// Implementation entry point registration to Operator
+REGISTRAR(Div_Op, "cpu", Aidge::DivImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_DIVIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/DivImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/DivImpl_kernels.hpp
similarity index 77%
rename from include/aidge/backend/cpu/operator/DivImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/DivImpl_kernels.hpp
index 74db1128c111ae62bedb6fa61682abca62429cdb..ed6e55a79acbe23a689a67c22477f64f785a3aef 100644
--- a/include/aidge/backend/cpu/operator/DivImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/DivImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_DIVIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_DIVIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_DIVIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_DIVIMPL_KERNELS_H_
 
 #include <numeric>     // std::accumulate
 #include <cstddef>     // std::size_t
@@ -69,19 +69,16 @@ constexpr void DivImpl_cpu_forward_kernel(const std::size_t input1size_,
     }
 }
 
-
-
-namespace {
-static Registrar<DivImplForward_cpu> registrarDivImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32, DataType::Float32},
-        Aidge::DivImpl_cpu_forward_kernel<float, float, float>);
-static Registrar<DivImplForward_cpu> registrarDivImplForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32, DataType::Int32},
-        Aidge::DivImpl_cpu_forward_kernel<std::int32_t, std::int32_t, std::int32_t>);
-static Registrar<DivImplForward_cpu> registrarDivImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64, DataType::Float64},
-        Aidge::DivImpl_cpu_forward_kernel<double, double, double>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(DivImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::inPlaceModel, Aidge::DivImpl_cpu_forward_kernel<float, float, float>, nullptr});
+REGISTRAR(DivImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::inPlaceModel, Aidge::DivImpl_cpu_forward_kernel<double, double, double>, nullptr});
+REGISTRAR(DivImpl_cpu,
+    {DataType::Int32},
+    {ProdConso::inPlaceModel, Aidge::DivImpl_cpu_forward_kernel<std::int32_t, std::int32_t, std::int32_t>, nullptr});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_DIVIMPL_FORWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_DIVIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/ErfImpl.hpp b/include/aidge/backend/cpu/operator/ErfImpl.hpp
index 6864803a542e4beed0259be9c4722d4215bec449..3d2835600367e81499cbe6af81a8475a0cd1b61e 100644
--- a/include/aidge/backend/cpu/operator/ErfImpl.hpp
+++ b/include/aidge/backend/cpu/operator/ErfImpl.hpp
@@ -12,7 +12,7 @@
 #ifndef AIDGE_CPU_OPERATOR_ERFIMPL_H_
 #define AIDGE_CPU_OPERATOR_ERFIMPL_H_
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/Erf.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
@@ -20,31 +20,12 @@
 #include <vector>
 
 namespace Aidge {
-// class Erf_Op;
+// Operator implementation entry point for the backend
+using ErfImpl_cpu = OperatorImpl_cpu<Erf_Op,
+    void(const std::size_t, const void*, void*)>;
 
-// compute kernel registry for forward and backward
-class ErfImplForward_cpu
-    : public Registrable<ErfImplForward_cpu, std::tuple<DataType, DataType>, void(const std::size_t, const void*, void*)> {
-};
-class ErfImplBackward_cpu
-    : public Registrable<ErfImplBackward_cpu, std::tuple<DataType, DataType>, void(const std::size_t, const void*, void*)> {
-};
-
-class ErfImpl_cpu : public OperatorImpl {
-public:
-    ErfImpl_cpu(const Erf_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<ErfImpl_cpu> create(const Erf_Op& op) {
-        return std::make_unique<ErfImpl_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-    void forward() override;
-};
-
-namespace {
-static Registrar<Erf_Op> registrarErfImpl_cpu("cpu", Aidge::ErfImpl_cpu::create);
-}
+// Implementation entry point registration to Operator
+REGISTRAR(Erf_Op, "cpu", Aidge::ErfImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_ERFIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/ErfImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/ErfImpl_kernels.hpp
similarity index 57%
rename from include/aidge/backend/cpu/operator/ErfImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/ErfImpl_kernels.hpp
index bb92401b6e72b1528d0342474bf394a7c29a4042..02041f55ce9a1b2476db575b40340b1bb6517ce1 100644
--- a/include/aidge/backend/cpu/operator/ErfImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/ErfImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_ERFIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_ERFIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_ERFIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_ERFIMPL_KERNELS_H_
 
 #include <cmath>
 
@@ -32,14 +32,16 @@ void ErfImpl_cpu_forward_kernel(std::size_t inputLenght,
     }
 }
 
-namespace {
-static Registrar<ErfImplForward_cpu> registrarErfImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32}, Aidge::ErfImpl_cpu_forward_kernel<float, float>);
-static Registrar<ErfImplForward_cpu> registrarErfImplForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32}, Aidge::ErfImpl_cpu_forward_kernel<int, int>);
-static Registrar<ErfImplForward_cpu> registrarErfImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64}, Aidge::ErfImpl_cpu_forward_kernel<double, double>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(ErfImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::inPlaceModel, Aidge::ErfImpl_cpu_forward_kernel<float, float>, nullptr});
+REGISTRAR(ErfImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::inPlaceModel, Aidge::ErfImpl_cpu_forward_kernel<double, double>, nullptr});
+REGISTRAR(ErfImpl_cpu,
+    {DataType::Int32},
+    {ProdConso::inPlaceModel, Aidge::ErfImpl_cpu_forward_kernel<std::int32_t, std::int32_t>, nullptr});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_ERFIMPL_FORWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_ERFIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/FCImpl.hpp b/include/aidge/backend/cpu/operator/FCImpl.hpp
index f21cd0ff330f61b942eb55f036c7b23458a5959a..e82352d9cba60440efef87faf97dfd4ed66565b6 100644
--- a/include/aidge/backend/cpu/operator/FCImpl.hpp
+++ b/include/aidge/backend/cpu/operator/FCImpl.hpp
@@ -16,57 +16,33 @@
 #include <memory>
 #include <vector>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/FC.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 
 namespace Aidge {
-// class FC_Op;
-
-// compute kernel registry for forward and backward
-class FCImplForward_cpu : public Registrable<FCImplForward_cpu,
-                                             std::tuple<DataType,
-                                                        DataType,
-                                                        DataType,
-                                                        DataType>,
-                                             void(const DimSize_t,
-                                                const DimSize_t,
-                                                const DimSize_t,
-                                                const void *,
-                                                const void *,
-                                                const void *,
-                                                void *)> {};
-class FCImplBackward_cpu : public Registrable<FCImplBackward_cpu,
-                                              std::tuple<DataType,
-                                                         DataType,
-                                                         DataType,
-                                                         DataType>,
-                                              void(const DimSize_t,
-                                                const DimSize_t,
-                                                const DimSize_t,
-                                                const void *,
-                                                const void *,
-                                                const void *,
-                                                void *,
-                                                void *,
-                                                void *)> {};
-
-class FCImpl_cpu : public OperatorImpl {
-public:
-    FCImpl_cpu(const FC_Op &op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<FCImpl_cpu> create(const FC_Op &op) {
-        return std::make_unique<FCImpl_cpu>(op);
-    }
-
-    void forward() override final;
-    void backward() override final;
-};
-
-namespace {
-static Registrar<FC_Op> registrarFCImpl_cpu("cpu", Aidge::FCImpl_cpu::create);
-}
+// Operator implementation entry point for the backend
+using FCImpl_cpu = OperatorImpl_cpu<FC_Op,
+    void(const DimSize_t,
+        const DimSize_t,
+        const DimSize_t,
+        const void *,
+        const void *,
+        const void *,
+        void *),
+    void(const DimSize_t,
+        const DimSize_t,
+        const DimSize_t,
+        const void *,
+        const void *,
+        const void *,
+        void *,
+        void *,
+        void *)>;
+
+// Implementation entry point registration to Operator
+REGISTRAR(FC_Op, "cpu", Aidge::FCImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_FCIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/FCImpl_backward_kernels.hpp b/include/aidge/backend/cpu/operator/FCImpl_backward_kernels.hpp
deleted file mode 100644
index c93a44d922dce2dc18df94bf903134ddadf5256f..0000000000000000000000000000000000000000
--- a/include/aidge/backend/cpu/operator/FCImpl_backward_kernels.hpp
+++ /dev/null
@@ -1,92 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2023 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_CPU_OPERATOR_FCIMPL_BACKWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_FCIMPL_BACKWARD_KERNEL_H_
-
-#include "aidge/utils/Registrar.hpp"
-#include <algorithm>
-
-#include "aidge/backend/cpu/operator/FCImpl.hpp"
-
-namespace Aidge {
-template <class I, class O, class W, class B>
-void FCImpl_cpu_backward_kernel(const DimSize_t batchSize,
-                                const DimSize_t inputFeatureSize,
-                                const DimSize_t outputFeatureSize,
-                                const void* input_,
-                                const void* originalInput_,
-                                const void* weight_,
-                                void* output_,
-                                void* weightGrad_,
-                                void* biasesGrad_)
-{
-    // FIXME: missing FC attributes as arguments
-    const I* input  = static_cast<const I*>(input_);
-    const I* originalInput  = static_cast<const I*>(originalInput_);
-    const W* weight = static_cast<const W*>(weight_);
-    O* output       = static_cast<O*>(output_);
-    W* weightGrad   = static_cast<W*>(weightGrad_);
-    B* biasesGrad   = static_cast<B*>(biasesGrad_);
-
-
-    // bias grad
-    if (biasesGrad == nullptr) { // no bias
-        std::fill(biasesGrad, biasesGrad + outputFeatureSize, B(0));
-    } else {
-        for (std::size_t o = 0; o < outputFeatureSize; ++o) { // nb outputs
-            B sum{0};
-            for (std::size_t b = 0; b < batchSize; ++b) {
-                sum += input[b*outputFeatureSize + o];
-            }
-            biasesGrad[o] = sum;
-        }
-    }
-
-    // weight grad
-    for (std::size_t o = 0; o < outputFeatureSize; ++o) {
-        for (std::size_t c = 0; c < inputFeatureSize; ++c) {
-            W sum{0};
-            for (std::size_t b = 0; b < batchSize; ++b) {
-                sum += originalInput[b*inputFeatureSize + c]*input[b*outputFeatureSize + o];
-            }
-            weightGrad[o*inputFeatureSize + c] = sum;
-        }
-    }
-
-    // input grad
-    for (std::size_t b = 0; b < batchSize; ++b) {
-        for (std::size_t c = 0; c < inputFeatureSize; ++c) {
-            O sum{0};
-            for (std::size_t o = 0; o < outputFeatureSize; ++o) {
-                sum += weight[o*inputFeatureSize + c] * input[b*outputFeatureSize + o];
-            }
-            output[b*inputFeatureSize + c] = sum;
-        }
-    }
-}
-
-
-namespace {
-static Registrar<FCImplBackward_cpu> registrarFCImpl2DBackward_cpu_Float32(
-        {DataType::Float32, DataType::Float32, DataType::Float32, DataType::Float32},
-        Aidge::FCImpl_cpu_backward_kernel<float, float, float, float>);
-static Registrar<FCImplBackward_cpu> registrarFCImpl2DBackward_cpu_Int32(
-        {DataType::Int32, DataType::Int32, DataType::Int32, DataType::Int32},
-        Aidge::FCImpl_cpu_backward_kernel<int, int, int, int>);
-static Registrar<FCImplBackward_cpu> registrarFCImpl2DBackward_cpu_Float64(
-        {DataType::Float64, DataType::Float64, DataType::Float64, DataType::Float64},
-        Aidge::FCImpl_cpu_backward_kernel<double, double, double, double>);
-}  // namespace
-
-}  // namespace Aidge
-
-#endif /* AIDGE_CPU_OPERATOR_FCIMPL_BACKWARD_KERNEL_H_ */
diff --git a/include/aidge/backend/cpu/operator/FCImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/FCImpl_kernels.hpp
similarity index 62%
rename from include/aidge/backend/cpu/operator/FCImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/FCImpl_kernels.hpp
index caeacd1bda2fde086fd649c50a733e790fc2c000..c57f86e6ac6e74acebb48f471991e7181920f7c3 100644
--- a/include/aidge/backend/cpu/operator/FCImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/FCImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_FCIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_FCIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_FCIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_FCIMPL_KERNELS_H_
 
 #include <algorithm>
 
@@ -115,19 +115,72 @@ void FCImpl_cpu_forward_kernel(const DimSize_t batchSize,
     }
 }
 
+template <class I, class O, class W, class B>
+void FCImpl_cpu_backward_kernel(const DimSize_t batchSize,
+                                const DimSize_t inputFeatureSize,
+                                const DimSize_t outputFeatureSize,
+                                const void* input_,
+                                const void* originalInput_,
+                                const void* weight_,
+                                void* output_,
+                                void* weightGrad_,
+                                void* biasesGrad_)
+{
+    // FIXME: missing FC attributes as arguments
+    const I* input  = static_cast<const I*>(input_);
+    const I* originalInput  = static_cast<const I*>(originalInput_);
+    const W* weight = static_cast<const W*>(weight_);
+    O* output       = static_cast<O*>(output_);
+    W* weightGrad   = static_cast<W*>(weightGrad_);
+    B* biasesGrad   = static_cast<B*>(biasesGrad_);
+
+
+    // bias grad
+    if (biasesGrad == nullptr) { // no bias
+        std::fill(biasesGrad, biasesGrad + outputFeatureSize, B(0));
+    } else {
+        for (std::size_t o = 0; o < outputFeatureSize; ++o) { // nb outputs
+            B sum{0};
+            for (std::size_t b = 0; b < batchSize; ++b) {
+                sum += input[b*outputFeatureSize + o];
+            }
+            biasesGrad[o] = sum;
+        }
+    }
 
-namespace {
-static Registrar<FCImplForward_cpu> registrarFCImpl2DForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32, DataType::Float32, DataType::Float32},
-        Aidge::FCImpl_cpu_forward_kernel<float, float, float, float>);
-static Registrar<FCImplForward_cpu> registrarFCImpl2DForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32, DataType::Int32, DataType::Int32},
-        Aidge::FCImpl_cpu_forward_kernel<int, int, int, int>);
-static Registrar<FCImplForward_cpu> registrarFCImpl2DForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64, DataType::Float64, DataType::Float64},
-        Aidge::FCImpl_cpu_forward_kernel<double, double, double, double>);
-}  // namespace
+    // weight grad
+    for (std::size_t o = 0; o < outputFeatureSize; ++o) {
+        for (std::size_t c = 0; c < inputFeatureSize; ++c) {
+            W sum{0};
+            for (std::size_t b = 0; b < batchSize; ++b) {
+                sum += originalInput[b*inputFeatureSize + c]*input[b*outputFeatureSize + o];
+            }
+            weightGrad[o*inputFeatureSize + c] = sum;
+        }
+    }
+
+    // input grad
+    for (std::size_t b = 0; b < batchSize; ++b) {
+        for (std::size_t c = 0; c < inputFeatureSize; ++c) {
+            O sum{0};
+            for (std::size_t o = 0; o < outputFeatureSize; ++o) {
+                sum += weight[o*inputFeatureSize + c] * input[b*outputFeatureSize + o];
+            }
+            output[b*inputFeatureSize + c] = sum;
+        }
+    }
+}
 
+// Kernels registration to implementation entry point
+REGISTRAR(FCImpl_cpu,
+    {ImplSpec::IOSpec{DataType::Any}, ImplSpec::IOSpec{DataType::Float32}},
+    {ProdConso::defaultModel, Aidge::FCImpl_cpu_forward_kernel<float, float, float, float>, Aidge::FCImpl_cpu_backward_kernel<float, float, float, float>});
+REGISTRAR(FCImpl_cpu,
+    {ImplSpec::IOSpec{DataType::Any}, ImplSpec::IOSpec{DataType::Float64}},
+    {ProdConso::defaultModel, Aidge::FCImpl_cpu_forward_kernel<double, double, double, double>, Aidge::FCImpl_cpu_backward_kernel<double, double, double, double>});
+REGISTRAR(FCImpl_cpu,
+    {ImplSpec::IOSpec{DataType::Any}, ImplSpec::IOSpec{DataType::Int32}},
+    {ProdConso::defaultModel, Aidge::FCImpl_cpu_forward_kernel<int32_t, int32_t, int32_t, int32_t>, Aidge::FCImpl_cpu_backward_kernel<int32_t, int32_t, int32_t, int32_t>});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_FCIMPL_FORWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_FCIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/FoldImpl.hpp b/include/aidge/backend/cpu/operator/FoldImpl.hpp
index 61701138b0cc1c7f0b7dcea0609ca0d463137e08..94ddbdcba8e33e12108968d536037ab1ccab2c8d 100644
--- a/include/aidge/backend/cpu/operator/FoldImpl.hpp
+++ b/include/aidge/backend/cpu/operator/FoldImpl.hpp
@@ -17,39 +17,26 @@
 #include <tuple>
 #include <vector>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/Fold.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 
 namespace Aidge {
-class FoldImpl2DForward_cpu
-    : public Registrable<FoldImpl2DForward_cpu,
-                         std::tuple<DataType, DataType>,
-                         void(const std::array<DimSize_t, 2>&,
-                            const std::array<DimSize_t, 2>&,
-                            const std::array<DimSize_t, 2>&,
-                            const std::array<DimSize_t, 2>&,
-                            const std::vector<DimSize_t> &,
-                            const void *,
-                            void *)> {};
-
-class FoldImpl2D_cpu : public OperatorImpl {
-public:
-    FoldImpl2D_cpu(const Fold_Op<2> &op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<FoldImpl2D_cpu> create(const Fold_Op<2> &op) {
-        return std::make_unique<FoldImpl2D_cpu>(op);
-    }
-
-    void forward() override;
-};
-
-namespace {
-// add cpu backend to Fold_Op<2> implementation registry
-static Registrar<Fold_Op<2>> registrarFoldImpl2D_cpu("cpu", Aidge::FoldImpl2D_cpu::create);
-}  // namespace
+// Operator implementation entry point for the backend
+using Fold2D_Op = Fold_Op<2>;
+using FoldImpl2D_cpu = OperatorImpl_cpu<Fold_Op<2>,
+    void(const std::array<DimSize_t, 2>&,
+        const std::array<DimSize_t, 2>&,
+        const std::array<DimSize_t, 2>&,
+        const std::array<DimSize_t, 2>&,
+        const std::vector<DimSize_t> &,
+        const void *,
+        void *)>;
+
+// Implementation entry point registration to Operator
+REGISTRAR(Fold2D_Op, "cpu", Aidge::FoldImpl2D_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_FOLDIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/FoldImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/FoldImpl_kernels.hpp
similarity index 80%
rename from include/aidge/backend/cpu/operator/FoldImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/FoldImpl_kernels.hpp
index 3dba2319af62fb3dfb2fa75ae9c592ee7ff88e65..8cced8958f49f1cc4215c7cf463cc3391fb29246 100644
--- a/include/aidge/backend/cpu/operator/FoldImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/FoldImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_FOLDIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_FOLDIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_FOLDIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_FOLDIMPL_KERNELS_H_
 
 #include "aidge/utils/Registrar.hpp"
 
@@ -71,17 +71,16 @@ void FoldImpl2D_cpu_forward_kernel(const std::array<DimSize_t, 2>& outputDims,
     }
 }
 
-namespace {
-static Registrar<FoldImpl2DForward_cpu> registrarFoldImpl2DForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32},
-        Aidge::FoldImpl2D_cpu_forward_kernel<float, float>);
-static Registrar<FoldImpl2DForward_cpu> registrarFoldImpl2DForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32},
-        Aidge::FoldImpl2D_cpu_forward_kernel<int, int>);
-static Registrar<FoldImpl2DForward_cpu> registrarFoldImpl2DForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64},
-        Aidge::FoldImpl2D_cpu_forward_kernel<double, double>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(FoldImpl2D_cpu,
+    {DataType::Float32},
+    {ProdConso::defaultModel, Aidge::FoldImpl2D_cpu_forward_kernel<float, float>, nullptr});
+REGISTRAR(FoldImpl2D_cpu,
+    {DataType::Float64},
+    {ProdConso::defaultModel, Aidge::FoldImpl2D_cpu_forward_kernel<double, double>, nullptr});
+REGISTRAR(FoldImpl2D_cpu,
+    {DataType::Int32},
+    {ProdConso::defaultModel, Aidge::FoldImpl2D_cpu_forward_kernel<int32_t, int32_t>, nullptr});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_FOLDIMPL_FORWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_FOLDIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/GlobalAveragePoolingImpl.hpp b/include/aidge/backend/cpu/operator/GlobalAveragePoolingImpl.hpp
index 758535de4cc506b8de4adf7004afbbfdd8185941..4e04b1a595a8660b1528e49921e7e3e7a567829a 100644
--- a/include/aidge/backend/cpu/operator/GlobalAveragePoolingImpl.hpp
+++ b/include/aidge/backend/cpu/operator/GlobalAveragePoolingImpl.hpp
@@ -15,41 +15,18 @@
 #include <memory>
 #include <vector>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/GlobalAveragePooling.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 
 namespace Aidge {
-// class GlobalAveragePooling_Op;
+// Operator implementation entry point for the backend
+using GlobalAveragePoolingImpl_cpu = OperatorImpl_cpu<GlobalAveragePooling_Op,
+    void(const std::vector<DimSize_t> &, const void *, void *)>;
 
-class GlobalAveragePoolingImplForward_cpu
-    : public Registrable<
-          GlobalAveragePoolingImplForward_cpu, std::tuple<DataType, DataType>,
-          void(const std::vector<DimSize_t> &, const void *, void *)> {};
-
-class GlobalAveragePoolingImplBackward_cpu
-    : public Registrable<
-          GlobalAveragePoolingImplBackward_cpu, std::tuple<DataType, DataType>,
-          void(const std::vector<DimSize_t> &, const void *, void *)> {};
-
-class GlobalAveragePoolingImpl_cpu : public OperatorImpl {
-public:
-  GlobalAveragePoolingImpl_cpu(const GlobalAveragePooling_Op &op)
-      : OperatorImpl(op, "cpu") {}
-
-  static std::unique_ptr<GlobalAveragePoolingImpl_cpu>
-  create(const GlobalAveragePooling_Op &op) {
-    return std::make_unique<GlobalAveragePoolingImpl_cpu>(op);
-  }
-
-  void forward() override;
-};
-
-namespace {
-static Registrar<GlobalAveragePooling_Op> registrarGlobalAveragePoolingImpl_cpu(
-    "cpu", Aidge::GlobalAveragePoolingImpl_cpu::create);
-}
+// Implementation entry point registration to Operator
+REGISTRAR(GlobalAveragePooling_Op, "cpu", Aidge::GlobalAveragePoolingImpl_cpu::create);
 } // namespace Aidge
 
 #endif /* _AIDGE_CPU_OPERATOR_GLOBALAVERAGEPOOLINGIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/GlobalAveragePoolingImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/GlobalAveragePoolingImpl_kernels.hpp
similarity index 68%
rename from include/aidge/backend/cpu/operator/GlobalAveragePoolingImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/GlobalAveragePoolingImpl_kernels.hpp
index 81f10975cc107a23448da3df14b88f6b31d55146..ed838a94cc0c0238a870427c3b774b29f7818b09 100644
--- a/include/aidge/backend/cpu/operator/GlobalAveragePoolingImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/GlobalAveragePoolingImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_GLOBALAVERAGEPOOLINGIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_GLOBALAVERAGEPOOLINGIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_GLOBALAVERAGEPOOLINGIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_GLOBALAVERAGEPOOLINGIMPL_KERNELS_H_
 
 #include <cstddef>
 #include <functional>  // std::multiplies
@@ -59,21 +59,16 @@ void GlobalAveragePoolingImpl_cpu_forward_kernel(
   }
 }
 
-// Then we add the Registrar declaration for different input/output types
-namespace {
-static Registrar<GlobalAveragePoolingImplForward_cpu>
-    registrarGlobalAveragePoolingImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32},
-        Aidge::GlobalAveragePoolingImpl_cpu_forward_kernel<float, float>);
-static Registrar<GlobalAveragePoolingImplForward_cpu>
-    registrarGlobalAveragePoolingImplForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32},
-        Aidge::GlobalAveragePoolingImpl_cpu_forward_kernel<int, int>);
-static Registrar<GlobalAveragePoolingImplForward_cpu>
-    registrarGlobalAveragePoolingImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64},
-        Aidge::GlobalAveragePoolingImpl_cpu_forward_kernel<double, double>);
-} // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(GlobalAveragePoolingImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::defaultModel, Aidge::GlobalAveragePoolingImpl_cpu_forward_kernel<float, float>, nullptr});
+REGISTRAR(GlobalAveragePoolingImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::defaultModel, Aidge::GlobalAveragePoolingImpl_cpu_forward_kernel<double, double>, nullptr});
+REGISTRAR(GlobalAveragePoolingImpl_cpu,
+    {DataType::Int32},
+    {ProdConso::defaultModel, Aidge::GlobalAveragePoolingImpl_cpu_forward_kernel<int32_t, int32_t>, nullptr});
 } // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_GLOBALAVERAGEPOOLINGIMPL_FORWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_GLOBALAVERAGEPOOLINGIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/GridSampleImpl.hpp b/include/aidge/backend/cpu/operator/GridSampleImpl.hpp
index a166cb36a601a9a8c7f957b6b65c9b54c47c4e8e..697bb35a983bc108c2a5d65db3c08ef462ffcdbd 100644
--- a/include/aidge/backend/cpu/operator/GridSampleImpl.hpp
+++ b/include/aidge/backend/cpu/operator/GridSampleImpl.hpp
@@ -17,49 +17,22 @@
 #include <tuple>
 #include <vector>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/GridSample.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 
 namespace Aidge {
-
-// compute kernel registry for forward and backward
-class GridSampleImpl1DForward_cpu
-    : public Registrable<GridSampleImpl1DForward_cpu,
-                         std::tuple<DataType, DataType>,
-                         void(const GridSample_Op&,
-                            const std::shared_ptr<Tensor>&,
-                            const std::shared_ptr<Tensor>&,
-                            const std::shared_ptr<Tensor>&)> {};
-
-class GridSampleImpl2DForward_cpu
-    : public Registrable<GridSampleImpl2DForward_cpu,
-                         std::tuple<DataType, DataType>,
-                         void(const GridSample_Op&,
-                            const std::shared_ptr<Tensor>&,
-                            const std::shared_ptr<Tensor>&,
-                            const std::shared_ptr<Tensor>&)> {};
-
-class GridSampleImpl_cpu : public OperatorImpl {
-   public:
-    GridSampleImpl_cpu(const GridSample_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<GridSampleImpl_cpu> create(const GridSample_Op &op) {
-        return std::make_unique<GridSampleImpl_cpu>(op);
-    }
-
-   public:
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-    void forward() override;
-};
-
-namespace {
-// add cpu backend to GridSample_Op<1> implementation registry
-static Registrar<GridSample_Op> registrarGridSampleImpl_cpu("cpu", Aidge::GridSampleImpl_cpu::create);
-}  // namespace
-
+// Operator implementation entry point for the backend
+using GridSampleImpl_cpu = OperatorImpl_cpu<GridSample_Op,
+    void(const GridSample_Op&,
+        const std::shared_ptr<Tensor>&,
+        const std::shared_ptr<Tensor>&,
+        const std::shared_ptr<Tensor>&)>;
+
+// Implementation entry point registration to Operator
+REGISTRAR(GridSample_Op, "cpu", Aidge::GridSampleImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_GRIDSAMPLEIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/GridSampleImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/GridSampleImpl_kernels.hpp
similarity index 91%
rename from include/aidge/backend/cpu/operator/GridSampleImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/GridSampleImpl_kernels.hpp
index 87b6634e467c30c2737afea31a28083d78d00588..ea62fd010db8c155a3ff86ff8396797da5ebb6be 100644
--- a/include/aidge/backend/cpu/operator/GridSampleImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/GridSampleImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_CONVIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_CONVIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_CONVIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_CONVIMPL_KERNELS_H_
 
 #include <algorithm>  // std::max, std::min
 #include <cmath>      // std::fabs, std::trunf, std::nearbyint
@@ -209,19 +209,20 @@ void GridSampleImpl1D_cpu_forward_kernel(const GridSample_Op& op,
     }
 }
 
-namespace {
-static Registrar<GridSampleImpl1DForward_cpu> registrarGridSampleImpl1DForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32},
-        Aidge::GridSampleImpl1D_cpu_forward_kernel<float, float>);
-static Registrar<GridSampleImpl1DForward_cpu> registrarGridSampleImpl1DForward_cpu_Float16(
-        {DataType::Float16, DataType::Float16},
-        Aidge::GridSampleImpl1D_cpu_forward_kernel<half_float::half, half_float::half>);
-static Registrar<GridSampleImpl1DForward_cpu> registrarGridSampleImpl1DForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32},
-        Aidge::GridSampleImpl1D_cpu_forward_kernel<int, int>);
-static Registrar<GridSampleImpl1DForward_cpu> registrarGridSampleImpl1DForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64},
-        Aidge::GridSampleImpl1D_cpu_forward_kernel<double, double>);
+// Kernels registration to implementation entry point
+// only accept 1st input with only 1 spatial feat. (nb dims = 1)
+REGISTRAR(GridSampleImpl_cpu,
+    {{{DataType::Any, DataFormat::Any, {{-1, -1}}}, {DataType::Any}}, {{DataType::Float16}}},
+    {ProdConso::defaultModel, Aidge::GridSampleImpl1D_cpu_forward_kernel<half_float::half, half_float::half>, nullptr});
+REGISTRAR(GridSampleImpl_cpu,
+    {{{DataType::Any, DataFormat::Any, {{-1, -1}}}, {DataType::Any}}, {{DataType::Float32}}},
+    {ProdConso::defaultModel, Aidge::GridSampleImpl1D_cpu_forward_kernel<float, float>, nullptr});
+REGISTRAR(GridSampleImpl_cpu,
+    {{{DataType::Any, DataFormat::Any, {{-1, -1}}}, {DataType::Any}}, {{DataType::Float64}}},
+    {ProdConso::defaultModel, Aidge::GridSampleImpl1D_cpu_forward_kernel<double, double>, nullptr});
+REGISTRAR(GridSampleImpl_cpu,
+    {{{DataType::Any, DataFormat::Any, {{-1, -1}}}, {DataType::Any}}, {{DataType::Int32}}},
+    {ProdConso::defaultModel, Aidge::GridSampleImpl1D_cpu_forward_kernel<int32_t, int32_t>, nullptr});
 
 
 /**
@@ -457,22 +458,20 @@ void GridSampleImpl2D_cpu_forward_kernel(const GridSample_Op& op,
     }
 }
 
-static Registrar<GridSampleImpl2DForward_cpu> registrarGridSampleImpl2DForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32},
-        Aidge::GridSampleImpl2D_cpu_forward_kernel<float, float>);
-static Registrar<GridSampleImpl2DForward_cpu> registrarGridSampleImpl2DForward_cpu_Float16(
-        {DataType::Float16, DataType::Float16},
-        Aidge::GridSampleImpl2D_cpu_forward_kernel<half_float::half, half_float::half>);
-static Registrar<GridSampleImpl2DForward_cpu> registrarGridSampleImpl2DForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32},
-        Aidge::GridSampleImpl2D_cpu_forward_kernel<int, int>);
-static Registrar<GridSampleImpl2DForward_cpu> registrarGridSampleImpl2DForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64},
-        Aidge::GridSampleImpl2D_cpu_forward_kernel<double, double>);
-}  // namespace
-
-
-
+// Kernels registration to implementation entry point
+// only accept 1st input with only 2 spatial feat. (nb dims = 2)
+REGISTRAR(GridSampleImpl_cpu,
+    {{{DataType::Any, DataFormat::Any, {{-1, -1}, {-1, -1}}}, {DataType::Any}}, {{DataType::Float16}}},
+    {ProdConso::defaultModel, Aidge::GridSampleImpl2D_cpu_forward_kernel<half_float::half, half_float::half>, nullptr});
+REGISTRAR(GridSampleImpl_cpu,
+    {{{DataType::Any, DataFormat::Any, {{-1, -1}, {-1, -1}}}, {DataType::Any}}, {{DataType::Float32}}},
+    {ProdConso::defaultModel, Aidge::GridSampleImpl2D_cpu_forward_kernel<float, float>, nullptr});
+REGISTRAR(GridSampleImpl_cpu,
+    {{{DataType::Any, DataFormat::Any, {{-1, -1}, {-1, -1}}}, {DataType::Any}}, {{DataType::Float64}}},
+    {ProdConso::defaultModel, Aidge::GridSampleImpl2D_cpu_forward_kernel<double, double>, nullptr});
+REGISTRAR(GridSampleImpl_cpu,
+    {{{DataType::Any, DataFormat::Any, {{-1, -1}, {-1, -1}}}, {DataType::Any}}, {{DataType::Int32}}},
+    {ProdConso::defaultModel, Aidge::GridSampleImpl2D_cpu_forward_kernel<int32_t, int32_t>, nullptr});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_CONVIMPL_FORWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_CONVIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/LeakyReLUImpl.hpp b/include/aidge/backend/cpu/operator/LeakyReLUImpl.hpp
index c9ad909eee631189a81067eda076c0b8cbb13377..1e8c1a14435f53ad7a63b327944e0bb8c70c8661 100644
--- a/include/aidge/backend/cpu/operator/LeakyReLUImpl.hpp
+++ b/include/aidge/backend/cpu/operator/LeakyReLUImpl.hpp
@@ -16,47 +16,26 @@
 #include <tuple>
 #include <vector>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/LeakyReLU.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 
 namespace Aidge {
-// compute kernel registry for forward and backward
-class LeakyReLUImplForward_cpu
-    : public Registrable<LeakyReLUImplForward_cpu,
-                        std::tuple<DataType, DataType>,
-                        void(const float,
-                            std::size_t,
-                            const void*,
-                            void*)> {};
-class LeakyReLUImplBackward_cpu
-    : public Registrable<LeakyReLUImplBackward_cpu,
-                        std::tuple<DataType, DataType>,
-                        void(const float,
-                            std::size_t,
-                            const void*,
-                            void*)> {};
-
-class LeakyReLUImpl_cpu : public OperatorImpl {
-public:
-    LeakyReLUImpl_cpu(const LeakyReLU_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<LeakyReLUImpl_cpu> create(const LeakyReLU_Op& op) {
-        return std::make_unique<LeakyReLUImpl_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-
-    void forward() override final;
-
-    void backward() override final;
-};
-
-namespace {
-static Registrar<LeakyReLU_Op> registrarLeakyReLUImpl_cpu("cpu", Aidge::LeakyReLUImpl_cpu::create);
-}
+// Operator implementation entry point for the backend
+using LeakyReLUImpl_cpu = OperatorImpl_cpu<LeakyReLU_Op,
+    void(const float,
+        std::size_t,
+        const void*,
+        void*),
+    void(const float,
+        std::size_t,
+        const void*,
+        void*)>;
+
+// Implementation entry point registration to Operator
+REGISTRAR(LeakyReLU_Op, "cpu", Aidge::LeakyReLUImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_LEAKYRELUIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/LeakyReLUImpl_backward_kernels.hpp b/include/aidge/backend/cpu/operator/LeakyReLUImpl_backward_kernels.hpp
deleted file mode 100644
index e308d940890101ad396c7ed20541bbc4f8b035cf..0000000000000000000000000000000000000000
--- a/include/aidge/backend/cpu/operator/LeakyReLUImpl_backward_kernels.hpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2023 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_CPU_OPERATOR_LEAKYRELUIMPL_BACKWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_LEAKYRELUIMPL_BACKWARD_KERNEL_H_
-
-#include "aidge/utils/Registrar.hpp"
-
-#include "aidge/backend/cpu/operator/LeakyReLUImpl.hpp"
-
-namespace Aidge {
-template <class I, class O>
-void LeakyReLUImpl_cpu_backward_kernel(const float negativeSlope_,
-                                     std::size_t inputLenght,
-                                     const void* input_,
-                                     void* output_) {
-
-    const I* input = static_cast<const I*>(input_);
-    O* output = static_cast<O*>(output_);
-    const I negativeSlope = static_cast<const I>(negativeSlope_);
-
-    for (std::size_t i = 0; i < inputLenght; ++i) {
-        output[i] = (input[i] > 0) ? input[i] : negativeSlope*input[i];
-    }
-}
-
-namespace {
-static Registrar<LeakyReLUImplBackward_cpu> registrarLeakyReLUImplBackward_cpu_Float32(
-        {DataType::Float32, DataType::Float32}, Aidge::LeakyReLUImpl_cpu_backward_kernel<float, float>);
-static Registrar<LeakyReLUImplBackward_cpu> registrarLeakyReLUImplBackward_cpu_Int32(
-        {DataType::Int32, DataType::Int32}, Aidge::LeakyReLUImpl_cpu_backward_kernel<int, int>);
-static Registrar<LeakyReLUImplBackward_cpu> registrarLeakyReLUImplBackward_cpu_Float64(
-        {DataType::Float64, DataType::Float64}, Aidge::LeakyReLUImpl_cpu_backward_kernel<double, double>);
-}  // namespace
-}  // namespace Aidge
-
-#endif /* AIDGE_CPU_OPERATOR_LEAKYRELUIMPL_BACKWARD_KERNEL_H_ */
diff --git a/include/aidge/backend/cpu/operator/LeakyReLUImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/LeakyReLUImpl_forward_kernels.hpp
deleted file mode 100644
index 450d0bf4ace4879f90e0104e14b5bf61366e96c2..0000000000000000000000000000000000000000
--- a/include/aidge/backend/cpu/operator/LeakyReLUImpl_forward_kernels.hpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2023 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_CPU_OPERATOR_LEAKYRELUIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_LEAKYRELUIMPL_FORWARD_KERNEL_H_
-
-#include "aidge/utils/Registrar.hpp"
-
-#include "aidge/backend/cpu/operator/LeakyReLUImpl.hpp"
-
-namespace Aidge {
-template <class I, class O>
-void LeakyReLUImpl_cpu_forward_kernel(const float negativeSlope_,
-                                     std::size_t inputLenght,
-                                     const void* input_,
-                                     void* output_) {
-
-    const I* input = static_cast<const I*>(input_);
-    O* output = static_cast<O*>(output_);
-    const I negativeSlope = static_cast<const I>(negativeSlope_);
-
-    for (std::size_t i = 0; i < inputLenght; ++i) {
-        output[i] = (input[i] >= 0) ? input[i] : input[i] * negativeSlope;
-    }
-}
-
-namespace {
-static Registrar<LeakyReLUImplForward_cpu> registrarLeakyReLUImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32}, Aidge::LeakyReLUImpl_cpu_forward_kernel<float, float>);
-static Registrar<LeakyReLUImplForward_cpu> registrarLeakyReLUImplForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32}, Aidge::LeakyReLUImpl_cpu_forward_kernel<int, int>);
-static Registrar<LeakyReLUImplForward_cpu> registrarLeakyReLUImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64}, Aidge::LeakyReLUImpl_cpu_forward_kernel<double, double>);
-}  // namespace
-}  // namespace Aidge
-
-#endif /* AIDGE_CPU_OPERATOR_LEAKYRELUIMPL_FORWARD_KERNEL_H_ */
diff --git a/include/aidge/backend/cpu/operator/LeakyReLUImpl_kernels.hpp b/include/aidge/backend/cpu/operator/LeakyReLUImpl_kernels.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..bc856f703aee8ba422887d43cb96db2132fc4603
--- /dev/null
+++ b/include/aidge/backend/cpu/operator/LeakyReLUImpl_kernels.hpp
@@ -0,0 +1,62 @@
+/********************************************************************************
+ * Copyright (c) 2023 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_CPU_OPERATOR_LEAKYRELUIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_LEAKYRELUIMPL_KERNELS_H_
+
+#include "aidge/utils/Registrar.hpp"
+
+#include "aidge/backend/cpu/operator/LeakyReLUImpl.hpp"
+
+namespace Aidge {
+template <class I, class O>
+void LeakyReLUImpl_cpu_forward_kernel(const float negativeSlope_,
+                                     std::size_t inputLenght,
+                                     const void* input_,
+                                     void* output_) {
+
+    const I* input = static_cast<const I*>(input_);
+    O* output = static_cast<O*>(output_);
+    const I negativeSlope = static_cast<const I>(negativeSlope_);
+
+    for (std::size_t i = 0; i < inputLenght; ++i) {
+        output[i] = (input[i] >= 0) ? input[i] : input[i] * negativeSlope;
+    }
+}
+
+template <class I, class O>
+void LeakyReLUImpl_cpu_backward_kernel(const float negativeSlope_,
+                                     std::size_t inputLenght,
+                                     const void* input_,
+                                     void* output_) {
+
+    const I* input = static_cast<const I*>(input_);
+    O* output = static_cast<O*>(output_);
+    const I negativeSlope = static_cast<const I>(negativeSlope_);
+
+    for (std::size_t i = 0; i < inputLenght; ++i) {
+        output[i] = (input[i] > 0) ? input[i] : negativeSlope*input[i];
+    }
+}
+
+// Kernels registration to implementation entry point
+REGISTRAR(LeakyReLUImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::inPlaceModel, Aidge::LeakyReLUImpl_cpu_forward_kernel<float, float>, Aidge::LeakyReLUImpl_cpu_backward_kernel<float, float>});
+REGISTRAR(LeakyReLUImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::inPlaceModel, Aidge::LeakyReLUImpl_cpu_forward_kernel<double, double>, Aidge::LeakyReLUImpl_cpu_backward_kernel<double, double>});
+REGISTRAR(LeakyReLUImpl_cpu,
+    {DataType::Int32},
+    {ProdConso::inPlaceModel, Aidge::LeakyReLUImpl_cpu_forward_kernel<int32_t, int32_t>, Aidge::LeakyReLUImpl_cpu_backward_kernel<int32_t, int32_t>});
+}  // namespace Aidge
+
+#endif /* AIDGE_CPU_OPERATOR_LEAKYRELUIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/LnImpl.hpp b/include/aidge/backend/cpu/operator/LnImpl.hpp
index faa03855a4f881f2a644ebc4023871b7acd6275c..d48a7ae437d9ed1c7769d3628691993c1e9dcb90 100755
--- a/include/aidge/backend/cpu/operator/LnImpl.hpp
+++ b/include/aidge/backend/cpu/operator/LnImpl.hpp
@@ -12,7 +12,7 @@
 #ifndef AIDGE_CPU_OPERATOR_LNIMPL_H_
 #define AIDGE_CPU_OPERATOR_LNIMPL_H_
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/Ln.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
@@ -21,34 +21,13 @@
 #include <vector>
 
 namespace Aidge {
-// class Ln_Op;
+// Operator implementation entry point for the backend
+using LnImpl_cpu = OperatorImpl_cpu<Ln_Op,
+    void(const std::size_t, const void*, void*),
+    void(const std::size_t, const void*, const void*, void*)>;
 
-// compute kernel registry for forward and backward
-class LnImplForward_cpu
-    : public Registrable<LnImplForward_cpu, std::tuple<DataType, DataType>, void(const std::size_t, const void*, void*)> {
-};
-class LnImplBackward_cpu
-    : public Registrable<LnImplBackward_cpu, std::tuple<DataType, DataType, DataType>, void(const std::size_t, const void*, const void*, void*)> {
-};
-
-class LnImpl_cpu : public OperatorImpl {
-public:
-    LnImpl_cpu(const Ln_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<LnImpl_cpu> create(const Ln_Op& op) {
-        return std::make_unique<LnImpl_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-	
-    void forward() override final;
-
-    void backward() override final;
-};
-
-namespace {
-static Registrar<Ln_Op> registrarLnImpl_cpu("cpu", Aidge::LnImpl_cpu::create);
-}
+// Implementation entry point registration to Operator
+REGISTRAR(Ln_Op, "cpu", Aidge::LnImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_LNIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/LnImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/LnImpl_forward_kernels.hpp
deleted file mode 100755
index ebb975512a6e7c0f7225c305372f0ec6e7060786..0000000000000000000000000000000000000000
--- a/include/aidge/backend/cpu/operator/LnImpl_forward_kernels.hpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2023 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_CPU_OPERATOR_LNIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_LNIMPL_FORWARD_KERNEL_H_
-
-#include "aidge/utils/Registrar.hpp"
-
-#include "aidge/backend/cpu/operator/LnImpl.hpp"
-
-namespace Aidge {
-template <class I, class O>
-void LnImpl_cpu_forward_kernel(std::size_t inputLenght,
-                               const void* input_,
-                               void* output_) {
-
-    const I* input = static_cast<const I*>(input_);
-    O* output = static_cast<O*>(output_);
-	const float eps = 1.0e-20f;
-
-//#pragma omp parallel for if (inputLenght > 1024)
-    for (std::size_t i = 0; i < inputLenght; ++i) {
-		if (input[i] > I(eps)) {
-			output[i] = std::log(input[i]);
-		} else {
-			output[i] = std::log(I(eps));
-		}
-    }
-}
-
-namespace {
-static Registrar<LnImplForward_cpu> registrarLnImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32}, Aidge::LnImpl_cpu_forward_kernel<float, float>);
-static Registrar<LnImplForward_cpu> registrarLnImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64}, Aidge::LnImpl_cpu_forward_kernel<double, double>);
-}  // namespace
-}  // namespace Aidge
-
-#endif /* AIDGE_CPU_OPERATOR_LNIMPL_FORWARD_KERNEL_H_ */
diff --git a/include/aidge/backend/cpu/operator/LnImpl_backward_kernels.hpp b/include/aidge/backend/cpu/operator/LnImpl_kernels.hpp
similarity index 50%
rename from include/aidge/backend/cpu/operator/LnImpl_backward_kernels.hpp
rename to include/aidge/backend/cpu/operator/LnImpl_kernels.hpp
index 5fb82e35f8855d9d6e2eb85e9ab380c9f1fc9b90..b30b05bb806de08d4e70c67e66979fb3138980df 100755
--- a/include/aidge/backend/cpu/operator/LnImpl_backward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/LnImpl_kernels.hpp
@@ -1,50 +1,67 @@
-/********************************************************************************
- * Copyright (c) 2023 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_CPU_OPERATOR_LNIMPL_BACKWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_LNIMPL_BACKWARD_KERNEL_H_
-
-#include <cstddef>  // std::size_t
-
-#include "aidge/backend/cpu/operator/LnImpl.hpp"
-#include "aidge/utils/Registrar.hpp"
-
-namespace Aidge {
-template <class I, class GI, class GO>
-void LnImpl_cpu_backward_kernel(const std::size_t inputLenght,
-                                const void* input_, const void* grad_output_,
-	                            void* grad_input_) {
-						 
-    const I* input = static_cast<const I*>(input_);
-    const GO* grad_output = static_cast<const GO*>(grad_output_);
-    GI* grad_input = static_cast<GI*>(grad_input_);
-	const float eps = 1.0e-20f;
-	
-    for (std::size_t i = 0; i < inputLenght; ++i) {
-		if (input[i] > I(eps)) {
-			grad_input[i] = grad_output[i] / input[i];
-		} else {
-			grad_input[i] = GI(0);
-		}
-    }
-}
-
-namespace {
-static Registrar<LnImplBackward_cpu> registrarLnImplBackward_cpu_Float32(
-    {DataType::Float32, DataType::Float32, DataType::Float32},
-    Aidge::LnImpl_cpu_backward_kernel<float, float, float>);	
-static Registrar<LnImplBackward_cpu> registrarLnImplBackward_cpu_Float64(
-    {DataType::Float64, DataType::Float64, DataType::Float64},
-    Aidge::LnImpl_cpu_backward_kernel<double, double, double>);
-}  // namespace
-}  // namespace Aidge
-
-#endif /* AIDGE_CPU_OPERATOR_LNIMPL_BACKWARD_KERNEL_H_ */
+/********************************************************************************
+ * Copyright (c) 2023 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_CPU_OPERATOR_LNIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_LNIMPL_KERNELS_H_
+
+#include "aidge/utils/Registrar.hpp"
+
+#include "aidge/backend/cpu/operator/LnImpl.hpp"
+
+namespace Aidge {
+template <class I, class O>
+void LnImpl_cpu_forward_kernel(std::size_t inputLenght,
+                               const void* input_,
+                               void* output_) {
+
+    const I* input = static_cast<const I*>(input_);
+    O* output = static_cast<O*>(output_);
+	const float eps = 1.0e-20f;
+
+//#pragma omp parallel for if (inputLenght > 1024)
+    for (std::size_t i = 0; i < inputLenght; ++i) {
+		if (input[i] > I(eps)) {
+			output[i] = std::log(input[i]);
+		} else {
+			output[i] = std::log(I(eps));
+		}
+    }
+}
+
+template <class I, class GI, class GO>
+void LnImpl_cpu_backward_kernel(const std::size_t inputLenght,
+                                const void* input_, const void* grad_output_,
+	                            void* grad_input_) {
+						 
+    const I* input = static_cast<const I*>(input_);
+    const GO* grad_output = static_cast<const GO*>(grad_output_);
+    GI* grad_input = static_cast<GI*>(grad_input_);
+	const float eps = 1.0e-20f;
+	
+    for (std::size_t i = 0; i < inputLenght; ++i) {
+		if (input[i] > I(eps)) {
+			grad_input[i] = grad_output[i] / input[i];
+		} else {
+			grad_input[i] = GI(0);
+		}
+    }
+}
+
+// Kernels registration to implementation entry point
+REGISTRAR(LnImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::inPlaceModel, Aidge::LnImpl_cpu_forward_kernel<float, float>, Aidge::LnImpl_cpu_backward_kernel<float, float, float>});
+REGISTRAR(LnImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::inPlaceModel, Aidge::LnImpl_cpu_forward_kernel<double, double>, Aidge::LnImpl_cpu_backward_kernel<double, double, double>});
+}  // namespace Aidge
+
+#endif /* AIDGE_CPU_OPERATOR_LNIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/MatMulImpl.hpp b/include/aidge/backend/cpu/operator/MatMulImpl.hpp
index e4b76d64baadbcb1baa7d24180c4bb13ed47215b..c07aa5f8ffa62f5fffe3ca02638cc3c66cdaeedb 100644
--- a/include/aidge/backend/cpu/operator/MatMulImpl.hpp
+++ b/include/aidge/backend/cpu/operator/MatMulImpl.hpp
@@ -16,37 +16,20 @@
 #include <memory>
 #include <vector>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/MatMul.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 
 namespace Aidge {
+// Operator implementation entry point for the backend
+using MatMulImpl_cpu = OperatorImpl_cpu<MatMul_Op,
+    void(const std::size_t, const std::size_t, const std::size_t,
+                              const void *, const void *, void *)>;
 
-class MatMulImplForward_cpu
-    : public Registrable<MatMulImplForward_cpu, std::tuple<DataType, DataType>,
-                         void(const std::size_t, const std::size_t, const std::size_t,
-                              const void *, const void *, void *)> {};
-class MatMulImplBackward_cpu
-    : public Registrable<MatMulImplBackward_cpu, std::tuple<DataType, DataType>,
-                         void(const std::vector<DimSize_t>&, const std::vector<DimSize_t>&,
-                              const void *, const void *, void *)> {};
-
-class MatMulImpl_cpu : public OperatorImpl {
-public:
-    MatMulImpl_cpu(const MatMul_Op &op): OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<MatMulImpl_cpu> create(const MatMul_Op &op) {
-        return std::make_unique<MatMulImpl_cpu>(op);
-    }
-
-    void forward() override;
-};
-
-namespace {
-static Registrar<MatMul_Op> registrarMatMulImpl_cpu("cpu", Aidge::MatMulImpl_cpu::create);
-}
+// Implementation entry point registration to Operator
+REGISTRAR(MatMul_Op, "cpu", Aidge::MatMulImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_MATMULIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/MatMulImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/MatMulImpl_forward_kernels.hpp
deleted file mode 100644
index 5045580fa599aac64f2c1414bfdf2b87ea57e313..0000000000000000000000000000000000000000
--- a/include/aidge/backend/cpu/operator/MatMulImpl_forward_kernels.hpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2023 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_CPU_OPERATOR_MATMULIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_MATMULIMPL_FORWARD_KERNEL_H_
-
-#include "aidge/backend/cpu/operator/MatMulImpl.hpp"
-
-namespace Aidge {
-
-template <class I, class O>
-void MatMulImpl_cpu_forward_kernel(const std::size_t n, const std::size_t k, const std::size_t m,
-                                    const void* input1_, const void* input2_, void* output_) {
-    // FIXME: missing MatMul parameters as arguments
-    const I* input1 = static_cast<const I*>(input1_);
-    const I* input2 = static_cast<const I*>(input2_);
-    O* output = static_cast<O*>(output_);
-
-    for (std::size_t i = 0; i < n; ++i) {
-        for (std::size_t j = 0; j < m; ++j) {
-            O sum = O(0);
-            for (std::size_t l = 0; l < k; ++l) {
-                sum += static_cast<O>(input1[i*k + l] * input2[l*m + j]);
-            }
-            output[i*m + j] = sum;
-        }
-    }
-}
-
-namespace {
-static Registrar<MatMulImplForward_cpu> registrarMatMulImpl2DForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32},
-        Aidge::MatMulImpl_cpu_forward_kernel<float, float>);
-static Registrar<MatMulImplForward_cpu> registrarMatMulImpl2DForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32},
-        Aidge::MatMulImpl_cpu_forward_kernel<int, int>);
-static Registrar<MatMulImplForward_cpu> registrarMatMulImpl2DForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64},
-        Aidge::MatMulImpl_cpu_forward_kernel<double, double>);
-}  // namespace
-
-}  // namespace Aidge
-
-#endif /* AIDGE_CPU_OPERATOR_MATMULIMPL_FORWARD_KERNEL_H_ */
diff --git a/include/aidge/backend/cpu/operator/MatMulImpl_kernels.hpp b/include/aidge/backend/cpu/operator/MatMulImpl_kernels.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5fc13baf49b1d0606eb4af5a54eec83fa5dce22a
--- /dev/null
+++ b/include/aidge/backend/cpu/operator/MatMulImpl_kernels.hpp
@@ -0,0 +1,50 @@
+/********************************************************************************
+ * Copyright (c) 2023 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_CPU_OPERATOR_MATMULIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_MATMULIMPL_KERNELS_H_
+
+#include "aidge/backend/cpu/operator/MatMulImpl.hpp"
+
+namespace Aidge {
+
+template <class I, class O>
+void MatMulImpl_cpu_forward_kernel(const std::size_t n, const std::size_t k, const std::size_t m,
+                                    const void* input1_, const void* input2_, void* __restrict output_) {
+    // FIXME: missing MatMul parameters as arguments
+    const I* input1 = static_cast<const I*>(input1_);
+    const I* input2 = static_cast<const I*>(input2_);
+    O* __restrict output = static_cast<O* __restrict>(output_);
+
+    std::memset(output, O(0), n * m * sizeof(O));
+
+    for (std::size_t i = 0; i < n; ++i) {
+        for (std::size_t l = 0; l < k; ++l) {
+            for (std::size_t j = 0; j < m; ++j) {
+                output[i*m + j] += static_cast<O>(input1[i*k + l] * input2[l*m + j]);
+            }
+        }
+    }
+}
+
+// Kernels registration to implementation entry point
+REGISTRAR(MatMulImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::defaultModel, Aidge::MatMulImpl_cpu_forward_kernel<float, float>, nullptr});
+REGISTRAR(MatMulImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::defaultModel, Aidge::MatMulImpl_cpu_forward_kernel<double, double>, nullptr});
+REGISTRAR(MatMulImpl_cpu,
+    {DataType::Int32},
+    {ProdConso::defaultModel, Aidge::MatMulImpl_cpu_forward_kernel<int32_t, int32_t>, nullptr});
+}  // namespace Aidge
+
+#endif /* AIDGE_CPU_OPERATOR_MATMULIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/MaxPoolingImpl.hpp b/include/aidge/backend/cpu/operator/MaxPoolingImpl.hpp
index 4dd30e1fb939837f6861313eda04d7d05f3c8110..68cc3621514de97d9837e10bcf90218abe559aaa 100644
--- a/include/aidge/backend/cpu/operator/MaxPoolingImpl.hpp
+++ b/include/aidge/backend/cpu/operator/MaxPoolingImpl.hpp
@@ -17,51 +17,25 @@
 #include <tuple>
 #include <vector>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/MaxPooling.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 
 namespace Aidge {
-// class MaxPooling_Op;
-
-// compute kernel registry for forward and backward
-class MaxPoolingImpl2DForward_cpu
-    : public Registrable<MaxPoolingImpl2DForward_cpu,
-                        std::tuple<DataType, DataType>,
-                        void(const std::array<DimSize_t, 2>&,
-                            const std::array<DimSize_t, 2>&,
-                            const bool,
-                            const std::array<DimSize_t, 4> &,
-                            const void *,
-                            void *)> {};
-class MaxPoolingImpl2DBackward_cpu
-    : public Registrable<MaxPoolingImpl2DBackward_cpu,
-                        std::tuple<DataType, DataType>,
-                        void(const std::array<DimSize_t, 2>&,
+// Operator implementation entry point for the backend
+using MaxPooling2D_Op = MaxPooling_Op<2>;
+using MaxPoolingImpl2D_cpu = OperatorImpl_cpu<MaxPooling_Op<2>,
+    void(const std::array<DimSize_t, 2>&,
                             const std::array<DimSize_t, 2>&,
                             const bool,
                             const std::array<DimSize_t, 4> &,
                             const void *,
-                            void *)> {};
-
-class MaxPoolingImpl2D_cpu : public OperatorImpl {
-public:
-    MaxPoolingImpl2D_cpu(const MaxPooling_Op<2> &op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<MaxPoolingImpl2D_cpu> create(const MaxPooling_Op<2> &op) {
-        return std::make_unique<MaxPoolingImpl2D_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-    void forward() override;
-};
+                            void *)>;
 
-namespace {
-// add cpu backend to MaxPooling_Op<2> implementation registry
-static Registrar<MaxPooling_Op<2>> registrarMaxPoolingImpl2D_cpu("cpu", Aidge::MaxPoolingImpl2D_cpu::create);
-}  // namespace
+// Implementation entry point registration to Operator
+REGISTRAR(MaxPooling2D_Op, "cpu", Aidge::MaxPoolingImpl2D_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_MaxPOOLINGIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/MaxPoolingImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/MaxPoolingImpl_kernels.hpp
similarity index 91%
rename from include/aidge/backend/cpu/operator/MaxPoolingImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/MaxPoolingImpl_kernels.hpp
index 79a7bd154f4d4e19a71d719597992466c37c6a9f..7b6f04f141eb701849a8d436561bcf9e37471cfa 100644
--- a/include/aidge/backend/cpu/operator/MaxPoolingImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/MaxPoolingImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_MaxPOOLINGIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_MaxPOOLINGIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_MaxPOOLINGIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_MaxPOOLINGIMPL_KERNELS_H_
 
 #include <array>
 #include <cmath>
@@ -199,17 +199,16 @@ void N2D2::PoolCell_Frame_Kernels::forwardMax(const T* alpha,
 
 */
 
-namespace {
-static Registrar<MaxPoolingImpl2DForward_cpu> registrarMaxPoolingImpl2DForward_cpu_Float32(
-        std::tuple<DataType, DataType>({DataType::Float32, DataType::Float32}),
-        Aidge::MaxPoolingImpl2D_cpu_forward_kernel<float, float>);
-static Registrar<MaxPoolingImpl2DForward_cpu> registrarMaxPoolingImpl2DForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32},
-        Aidge::MaxPoolingImpl2D_cpu_forward_kernel<int, int>);
-static Registrar<MaxPoolingImpl2DForward_cpu> registrarMaxPoolingImpl2DForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64},
-        Aidge::MaxPoolingImpl2D_cpu_forward_kernel<double, double>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(MaxPoolingImpl2D_cpu,
+    {DataType::Float32},
+    {ProdConso::inPlaceModel, Aidge::MaxPoolingImpl2D_cpu_forward_kernel<float, float>, nullptr});
+REGISTRAR(MaxPoolingImpl2D_cpu,
+    {DataType::Float64},
+    {ProdConso::inPlaceModel, Aidge::MaxPoolingImpl2D_cpu_forward_kernel<double, double>, nullptr});
+REGISTRAR(MaxPoolingImpl2D_cpu,
+    {DataType::Int32},
+    {ProdConso::inPlaceModel, Aidge::MaxPoolingImpl2D_cpu_forward_kernel<int32_t, int32_t>, nullptr});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_MaxPOOLINGIMPL_FORWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_MaxPOOLINGIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/MulImpl.hpp b/include/aidge/backend/cpu/operator/MulImpl.hpp
index 008edf176594a326e464a242f9f31d7b936a6940..05fceba17471229d83d9f8738614b2e747121b49 100644
--- a/include/aidge/backend/cpu/operator/MulImpl.hpp
+++ b/include/aidge/backend/cpu/operator/MulImpl.hpp
@@ -12,7 +12,7 @@
 #ifndef AIDGE_CPU_OPERATOR_MULIMPL_H_
 #define AIDGE_CPU_OPERATOR_MULIMPL_H_
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/Mul.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
@@ -21,47 +21,27 @@
 #include <vector>
 
 namespace Aidge {
-// class Mul_Op;
-
-// compute kernel registry for forward and backward
-class MulImplForward_cpu
-    : public Registrable<MulImplForward_cpu, std::tuple<DataType, DataType, DataType>, void(const std::vector<std::size_t>&, 
-                                                                                            const std::vector<std::size_t>&, 
-                                                                                            const std::vector<std::size_t>&, 
-                                                                                            const void*, 
-                                                                                            const void*,
-                                                                                            void*)> {};
-
-class MulImplBackward_cpu
-    : public Registrable<MulImplBackward_cpu, std::tuple<DataType, DataType, DataType>, void(const std::size_t, 
-                                                                                             const std::size_t, 
-                                                                                             const std::size_t,
-                                                                                             const std::vector<std::size_t>,
-                                                                                             const std::vector<std::size_t>,
-                                                                                             const void*, 
-                                                                                             const void*, 
-                                                                                             const void*, 
-                                                                                             void*, 
-                                                                                             void*)> {};
-
-
-class MulImpl_cpu : public OperatorImpl {
-public:
-    MulImpl_cpu(const Mul_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<MulImpl_cpu> create(const Mul_Op& op) {
-        return std::make_unique<MulImpl_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-
-    void forward() override;
-    void backward() override;
-};
-
-namespace {
-static Registrar<Mul_Op> registrarMulImpl_cpu("cpu", Aidge::MulImpl_cpu::create);
-}
+// Operator implementation entry point for the backend
+using MulImpl_cpu = OperatorImpl_cpu<Mul_Op,
+    void(const std::vector<std::size_t>&,
+        const std::vector<std::size_t>&, 
+        const std::vector<std::size_t>&, 
+        const void*, 
+        const void*,
+        void*),
+    void(const std::size_t, 
+        const std::size_t, 
+        const std::size_t,
+        const std::vector<std::size_t>,
+        const std::vector<std::size_t>,
+        const void*, 
+        const void*, 
+        const void*, 
+        void*, 
+        void*)>;
+
+// Implementation entry point registration to Operator
+REGISTRAR(Mul_Op, "cpu", Aidge::MulImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_MULIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/MulImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/MulImpl_forward_kernels.hpp
deleted file mode 100644
index c44199ba4797682362f4a7cb223435d6d1585443..0000000000000000000000000000000000000000
--- a/include/aidge/backend/cpu/operator/MulImpl_forward_kernels.hpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2023 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_CPU_OPERATOR_MULIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_MULIMPL_FORWARD_KERNEL_H_
-
-#include "aidge/utils/Registrar.hpp"
-
-#include <cstdint>     // std::int32_t, std::int64_t
-
-#include "aidge/backend/cpu/data/Broadcasting.hpp"
-#include "aidge/backend/cpu/operator/MulImpl.hpp"
-
-namespace Aidge {
-template <class I1, class I2, class O>
-void MulImpl_cpu_forward_kernel(const std::vector<std::size_t>& input1Dims,
-                                const std::vector<std::size_t>& input2Dims,
-                                const std::vector<std::size_t>& outputDims,
-                                const void* input1_,
-                                const void* input2_,
-                                void* output_) {
-
-    const I1* input_1 = static_cast<const I1*>(input1_);
-    const I2* input_2 = static_cast<const I2*>(input2_);
-    O* output = static_cast<O*>(output_);
-
-    size_t totalElements = 1;
-    for (size_t dimSize : outputDims) {
-        totalElements *= dimSize;
-    }
-
-	for (std::size_t oIndex = 0; oIndex < totalElements; ++oIndex)
-	{
-		std::vector<size_t> indexes = getMultiDimIndices(outputDims, oIndex);
-
-		std::size_t idx1 = getFlattenedIndex(input1Dims, indexes);
-		std::size_t idx2 = getFlattenedIndex(input2Dims, indexes);
-
-        output[oIndex] = input_1[idx1] * input_2[idx2];
-    }
-}
-
-namespace {
-static Registrar<MulImplForward_cpu> registrarMulImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32, DataType::Float32},
-        Aidge::MulImpl_cpu_forward_kernel<float, float, float>);
-static Registrar<MulImplForward_cpu> registrarMulImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64, DataType::Float64},
-        Aidge::MulImpl_cpu_forward_kernel<double, double, double>);
-static Registrar<MulImplForward_cpu> registrarMulImplForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32, DataType::Int32},
-        Aidge::MulImpl_cpu_forward_kernel<std::int32_t, std::int32_t, std::int32_t>);
-static Registrar<MulImplForward_cpu> registrarMulImplForward_cpu_Int64(
-        {DataType::Int64, DataType::Int64, DataType::Int64},
-        Aidge::MulImpl_cpu_forward_kernel<std::int64_t, std::int64_t, std::int64_t>);
-}  // namespace
-}  // namespace Aidge
-
-#endif /* AIDGE_CPU_OPERATOR_MULIMPL_FORWARD_KERNEL_H_ */
diff --git a/include/aidge/backend/cpu/operator/MulImpl_backward_kernels.hpp b/include/aidge/backend/cpu/operator/MulImpl_kernels.hpp
similarity index 51%
rename from include/aidge/backend/cpu/operator/MulImpl_backward_kernels.hpp
rename to include/aidge/backend/cpu/operator/MulImpl_kernels.hpp
index db4cf81f0733b476957acc5cc21ad31e9c88ac72..c015b8f0182608fecd3da94220e9411decfd186c 100644
--- a/include/aidge/backend/cpu/operator/MulImpl_backward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/MulImpl_kernels.hpp
@@ -1,17 +1,52 @@
-#ifndef AIDGE_CPU_OPERATOR_MULIMPL_BACKWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_MULIMPL_BACKWARD_KERNEL_H_
-
+/********************************************************************************
+ * Copyright (c) 2023 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_CPU_OPERATOR_MULIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_MULIMPL_KERNELS_H_
 
 #include "aidge/utils/Registrar.hpp"
 
 #include <cstdint>     // std::int32_t, std::int64_t
-#include <algorithm>
 
 #include "aidge/backend/cpu/data/Broadcasting.hpp"
 #include "aidge/backend/cpu/operator/MulImpl.hpp"
 
-
 namespace Aidge {
+template <class I1, class I2, class O>
+void MulImpl_cpu_forward_kernel(const std::vector<std::size_t>& input1Dims,
+                                const std::vector<std::size_t>& input2Dims,
+                                const std::vector<std::size_t>& outputDims,
+                                const void* input1_,
+                                const void* input2_,
+                                void* output_) {
+
+    const I1* input_1 = static_cast<const I1*>(input1_);
+    const I2* input_2 = static_cast<const I2*>(input2_);
+    O* output = static_cast<O*>(output_);
+
+    size_t totalElements = 1;
+    for (size_t dimSize : outputDims) {
+        totalElements *= dimSize;
+    }
+
+	for (std::size_t oIndex = 0; oIndex < totalElements; ++oIndex)
+	{
+		std::vector<size_t> indexes = getMultiDimIndices(outputDims, oIndex);
+
+		std::size_t idx1 = getFlattenedIndex(input1Dims, indexes);
+		std::size_t idx2 = getFlattenedIndex(input2Dims, indexes);
+
+        output[oIndex] = input_1[idx1] * input_2[idx2];
+    }
+}
 
 template <class I1, class I2, class O>
 void MulImpl_cpu_backward_kernel(const std::size_t input0Length, 
@@ -73,20 +108,19 @@ void MulImpl_cpu_backward_kernel(const std::size_t input0Length,
     }
 }
 
-
-namespace {
-static Registrar<MulImplBackward_cpu> registrarMulImplBackward_cpu_Float32(
-        {DataType::Float32, DataType::Float32, DataType::Float32},
-        Aidge::MulImpl_cpu_backward_kernel<float, float, float>);
-static Registrar<MulImplBackward_cpu> registrarMulImplBackward_cpu_Float64(
-        {DataType::Float64, DataType::Float64, DataType::Float64},
-        Aidge::MulImpl_cpu_backward_kernel<double, double, double>);
-static Registrar<MulImplBackward_cpu> registrarMulImplBackward_cpu_Int32(
-        {DataType::Int32, DataType::Int32, DataType::Int32},
-        Aidge::MulImpl_cpu_backward_kernel<std::int32_t, std::int32_t, std::int32_t>);
-static Registrar<MulImplBackward_cpu> registrarMulImplBackward_cpu_Int64(
-        {DataType::Int64, DataType::Int64, DataType::Int64},
-        Aidge::MulImpl_cpu_backward_kernel<std::int64_t, std::int64_t, std::int64_t>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(MulImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::inPlaceModel, Aidge::MulImpl_cpu_forward_kernel<float, float, float>, Aidge::MulImpl_cpu_backward_kernel<float, float, float>});
+REGISTRAR(MulImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::inPlaceModel, Aidge::MulImpl_cpu_forward_kernel<double, double, double>, Aidge::MulImpl_cpu_backward_kernel<double, double, double>});
+REGISTRAR(MulImpl_cpu,
+    {DataType::Int32},
+    {ProdConso::inPlaceModel, Aidge::MulImpl_cpu_forward_kernel<std::int32_t, std::int32_t, std::int32_t>, Aidge::MulImpl_cpu_backward_kernel<std::int32_t, std::int32_t, std::int32_t>});
+REGISTRAR(MulImpl_cpu,
+    {DataType::Int64},
+    {ProdConso::inPlaceModel, Aidge::MulImpl_cpu_forward_kernel<std::int64_t, std::int64_t, std::int64_t>, Aidge::MulImpl_cpu_backward_kernel<std::int64_t, std::int64_t, std::int64_t>});
 }  // namespace Aidge
-#endif
+
+#endif /* AIDGE_CPU_OPERATOR_MULIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/OperatorImpl.hpp b/include/aidge/backend/cpu/operator/OperatorImpl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..abf94ab9069a07e8f87819cb29c027b1adbfd9c6
--- /dev/null
+++ b/include/aidge/backend/cpu/operator/OperatorImpl.hpp
@@ -0,0 +1,50 @@
+/********************************************************************************
+ * Copyright (c) 2023 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_CPU_OPERATOR_IMPL_H_
+#define AIDGE_CPU_OPERATOR_IMPL_H_
+
+#include <cstddef>  // std::size_t
+#include <memory>
+#include <tuple>    // std::tuple
+#include <vector>
+
+#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/utils/Registrar.hpp"
+#include "aidge/utils/Types.h"
+
+namespace Aidge {
+template <class Op, class FwdFunc, class BwdFunc = void()>
+class OperatorImpl_cpu : public OperatorImpl,
+    public Registrable<OperatorImpl_cpu<Op, FwdFunc, BwdFunc>, ImplSpec, Impl<FwdFunc, BwdFunc>>
+{
+public:
+    OperatorImpl_cpu(const Op& op) : OperatorImpl(op, "cpu") {}
+
+    static std::unique_ptr<OperatorImpl_cpu<Op, FwdFunc, BwdFunc>> create(const Op& op) {
+        return std::make_unique<OperatorImpl_cpu<Op, FwdFunc, BwdFunc>>(op);
+    }
+
+    virtual std::shared_ptr<ProdConso> getProdConso() const override {
+        const auto impl = Registrar<OperatorImpl_cpu>::create(getBestMatch(getRequiredSpec()));
+        return impl.prodConso(mOp);
+    }
+
+    virtual std::set<ImplSpec> getAvailableImplSpecs() const override {
+        return Registrar<OperatorImpl_cpu>::getKeys();
+    }
+
+    void forward() override;
+    void backward() override;
+};
+}  // namespace Aidge
+
+#endif /* AIDGE_CPU_OPERATOR_IMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/PadImpl.hpp b/include/aidge/backend/cpu/operator/PadImpl.hpp
index c6e41c29fd203fdd80b2acb9ad0dfcac91a0f66c..bc0bd8cad3b630b89f728d78b59652f31bbcf410 100644
--- a/include/aidge/backend/cpu/operator/PadImpl.hpp
+++ b/include/aidge/backend/cpu/operator/PadImpl.hpp
@@ -17,79 +17,46 @@
 #include <tuple>
 #include <vector>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/Pad.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 
 namespace Aidge {
-// class Pad_Op;
-// compute kernel registry for forward and backward
-class PadImpl1DForward_cpu
-    : public Registrable<PadImpl1DForward_cpu,
-                         std::tuple<DataType, DataType>,
-                         void(const std::array<DimSize_t, 2>&,
-                            const PadBorderType,
-                            const double,
-                            const std::array<DimSize_t, 3> &,
-                            const void *,
-                            void *)> {};
-
-class PadImpl1D_cpu : public OperatorImpl {
+class Pad_ProdConso_cpu : public ProdConso {
 public:
-    PadImpl1D_cpu(const Pad_Op<1> &op) : OperatorImpl(op, "cpu") {}
+    Pad_ProdConso_cpu(const Operator& op): ProdConso(op) {}
 
-    static std::unique_ptr<PadImpl1D_cpu> create(const Pad_Op<1> &op) {
-        return std::make_unique<PadImpl1D_cpu>(op);
+    static std::unique_ptr<ProdConso> defaultModel(const Operator& op) {
+        return std::make_unique<Pad_ProdConso_cpu>(op);
     }
 
     Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-    void forward() override;
 };
 
-namespace {
-// add cpu backend to Pad_Op<1> implementation registry
-static Registrar<Pad_Op<1>> registrarPadImpl1D_cpu("cpu", Aidge::PadImpl1D_cpu::create);
-}  // namespace
-
-
-// compute kernel registry for forward and backward
-class PadImpl2DForward_cpu
-    : public Registrable<PadImpl2DForward_cpu,
-                         std::tuple<DataType, DataType>,
-                         void(const std::array<DimSize_t, 4>&,
+// Operator implementation entry point for the backend
+using Pad1D_Op = Pad_Op<1>;
+using PadImpl1D_cpu = OperatorImpl_cpu<Pad_Op<1>,
+    void(const std::array<DimSize_t, 2>&,
                             const PadBorderType,
                             const double,
-                            const std::array<DimSize_t, 4> &,
+                            const std::array<DimSize_t, 3> &,
                             const void *,
-                            void *)> {};
-class PadImpl2DBackward_cpu
-    : public Registrable<PadImpl2DBackward_cpu,
-                         std::tuple<DataType, DataType>,
-                         void(const std::array<DimSize_t, 4>&,
+                            void *)>;
+
+using Pad2D_Op = Pad_Op<2>;
+using PadImpl2D_cpu = OperatorImpl_cpu<Pad_Op<2>,
+    void(const std::array<DimSize_t, 4>&,
                             const PadBorderType,
                             const double,
                             const std::array<DimSize_t, 4> &,
                             const void *,
-                            void *)> {};
-
-class PadImpl2D_cpu : public OperatorImpl {
-public:
-    PadImpl2D_cpu(const Pad_Op<2> &op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<PadImpl2D_cpu> create(const Pad_Op<2> &op) {
-        return std::make_unique<PadImpl2D_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-    void forward() override;
-};
+                            void *)>;
 
-namespace {
-// add cpu backend to Pad_Op<2> implementation registry
-static Registrar<Pad_Op<2>> registrarPadImpl2D_cpu("cpu", Aidge::PadImpl2D_cpu::create);
-}  // namespace
+// Implementation entry point registration to Operator
+REGISTRAR(Pad1D_Op, "cpu", Aidge::PadImpl1D_cpu::create);
+REGISTRAR(Pad2D_Op, "cpu", Aidge::PadImpl2D_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_PADIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/PadImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/PadImpl_kernels.hpp
similarity index 81%
rename from include/aidge/backend/cpu/operator/PadImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/PadImpl_kernels.hpp
index 8ca23a4d39600af29065992804d75c42b822ea1b..a362be0944aa18c36dd74a2f0066aaa21a1fc4c0 100644
--- a/include/aidge/backend/cpu/operator/PadImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/PadImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_PADIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_PADIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_PADIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_PADIMPL_KERNELS_H_
 
 #include <algorithm>  // std::max, std::min
 #include <array>
@@ -88,17 +88,16 @@ void PadImpl1D_cpu_forward_kernel(const std::array<DimSize_t, 2>& beginEndBorder
     }
 }
 
-namespace {
-static Registrar<PadImpl1DForward_cpu> registrarPadImpl1DForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32},
-        PadImpl1D_cpu_forward_kernel<cpptype_t<DataType::Float32>, cpptype_t<DataType::Float32>>);
-static Registrar<PadImpl1DForward_cpu> registrarPadImpl1DForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32},
-        PadImpl1D_cpu_forward_kernel<cpptype_t<DataType::Int32>, cpptype_t<DataType::Int32>>);
-static Registrar<PadImpl1DForward_cpu> registrarPadImpl1DForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64},
-        PadImpl1D_cpu_forward_kernel<cpptype_t<DataType::Float64>, cpptype_t<DataType::Float64>>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(PadImpl1D_cpu,
+    {{DataType::Float32, DataFormat::NCHW}, {DataType::Float32, DataFormat::NCHW}},
+    {Pad_ProdConso_cpu::defaultModel, Aidge::PadImpl1D_cpu_forward_kernel<cpptype_t<DataType::Float32>, cpptype_t<DataType::Float32>>, nullptr});
+REGISTRAR(PadImpl1D_cpu,
+    {{DataType::Float64, DataFormat::NCHW}, {DataType::Float64, DataFormat::NCHW}},
+    {Pad_ProdConso_cpu::defaultModel, Aidge::PadImpl1D_cpu_forward_kernel<cpptype_t<DataType::Float64>, cpptype_t<DataType::Float64>>, nullptr});
+REGISTRAR(PadImpl1D_cpu,
+    {{DataType::Int32, DataFormat::NCHW}, {DataType::Int32, DataFormat::NCHW}},
+    {Pad_ProdConso_cpu::defaultModel, Aidge::PadImpl1D_cpu_forward_kernel<cpptype_t<DataType::Int32>, cpptype_t<DataType::Int32>>, nullptr});
 
 
 /**
@@ -178,17 +177,16 @@ void PadImpl2D_cpu_forward_kernel(const std::array<DimSize_t, 4>& beginEndBorder
     }
 }
 
-namespace {
-static Registrar<PadImpl2DForward_cpu> registrarPadImpl2DForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32},
-        Aidge::PadImpl2D_cpu_forward_kernel<float, float>);
-static Registrar<PadImpl2DForward_cpu> registrarPadImpl2DForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32},
-        Aidge::PadImpl2D_cpu_forward_kernel<std::int32_t, std::int32_t>);
-static Registrar<PadImpl2DForward_cpu> registrarPadImpl2DForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64},
-        Aidge::PadImpl2D_cpu_forward_kernel<double, double>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(PadImpl2D_cpu,
+    {{DataType::Float32, DataFormat::NCHW}, {DataType::Float32, DataFormat::NCHW}},
+    {Pad_ProdConso_cpu::defaultModel, Aidge::PadImpl2D_cpu_forward_kernel<cpptype_t<DataType::Float32>, cpptype_t<DataType::Float32>>, nullptr});
+REGISTRAR(PadImpl2D_cpu,
+    {{DataType::Float64, DataFormat::NCHW}, {DataType::Float64, DataFormat::NCHW}},
+    {Pad_ProdConso_cpu::defaultModel, Aidge::PadImpl2D_cpu_forward_kernel<cpptype_t<DataType::Float64>, cpptype_t<DataType::Float64>>, nullptr});
+REGISTRAR(PadImpl2D_cpu,
+    {{DataType::Int32, DataFormat::NCHW}, {DataType::Int32, DataFormat::NCHW}},
+    {Pad_ProdConso_cpu::defaultModel, Aidge::PadImpl2D_cpu_forward_kernel<cpptype_t<DataType::Int32>, cpptype_t<DataType::Int32>>, nullptr});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_PADIMPL_FORWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_PADIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/PowImpl.hpp b/include/aidge/backend/cpu/operator/PowImpl.hpp
index 747a9135645ad86fc8ef3ab98e7bdd461f8931d8..cfbb8173d1f83162519016a8f2b3c3166977a5b7 100644
--- a/include/aidge/backend/cpu/operator/PowImpl.hpp
+++ b/include/aidge/backend/cpu/operator/PowImpl.hpp
@@ -12,7 +12,7 @@
 #ifndef AIDGE_CPU_OPERATOR_POWIMPL_H_
 #define AIDGE_CPU_OPERATOR_POWIMPL_H_
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/Pow.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
@@ -21,32 +21,14 @@
 #include <vector>
 
 namespace Aidge {
-// class Pow_Op;
+// Operator implementation entry point for the backend
+using PowImpl_cpu = OperatorImpl_cpu<Pow_Op,
+    void(const std::vector<std::size_t>&, const std::vector<std::size_t>&, const std::vector<std::size_t>&, const void*, const void*,void*),
+    void(const std::vector<std::size_t>&, const std::vector<std::size_t>&, const std::vector<std::size_t>&, const void*, const void*, const void*, void*, void*)>;
 
-// compute kernel registry for forward and backward
-class PowImplForward_cpu
-    : public Registrable<PowImplForward_cpu, std::tuple<DataType, DataType, DataType>, void(const std::vector<std::size_t>&, const std::vector<std::size_t>&, const std::vector<std::size_t>&, const void*, const void*,void*)> {
-};
-class PowImplBackward_cpu
-    : public Registrable<PowImplBackward_cpu, std::tuple<DataType, DataType, DataType>, void(const std::vector<std::size_t>&, const std::vector<std::size_t>&, const std::vector<std::size_t>&, const void*, const void*, const void*, void*, void*)> {
-};
 
-class PowImpl_cpu : public OperatorImpl {
-public:
-    PowImpl_cpu(const Pow_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<PowImpl_cpu> create(const Pow_Op& op) {
-        return std::make_unique<PowImpl_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-    void forward() override;
-    void backward() override;
-};
-
-namespace {
-static Registrar<Pow_Op> registrarPowImpl_cpu("cpu", Aidge::PowImpl_cpu::create);
-}
+// Implementation entry point registration to Operator
+REGISTRAR(Pow_Op, "cpu", Aidge::PowImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_POWIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/PowImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/PowImpl_forward_kernels.hpp
deleted file mode 100644
index 1146cfa77464f8bd1c33a0ec0113415dcf599b53..0000000000000000000000000000000000000000
--- a/include/aidge/backend/cpu/operator/PowImpl_forward_kernels.hpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2023 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_CPU_OPERATOR_POWIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_POWIMPL_FORWARD_KERNEL_H_
-
-#include "aidge/utils/Registrar.hpp"
-#include <cmath>
-
-#include "aidge/backend/cpu/data/Broadcasting.hpp"
-#include "aidge/backend/cpu/operator/PowImpl.hpp"
-
-namespace Aidge {
-template <class I1, class I2, class O>
-void PowImpl_cpu_forward_kernel(const std::vector<std::size_t>& input1Dims,
-                                const std::vector<std::size_t>& input2Dims,
-                                const std::vector<std::size_t>& outputDims,
-                                const void* input1_,
-                                const void* input2_,
-                                void* output_) {
-
-    const I1* input_1 = static_cast<const I1*>(input1_);
-    const I2* input_2 = static_cast<const I2*>(input2_);
-    O* output = static_cast<O*>(output_);
-
-    size_t totalElements = 1;
-    for (size_t dimSize : outputDims) {
-        totalElements *= dimSize;
-    }
-
-	for (std::size_t oIndex = 0; oIndex < totalElements; ++oIndex) 
-	{
-		std::vector<size_t> indexes = getMultiDimIndices(outputDims, oIndex);
-
-		std::size_t idx1 = getFlattenedIndex(input1Dims, indexes);
-		std::size_t idx2 = getFlattenedIndex(input2Dims, indexes);
-		
-        output[oIndex] = std::pow(input_1[idx1], input_2[idx2]);
-	}
-}
-
-namespace {
-static Registrar<PowImplForward_cpu> registrarPowImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32, DataType::Float32},
-        Aidge::PowImpl_cpu_forward_kernel<float, float, float>);
-static Registrar<PowImplForward_cpu> registrarPowImplForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32, DataType::Int32},
-        Aidge::PowImpl_cpu_forward_kernel<int, int, int>);
-static Registrar<PowImplForward_cpu> registrarPowImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64, DataType::Float64},
-        Aidge::PowImpl_cpu_forward_kernel<double, double, double>);
-}  // namespace
-}  // namespace Aidge
-
-#endif /* AIDGE_CPU_OPERATOR_POWIMPL_FORWARD_KERNEL_H_ */
diff --git a/include/aidge/backend/cpu/operator/PowImpl_kernels.hpp b/include/aidge/backend/cpu/operator/PowImpl_kernels.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a89dc9ff6d25ac489255498037fb6cc4749a6980
--- /dev/null
+++ b/include/aidge/backend/cpu/operator/PowImpl_kernels.hpp
@@ -0,0 +1,99 @@
+/********************************************************************************
+ * Copyright (c) 2023 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_CPU_OPERATOR_POWIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_POWIMPL_KERNELS_H_
+
+#include "aidge/utils/Registrar.hpp"
+#include <cmath>
+
+#include "aidge/backend/cpu/data/Broadcasting.hpp"
+#include "aidge/backend/cpu/operator/PowImpl.hpp"
+
+namespace Aidge {
+template <class I1, class I2, class O>
+void PowImpl_cpu_forward_kernel(const std::vector<std::size_t>& input1Dims,
+                                const std::vector<std::size_t>& input2Dims,
+                                const std::vector<std::size_t>& outputDims,
+                                const void* input1_,
+                                const void* input2_,
+                                void* output_) {
+
+    const I1* input_1 = static_cast<const I1*>(input1_);
+    const I2* input_2 = static_cast<const I2*>(input2_);
+    O* output = static_cast<O*>(output_);
+
+    size_t totalElements = 1;
+    for (size_t dimSize : outputDims) {
+        totalElements *= dimSize;
+    }
+
+	for (std::size_t oIndex = 0; oIndex < totalElements; ++oIndex) 
+	{
+		std::vector<size_t> indexes = getMultiDimIndices(outputDims, oIndex);
+
+		std::size_t idx1 = getFlattenedIndex(input1Dims, indexes);
+		std::size_t idx2 = getFlattenedIndex(input2Dims, indexes);
+		
+        output[oIndex] = std::pow(input_1[idx1], input_2[idx2]);
+	}
+}
+
+template <class I1, class I2, class O>
+void PowImpl_cpu_backward_kernel(const std::vector<std::size_t>& input0Dims,
+                                const std::vector<std::size_t>& input1Dims,
+                                const std::vector<std::size_t>& outputDims,
+                                const void* input0_,
+                                const void* input1_,
+                                const void* gradOutput_,
+                                void* gradientInput0_,
+                                void* gradientInput1_) {
+	const I1* input0 = static_cast<const I1*>(input0_);
+	I1* grad0 = static_cast<I1*>(gradientInput0_);
+    const I2* input1 = static_cast<const I2*>(input1_);
+    I2* grad1 = static_cast<I2*>(gradientInput1_);
+    const O* gradOut = static_cast<const O*>(gradOutput_);
+
+    // Fill input grads with zeros
+	auto input0Elements = std::accumulate(input0Dims.cbegin(), input0Dims.cend(), std::size_t(1), std::multiplies<std::size_t>());
+	std::fill(grad0, grad0 + input0Elements, I1(0));
+	auto input1Elements = std::accumulate(input1Dims.cbegin(), input1Dims.cend(), std::size_t(1), std::multiplies<std::size_t>());
+	std::fill(grad1, grad1 + input1Elements, I1(0));
+
+	auto totalElements = std::accumulate(outputDims.cbegin(), outputDims.cend(), std::size_t(1), std::multiplies<std::size_t>());
+    for (size_t i = 0; i < totalElements; ++i)
+    {
+        // Compute indexes in inputs 0 and 1 to support broadcasting
+        std::vector<std::size_t> indexes = getMultiDimIndices(outputDims, i);
+        std::size_t idx0 = getFlattenedIndex(input0Dims, indexes);
+        std::size_t idx1 = getFlattenedIndex(input1Dims, indexes);
+
+        // grad0 = grad_output * (input1 * pow(input0, (input1 -1)))
+        grad0[idx0] += gradOut[i]*input1[idx1]* std::pow(input0[idx0], input1[idx1]-1);
+
+        // grad1 = grad_output * (output * ln(input0))
+        grad1[idx1] += gradOut[i] * std::pow(input0[idx0], input1[idx1]) * std::log(input0[idx0]);
+    }
+}
+
+// Kernels registration to implementation entry point
+REGISTRAR(PowImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::inPlaceModel, Aidge::PowImpl_cpu_forward_kernel<float, float, float>, Aidge::PowImpl_cpu_backward_kernel<float, float, float>});
+REGISTRAR(PowImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::inPlaceModel, Aidge::PowImpl_cpu_forward_kernel<double, double, double>, Aidge::PowImpl_cpu_backward_kernel<double, double, double>});
+REGISTRAR(PowImpl_cpu,
+    {DataType::Int32},
+    {ProdConso::inPlaceModel, Aidge::PowImpl_cpu_forward_kernel<int32_t, int32_t, int32_t>, Aidge::PowImpl_cpu_backward_kernel<int32_t, int32_t, int32_t>});
+}  // namespace Aidge
+
+#endif /* AIDGE_CPU_OPERATOR_POWIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/ReLUImpl.hpp b/include/aidge/backend/cpu/operator/ReLUImpl.hpp
index e2ebf44616db876b462157db650ff48362dd7bac..5b900618abce83ff1c3822d4f61cc62c93f5081f 100644
--- a/include/aidge/backend/cpu/operator/ReLUImpl.hpp
+++ b/include/aidge/backend/cpu/operator/ReLUImpl.hpp
@@ -17,40 +17,19 @@
 #include <tuple>    // std::tuple
 #include <vector>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/ReLU.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 
 namespace Aidge {
-// class ReLU_Op;
+// Operator implementation entry point for the backend
+using ReLUImpl_cpu = OperatorImpl_cpu<ReLU_Op,
+    void(const std::size_t, const void*, void*),
+    void(const std::size_t, const void*, const void*, void*)>;
 
-// compute kernel registry for forward and backward
-class ReLUImplForward_cpu
-    : public Registrable<ReLUImplForward_cpu, std::tuple<DataType, DataType>, void(const std::size_t, const void*, void*)> {
-};
-class ReLUImplBackward_cpu
-    : public Registrable<ReLUImplBackward_cpu, std::tuple<DataType, DataType, DataType>, void(const std::size_t, const void*, const void*, void*)> {
-};
-
-class ReLUImpl_cpu : public OperatorImpl {
-public:
-    ReLUImpl_cpu(const ReLU_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<ReLUImpl_cpu> create(const ReLU_Op& op) {
-        return std::make_unique<ReLUImpl_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-
-    void forward() override final;
-
-    void backward() override final;
-};
-
-namespace {
-static Registrar<ReLU_Op> registrarReLUImpl_cpu("cpu", Aidge::ReLUImpl_cpu::create);
-}
+// Implementation entry point registration to Operator
+REGISTRAR(ReLU_Op, "cpu", Aidge::ReLUImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_RELUIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/ReLUImpl_backward_kernels.hpp b/include/aidge/backend/cpu/operator/ReLUImpl_backward_kernels.hpp
deleted file mode 100644
index 1bd932e43608d98f737cc9046aed74b2fec6abc6..0000000000000000000000000000000000000000
--- a/include/aidge/backend/cpu/operator/ReLUImpl_backward_kernels.hpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2023 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_CPU_OPERATOR_RELUIMPL_BACKWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_RELUIMPL_BACKWARD_KERNEL_H_
-
-#include <cstddef>  // std::size_t
-
-#include "aidge/backend/cpu/operator/ReLUImpl.hpp"
-#include "aidge/utils/Registrar.hpp"
-
-namespace Aidge {
-template <class I, class GI, class GO>
-void ReLUImpl_cpu_backward_kernel(const std::size_t inputLenght,
-                                  const void* input_, const void* grad_output_,
-				  void* grad_input_) {
-    const I* input = static_cast<const I*>(input_);
-    const GO* grad_output = static_cast<const GO*>(grad_output_);
-    GI* grad_input = static_cast<GI*>(grad_input_);
-    for (std::size_t i = 0; i < inputLenght; ++i) {
-        grad_input[i] = (input[i] > 0) ? grad_output[i] : 0;
-    }
-}
-
-namespace {
-static Registrar<ReLUImplBackward_cpu> registrarReLUImplBackward_cpu_Float32(
-    {DataType::Float32, DataType::Float32, DataType::Float32},
-    Aidge::ReLUImpl_cpu_backward_kernel<float, float, float>);
-static Registrar<ReLUImplBackward_cpu> registrarReLUImplBackward_cpu_Int32(
-    {DataType::Int32, DataType::Int32, DataType::Int32},
-    Aidge::ReLUImpl_cpu_backward_kernel<int, int, int>);
-static Registrar<ReLUImplBackward_cpu> registrarReLUImplBackward_cpu_Float64(
-    {DataType::Float64, DataType::Float64, DataType::Float64},
-    Aidge::ReLUImpl_cpu_backward_kernel<double, double, double>);
-}  // namespace
-}  // namespace Aidge
-
-#endif /* AIDGE_CPU_OPERATOR_RELUIMPL_BACKWARD_KERNEL_H_ */
diff --git a/include/aidge/backend/cpu/operator/ReLUImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/ReLUImpl_forward_kernels.hpp
deleted file mode 100644
index af9c65590c7182185c9d79669dde49e592cbeb5d..0000000000000000000000000000000000000000
--- a/include/aidge/backend/cpu/operator/ReLUImpl_forward_kernels.hpp
+++ /dev/null
@@ -1,44 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2023 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_CPU_OPERATOR_RELUIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_RELUIMPL_FORWARD_KERNEL_H_
-
-#include "aidge/utils/Registrar.hpp"
-
-#include "aidge/backend/cpu/operator/ReLUImpl.hpp"
-
-namespace Aidge {
-template <class I, class O>
-void ReLUImpl_cpu_forward_kernel(std::size_t inputLenght,
-                                     const void* input_,
-                                     void* output_) {
-
-    const I* input = static_cast<const I*>(input_);
-    O* output = static_cast<O*>(output_);
-
-//#pragma omp parallel for if (inputLenght > 1024)
-    for (std::size_t i = 0; i < inputLenght; ++i) {
-        output[i] = (input[i] > 0) ? input[i] : 0;
-    }
-}
-
-namespace {
-static Registrar<ReLUImplForward_cpu> registrarReLUImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32}, Aidge::ReLUImpl_cpu_forward_kernel<float, float>);
-static Registrar<ReLUImplForward_cpu> registrarReLUImplForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32}, Aidge::ReLUImpl_cpu_forward_kernel<int, int>);
-static Registrar<ReLUImplForward_cpu> registrarReLUImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64}, Aidge::ReLUImpl_cpu_forward_kernel<double, double>);
-}  // namespace
-}  // namespace Aidge
-
-#endif /* AIDGE_CPU_OPERATOR_RELUIMPL_FORWARD_KERNEL_H_ */
diff --git a/include/aidge/backend/cpu/operator/ReLUImpl_kernels.hpp b/include/aidge/backend/cpu/operator/ReLUImpl_kernels.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e39e9b7decd91e392c5db7e9e9bc4ed0f366829d
--- /dev/null
+++ b/include/aidge/backend/cpu/operator/ReLUImpl_kernels.hpp
@@ -0,0 +1,66 @@
+/********************************************************************************
+ * Copyright (c) 2023 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_CPU_OPERATOR_RELUIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_RELUIMPL_KERNELS_H_
+
+#include <cstddef>  // std::size_t
+#include <memory>
+#include <tuple>    // std::tuple
+#include <vector>
+
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/ReLUImpl.hpp"
+#include "aidge/operator/ReLU.hpp"
+#include "aidge/utils/Registrar.hpp"
+#include "aidge/utils/Types.h"
+
+namespace Aidge {
+// Kernels
+template <class I, class O>
+void ReLUImpl_cpu_forward_kernel(std::size_t inputLenght,
+                                     const void* input_,
+                                     void* output_) {
+
+    const I* input = static_cast<const I*>(input_);
+    O* output = static_cast<O*>(output_);
+
+//#pragma omp parallel for if (inputLenght > 1024)
+    for (std::size_t i = 0; i < inputLenght; ++i) {
+        output[i] = (input[i] > 0) ? input[i] : 0;
+    }
+}
+
+template <class I, class GI, class GO>
+void ReLUImpl_cpu_backward_kernel(const std::size_t inputLenght,
+                                  const void* input_, const void* grad_output_,
+				  void* grad_input_) {
+    const I* input = static_cast<const I*>(input_);
+    const GO* grad_output = static_cast<const GO*>(grad_output_);
+    GI* grad_input = static_cast<GI*>(grad_input_);
+    for (std::size_t i = 0; i < inputLenght; ++i) {
+        grad_input[i] = (input[i] > 0) ? grad_output[i] : 0;
+    }
+}
+
+// Kernels registration to implementation entry point
+REGISTRAR(ReLUImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::inPlaceModel, Aidge::ReLUImpl_cpu_forward_kernel<float, float>, Aidge::ReLUImpl_cpu_backward_kernel<float, float, float>});
+REGISTRAR(ReLUImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::inPlaceModel, Aidge::ReLUImpl_cpu_forward_kernel<double, double>, Aidge::ReLUImpl_cpu_backward_kernel<double, double, double>});
+REGISTRAR(ReLUImpl_cpu,
+    {DataType::Int32},
+    {ProdConso::inPlaceModel, Aidge::ReLUImpl_cpu_forward_kernel<int32_t, int32_t>, Aidge::ReLUImpl_cpu_backward_kernel<int32_t, int32_t, int32_t>});
+}  // namespace Aidge
+
+#endif /* AIDGE_CPU_OPERATOR_RELUIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/ReduceMeanImpl.hpp b/include/aidge/backend/cpu/operator/ReduceMeanImpl.hpp
index 8d784c38dc006ea82f040dfe83b4bef05908dd68..1c50805d5af768dfc160488fda1e8fadfa798454 100644
--- a/include/aidge/backend/cpu/operator/ReduceMeanImpl.hpp
+++ b/include/aidge/backend/cpu/operator/ReduceMeanImpl.hpp
@@ -17,116 +17,22 @@
 #include <tuple>
 #include <vector>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/ReduceMean.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 
 namespace Aidge {
-// class ReduceMean_Op;
-
-// Every DIM
-class ReduceMeanImplForward_cpu
-    : public Registrable<ReduceMeanImplForward_cpu,
-                        std::tuple<DataType, DataType>,
-                        void(const std::vector<std::int32_t>&,
+// Operator implementation entry point for the backend
+using ReduceMeanImpl_cpu = OperatorImpl_cpu<ReduceMean_Op,
+    void(const std::vector<std::int32_t>&,
                             DimSize_t,
                             const std::vector<DimSize_t>&,
                             const void *,
-                            void *)> {};
-class ReduceMeanImpl1DBackward_cpu
-    : public Registrable<ReduceMeanImpl1DBackward_cpu,
-                        std::tuple<DataType, DataType>,
-                        void(const std::vector<std::int32_t>&,
-                            DimSize_t,
-                            const std::vector<DimSize_t>&,
-                            const void *,
-                            void *)> {};
-
-class ReduceMeanImpl_cpu : public OperatorImpl {
-   public:
-    ReduceMeanImpl_cpu(const ReduceMean_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<ReduceMeanImpl_cpu> create(const ReduceMean_Op &op) {
-        return std::make_unique<ReduceMeanImpl_cpu>(op);
-    }
-
-   public:
-    void forward() override;
-};
-
-// // compute kernel registry for forward and backward
-// // DIM 1
-// class ReduceMeanImpl1DForward_cpu
-//     : public Registrable<ReduceMeanImpl1DForward_cpu,
-//                          std::tuple<DataType, DataType>,
-//                          void(const ReduceMean_Op<1>::Attrs &, const std::vector<DimSize_t>&, const void *, void *)> {};
-// class ReduceMeanImpl1DBackward_cpu
-//     : public Registrable<ReduceMeanImpl1DBackward_cpu,
-//                          std::tuple<DataType, DataType>,
-//                          void(const ReduceMean_Op<1>::Attrs &, const std::vector<DimSize_t>&, const void *,  void *)> {};
-
-// // DIM 2
-// class ReduceMeanImpl2DForward_cpu
-//     : public Registrable<ReduceMeanImpl2DForward_cpu,
-//                          std::tuple<DataType, DataType>,
-//                          void(const ReduceMean_Op<2>::Attrs &, const std::vector<DimSize_t>&, const void *, void *)> {};
-// class ReduceMeanImpl2DBackward_cpu
-//     : public Registrable<ReduceMeanImpl2DBackward_cpu,
-//                          std::tuple<DataType, DataType>,
-//                          void(const ReduceMean_Op<2>::Attrs &, const std::vector<DimSize_t>&, const void *,  void *)> {};
-// // DIM 3
-// class ReduceMeanImpl3DForward_cpu
-//     : public Registrable<ReduceMeanImpl3DForward_cpu,
-//                          std::tuple<DataType, DataType>,
-//                          void(const ReduceMean_Op<3>::Attrs &, const std::vector<DimSize_t>&, const void *, void *)> {};
-// class ReduceMeanImpl3DBackward_cpu
-//     : public Registrable<ReduceMeanImpl3DBackward_cpu,
-//                          std::tuple<DataType, DataType>,
-//                          void(const ReduceMean_Op<3>::Attrs &, const std::vector<DimSize_t>&, const void *, void *)> {};
-
-// class ReduceMeanImpl1D_cpu : public OperatorImpl {
-//    public:
-//     ReduceMeanImpl1D_cpu(const ReduceMean_Op<1>& op) : OperatorImpl(op, "cpu") {}
-
-//     static std::unique_ptr<ReduceMeanImpl1D_cpu> create(const ReduceMean_Op<1> &op) {
-//         return std::make_unique<ReduceMeanImpl1D_cpu>(op);
-//     }
-
-//    public:
-//     void forward() override;
-// };
-
-// class ReduceMeanImpl2D_cpu : public OperatorImpl {
-//    public:
-//     ReduceMeanImpl2D_cpu(const ReduceMean_Op<2>& op) : OperatorImpl(op, "cpu") {}
-
-//     static std::unique_ptr<ReduceMeanImpl2D_cpu> create(const ReduceMean_Op<2> &op) {
-//         return std::make_unique<ReduceMeanImpl2D_cpu>(op);
-//     }
-
-//    public:
-//     void forward() override;
-// };
-
-// class ReduceMeanImpl3D_cpu : public OperatorImpl {
-//    public:
-//     ReduceMeanImpl3D_cpu(const ReduceMean_Op<3>& op) : OperatorImpl(op, "cpu") {}
-
-//     static std::unique_ptr<ReduceMeanImpl3D_cpu> create(const ReduceMean_Op<3> &op) {
-//         return std::make_unique<ReduceMeanImpl3D_cpu>(op);
-//     }
+                            void *)>;
 
-//    public:
-//     void forward() override;
-// };
-namespace {
-// add cpu backend to ReduceMean_Op<2> implementation registry
-static Registrar<ReduceMean_Op> registrarReduceMeanImpl_cpu("cpu", Aidge::ReduceMeanImpl_cpu::create);
-// static Registrar<ReduceMean_Op<1>> registrarReduceMeanImpl1D_cpu("cpu", Aidge::ReduceMeanImpl1D_cpu::create);
-// static Registrar<ReduceMean_Op<2>> registrarReduceMeanImpl2D_cpu("cpu", Aidge::ReduceMeanImpl2D_cpu::create);
-// static Registrar<ReduceMean_Op<3>> registrarReduceMeanImpl3D_cpu("cpu", Aidge::ReduceMeanImpl3D_cpu::create);
-}  // namespace
+// Implementation entry point registration to Operator
+REGISTRAR(ReduceMean_Op, "cpu", Aidge::ReduceMeanImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_REDUCEMEANIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/ReduceMeanImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/ReduceMeanImpl_kernels.hpp
similarity index 63%
rename from include/aidge/backend/cpu/operator/ReduceMeanImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/ReduceMeanImpl_kernels.hpp
index fb14893fdc96f9d91f1b8ee6375fd536a7a8a1c7..5a143164d7e4fa2585ea72c38eaaa123f215d21a 100644
--- a/include/aidge/backend/cpu/operator/ReduceMeanImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/ReduceMeanImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_REDUCEMEANIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_REDUCEMEANIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_REDUCEMEANIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_REDUCEMEANIMPL_KERNELS_H_
 
 #include <algorithm>   // std::for_each
 #include <cstddef>     // std::size_t
@@ -107,38 +107,16 @@ void ReduceMeanImpl_cpu_forward_kernel(const std::vector<std::int32_t>& axes,
     }
 }
 
-namespace {
-static Registrar<ReduceMeanImplForward_cpu> registrarReduceMeanImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32}, Aidge::ReduceMeanImpl_cpu_forward_kernel<float, float>);
-static Registrar<ReduceMeanImplForward_cpu> registrarReduceMeanImplForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32}, Aidge::ReduceMeanImpl_cpu_forward_kernel<int, int>);
-static Registrar<ReduceMeanImplForward_cpu> registrarReduceMeanImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64}, Aidge::ReduceMeanImpl_cpu_forward_kernel<double, double>);
-
-// // DIM = 1
-// static Registrar<ReduceMeanImpl1DForward_cpu> registrarReduceMeanImplForward_1D_cpu_Float32(
-//         {DataType::Float32, DataType::Float32}, Aidge::ReduceMeanImpl_cpu_forward_kernel<float, float,1>);
-// static Registrar<ReduceMeanImpl1DForward_cpu> registrarReduceMeanImplForward_1D_cpu_Int32(
-//         {DataType::Int32, DataType::Int32}, Aidge::ReduceMeanImpl_cpu_forward_kernel<int, int,1>);
-// static Registrar<ReduceMeanImpl1DForward_cpu> registrarReduceMeanImplForward_1D_cpu_Float64(
-//         {DataType::Float64, DataType::Float64}, Aidge::ReduceMeanImpl_cpu_forward_kernel<double, double,1>);
-
-// // DIM = 2
-// static Registrar<ReduceMeanImpl2DForward_cpu> registrarReduceMeanImplForward_2D_cpu_Float32(
-//         {DataType::Float32, DataType::Float32}, Aidge::ReduceMeanImpl_cpu_forward_kernel<float, float,2>);
-// static Registrar<ReduceMeanImpl2DForward_cpu> registrarReduceMeanImplForward_2D_cpu_Int32(
-//         {DataType::Int32, DataType::Int32}, Aidge::ReduceMeanImpl_cpu_forward_kernel<int, int,2>);
-// static Registrar<ReduceMeanImpl2DForward_cpu> registrarReduceMeanImplForward_2D_cpu_Float64(
-//         {DataType::Float64, DataType::Float64}, Aidge::ReduceMeanImpl_cpu_forward_kernel<double, double,2>);
-
-// // DIM = 3
-// static Registrar<ReduceMeanImpl3DForward_cpu> registrarReduceMeanImplForward_3D_cpu_Float32(
-//         {DataType::Float32, DataType::Float32}, Aidge::ReduceMeanImpl_cpu_forward_kernel<float, float,3>);
-// static Registrar<ReduceMeanImpl3DForward_cpu> registrarReduceMeanImplForward_3D_cpu_Int32(
-//         {DataType::Int32, DataType::Int32}, Aidge::ReduceMeanImpl_cpu_forward_kernel<int, int,3>);
-// static Registrar<ReduceMeanImpl3DForward_cpu> registrarReduceMeanImplForward_3D_cpu_Float64(
-//         {DataType::Float64, DataType::Float64}, Aidge::ReduceMeanImpl_cpu_forward_kernel<double, double,3>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(ReduceMeanImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::inPlaceModel, Aidge::ReduceMeanImpl_cpu_forward_kernel<float, float>, nullptr});
+REGISTRAR(ReduceMeanImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::inPlaceModel, Aidge::ReduceMeanImpl_cpu_forward_kernel<double, double>, nullptr});
+REGISTRAR(ReduceMeanImpl_cpu,
+    {DataType::Int32},
+    {ProdConso::inPlaceModel, Aidge::ReduceMeanImpl_cpu_forward_kernel<int32_t, int32_t>, nullptr});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_REDUCEMEANIMPL_FORWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_REDUCEMEANIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/ReduceSumImpl.hpp b/include/aidge/backend/cpu/operator/ReduceSumImpl.hpp
index 3b265e134e7282a81476b5aa562237ecbc93141e..4138c62c24149c15cfad5e85e8f50889b2b6a433 100644
--- a/include/aidge/backend/cpu/operator/ReduceSumImpl.hpp
+++ b/include/aidge/backend/cpu/operator/ReduceSumImpl.hpp
@@ -17,44 +17,22 @@
 #include <tuple>
 #include <vector>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/ReduceSum.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 
 namespace Aidge {
-class ReduceSumImplForward_cpu
-    : public Registrable<ReduceSumImplForward_cpu,
-                        std::tuple<DataType, DataType>,
-                        void(const std::vector<std::int32_t>&,
+// Operator implementation entry point for the backend
+using ReduceSumImpl_cpu = OperatorImpl_cpu<ReduceSum_Op,
+    void(const std::vector<std::int32_t>&,
                             DimSize_t,
                             const std::vector<DimSize_t>&,
                             const void *,
-                            void *)> {};
-class ReduceSumImpl1DBackward_cpu
-    : public Registrable<ReduceSumImpl1DBackward_cpu,
-                        std::tuple<DataType, DataType>,
-                        void(const std::vector<std::int32_t>&,
-                            DimSize_t,
-                            const std::vector<DimSize_t>&,
-                            const void *,
-                            void *)> {};
-
-class ReduceSumImpl_cpu : public OperatorImpl {
-   public:
-    ReduceSumImpl_cpu(const ReduceSum_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<ReduceSumImpl_cpu> create(const ReduceSum_Op &op) {
-        return std::make_unique<ReduceSumImpl_cpu>(op);
-    }
-
-   public:
-    void forward() override;
-};
+                            void *)>;
 
-namespace {
-static Registrar<ReduceSum_Op> registrarReduceSumImpl_cpu("cpu", Aidge::ReduceSumImpl_cpu::create);
-}  // namespace
+// Implementation entry point registration to Operator
+REGISTRAR(ReduceSum_Op, "cpu", Aidge::ReduceSumImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_REDUCESUMIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/ReduceSumImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/ReduceSumImpl_kernels.hpp
similarity index 85%
rename from include/aidge/backend/cpu/operator/ReduceSumImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/ReduceSumImpl_kernels.hpp
index f215065af459a886a34a43d958ecd9e09ada4041..72671421796a0d5e799e6f762dfcaf02457220f3 100644
--- a/include/aidge/backend/cpu/operator/ReduceSumImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/ReduceSumImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_REDUCESUMIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_REDUCESUMIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_REDUCESUMIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_REDUCESUMIMPL_KERNELS_H_
 
 #include <algorithm>   // std::for_each
 #include <cstddef>     // std::size_t
@@ -105,14 +105,16 @@ void ReduceSumImpl_cpu_forward_kernel(const std::vector<std::int32_t>& axes,
     }
 }
 
-namespace {
-static Registrar<ReduceSumImplForward_cpu> registrarReduceSumImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32}, Aidge::ReduceSumImpl_cpu_forward_kernel<float, float>);
-static Registrar<ReduceSumImplForward_cpu> registrarReduceSumImplForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32}, Aidge::ReduceSumImpl_cpu_forward_kernel<int, int>);
-static Registrar<ReduceSumImplForward_cpu> registrarReduceSumImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64}, Aidge::ReduceSumImpl_cpu_forward_kernel<double, double>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(ReduceSumImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::inPlaceModel, Aidge::ReduceSumImpl_cpu_forward_kernel<float, float>, nullptr});
+REGISTRAR(ReduceSumImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::inPlaceModel, Aidge::ReduceSumImpl_cpu_forward_kernel<double, double>, nullptr});
+REGISTRAR(ReduceSumImpl_cpu,
+    {DataType::Int32},
+    {ProdConso::inPlaceModel, Aidge::ReduceSumImpl_cpu_forward_kernel<int32_t, int32_t>, nullptr});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_REDUCESUMIMPL_FORWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_REDUCESUMIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/ScalingImpl.hpp b/include/aidge/backend/cpu/operator/ScalingImpl.hpp
index 8590169272818a225fe4299150f873733cdd9cd9..c1cc247c548701d43e01b1e92d02f42a11cfc710 100644
--- a/include/aidge/backend/cpu/operator/ScalingImpl.hpp
+++ b/include/aidge/backend/cpu/operator/ScalingImpl.hpp
@@ -12,7 +12,7 @@
 #ifndef __AIDGE_CPU_OPERATOR_ScalingIMPL_H__
 #define __AIDGE_CPU_OPERATOR_ScalingIMPL_H__
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/Scaling.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
@@ -22,43 +22,17 @@
 #include <array>
 
 namespace Aidge {
-// class Scaling_Op;
-
-// compute kernel registry for forward and backward
-class ScalingImplForward_cpu
-    : public Registrable<ScalingImplForward_cpu,
-                        std::tuple<DataType, DataType>,
-                        void(const float,
-                            const std::size_t,
-                            const bool,
-                            std::size_t,
-                            const void*,
-                            void*)> {};
-class ScalingImplBackward_cpu
-    : public Registrable<ScalingImplBackward_cpu,
-                        std::tuple<DataType, DataType>,
-                        void(const float,
-                            const std::size_t,
-                            const bool,
-                            std::size_t,
-                            const void*,
-                            void*)> {};
-
-class ScalingImpl_cpu : public OperatorImpl {
-public:
-    ScalingImpl_cpu(const Scaling_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<ScalingImpl_cpu> create(const Scaling_Op& op) {
-        return std::make_unique<ScalingImpl_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-    void forward() override;
-};
-
-namespace {
-static Registrar<Scaling_Op> registrarScalingImpl_cpu("cpu", Aidge::ScalingImpl_cpu::create);
-}
+// Operator implementation entry point for the backend
+using ScalingImpl_cpu = OperatorImpl_cpu<Scaling_Op,
+    void(const float,
+        const std::size_t,
+        const bool,
+        std::size_t,
+        const void*,
+        void*)>;
+
+// Implementation entry point registration to Operator
+REGISTRAR(Scaling_Op, "cpu", Aidge::ScalingImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* __AIDGE_CPU_OPERATOR_ScalingIMPL_H__ */
\ No newline at end of file
diff --git a/include/aidge/backend/cpu/operator/ScalingImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/ScalingImpl_kernels.hpp
similarity index 79%
rename from include/aidge/backend/cpu/operator/ScalingImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/ScalingImpl_kernels.hpp
index c654265dd6f650129201037976d89da4b0f39d96..c758c9cf39e76bb370c6d03c28e3a670c280eefc 100644
--- a/include/aidge/backend/cpu/operator/ScalingImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/ScalingImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_SCALINGIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_SCALINGIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_SCALINGIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_SCALINGIMPL_KERNELS_H_
 
 #include <cmath>
 #include <cstddef>
@@ -92,14 +92,16 @@ void ScalingImpl_cpu_forward_kernel(const float scalingFactor,
     }
 }
 
-namespace {
-static Registrar<ScalingImplForward_cpu> registrarScalingImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32}, Aidge::ScalingImpl_cpu_forward_kernel<float, float>);
-static Registrar<ScalingImplForward_cpu> registrarScalingImplForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32}, Aidge::ScalingImpl_cpu_forward_kernel<int, int>);
-static Registrar<ScalingImplForward_cpu> registrarScalingImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64}, Aidge::ScalingImpl_cpu_forward_kernel<double, double>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(ScalingImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::inPlaceModel, Aidge::ScalingImpl_cpu_forward_kernel<float, float>, nullptr});
+REGISTRAR(ScalingImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::inPlaceModel, Aidge::ScalingImpl_cpu_forward_kernel<double, double>, nullptr});
+REGISTRAR(ScalingImpl_cpu,
+    {DataType::Int32},
+    {ProdConso::inPlaceModel, Aidge::ScalingImpl_cpu_forward_kernel<int32_t, int32_t>, nullptr});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_SCALINGIMPL_FORWARD_KERNEL_H_ */
\ No newline at end of file
+#endif /* AIDGE_CPU_OPERATOR_SCALINGIMPL_KERNELS_H_ */
\ No newline at end of file
diff --git a/include/aidge/backend/cpu/operator/SigmoidImpl.hpp b/include/aidge/backend/cpu/operator/SigmoidImpl.hpp
index 34340e6166a48b465c7723e85d91c195bfb42277..ee1c36edecbe50cc1765da59737509a2b6333caf 100644
--- a/include/aidge/backend/cpu/operator/SigmoidImpl.hpp
+++ b/include/aidge/backend/cpu/operator/SigmoidImpl.hpp
@@ -12,7 +12,7 @@
 #ifndef AIDGE_CPU_OPERATOR_SIGMOIDIMPL_H_
 #define AIDGE_CPU_OPERATOR_SIGMOIDIMPL_H_
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/Sigmoid.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
@@ -21,34 +21,13 @@
 #include <vector>
 
 namespace Aidge {
-// class Sigmoid_Op;
+// Operator implementation entry point for the backend
+using SigmoidImpl_cpu = OperatorImpl_cpu<Sigmoid_Op,
+    void(const std::size_t, const void*, void*),
+    void(const std::size_t, const void*, const void*, void*)>;
 
-// compute kernel registry for forward and backward
-class SigmoidImplForward_cpu
-    : public Registrable<SigmoidImplForward_cpu, std::tuple<DataType, DataType>, void(const std::size_t, const void*, void*)> {
-};
-class SigmoidImplBackward_cpu
-    : public Registrable<SigmoidImplBackward_cpu, std::tuple<DataType, DataType, DataType>, void(const std::size_t, const void*, const void*, void*)> {
-};
-
-class SigmoidImpl_cpu : public OperatorImpl {
-public:
-    SigmoidImpl_cpu(const Sigmoid_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<SigmoidImpl_cpu> create(const Sigmoid_Op& op) {
-        return std::make_unique<SigmoidImpl_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-	
-    void forward() override final;
-
-    void backward() override final;
-};
-
-namespace {
-static Registrar<Sigmoid_Op> registrarSigmoidImpl_cpu("cpu", Aidge::SigmoidImpl_cpu::create);
-}
+// Implementation entry point registration to Operator
+REGISTRAR(Sigmoid_Op, "cpu", Aidge::SigmoidImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_SIGMOIDIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/SigmoidImpl_backward_kernels.hpp b/include/aidge/backend/cpu/operator/SigmoidImpl_backward_kernels.hpp
deleted file mode 100644
index 4ceb3bd7ed9a3fb739591eee488f8035770fef18..0000000000000000000000000000000000000000
--- a/include/aidge/backend/cpu/operator/SigmoidImpl_backward_kernels.hpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2023 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_CPU_OPERATOR_SIGMOIDIMPL_BACKWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_SIGMOIDIMPL_BACKWARD_KERNEL_H_
-
-#include <cstddef>  // std::size_t
-
-#include "aidge/backend/cpu/operator/SigmoidImpl.hpp"
-#include "aidge/utils/Registrar.hpp"
-
-namespace Aidge {
-template <class O, class GI, class GO>
-void SigmoidImpl_cpu_backward_kernel(const std::size_t inputLenght,
-                                     const void* output_, const void* grad_output_,
-				     void* grad_input_) {
-    const O* output = static_cast<const O*>(output_);
-    const GO* grad_output = static_cast<const GO*>(grad_output_);
-    GI* grad_input = static_cast<GI*>(grad_input_);
-    for (std::size_t i = 0; i < inputLenght; ++i) {
-        grad_input[i] = output[i] * (O(1) - output[i]) * grad_output[i];
-    }
-}
-
-namespace {
-static Registrar<SigmoidImplBackward_cpu> registrarSigmoidImplBackward_cpu_Float32(
-    {DataType::Float32, DataType::Float32, DataType::Float32},
-    Aidge::SigmoidImpl_cpu_backward_kernel<float, float, float>);
-static Registrar<SigmoidImplBackward_cpu> registrarSigmoidImplBackward_cpu_Float64(
-    {DataType::Float64, DataType::Float64, DataType::Float64},
-    Aidge::SigmoidImpl_cpu_backward_kernel<double, double, double>);
-}  // namespace
-}  // namespace Aidge
-
-#endif /* AIDGE_CPU_OPERATOR_SIGMOIDIMPL_BACKWARD_KERNEL_H_ */
diff --git a/include/aidge/backend/cpu/operator/SigmoidImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/SigmoidImpl_forward_kernels.hpp
deleted file mode 100644
index 24ba11a0bca7f3fa15f9ac1e2c13e29f88eaf074..0000000000000000000000000000000000000000
--- a/include/aidge/backend/cpu/operator/SigmoidImpl_forward_kernels.hpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2023 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_CPU_OPERATOR_SIGMOIDIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_SIGMOIDIMPL_FORWARD_KERNEL_H_
-
-#include "aidge/utils/Registrar.hpp"
-
-#include "aidge/backend/cpu/operator/SigmoidImpl.hpp"
-
-namespace Aidge {
-template <class I, class O>
-void SigmoidImpl_cpu_forward_kernel(std::size_t inputLenght,
-                                    const void* input_,
-                                    void* output_) {
-
-    const I* input = static_cast<const I*>(input_);
-    O* output = static_cast<O*>(output_);
-
-//#pragma omp parallel for if (inputLenght > 1024)
-    for (std::size_t i = 0; i < inputLenght; ++i) {
-		if (input[i] > I(0)) {
-			output[i] = O(1) / (O(1) + std::exp(-input[i]));
-		} else {
-			output[i] = std::exp(input[i]) / (O(1) + std::exp(input[i]));
-		}
-    }
-}
-
-namespace {
-static Registrar<SigmoidImplForward_cpu> registrarSigmoidImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32}, Aidge::SigmoidImpl_cpu_forward_kernel<float, float>);
-static Registrar<SigmoidImplForward_cpu> registrarSigmoidImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64}, Aidge::SigmoidImpl_cpu_forward_kernel<double, double>);
-}  // namespace
-}  // namespace Aidge
-
-#endif /* AIDGE_CPU_OPERATOR_SIGMOIDIMPL_FORWARD_KERNEL_H_ */
diff --git a/include/aidge/backend/cpu/operator/SigmoidImpl_kernels.hpp b/include/aidge/backend/cpu/operator/SigmoidImpl_kernels.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..dfd71ce0a878efbeb779f3a67ad4ccc762bb8363
--- /dev/null
+++ b/include/aidge/backend/cpu/operator/SigmoidImpl_kernels.hpp
@@ -0,0 +1,59 @@
+/********************************************************************************
+ * Copyright (c) 2023 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_CPU_OPERATOR_SIGMOIDIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_SIGMOIDIMPL_KERNELS_H_
+
+#include "aidge/utils/Registrar.hpp"
+
+#include "aidge/backend/cpu/operator/SigmoidImpl.hpp"
+
+namespace Aidge {
+template <class I, class O>
+void SigmoidImpl_cpu_forward_kernel(std::size_t inputLenght,
+                                    const void* input_,
+                                    void* output_) {
+
+    const I* input = static_cast<const I*>(input_);
+    O* output = static_cast<O*>(output_);
+
+//#pragma omp parallel for if (inputLenght > 1024)
+    for (std::size_t i = 0; i < inputLenght; ++i) {
+		if (input[i] > I(0)) {
+			output[i] = O(1) / (O(1) + std::exp(-input[i]));
+		} else {
+			output[i] = std::exp(input[i]) / (O(1) + std::exp(input[i]));
+		}
+    }
+}
+
+template <class O, class GI, class GO>
+void SigmoidImpl_cpu_backward_kernel(const std::size_t inputLenght,
+                                     const void* output_, const void* grad_output_,
+				     void* grad_input_) {
+    const O* output = static_cast<const O*>(output_);
+    const GO* grad_output = static_cast<const GO*>(grad_output_);
+    GI* grad_input = static_cast<GI*>(grad_input_);
+    for (std::size_t i = 0; i < inputLenght; ++i) {
+        grad_input[i] = output[i] * (O(1) - output[i]) * grad_output[i];
+    }
+}
+
+// Kernels registration to implementation entry point
+REGISTRAR(SigmoidImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::inPlaceModel, Aidge::SigmoidImpl_cpu_forward_kernel<float, float>, Aidge::SigmoidImpl_cpu_backward_kernel<float, float, float>});
+REGISTRAR(SigmoidImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::inPlaceModel, Aidge::SigmoidImpl_cpu_forward_kernel<double, double>, Aidge::SigmoidImpl_cpu_backward_kernel<double, double, double>});
+}  // namespace Aidge
+
+#endif /* AIDGE_CPU_OPERATOR_SIGMOIDIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/SliceImpl.hpp b/include/aidge/backend/cpu/operator/SliceImpl.hpp
index 61aed1553bfbd2e67fc837ec6ea8d80b26ef3558..fd98b38d7117eaa14e35fe3cb89abf95b2913997 100644
--- a/include/aidge/backend/cpu/operator/SliceImpl.hpp
+++ b/include/aidge/backend/cpu/operator/SliceImpl.hpp
@@ -16,52 +16,25 @@
 #include <vector>
 #include <array>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/Slice.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 
 namespace Aidge {
-// class Slice_Op;
-
-// compute kernel registry for forward and backward
-class SliceImplForward_cpu
-    : public Registrable<SliceImplForward_cpu,
-                        std::tuple<DataType, DataType>,
-                        void(const std::vector<std::int64_t>&,
+// Operator implementation entry point for the backend
+using SliceImpl_cpu = OperatorImpl_cpu<Slice_Op,
+    void(const std::vector<std::int64_t>&,
                             const std::vector<std::int64_t>&,
                             const std::vector<std::int8_t>&,
                             const std::vector<std::int64_t>&,
                             const std::vector<DimSize_t>&,
                             const void*,
-                            void*)> {};
-class SliceImplBackward_cpu
-    : public Registrable<SliceImplBackward_cpu,
-                        std::tuple<DataType, DataType>,
-                        void(const std::vector<std::int64_t>&,
-                            const std::vector<std::int64_t>&,
-                            const std::vector<std::int8_t>&,
-                            const std::vector<std::int64_t>&,
-                            const std::vector<DimSize_t>&,
-                            const void*,
-                            void*)> {};
-
-class SliceImpl_cpu : public OperatorImpl {
-public:
-    SliceImpl_cpu(const Slice_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<SliceImpl_cpu> create(const Slice_Op& op) {
-        return std::make_unique<SliceImpl_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-    void forward() override;
-};
+                            void*)>;
 
-namespace {
-static Registrar<Slice_Op> registrarSliceImpl_cpu("cpu", Aidge::SliceImpl_cpu::create);
-}
+// Implementation entry point registration to Operator
+REGISTRAR(Slice_Op, "cpu", Aidge::SliceImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* __AIDGE_CPU_OPERATOR_SLICEIMPL_H__ */
diff --git a/include/aidge/backend/cpu/operator/SliceImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/SliceImpl_kernels.hpp
similarity index 84%
rename from include/aidge/backend/cpu/operator/SliceImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/SliceImpl_kernels.hpp
index 31e409369cc640bbda9f54c54652af7f72b509b6..1bf4c491723c570fa8bfd1774beca1630d2de9be 100644
--- a/include/aidge/backend/cpu/operator/SliceImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/SliceImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_SLICEIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_SLICEIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_SLICEIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_SLICEIMPL_KERNELS_H_
 
 #include <algorithm>
 #include <cmath>
@@ -88,14 +88,15 @@ void SliceImpl_cpu_forward_kernel(const std::vector<std::int64_t>& starts,
     }
 }
 
-namespace {
-static Registrar<SliceImplForward_cpu> registrarSliceImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32}, Aidge::SliceImpl_cpu_forward_kernel<float, float>);
-static Registrar<SliceImplForward_cpu> registrarSliceImplForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32}, Aidge::SliceImpl_cpu_forward_kernel<int, int>);
-static Registrar<SliceImplForward_cpu> registrarSliceImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64}, Aidge::SliceImpl_cpu_forward_kernel<double, double>);
-}  // namespace
+REGISTRAR(SliceImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::inPlaceModel, Aidge::SliceImpl_cpu_forward_kernel<float, float>, nullptr});
+REGISTRAR(SliceImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::inPlaceModel, Aidge::SliceImpl_cpu_forward_kernel<double, double>, nullptr});
+REGISTRAR(SliceImpl_cpu,
+    {DataType::Int32},
+    {ProdConso::inPlaceModel, Aidge::SliceImpl_cpu_forward_kernel<int32_t, int32_t>, nullptr});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_SLICEIMPL_FORWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_SLICEIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/SoftmaxImpl.hpp b/include/aidge/backend/cpu/operator/SoftmaxImpl.hpp
index 2b2fab485656efdc37ee134cb4ae574b6b403405..ec2c2696ed6e2ba8cad1536519298d9331921c07 100644
--- a/include/aidge/backend/cpu/operator/SoftmaxImpl.hpp
+++ b/include/aidge/backend/cpu/operator/SoftmaxImpl.hpp
@@ -12,7 +12,7 @@
 #ifndef AIDGE_CPU_OPERATOR_SOFTMAXIMPL_H_
 #define AIDGE_CPU_OPERATOR_SOFTMAXIMPL_H_
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/Softmax.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
@@ -21,31 +21,12 @@
 #include <vector>
 
 namespace Aidge {
-// class Softmax_Op;
+// Operator implementation entry point for the backend
+using SoftmaxImpl_cpu = OperatorImpl_cpu<Softmax_Op,
+    void(std::size_t, const std::vector<DimSize_t>&, const void*, void*)>;
 
-// compute kernel registry for forward and backward
-class SoftmaxImplForward_cpu
-    : public Registrable<SoftmaxImplForward_cpu, std::tuple<DataType, DataType>, void(std::size_t, const std::vector<DimSize_t>&, const void*, void*)> {
-};
-class SoftmaxImplBackward_cpu
-    : public Registrable<SoftmaxImplBackward_cpu, std::tuple<DataType, DataType>, void(std::size_t, const std::vector<DimSize_t>&, const void*, void*)> {
-};
-
-class SoftmaxImpl_cpu : public OperatorImpl {
-public:
-    SoftmaxImpl_cpu(const Softmax_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<SoftmaxImpl_cpu> create(const Softmax_Op& op) {
-        return std::make_unique<SoftmaxImpl_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-    void forward() override;
-};
-
-namespace {
-static Registrar<Softmax_Op> registrarSoftmaxImpl_cpu("cpu", Aidge::SoftmaxImpl_cpu::create);
-}
+// Implementation entry point registration to Operator
+REGISTRAR(Softmax_Op, "cpu", Aidge::SoftmaxImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_SOFTMAXIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/SoftmaxImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/SoftmaxImpl_kernels.hpp
similarity index 75%
rename from include/aidge/backend/cpu/operator/SoftmaxImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/SoftmaxImpl_kernels.hpp
index 6ff8b3ddf39412aa6febdc188b7c27e8bfdcc178..07486a48f1b8cf29f6a6ef8aa934a9decdbafef7 100644
--- a/include/aidge/backend/cpu/operator/SoftmaxImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/SoftmaxImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_SOFTMAXIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_SOFTMAXIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_SOFTMAXIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_SOFTMAXIMPL_KERNELS_H_
 
 #include "aidge/utils/Registrar.hpp"
 #include <cstddef>
@@ -61,14 +61,15 @@ void SoftmaxImpl_cpu_forward_kernel(std::size_t axisIdx, const std::vector<DimSi
     }
 }
 
-namespace {
-static Registrar<SoftmaxImplForward_cpu> registrarSoftmaxImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32}, Aidge::SoftmaxImpl_cpu_forward_kernel<float, float>);
-static Registrar<SoftmaxImplForward_cpu> registrarSoftmaxImplForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32}, Aidge::SoftmaxImpl_cpu_forward_kernel<int, int>);
-static Registrar<SoftmaxImplForward_cpu> registrarSoftmaxImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64}, Aidge::SoftmaxImpl_cpu_forward_kernel<double, double>);
-}  // namespace
+REGISTRAR(SoftmaxImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::inPlaceModel, Aidge::SoftmaxImpl_cpu_forward_kernel<float, float>, nullptr});
+REGISTRAR(SoftmaxImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::inPlaceModel, Aidge::SoftmaxImpl_cpu_forward_kernel<double, double>, nullptr});
+REGISTRAR(SoftmaxImpl_cpu,
+    {DataType::Int32},
+    {ProdConso::inPlaceModel, Aidge::SoftmaxImpl_cpu_forward_kernel<int32_t, int32_t>, nullptr});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_SOFTMAXIMPL_FORWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_SOFTMAXIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/SqrtImpl.hpp b/include/aidge/backend/cpu/operator/SqrtImpl.hpp
index 1691d951678509274736d558360c8110958820a9..dba75d1c58fb19ab2284ee0e98a32bff7ac58557 100644
--- a/include/aidge/backend/cpu/operator/SqrtImpl.hpp
+++ b/include/aidge/backend/cpu/operator/SqrtImpl.hpp
@@ -17,39 +17,19 @@
 #include <tuple>
 #include <vector>
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/Sqrt.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 
 namespace Aidge {
+// Operator implementation entry point for the backend
+using SqrtImpl_cpu = OperatorImpl_cpu<Sqrt_Op,
+    void(const std::size_t, const void*, void*),
+    void(const std::size_t, const void*, void*)>;
 
-// compute kernel registry for forward and backward
-class SqrtImplForward_cpu
-    : public Registrable<SqrtImplForward_cpu, std::tuple<DataType, DataType>, void(const std::size_t, const void*, void*)> {
-};
-class SqrtImplBackward_cpu
-    : public Registrable<SqrtImplBackward_cpu, std::tuple<DataType, DataType>, void(const std::size_t, const void*, void*)> {
-};
-
-class SqrtImpl_cpu : public OperatorImpl {
-public:
-    SqrtImpl_cpu(const Sqrt_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<SqrtImpl_cpu> create(const Sqrt_Op& op) {
-        return std::make_unique<SqrtImpl_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-
-    void forward() override final;
-
-    void backward() override final;
-};
-
-namespace {
-static Registrar<Sqrt_Op> registrarSqrtImpl_cpu("cpu", Aidge::SqrtImpl_cpu::create);
-}
+// Implementation entry point registration to Operator
+REGISTRAR(Sqrt_Op, "cpu", Aidge::SqrtImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_SQRTIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/SqrtImpl_backward_kernels.hpp b/include/aidge/backend/cpu/operator/SqrtImpl_backward_kernels.hpp
deleted file mode 100644
index 9cf5118a5ac81520d7a180b6aba22417ca512890..0000000000000000000000000000000000000000
--- a/include/aidge/backend/cpu/operator/SqrtImpl_backward_kernels.hpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2023 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_CPU_OPERATOR_SQRTIMPL_BACKWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_SQRTIMPL_BACKWARD_KERNEL_H_
-
-#include <cmath>    // std::sqrt
-#include <cstddef>  // std::size_t
-
-#include "aidge/utils/Registrar.hpp"
-
-#include "aidge/backend/cpu/operator/SqrtImpl.hpp"
-
-namespace Aidge {
-template <class I, class O>
-void SqrtImpl_cpu_backward_kernel(const std::size_t inputLenght,
-                                     const void* input_,
-                                     void* output_) {
-
-    const I* input = static_cast<const I*>(input_);
-    O* output = static_cast<O*>(output_);
-
-    for (std::size_t i = 0; i < inputLenght; ++i) {
-        output[i] = static_cast<O>(0.5/(std::sqrt(static_cast<float>(input[i]))));
-    }
-}
-
-namespace {
-static Registrar<SqrtImplBackward_cpu> registrarSqrtImplBackward_cpu_Float32(
-        {DataType::Float32, DataType::Float32}, Aidge::SqrtImpl_cpu_backward_kernel<float, float>);
-static Registrar<SqrtImplBackward_cpu> registrarSqrtImplBackward_cpu_Int32(
-        {DataType::Int32, DataType::Int32}, Aidge::SqrtImpl_cpu_backward_kernel<int, int>);
-static Registrar<SqrtImplBackward_cpu> registrarSqrtImplBackward_cpu_Float64(
-        {DataType::Float64, DataType::Float64}, Aidge::SqrtImpl_cpu_backward_kernel<double, double>);
-}  // namespace
-}  // namespace Aidge
-
-#endif /* AIDGE_CPU_OPERATOR_SQRTIMPL_BACKWARD_KERNEL_H_ */
diff --git a/include/aidge/backend/cpu/operator/SqrtImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/SqrtImpl_forward_kernels.hpp
deleted file mode 100644
index 886b978c2345ce555d229d684ba83f952be9e00e..0000000000000000000000000000000000000000
--- a/include/aidge/backend/cpu/operator/SqrtImpl_forward_kernels.hpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2023 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_CPU_OPERATOR_SQRTIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_SQRTIMPL_FORWARD_KERNEL_H_
-
-#include <cmath>    // std::sqrt
-#include <cstddef>  // std::size_t
-
-#include "aidge/utils/Registrar.hpp"
-
-#include "aidge/backend/cpu/operator/SqrtImpl.hpp"
-
-namespace Aidge {
-template <class I, class O>
-void SqrtImpl_cpu_forward_kernel(const std::size_t inputLenght,
-                                     const void* input_,
-                                     void* output_) {
-
-    const I* input = static_cast<const I*>(input_);
-    O* output = static_cast<O*>(output_);
-
-    for (std::size_t i = 0; i < inputLenght; ++i) {
-        output[i] = static_cast<O>(std::sqrt(static_cast<float>(input[i])));
-    }
-}
-
-namespace {
-static Registrar<SqrtImplForward_cpu> registrarSqrtImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32}, Aidge::SqrtImpl_cpu_forward_kernel<float, float>);
-static Registrar<SqrtImplForward_cpu> registrarSqrtImplForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32}, Aidge::SqrtImpl_cpu_forward_kernel<int, int>);
-static Registrar<SqrtImplForward_cpu> registrarSqrtImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64}, Aidge::SqrtImpl_cpu_forward_kernel<double, double>);
-}  // namespace
-}  // namespace Aidge
-
-#endif /* AIDGE_CPU_OPERATOR_SQRTIMPL_FORWARD_KERNEL_H_ */
diff --git a/include/aidge/backend/cpu/operator/SqrtImpl_kernels.hpp b/include/aidge/backend/cpu/operator/SqrtImpl_kernels.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0464119cad60742bc58c79da984b30776bc7932f
--- /dev/null
+++ b/include/aidge/backend/cpu/operator/SqrtImpl_kernels.hpp
@@ -0,0 +1,60 @@
+/********************************************************************************
+ * Copyright (c) 2023 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_CPU_OPERATOR_SQRTIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_SQRTIMPL_KERNELS_H_
+
+#include <cmath>    // std::sqrt
+#include <cstddef>  // std::size_t
+
+#include "aidge/utils/Registrar.hpp"
+
+#include "aidge/backend/cpu/operator/SqrtImpl.hpp"
+
+namespace Aidge {
+template <class I, class O>
+void SqrtImpl_cpu_forward_kernel(const std::size_t inputLenght,
+                                     const void* input_,
+                                     void* output_) {
+
+    const I* input = static_cast<const I*>(input_);
+    O* output = static_cast<O*>(output_);
+
+    for (std::size_t i = 0; i < inputLenght; ++i) {
+        output[i] = static_cast<O>(std::sqrt(static_cast<float>(input[i])));
+    }
+}
+
+template <class I, class O>
+void SqrtImpl_cpu_backward_kernel(const std::size_t inputLenght,
+                                     const void* input_,
+                                     void* output_) {
+
+    const I* input = static_cast<const I*>(input_);
+    O* output = static_cast<O*>(output_);
+
+    for (std::size_t i = 0; i < inputLenght; ++i) {
+        output[i] = static_cast<O>(0.5/(std::sqrt(static_cast<float>(input[i]))));
+    }
+}
+
+REGISTRAR(SqrtImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::inPlaceModel, Aidge::SqrtImpl_cpu_forward_kernel<float, float>, Aidge::SqrtImpl_cpu_backward_kernel<float, float>});
+REGISTRAR(SqrtImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::inPlaceModel, Aidge::SqrtImpl_cpu_forward_kernel<double, double>, Aidge::SqrtImpl_cpu_backward_kernel<double, double>});
+REGISTRAR(SqrtImpl_cpu,
+    {DataType::Int32},
+    {ProdConso::inPlaceModel, Aidge::SqrtImpl_cpu_forward_kernel<int32_t, int32_t>, Aidge::SqrtImpl_cpu_backward_kernel<int32_t, int32_t>});
+}  // namespace Aidge
+
+#endif /* AIDGE_CPU_OPERATOR_SQRTIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/SubImpl.hpp b/include/aidge/backend/cpu/operator/SubImpl.hpp
index 15c028ae6289f39e0b6e6fd74e51e138b1f2675c..2bb22bda74edf7db09404fd5613b6714ddcdf513 100644
--- a/include/aidge/backend/cpu/operator/SubImpl.hpp
+++ b/include/aidge/backend/cpu/operator/SubImpl.hpp
@@ -12,7 +12,7 @@
 #ifndef AIDGE_CPU_OPERATOR_SUBIMPL_H_
 #define AIDGE_CPU_OPERATOR_SUBIMPL_H_
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/Sub.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
@@ -21,31 +21,12 @@
 #include <vector>
 
 namespace Aidge {
-// class Sub_Op;
+// Operator implementation entry point for the backend
+using SubImpl_cpu = OperatorImpl_cpu<Sub_Op,
+    void(const std::vector<std::size_t>&, const std::vector<std::size_t>&, const std::vector<std::size_t>&, const void*, const void*,void*)>;
 
-// compute kernel registry for forward and backward
-class SubImplForward_cpu
-    : public Registrable<SubImplForward_cpu, std::tuple<DataType, DataType, DataType>, void(const std::vector<std::size_t>&, const std::vector<std::size_t>&, const std::vector<std::size_t>&, const void*, const void*,void*)> {
-};
-class SubImplBackward_cpu
-    : public Registrable<SubImplBackward_cpu, std::tuple<DataType, DataType, DataType>, void(const std::vector<std::size_t>&, const std::vector<std::size_t>&, const std::vector<std::size_t>&, const void*, const void*, void*)> {
-};
-
-class SubImpl_cpu : public OperatorImpl {
-public:
-    SubImpl_cpu(const Sub_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<SubImpl_cpu> create(const Sub_Op& op) {
-        return std::make_unique<SubImpl_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-    void forward() override;
-};
-
-namespace {
-static Registrar<Sub_Op> registrarSubImpl_cpu("cpu", Aidge::SubImpl_cpu::create);
-}
+// Implementation entry point registration to Operator
+REGISTRAR(Sub_Op, "cpu", Aidge::SubImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_SUBIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/SubImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/SubImpl_kernels.hpp
similarity index 62%
rename from include/aidge/backend/cpu/operator/SubImpl_forward_kernels.hpp
rename to include/aidge/backend/cpu/operator/SubImpl_kernels.hpp
index 10e6f58bb44b63f2d8712dc0aa64e0660f3356b2..0486ed2105b23e95f9cdfcda578e14900fcb2c8e 100644
--- a/include/aidge/backend/cpu/operator/SubImpl_forward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/SubImpl_kernels.hpp
@@ -9,8 +9,8 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_SUBIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_SUBIMPL_FORWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_SUBIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_SUBIMPL_KERNELS_H_
 
 #include "aidge/utils/Registrar.hpp"
 
@@ -49,20 +49,19 @@ void SubImpl_cpu_forward_kernel(const std::vector<std::size_t>& input1Dims,
 	}
 }
 
-namespace {
-static Registrar<SubImplForward_cpu> registrarSubImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32, DataType::Float32},
-        Aidge::SubImpl_cpu_forward_kernel<float, float, float>);
-static Registrar<SubImplForward_cpu> registrarSubImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64, DataType::Float64},
-        Aidge::SubImpl_cpu_forward_kernel<double, double, double>);
-static Registrar<SubImplForward_cpu> registrarSubImplForward_cpu_Int32(
-        {DataType::Int32, DataType::Int32, DataType::Int32},
-        Aidge::SubImpl_cpu_forward_kernel<std::int32_t, std::int32_t, std::int32_t>);
-static Registrar<SubImplForward_cpu> registrarSubImplForward_cpu_Int64(
-        {DataType::Int64, DataType::Int64, DataType::Int64},
-        Aidge::SubImpl_cpu_forward_kernel<std::int64_t, std::int64_t, std::int64_t>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(SubImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::inPlaceModel, Aidge::SubImpl_cpu_forward_kernel<float, float, float>, nullptr});
+REGISTRAR(SubImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::inPlaceModel, Aidge::SubImpl_cpu_forward_kernel<double, double, double>, nullptr});
+REGISTRAR(SubImpl_cpu,
+    {DataType::Int32},
+    {ProdConso::inPlaceModel, Aidge::SubImpl_cpu_forward_kernel<std::int32_t, std::int32_t, std::int32_t>, nullptr});
+REGISTRAR(SubImpl_cpu,
+    {DataType::Int64},
+    {ProdConso::inPlaceModel, Aidge::SubImpl_cpu_forward_kernel<std::int64_t, std::int64_t, std::int64_t>, nullptr});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_SUBIMPL_FORWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_SUBIMPL_KERNELS_H_ */
diff --git a/include/aidge/backend/cpu/operator/TanhImpl.hpp b/include/aidge/backend/cpu/operator/TanhImpl.hpp
index 0bf851e77d94c160c0362301df33d682347daf0c..b1c2217bd29805eca2cf7b7906316756b75a74e0 100644
--- a/include/aidge/backend/cpu/operator/TanhImpl.hpp
+++ b/include/aidge/backend/cpu/operator/TanhImpl.hpp
@@ -12,7 +12,7 @@
 #ifndef AIDGE_CPU_OPERATOR_TANHIMPL_H_
 #define AIDGE_CPU_OPERATOR_TANHIMPL_H_
 
-#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/backend/cpu/operator/OperatorImpl.hpp"
 #include "aidge/operator/Tanh.hpp"
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
@@ -21,34 +21,13 @@
 #include <vector>
 
 namespace Aidge {
-// class Tanh_Op;
+// Operator implementation entry point for the backend
+using TanhImpl_cpu = OperatorImpl_cpu<Tanh_Op,
+    void(const std::size_t, const void*, void*),
+    void(const std::size_t, const void*, const void*, void*)>;
 
-// compute kernel registry for forward and backward
-class TanhImplForward_cpu
-    : public Registrable<TanhImplForward_cpu, std::tuple<DataType, DataType>, void(const std::size_t, const void*, void*)> {
-};
-class TanhImplBackward_cpu
-    : public Registrable<TanhImplBackward_cpu, std::tuple<DataType, DataType, DataType>, void(const std::size_t, const void*, const void*, void*)> {
-};
-
-class TanhImpl_cpu : public OperatorImpl {
-public:
-    TanhImpl_cpu(const Tanh_Op& op) : OperatorImpl(op, "cpu") {}
-
-    static std::unique_ptr<TanhImpl_cpu> create(const Tanh_Op& op) {
-        return std::make_unique<TanhImpl_cpu>(op);
-    }
-
-    Elts_t getNbRequiredProtected(const IOIndex_t inputIdx) const override final;
-	
-    void forward() override final;
-
-    void backward() override final;
-};
-
-namespace {
-static Registrar<Tanh_Op> registrarTanhImpl_cpu("cpu", Aidge::TanhImpl_cpu::create);
-}
+// Implementation entry point registration to Operator
+REGISTRAR(Tanh_Op, "cpu", Aidge::TanhImpl_cpu::create);
 }  // namespace Aidge
 
 #endif /* AIDGE_CPU_OPERATOR_TANHIMPL_H_ */
diff --git a/include/aidge/backend/cpu/operator/TanhImpl_forward_kernels.hpp b/include/aidge/backend/cpu/operator/TanhImpl_forward_kernels.hpp
deleted file mode 100644
index 9e57b6dfcb0da322f5b21944fb10ec7a10cd0ab8..0000000000000000000000000000000000000000
--- a/include/aidge/backend/cpu/operator/TanhImpl_forward_kernels.hpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2023 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_CPU_OPERATOR_TANHIMPL_FORWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_TANHIMPL_FORWARD_KERNEL_H_
-
-#include "aidge/utils/Registrar.hpp"
-
-#include "aidge/backend/cpu/operator/TanhImpl.hpp"
-
-namespace Aidge {
-template <class I, class O>
-void TanhImpl_cpu_forward_kernel(std::size_t inputLenght,
-                                     const void* input_,
-                                     void* output_) {
-
-    const I* input = static_cast<const I*>(input_);
-    O* output = static_cast<O*>(output_);
-
-//#pragma omp parallel for if (inputLenght > 1024)
-    for (std::size_t i = 0; i < inputLenght; ++i) {
-        output[i] = std::tanh(input[i]);
-    }
-}
-
-namespace {
-static Registrar<TanhImplForward_cpu> registrarTanhImplForward_cpu_Float32(
-        {DataType::Float32, DataType::Float32}, Aidge::TanhImpl_cpu_forward_kernel<float, float>);
-static Registrar<TanhImplForward_cpu> registrarTanhImplForward_cpu_Float64(
-        {DataType::Float64, DataType::Float64}, Aidge::TanhImpl_cpu_forward_kernel<double, double>);
-}  // namespace
-}  // namespace Aidge
-
-#endif /* AIDGE_CPU_OPERATOR_TANHIMPL_FORWARD_KERNEL_H_ */
diff --git a/include/aidge/backend/cpu/operator/TanhImpl_backward_kernels.hpp b/include/aidge/backend/cpu/operator/TanhImpl_kernels.hpp
similarity index 51%
rename from include/aidge/backend/cpu/operator/TanhImpl_backward_kernels.hpp
rename to include/aidge/backend/cpu/operator/TanhImpl_kernels.hpp
index 3a13c2cad21c35822fc6248590550e4716ee046d..fdcac210484b11f2220dcc2a6813efed503d1913 100644
--- a/include/aidge/backend/cpu/operator/TanhImpl_backward_kernels.hpp
+++ b/include/aidge/backend/cpu/operator/TanhImpl_kernels.hpp
@@ -9,15 +9,28 @@
  *
  ********************************************************************************/
 
-#ifndef AIDGE_CPU_OPERATOR_TANHIMPL_BACKWARD_KERNEL_H_
-#define AIDGE_CPU_OPERATOR_TANHIMPL_BACKWARD_KERNEL_H_
+#ifndef AIDGE_CPU_OPERATOR_TANHIMPL_KERNELS_H_
+#define AIDGE_CPU_OPERATOR_TANHIMPL_KERNELS_H_
 
-#include <cstddef>  // std::size_t
+#include "aidge/utils/Registrar.hpp"
 
 #include "aidge/backend/cpu/operator/TanhImpl.hpp"
-#include "aidge/utils/Registrar.hpp"
 
 namespace Aidge {
+template <class I, class O>
+void TanhImpl_cpu_forward_kernel(std::size_t inputLenght,
+                                     const void* input_,
+                                     void* output_) {
+
+    const I* input = static_cast<const I*>(input_);
+    O* output = static_cast<O*>(output_);
+
+//#pragma omp parallel for if (inputLenght > 1024)
+    for (std::size_t i = 0; i < inputLenght; ++i) {
+        output[i] = std::tanh(input[i]);
+    }
+}
+
 template <class O, class GI, class GO>
 void TanhImpl_cpu_backward_kernel(const std::size_t inputLenght,
                                   const void* output_, const void* grad_output_,
@@ -30,14 +43,13 @@ void TanhImpl_cpu_backward_kernel(const std::size_t inputLenght,
     }
 }
 
-namespace {
-static Registrar<TanhImplBackward_cpu> registrarTanhImplBackward_cpu_Float32(
-    {DataType::Float32, DataType::Float32, DataType::Float32},
-    Aidge::TanhImpl_cpu_backward_kernel<float, float, float>);
-static Registrar<TanhImplBackward_cpu> registrarTanhImplBackward_cpu_Float64(
-    {DataType::Float64, DataType::Float64, DataType::Float64},
-    Aidge::TanhImpl_cpu_backward_kernel<double, double, double>);
-}  // namespace
+// Kernels registration to implementation entry point
+REGISTRAR(TanhImpl_cpu,
+    {DataType::Float32},
+    {ProdConso::inPlaceModel, Aidge::TanhImpl_cpu_forward_kernel<float, float>, Aidge::TanhImpl_cpu_backward_kernel<float, float, float>});
+REGISTRAR(TanhImpl_cpu,
+    {DataType::Float64},
+    {ProdConso::inPlaceModel, Aidge::TanhImpl_cpu_forward_kernel<double, double>, Aidge::TanhImpl_cpu_backward_kernel<double, double, double>});
 }  // namespace Aidge
 
-#endif /* AIDGE_CPU_OPERATOR_TANHIMPL_BACKWARD_KERNEL_H_ */
+#endif /* AIDGE_CPU_OPERATOR_TANHIMPL_KERNELS_H_ */
diff --git a/pyproject.toml b/pyproject.toml
index aa43189d3f4f7d3796009c2646175635382796bf..9dbdbede6083ea2ededd5a861449a2dfbea6f40e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -17,8 +17,7 @@ dynamic = ["version"] # defined in tool.setuptools_scm
 requires = [
     "setuptools>=64",
     "setuptools_scm[toml]==7.1.0",
-    "cmake>=3.15.3.post1",
-    "toml"
+    "cmake>=3.18.4.post1"
 ]
 build-backend = "setuptools.build_meta"
 
diff --git a/setup.py b/setup.py
index 35520fd344c505bf38a60fcd5484c28517b0d2bd..22cbd9732c8b9e1099c3e322032e8377f6d4506b 100644
--- a/setup.py
+++ b/setup.py
@@ -8,17 +8,13 @@ import multiprocessing
 
 from math import ceil
 
-import toml
-
 from setuptools import setup, Extension
 from setuptools.command.build_ext import build_ext
 
 
-def get_project_name() -> str:
-    with open(pathlib.Path().absolute() / "pyproject.toml", "r") as file:
-        project_toml = toml.load(file)
-        return project_toml["project"]["name"]
+PROJECT_NAME = "aidge_backend_cpu"
 
+SETUP_DIR = pathlib.Path(__file__).parent
 
 class AidgeBuildExtension(Extension):
     def __init__(self, name):
@@ -26,6 +22,15 @@ class AidgeBuildExtension(Extension):
 
 
 class AidgePkgBuild(build_ext):
+    def __init__(self, dist, *args, **kwargs):
+        super().__init__(dist, *args, **kwargs)
+        # Detect editable_mode for old versions of setuptools
+        if not hasattr(self, "editable_mode"):
+            if hasattr(dist, "commands"):
+                self.editable_mode = "develop" in dist.commands
+            else:
+                self.editable_mode = False
+
     def run(self):
         ####################################
         # BUILD PACKAGE
@@ -43,36 +48,35 @@ class AidgePkgBuild(build_ext):
         if not build_lib.exists():
             build_lib.mkdir(parents=True, exist_ok=True)
 
-        os.chdir(str(build_temp))
+        package_prefix = build_lib if not self.editable_mode else SETUP_DIR
+        pybind_install_prefix = (package_prefix / PROJECT_NAME).absolute()
 
-        compile_type = (
-            "Release"
-            if "AIDGE_PYTHON_BUILD_TYPE" not in os.environ
-            else os.environ["AIDGE_PYTHON_BUILD_TYPE"]
-        )
+        os.chdir(str(build_temp))
 
+        compile_type = os.environ.get("AIDGE_PYTHON_BUILD_TYPE", "Release")
         install_path = (
             os.path.join(sys.prefix, "lib", "libAidge")
             if "AIDGE_INSTALL" not in os.environ
             else os.environ["AIDGE_INSTALL"]
         )
-
-        # using ninja as default build system to build faster and with the same compiler as on windows
-        build_gen = (
-            ["-G", os.environ["AIDGE_BUILD_GEN"]]
-            if "AIDGE_BUILD_GEN" in os.environ
+        build_gen = os.environ.get("AIDGE_BUILD_GEN", "")
+        build_gen_opts = (
+            ["-G", build_gen]
+            if build_gen
             else []
         )
+        test_onoff = os.environ.get("AIDGE_BUILD_TEST", "OFF")
         
         self.spawn(
             [
                 "cmake",
-                *build_gen,
+                *build_gen_opts,
                 str(cwd),
-                "-DTEST=OFF",
+                f"-DTEST={test_onoff}",
                 f"-DCMAKE_INSTALL_PREFIX:PATH={install_path}",
                 f"-DCMAKE_BUILD_TYPE={compile_type}",
                 "-DPYBIND=ON",
+                f"-DPYBIND_INSTALL_PREFIX:PATH={pybind_install_prefix}",
                 "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
                 "-DCOVERAGE=OFF",
             ]
@@ -85,25 +89,11 @@ class AidgePkgBuild(build_ext):
             self.spawn(["cmake", "--install", ".", "--config", compile_type])
         os.chdir(str(cwd))
 
-        aidge_package = build_lib / (get_project_name())
-
-        # Get "aidge core" package
-        # ext_lib = build_temp
-        print(build_temp.absolute())
-        # Copy all shared object files from build_temp/lib to aidge_package
-        for root, _, files in os.walk(build_temp.absolute()):
-            for file in files:
-                if (file.endswith(".so") or file.endswith(".pyd")) and (
-                    root != str(aidge_package.absolute())
-                ):
-                    currentFile = os.path.join(root, file)
-                    shutil.copy(currentFile, str(aidge_package.absolute()))
-
 
 if __name__ == "__main__":
     setup(
         include_package_data=True,
-        ext_modules=[AidgeBuildExtension(get_project_name())],
+        ext_modules=[AidgeBuildExtension(PROJECT_NAME)],
         cmdclass={
             "build_ext": AidgePkgBuild,
         },
diff --git a/src/operator/AbsImpl.cpp b/src/operator/AbsImpl.cpp
index 1eb86c91289d3000f9cc0792e5ca2da29d4d8c24..130d6cf7a64e1e75b8ef128974101a477f802caf 100644
--- a/src/operator/AbsImpl.cpp
+++ b/src/operator/AbsImpl.cpp
@@ -14,29 +14,27 @@
 #include <memory>
 #include <vector>
 
-#include "aidge/backend/cpu/operator/AbsImpl_forward_kernels.hpp"
+#include "aidge/backend/cpu/operator/AbsImpl_kernels.hpp"
 #include "aidge/data/Tensor.hpp"
 #include "aidge/operator/Abs.hpp"
 #include "aidge/utils/Types.h"
 
-Aidge::Elts_t Aidge::AbsImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
-
+template <>
 void Aidge::AbsImpl_cpu::forward() {
     const Abs_Op& op = static_cast<const Abs_Op&>(mOp);
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<AbsImplForward_cpu>::create({
-                            op.getInput(0)->dataType(),
-                            op.getOutput(0)->dataType()
-                        });
+    const auto impl = Registrar<AbsImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(
+    impl.forward(
         op.getInput(0)->size(),
         op.getInput(0)->getImpl()->rawPtr(),
         op.getOutput(0)->getImpl()->rawPtr()
     );
 }
+
+template <>
+void Aidge::AbsImpl_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for Abs_Op on backend cpu");
+}
diff --git a/src/operator/AddImpl.cpp b/src/operator/AddImpl.cpp
index d6d75a608e4da7d8b9ed8a28912ff2eb1751e042..457a0b17e531fac35ff873f9eedca7bbbe82d459 100644
--- a/src/operator/AddImpl.cpp
+++ b/src/operator/AddImpl.cpp
@@ -16,69 +16,57 @@
 #include <vector>
 
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
-#include "aidge/backend/cpu/operator/AddImpl_forward_kernels.hpp"
+#include "aidge/backend/cpu/operator/AddImpl_kernels.hpp"
 #include "aidge/data/Data.hpp"
 #include "aidge/data/Tensor.hpp"
 #include "aidge/utils/Types.h"
 #include "aidge/utils/ErrorHandling.hpp"
 
-Aidge::Elts_t  Aidge::AddImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
-
+template <>
 void  Aidge::AddImpl_cpu::forward() {
-    const auto& opTensor = static_cast<const OperatorTensor&>(mOp);
-    AIDGE_ASSERT(opTensor.getInput(0)->hasImpl(), "cannot run Add forward because the 0-th input has no implementation.");
-    assert(opTensor.getInput(0) && "missing input in Add operator");
-    DataType datatypeFirstInput = opTensor.getInput(0)->dataType();
-    for (IOIndex_t i = 1; i < opTensor.nbInputs(); ++i) {
-        AIDGE_ASSERT(opTensor.getInput(i)->hasImpl(), "cannot run Add forward because the {}-th input has no implementation.", i);
-        assert(opTensor.getInput(i) && "missing input in Add operator");
-        assert(opTensor.getInput(i)->dataType() == datatypeFirstInput);
+    const Add_Op& op = static_cast<const Add_Op&>(mOp);
+    // Check inputs
+    AIDGE_ASSERT(op.getInput(0), "missing input in Add operator");
+    AIDGE_ASSERT(op.getInput(0)->hasImpl(), "cannot run Add forward because the 0-th input has no implementation.");
+    DataType datatypeFirstInput = op.getInput(0)->dataType();
+    for (IOIndex_t i = 1; i < op.nbInputs(); ++i) {
+        AIDGE_ASSERT(op.getInput(i), "missing input in Add operator");
+        AIDGE_ASSERT(op.getInput(i)->hasImpl(), "cannot run Add forward because the {}-th input has no implementation.", i);
+        AIDGE_ASSERT(op.getInput(i)->dataType() == datatypeFirstInput, "Cannot add inputs with two differents data type.");
     }
 
     // Find the correct kernel type
-    const auto outputDataType = opTensor.getOutput(0)->dataType();
-    const Registrar<AddImplForward_cpu>::registrar_key registrarKey = {
-        datatypeFirstInput,
-        outputDataType};
-
-    Registrar<AddImplForward_cpu>::registrar_type kernelFunc;
-    if (Registrar<AddImplForward_cpu>::exists(registrarKey)) {
-        // One exists with the right inputs/output types
-        kernelFunc = Registrar<AddImplForward_cpu>::create(registrarKey);
-    }
-    else {
-        // Otherwise, fallback to the kernel with all types matching output type
-        kernelFunc = Registrar<AddImplForward_cpu>::create({
-            outputDataType, outputDataType});
-    }
+    const auto impl = Registrar<AddImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Convert input data (no overhead if not needed!)
     // TODO: right now, if needed, memory will be allocated/deallocated at each
     // call to forward(). We might put the following shared_ptr as members of
     // this class to avoid that.
-    const std::size_t nbDims = opTensor.getOutput(0)->nbDims();
+    const std::size_t nbDims = op.getOutput(0)->nbDims();
     std::vector<std::vector<std::size_t>> inputsDims;
     std::vector<const void*> opInputs;
-    std::vector<std::shared_ptr<Tensor>> inputsFallback(opTensor.nbInputs());
-    for (IOIndex_t i = 0; i < opTensor.nbInputs(); ++i) {
+    std::vector<std::shared_ptr<Tensor>> inputsFallback(op.nbInputs());
+    for (IOIndex_t i = 0; i < op.nbInputs(); ++i) {
         std::vector<std::size_t> inputDims(nbDims, 1);
-        auto dims = opTensor.getInput(i)->dims();
+        auto dims = op.getInput(i)->dims();
 		for(std::size_t j=dims.size()-1; j+1>0; --j)
 		{
 			std::size_t idx = nbDims - (dims.size()-j);
 			inputDims[idx] = dims[j];
 		}
         inputsDims.push_back(inputDims);
-        const auto& input = opTensor.getInput(i)->refCastFrom(inputsFallback[i], *opTensor.getOutput(0));
+        const auto& input = op.getInput(i)->refCastFrom(inputsFallback[i], *op.getOutput(0));
         opInputs.push_back(input.getImpl()->rawPtr());
     }
 
-    kernelFunc(opInputs,
+    impl.forward(opInputs,
                inputsDims,
-               opTensor.getOutput(0)->size(),
-               opTensor.getOutput(0)->dims(),
-               getCPUPtr(opTensor.getRawOutput(0)));
+               op.getOutput(0)->size(),
+               op.getOutput(0)->dims(),
+               getCPUPtr(op.getRawOutput(0)));
+}
+
+template <>
+void Aidge::AddImpl_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for Add_Op on backend cpu");
 }
diff --git a/src/operator/AndImpl.cpp b/src/operator/AndImpl.cpp
index bc447e74a1af797a69c942eab9ff816bc195388a..2e0f59769ad86f6e4143ab59d089706e34792244 100644
--- a/src/operator/AndImpl.cpp
+++ b/src/operator/AndImpl.cpp
@@ -21,30 +21,29 @@
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 
 #include "aidge/backend/cpu/operator/AndImpl.hpp"
-#include "aidge/backend/cpu/operator/AndImpl_forward_kernels.hpp"
-
-Aidge::Elts_t Aidge::AndImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
+#include "aidge/backend/cpu/operator/AndImpl_kernels.hpp"
 
+template <>
 void Aidge::AndImpl_cpu::forward() {
-    // Find the correct kernel type
-    auto kernelFunc = Registrar<AndImplForward_cpu>::create({
-        std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dataType(),
-        std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->dataType(),
-        std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dataType()});
-
     const std::vector<std::size_t> inputDims0 = getBroadcastedDims(std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dims(),
                                                                    std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dims());
     const std::vector<std::size_t> inputDims1 = getBroadcastedDims(std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dims(),
                                                                    std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->dims());
 
+
+    // Find the correct kernel type
+    const auto impl = Registrar<AndImpl_cpu>::create(getBestMatch(getRequiredSpec()));
+
     // Call kernel
-    kernelFunc(inputDims0,
+    impl.forward(inputDims0,
         inputDims1,
         std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dims(),
         getCPUPtr(mOp.getRawInput(0)),
         getCPUPtr(mOp.getRawInput(1)),
         getCPUPtr(mOp.getRawOutput(0)));
 }
+
+template <>
+void Aidge::AndImpl_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for And_Op on backend cpu");
+}
diff --git a/src/operator/ArgMaxImpl.cpp b/src/operator/ArgMaxImpl.cpp
index eda3c0b2de62e8e170c2562945999273bdb921a7..b8fb85a7cd86a788cda69307d5ed8f363619f9f0 100644
--- a/src/operator/ArgMaxImpl.cpp
+++ b/src/operator/ArgMaxImpl.cpp
@@ -16,19 +16,24 @@
 
 #include "aidge/utils/Types.h"
 #include "aidge/operator/ArgMax.hpp"
-#include "aidge/backend/cpu/operator/ArgMaxImpl_forward_kernels.hpp"
+#include "aidge/backend/cpu/operator/ArgMaxImpl_kernels.hpp"
 
+template <>
 void Aidge::ArgMaxImpl_cpu::forward() {
     const ArgMax_Op& op_ = dynamic_cast<const ArgMax_Op&>(mOp);
+
     // Find the correct kernel type
-    auto kernelFunc = Registrar<ArgMaxImplForward_cpu>::create({
-        op_.getInput(0)->dataType(),
-        op_.getOutput(0)->dataType()});
+    const auto impl = Registrar<ArgMaxImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(op_.axis(),
+    impl.forward(op_.axis(),
                 op_.selectLastIndex(),
                 op_.getInput(0)->dims(),
                 op_.getInput(0)->getImpl()->rawPtr(),
                 op_.getOutput(0)->getImpl()->rawPtr());
 }
+
+template <>
+void Aidge::ArgMaxImpl_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for ArgMax_Op on backend cpu");
+}
diff --git a/src/operator/AvgPoolingImpl.cpp b/src/operator/AvgPoolingImpl.cpp
index feaa7e67a8d0bc726462aed99e557493d3b8d0c6..01a5e8cf1772161f5cf98d3a8bd52f43ac7a1d0d 100644
--- a/src/operator/AvgPoolingImpl.cpp
+++ b/src/operator/AvgPoolingImpl.cpp
@@ -16,29 +16,29 @@
 #include <vector>
 
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
-#include "aidge/backend/cpu/operator/AvgPoolingImpl_forward_kernels.hpp"
+#include "aidge/backend/cpu/operator/AvgPoolingImpl_kernels.hpp"
 #include "aidge/data/Tensor.hpp"
 #include "aidge/operator/AvgPooling.hpp"
 #include "aidge/utils/Types.h"
 
-Aidge::Elts_t Aidge::AvgPoolingImpl2D_cpu::getNbRequiredProtected(IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
-
+template <>
 void Aidge::AvgPoolingImpl2D_cpu::forward() {
     const auto& op_ = dynamic_cast<const AvgPooling_Op<2>&>(mOp);
     assert(op_.getInput(0) && "missing input #0");
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<AvgPoolingImpl2DForward_cpu>::create(
-        {op_.getInput(0)->dataType(),
-         op_.getOutput(0)->dataType()});
+    const auto impl = Registrar<AvgPoolingImpl2D_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(op_.strideDims(),
+    impl.forward(op_.strideDims(),
                op_.kernelDims(),
                op_.getInput(0)->template dims<4>(),
                getCPUPtr(op_.getInput(0)),
                getCPUPtr(op_.getOutput(0)));
 }
+
+template <>
+void Aidge::AvgPoolingImpl2D_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for AvgPooling_Op<2> on backend cpu");
+}
+
diff --git a/src/operator/BatchNormImpl.cpp b/src/operator/BatchNormImpl.cpp
index 3046eea9bd241732daf39cce1783b5ee50de01c7..9f1d986e63f14e6038c80054e5e3bc631ec24224 100644
--- a/src/operator/BatchNormImpl.cpp
+++ b/src/operator/BatchNormImpl.cpp
@@ -19,13 +19,9 @@
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 #include "aidge/operator/BatchNorm.hpp"
 
-#include "aidge/backend/cpu/operator/BatchNormImpl_forward_kernels.hpp"
-
-Aidge::Elts_t Aidge::BatchNormImpl2D_cpu::getNbRequiredProtected(IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
+#include "aidge/backend/cpu/operator/BatchNormImpl_kernels.hpp"
 
+template <>
 void Aidge::BatchNormImpl2D_cpu::forward() {
     const auto& op_ = dynamic_cast<const BatchNorm_Op<2>&>(mOp);
     AIDGE_ASSERT(op_.getInput(0), "missing input #0 for BatchNorm Operator");
@@ -35,14 +31,12 @@ void Aidge::BatchNormImpl2D_cpu::forward() {
     AIDGE_ASSERT(op_.getInput(4), "missing input #4 for BatchNorm Operator");
 
     AIDGE_ASSERT(op_.getOutput(0)->nbDims() == 4, "");
+
     // Find the correct kernel type
-    auto kernelFunc =
-            Registrar<BatchNormImpl2DForward_cpu>::create({op_.getInput(0)->dataType(),
-                                                           op_.getInput(1)->dataType(),
-                                                           op_.getOutput(0)->dataType()});
+    const auto impl = Registrar<BatchNormImpl2D_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(op_.epsilon(),
+    impl.forward(op_.epsilon(),
             op_.momentum(),
             op_.getInput(0)->template dims<4>(),
             getCPUPtr(op_.getRawInput(0)),
@@ -53,3 +47,8 @@ void Aidge::BatchNormImpl2D_cpu::forward() {
             getCPUPtr(op_.getRawOutput(0)),
             true);
 }
+
+template <>
+void Aidge::BatchNormImpl2D_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for BatchNorm_Op<2> on backend cpu");
+}
diff --git a/src/operator/ConstantOfShapeImpl.cpp b/src/operator/ConstantOfShapeImpl.cpp
index 7d727c04d13b7cd8822fc9be4cf62b8a7bf7754f..16e4b762ba04e5f01bfccf965f6de3650fa2e734 100644
--- a/src/operator/ConstantOfShapeImpl.cpp
+++ b/src/operator/ConstantOfShapeImpl.cpp
@@ -15,7 +15,7 @@
 #include <memory>
 #include <vector>
 
-#include "aidge/backend/cpu/operator/ConstantOfShapeImpl_forward_kernels.hpp"
+#include "aidge/backend/cpu/operator/ConstantOfShapeImpl_kernels.hpp"
 #include "aidge/data/Data.hpp"
 #include "aidge/data/Tensor.hpp"
 #include "aidge/operator/ConstantOfShape.hpp"
@@ -23,18 +23,22 @@
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 
+template <>
 void Aidge::ConstantOfShapeImpl_cpu::forward() {
   const ConstantOfShape_Op &op_ = static_cast<const ConstantOfShape_Op &>(mOp);
   // Check if input is provided
   AIDGE_ASSERT(op_.getInput(0), "{} : Missing input 0", __func__);
 
-  // Create the forward kernal with the wanted types
-  auto kernelFunc = Registrar<ConstantOfShapeImplForward_cpu>::create(
-      {op_.getOutput(0)->dataType()});
+    // Find the correct kernel type
+    const auto impl = Registrar<ConstantOfShapeImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
-  // Call kernel
-  kernelFunc(op_.getOutput(0)->dims(),
+    // Call kernel
+    impl.forward(op_.getOutput(0)->dims(),
              op_.value(), 
              op_.getOutput(0)->getImpl()->rawPtr());
 }
 
+template <>
+void Aidge::ConstantOfShapeImpl_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for ConstantOfShape_Op on backend cpu");
+}
diff --git a/src/operator/ConvDepthWiseImpl.cpp b/src/operator/ConvDepthWiseImpl.cpp
index 591e8a0637d1e52c75193ac1750a210a08815ccc..d86bba8d1abf348eb25e2d9c69d04b5c33a8a176 100644
--- a/src/operator/ConvDepthWiseImpl.cpp
+++ b/src/operator/ConvDepthWiseImpl.cpp
@@ -15,18 +15,13 @@
 #include <vector>
 
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
-#include "aidge/backend/cpu/operator/ConvDepthWiseImpl_forward_kernels.hpp"
+#include "aidge/backend/cpu/operator/ConvDepthWiseImpl_kernels.hpp"
 #include "aidge/data/Tensor.hpp"
 #include "aidge/operator/ConvDepthWise.hpp"
 #include "aidge/utils/Log.hpp"
 #include "aidge/utils/Types.h"
 
-
-Aidge::Elts_t Aidge::ConvDepthWiseImpl1D_cpu::getNbRequiredProtected(Aidge::IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
-
+template <>
 void Aidge::ConvDepthWiseImpl1D_cpu::forward() {
     const auto& op_ = dynamic_cast<const ConvDepthWise_Op<1>&>(mOp);
 
@@ -36,23 +31,7 @@ void Aidge::ConvDepthWiseImpl1D_cpu::forward() {
     AIDGE_ASSERT((op_.getInput(0)->nbDims() == 3), "support for 4-dimensions tensors only");
 
     // Find the correct kernel type
-    const auto outputDataType = op_.getOutput(0)->dataType();
-    const Registrar<ConvDepthWiseImpl1DForward_cpu>::registrar_key registrarKey = {
-        op_.getInput(0)->dataType(),
-        op_.getInput(1)->dataType(),
-        ((op_.getInput(2)) ? op_.getInput(2)->dataType() : op_.getInput(1)->dataType()),
-        outputDataType};
-
-    Registrar<ConvDepthWiseImpl1DForward_cpu>::registrar_type kernelFunc;
-    if (Registrar<ConvDepthWiseImpl1DForward_cpu>::exists(registrarKey)) {
-        // One exists with the right inputs/output types
-        kernelFunc = Registrar<ConvDepthWiseImpl1DForward_cpu>::create(registrarKey);
-    }
-    else {
-        // Otherwise, fallback to the kernel with all types matching output type
-        kernelFunc = Registrar<ConvDepthWiseImpl1DForward_cpu>::create({
-            outputDataType, outputDataType, outputDataType, outputDataType});
-    }
+    const auto impl = Registrar<ConvDepthWiseImpl1D_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Convert input data (no overhead if not needed!)
     // TODO: right now, if needed, memory will be allocated/deallocated at each
@@ -64,7 +43,7 @@ void Aidge::ConvDepthWiseImpl1D_cpu::forward() {
     const auto& input2 = (op_.getInput(2)) ? op_.getInput(2)->refCastFrom(input2Fallback, *op_.getOutput(0)) : Tensor();
 
     // Call kernel
-    kernelFunc(op_.strideDims(),
+    impl.forward(op_.strideDims(),
                 op_.dilationDims(),
                 op_.kernelDims(), // Conv attributes
                op_.getInput(0)->template dims<3>(), // input dimensions
@@ -75,11 +54,12 @@ void Aidge::ConvDepthWiseImpl1D_cpu::forward() {
             );
 }
 
-Aidge::Elts_t Aidge::ConvDepthWiseImpl2D_cpu::getNbRequiredProtected(IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
+template <>
+void Aidge::ConvDepthWiseImpl1D_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for ConvDepthWise_Op<1> on backend cpu");
 }
 
+template <>
 void Aidge::ConvDepthWiseImpl2D_cpu::forward() {
     const auto& op_ = dynamic_cast<const ConvDepthWise_Op<2>&>(mOp);
 
@@ -90,11 +70,7 @@ void Aidge::ConvDepthWiseImpl2D_cpu::forward() {
     AIDGE_ASSERT((op_.getInput(0)->nbDims() == 4), "support for 4-dimensions tensors only");
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<ConvDepthWiseImpl2DForward_cpu>::create(
-        {op_.getInput(0)->dataType(),
-        op_.getInput(1)->dataType(),
-        op_.getInput(2)->dataType(),
-        op_.getOutput(0)->dataType()});
+    const auto impl = Registrar<ConvDepthWiseImpl2D_cpu>::create(getBestMatch(getRequiredSpec()));
 
         // Convert input data (no overhead if not needed!)
     // TODO: right now, if needed, memory will be allocated/deallocated at each
@@ -106,7 +82,7 @@ void Aidge::ConvDepthWiseImpl2D_cpu::forward() {
     const auto& input2 = op_.getInput(2) ? op_.getInput(2)->refCastFrom(input2Fallback, *op_.getOutput(0)) : Tensor();
 
     // Call kernel
-    kernelFunc(op_.strideDims(),
+    impl.forward(op_.strideDims(),
             op_.dilationDims(),
             op_.kernelDims(),
             op_.getInput(0)->template dims<4>(),
@@ -115,3 +91,8 @@ void Aidge::ConvDepthWiseImpl2D_cpu::forward() {
             op_.getInput(2) ?  input2.getImpl()->rawPtr() : nullptr,
             getCPUPtr(op_.getRawOutput(0)));
 }
+
+template <>
+void Aidge::ConvDepthWiseImpl2D_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for ConvDepthWise_Op<2> on backend cpu");
+}
diff --git a/src/operator/ConvImpl.cpp b/src/operator/ConvImpl.cpp
index 9eb3bdabe5bbaa1aefa97604b4ec0159004daa60..fdfe19fbf4bf3e71c86aa28b966cfb21a1b5ba40 100644
--- a/src/operator/ConvImpl.cpp
+++ b/src/operator/ConvImpl.cpp
@@ -10,6 +10,7 @@
  ********************************************************************************/
 
 #include "aidge/backend/cpu/operator/ConvImpl.hpp"
+#include "aidge/backend/cpu/operator/ConvImpl_kernels.hpp"
 
 #include <cassert>
 #include <chrono>  // std::chrono::milliseconds
@@ -18,40 +19,19 @@
 #include <vector>
 
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
-#include "aidge/backend/cpu/operator/ConvImpl_forward_kernels.hpp"
 #include "aidge/operator/Conv.hpp"
 #include "aidge/utils/Types.h"
 
-Aidge::Elts_t Aidge::ConvImpl1D_cpu::getNbRequiredProtected(IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
-
+template <>
 void Aidge::ConvImpl1D_cpu::forward() {
     const auto& op_ = static_cast<const Conv_Op<1>&>(mOp);
 
     // FIXME: uncomment the following code once memory handling will work
-AIDGE_ASSERT(op_.getInput(0), "missing input #0 in Conv Operator.");
+    AIDGE_ASSERT(op_.getInput(0), "missing input #0 in Conv Operator.");
     AIDGE_ASSERT(op_.getInput(1), "missing input #1 in Conv Operator.");
 
     // Find the correct kernel type
-    const auto outputDataType = op_.getOutput(0)->dataType();
-    const Registrar<ConvImpl1DForward_cpu>::registrar_key registrarKey = {
-        op_.getInput(0)->dataType(),
-        op_.getInput(1)->dataType(),
-        (op_.getInput(2) ? op_.getInput(2)->dataType() : op_.getInput(1)->dataType()),
-        outputDataType};
-
-    Registrar<ConvImpl1DForward_cpu>::registrar_type kernelFunc;
-    if (Registrar<ConvImpl1DForward_cpu>::exists(registrarKey)) {
-        // One exists with the right inputs/output types
-        kernelFunc = Registrar<ConvImpl1DForward_cpu>::create(registrarKey);
-    }
-    else {
-        // Otherwise, fallback to the kernel with all types matching output type
-        kernelFunc = Registrar<ConvImpl1DForward_cpu>::create({
-            outputDataType, outputDataType, outputDataType, outputDataType});
-    }
+    const auto impl = Registrar<ConvImpl1D_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Convert input data (no overhead if not needed!)
     // TODO: right now, if needed, memory will be allocated/deallocated at each
@@ -63,7 +43,7 @@ AIDGE_ASSERT(op_.getInput(0), "missing input #0 in Conv Operator.");
     const auto& input2 = (op_.getInput(2)) ? op_.getInput(2)->refCastFrom(input2Fallback, *op_.getOutput(0)) : Tensor();
 
     // Call kernel
-    kernelFunc(op_.strideDims(),
+    impl.forward(op_.strideDims(),
             op_.dilationDims(),
             op_.kernelDims(),
             op_.getInput(0)->template dims<3>(), // input dimensions
@@ -75,11 +55,12 @@ AIDGE_ASSERT(op_.getInput(0), "missing input #0 in Conv Operator.");
             );
 }
 
-Aidge::Elts_t Aidge::ConvImpl2D_cpu::getNbRequiredProtected(IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
+template <>
+void Aidge::ConvImpl1D_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for Conv_Op<1> on backend cpu");
 }
 
+template <>
 void Aidge::ConvImpl2D_cpu::forward() {
     const auto& op_ = dynamic_cast<const Conv_Op<2>&>(mOp);
 
@@ -88,23 +69,7 @@ void Aidge::ConvImpl2D_cpu::forward() {
     AIDGE_ASSERT(op_.getInput(1), "missing input #1 in Conv Operator.");
 
     // Find the correct kernel type
-    const auto outputDataType = op_.getOutput(0)->dataType();
-    const Registrar<ConvImpl2DForward_cpu>::registrar_key registrarKey = {
-        op_.getInput(0)->dataType(),
-        op_.getInput(1)->dataType(),
-        (op_.getInput(2) ? op_.getInput(2)->dataType() : op_.getInput(1)->dataType()),
-        outputDataType};
-
-    Registrar<ConvImpl2DForward_cpu>::registrar_type kernelFunc;
-    if (Registrar<ConvImpl2DForward_cpu>::exists(registrarKey)) {
-        // One exists with the right inputs/output types
-        kernelFunc = Registrar<ConvImpl2DForward_cpu>::create(registrarKey);
-    }
-    else {
-        // Otherwise, fallback to the kernel with all types matching output type
-        kernelFunc = Registrar<ConvImpl2DForward_cpu>::create({
-            outputDataType, outputDataType, outputDataType, outputDataType});
-    }
+    const auto impl = Registrar<ConvImpl2D_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Convert input data (no overhead if not needed!)
     // TODO: right now, if needed, memory will be allocated/deallocated at each
@@ -116,7 +81,7 @@ void Aidge::ConvImpl2D_cpu::forward() {
     const auto& input2 = (op_.getInput(2)) ? op_.getInput(2)->refCastFrom(input2Fallback, *op_.getOutput(0)) : Tensor();
 
     // Call kernel
-    kernelFunc(op_.strideDims(),
+    impl.forward(op_.strideDims(),
             op_.dilationDims(),
             op_.kernelDims(),
             op_.getInput(0)->template dims<4>(), // input dimensions
@@ -127,3 +92,8 @@ void Aidge::ConvImpl2D_cpu::forward() {
             getCPUPtr(mOp.getRawOutput(0)) // output
             );
 }
+
+template <>
+void Aidge::ConvImpl2D_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for Conv_Op<2> on backend cpu");
+}
diff --git a/src/operator/DivImpl.cpp b/src/operator/DivImpl.cpp
index cfd74be45b29852c89e4a27035ce2d38fc7266cc..135b32b5005a961e55910e758f9b7102ca51b63c 100644
--- a/src/operator/DivImpl.cpp
+++ b/src/operator/DivImpl.cpp
@@ -15,15 +15,11 @@
 #include "aidge/backend/cpu/data/Broadcasting.hpp"
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 #include "aidge/backend/cpu/operator/DivImpl.hpp"
-#include "aidge/backend/cpu/operator/DivImpl_forward_kernels.hpp"
+#include "aidge/backend/cpu/operator/DivImpl_kernels.hpp"
 #include "aidge/data/Tensor.hpp"
 #include "aidge/utils/Types.h"
 
-Aidge::Elts_t Aidge::DivImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
-
+template <>
 void Aidge::DivImpl_cpu::forward() {
     // Find the correct kernel type
     // auto kernelFunc = Registrar<DivImplForward_cpu>::create({
@@ -60,10 +56,7 @@ void Aidge::DivImpl_cpu::forward() {
     const auto& opTensor = static_cast<const Div_Op&>(mOp);
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<DivImplForward_cpu>::create({
-        opTensor.getInput(0)->dataType(),
-        opTensor.getInput(1)->dataType(),
-        opTensor.getOutput(0)->dataType()});
+    const auto impl = Registrar<DivImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Compute compatible input dimensions
     std::vector<std::size_t>        dims0   = opTensor.getInput(0)->dims();
@@ -73,7 +66,7 @@ void Aidge::DivImpl_cpu::forward() {
     // special case for equal dimensions, the kernel is called with the entire arrays at once
     if (dims0 == dims1) {
         const std::size_t input0_contiguous_size = std::accumulate(dims0.cbegin(), dims0.cend(), std::size_t(1), std::multiplies<std::size_t>());
-        kernelFunc(input0_contiguous_size, input0_contiguous_size, input0_contiguous_size,
+        impl.forward(input0_contiguous_size, input0_contiguous_size, input0_contiguous_size,
                     getCPUPtr(mOp.getRawInput(0)),
                     getCPUPtr(mOp.getRawInput(1)),
                     getCPUPtr(mOp.getRawOutput(0)));
@@ -139,7 +132,7 @@ void Aidge::DivImpl_cpu::forward() {
     std::size_t dim = contiguousIdx - 1;
     const std::size_t nbStacks = std::accumulate(outDims.cbegin(), outDims.cbegin() + contiguousIdx, std::size_t(1), std::multiplies<std::size_t>());
     for (std::size_t stack = 0; stack < nbStacks;) {
-        kernelFunc(input0_contiguous_size, input1_contiguous_size, output_contiguous_size,
+        impl.forward(input0_contiguous_size, input1_contiguous_size, output_contiguous_size,
                     getCPUPtr(mOp.getRawInput(0), offsetIn0*input0_contiguous_size),
                     getCPUPtr(mOp.getRawInput(1), offsetIn1*input1_contiguous_size),
                     getCPUPtr(mOp.getRawOutput(0), offsetOut*output_contiguous_size));
@@ -156,3 +149,8 @@ void Aidge::DivImpl_cpu::forward() {
         }
     }
 }
+
+template <>
+void Aidge::DivImpl_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for Div_Op on backend cpu");
+}
diff --git a/src/operator/ErfImpl.cpp b/src/operator/ErfImpl.cpp
index ace098468c05b80c4116e6f85d00b5fabaf754cd..42c6ce878abe227f74d7df4a9bf31ebc4c63eb88 100644
--- a/src/operator/ErfImpl.cpp
+++ b/src/operator/ErfImpl.cpp
@@ -14,29 +14,27 @@
 #include <memory>
 #include <vector>
 
-#include "aidge/backend/cpu/operator/ErfImpl_forward_kernels.hpp"
+#include "aidge/backend/cpu/operator/ErfImpl_kernels.hpp"
 #include "aidge/data/Tensor.hpp"
 #include "aidge/operator/Erf.hpp"
 #include "aidge/utils/Types.h"
 
-Aidge::Elts_t Aidge::ErfImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
-
+template <>
 void Aidge::ErfImpl_cpu::forward() {
     const Erf_Op& op = static_cast<const Erf_Op&>(mOp);
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<ErfImplForward_cpu>::create({
-                            op.getInput(0)->dataType(),
-                            op.getOutput(0)->dataType()
-                        });
+    const auto impl = Registrar<ErfImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(
+    impl.forward(
         op.getInput(0)->size(),
         op.getInput(0)->getImpl()->rawPtr(),
         op.getOutput(0)->getImpl()->rawPtr()
     );
 }
+
+template <>
+void Aidge::ErfImpl_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for Erf_Op on backend cpu");
+}
diff --git a/src/operator/FCImpl.cpp b/src/operator/FCImpl.cpp
index f7eebb7b21512fb3b388b6927409fba9a1d92b34..359452712f94be078122266089cc1da89baf50d5 100644
--- a/src/operator/FCImpl.cpp
+++ b/src/operator/FCImpl.cpp
@@ -17,37 +17,20 @@
 #include <tuple>
 
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
-#include "aidge/backend/cpu/operator/FCImpl_backward_kernels.hpp"
-#include "aidge/backend/cpu/operator/FCImpl_forward_kernels.hpp"
+#include "aidge/backend/cpu/operator/FCImpl_kernels.hpp"
 #include "aidge/operator/FC.hpp"
 #include "aidge/utils/ErrorHandling.hpp"
 #include "aidge/utils/Types.h"
 
 
+template <>
 void Aidge::FCImpl_cpu::forward()
 {
     const FC_Op& op_ = dynamic_cast<const FC_Op&>(mOp);
     AIDGE_ASSERT(op_.getInput(0), "missing input #0");
     AIDGE_ASSERT(op_.getInput(1), "missing input #1");
 
-    // Find the correct kernel type
-    const auto outputDataType = op_.getOutput(0)->dataType();
-    const Registrar<FCImplForward_cpu>::registrar_key registrarKey = {
-        op_.getInput(0)->dataType(),
-        op_.getInput(1)->dataType(),
-        ((op_.getInput(2)) ? op_.getInput(2)->dataType() : op_.getInput(1)->dataType()),
-        outputDataType};
-
-    Registrar<FCImplForward_cpu>::registrar_type kernelFunc;
-    if (Registrar<FCImplForward_cpu>::exists(registrarKey)) {
-        // One exists with the right inputs/output types
-        kernelFunc = Registrar<FCImplForward_cpu>::create(registrarKey);
-    }
-    else {
-        // Otherwise, fallback to the kernel with all types matching output type
-        kernelFunc = Registrar<FCImplForward_cpu>::create({
-            outputDataType, outputDataType, outputDataType, outputDataType});
-    }
+    const auto impl = Registrar<FCImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Convert input data (no overhead if not needed!)
     // TODO: right now, if needed, memory will be allocated/deallocated at each
@@ -60,7 +43,7 @@ void Aidge::FCImpl_cpu::forward()
 
     // Call kernel
     const auto batchSize = (input0.dims().size() > 1) ? input0.dims()[0] : 1;
-    kernelFunc(batchSize,
+    impl.forward(batchSize,
         input1.dims()[1], // nb input features
         input1.dims()[0], // nb output features
         input0.getImpl()->rawPtr(),
@@ -69,6 +52,7 @@ void Aidge::FCImpl_cpu::forward()
         getCPUPtr(mOp.getRawOutput(0)));
 }
 
+template <>
 void Aidge::FCImpl_cpu::backward()
 {
     const FC_Op& op_ = dynamic_cast<const FC_Op&>(mOp);
@@ -77,23 +61,7 @@ void Aidge::FCImpl_cpu::backward()
     AIDGE_ASSERT(op_.getInput(0)->grad(), "missing input #0 gradient");
     AIDGE_ASSERT(op_.getInput(1)->grad(), "missing input #1 gradient");
 
-    // Find the correct kernel type
-    const Registrar<FCImplBackward_cpu>::registrar_key registrarKey = {
-        fc_grad->dataType(),
-        op_.getInput(1)->grad()->dataType(),
-        (op_.getInput(2)) ? op_.getInput(2)->grad()->dataType() : op_.getInput(1)->grad()->dataType(),
-        op_.getInput(0)->grad()->dataType()};
-
-    Registrar<FCImplBackward_cpu>::registrar_type kernelFunc;
-    if (Registrar<FCImplBackward_cpu>::exists(registrarKey)) {
-        // One exists with the right inputs/output types
-        kernelFunc = Registrar<FCImplBackward_cpu>::create(registrarKey);
-    }
-    else {
-        // Otherwise, fallback to the kernel with all types matching output type
-        kernelFunc = Registrar<FCImplBackward_cpu>::create({
-            fc_grad->dataType(), fc_grad->dataType(), fc_grad->dataType(), fc_grad->dataType()});
-    }
+    const auto impl = Registrar<FCImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Convert input data (no overhead if not needed!)
     // TODO: right now, if needed, memory will be allocated/deallocated at each
@@ -106,7 +74,7 @@ void Aidge::FCImpl_cpu::backward()
 
     // Call kernel
     const auto batchSize = (input0grad.dims().size() > 1) ? input0grad.dims()[0] : 1;
-    kernelFunc(batchSize,
+    impl.backward(batchSize,
         input1grad.dims()[1], // nb input features
         input1grad.dims()[0], // nb output features
         getCPUPtr(fc_grad),
diff --git a/src/operator/FoldImpl.cpp b/src/operator/FoldImpl.cpp
index 532ba946ab8a615a4ba0cb162faca28f1ca6c550..10f3d7b50bac9a1fbfc403609bdccb67a79cceac 100644
--- a/src/operator/FoldImpl.cpp
+++ b/src/operator/FoldImpl.cpp
@@ -20,18 +20,18 @@
 #include "aidge/operator/Conv.hpp"
 
 #include "aidge/backend/cpu/operator/FoldImpl.hpp"
-#include "aidge/backend/cpu/operator/FoldImpl_forward_kernels.hpp"
+#include "aidge/backend/cpu/operator/FoldImpl_kernels.hpp"
 
+template <>
 void Aidge::FoldImpl2D_cpu::forward() {
+    const auto& op_ = static_cast<const Fold_Op<2>&>(mOp);
     assert(std::static_pointer_cast<Tensor>(mOp.getRawInput(0)) && "missing input #0");
 
     // Find the correct kernel type
-    auto kernelFunc =
-            Registrar<FoldImpl2DForward_cpu>::create({std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dataType(), std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dataType()});
+    const auto impl = Registrar<FoldImpl2D_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    const auto& op_ = static_cast<const Fold_Op<2>&>(mOp);
-    kernelFunc(op_.outputDims(),
+    impl.forward(op_.outputDims(),
                 op_.strideDims(),
                 op_.dilationDims(),
                 op_.kernelDims(),
@@ -39,3 +39,8 @@ void Aidge::FoldImpl2D_cpu::forward() {
                 getCPUPtr(mOp.getRawInput(0)),
                 getCPUPtr(mOp.getRawOutput(0)));
 }
+
+template <>
+void Aidge::FoldImpl2D_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for Fold_Op<2> on backend cpu");
+}
diff --git a/src/operator/GlobalAveragePoolingImpl.cpp b/src/operator/GlobalAveragePoolingImpl.cpp
index f7280360a4486fe5db6c4dfdd4c492bbe6ba302b..c53f92e199aee30d55ddafe39b5ef121979acbf7 100644
--- a/src/operator/GlobalAveragePoolingImpl.cpp
+++ b/src/operator/GlobalAveragePoolingImpl.cpp
@@ -15,7 +15,7 @@
 #include <memory>
 #include <vector>
 
-#include "aidge/backend/cpu/operator/GlobalAveragePoolingImpl_forward_kernels.hpp"
+#include "aidge/backend/cpu/operator/GlobalAveragePoolingImpl_kernels.hpp"
 #include "aidge/data/Data.hpp"
 #include "aidge/data/Tensor.hpp"
 #include "aidge/operator/GlobalAveragePooling.hpp"
@@ -24,18 +24,23 @@
 #include "aidge/utils/Types.h"
 
 
+template <>
 void Aidge::GlobalAveragePoolingImpl_cpu::forward()
 {
     const GlobalAveragePooling_Op& op_ = static_cast<const GlobalAveragePooling_Op&>(mOp);
     // Check if input is provided
     AIDGE_ASSERT(op_.getInput(0), "missing input 0");
 
-    // Create the forward kernal with the wanted types
-    auto kernelFunc = Registrar<GlobalAveragePoolingImplForward_cpu>::create({op_.getInput(0)->dataType(),
-                                                                              op_.getOutput(0)->dataType()});
+    // Find the correct kernel type
+    const auto impl = Registrar<GlobalAveragePoolingImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(op_.getInput(0)->dims(),
+    impl.forward(op_.getInput(0)->dims(),
                op_.getInput(0)->getImpl()->rawPtr(),
                op_.getOutput(0)->getImpl()->rawPtr());
-}
\ No newline at end of file
+}
+
+template <>
+void Aidge::GlobalAveragePoolingImpl_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for GlobalAveragePooling_Op on backend cpu");
+}
diff --git a/src/operator/GridSampleImpl.cpp b/src/operator/GridSampleImpl.cpp
index 3f465d4dc9915eb2270f650b5a2f29bcd83377b5..5b87390fc3de21d5d406d893e4827e80cce06c35 100644
--- a/src/operator/GridSampleImpl.cpp
+++ b/src/operator/GridSampleImpl.cpp
@@ -15,43 +15,16 @@
 #include <vector>
 
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
-#include "aidge/backend/cpu/operator/GridSampleImpl_forward_kernels.hpp"
+#include "aidge/backend/cpu/operator/GridSampleImpl_kernels.hpp"
 #include "aidge/operator/GridSample.hpp"
 #include "aidge/utils/Types.h"
 
-Aidge::Elts_t Aidge::GridSampleImpl_cpu::getNbRequiredProtected(IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
-
+template <>
 void Aidge::GridSampleImpl_cpu::forward() {
     const auto& op_ = static_cast<const GridSample_Op&>(mOp);
 
     // Find the correct kernel type
-    const auto outputDataType = op_.getOutput(0)->dataType();
-
-    const Registrar<GridSampleImpl1DForward_cpu>::registrar_key registrarKey = {
-        op_.getInput(0)->dataType(),
-        outputDataType};
-
-    std::function<void(const GridSample_Op&,
-                            const std::shared_ptr<Tensor>&,
-                            const std::shared_ptr<Tensor>&,
-                            const std::shared_ptr<Tensor>&)> kernelFunc;
-
-    const std::size_t nbSpatialFeat = op_.getInput(0)->nbDims();
-    switch (nbSpatialFeat)
-    {
-    case 1:
-        kernelFunc = Registrar<GridSampleImpl1DForward_cpu>::create(registrarKey);
-        break;
-    case 2:
-        kernelFunc = Registrar<GridSampleImpl2DForward_cpu>::create(registrarKey);
-        break;
-    default:
-        AIDGE_THROW_OR_ABORT(std::runtime_error, "No CPU {} kernel available for {} dimensions.", op_.type(), nbSpatialFeat);
-        break;
-    }
+    const auto impl = Registrar<GridSampleImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Convert input data (no overhead if not needed!)
     // TODO: right now, if needed, memory will be allocated/deallocated at each
@@ -62,9 +35,14 @@ void Aidge::GridSampleImpl_cpu::forward() {
     const auto& input1 = std::make_shared<Tensor>(op_.getInput(1)->refCastFrom(input1Fallback, *op_.getOutput(0)));
 
     // Call kernel
-    kernelFunc(op_,
+    impl.forward(op_,
             input0, // input
             input1, // grid
             op_.getOutput(0) // output
             );
 }
+
+template <>
+void Aidge::GridSampleImpl_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for GridSample_Op on backend cpu");
+}
diff --git a/src/operator/LeakyReLUImpl.cpp b/src/operator/LeakyReLUImpl.cpp
index 9d4f2a7edcdf263751ec1d9cea10cd4d60055610..6c0802dd967d2a20b34a2f1ca91fc0640c063c83 100644
--- a/src/operator/LeakyReLUImpl.cpp
+++ b/src/operator/LeakyReLUImpl.cpp
@@ -14,20 +14,14 @@
 #include <vector>
 
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
-#include "aidge/backend/cpu/operator/LeakyReLUImpl_forward_kernels.hpp"
-#include "aidge/backend/cpu/operator/LeakyReLUImpl_backward_kernels.hpp"
+#include "aidge/backend/cpu/operator/LeakyReLUImpl_kernels.hpp"
 #include "aidge/data/Tensor.hpp"
 #include "aidge/operator/LeakyReLU.hpp"
 #include "aidge/utils/Log.hpp"
 #include "aidge/utils/Types.h"
 #include "aidge/utils/Registrar.hpp"
 
-
-Aidge::Elts_t Aidge::LeakyReLUImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
-
+template <>
 void Aidge::LeakyReLUImpl_cpu::forward() {
     const LeakyReLU_Op& op_ = dynamic_cast<const LeakyReLU_Op&>(mOp);
 
@@ -36,17 +30,16 @@ void Aidge::LeakyReLUImpl_cpu::forward() {
     AIDGE_ASSERT(in0, "missing input #0");
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<LeakyReLUImplForward_cpu>::create({
-        in0->dataType(),
-        out0->dataType()});
+    const auto impl = Registrar<LeakyReLUImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(op_.negativeSlope(),
+    impl.forward(op_.negativeSlope(),
         in0->size(),
         getCPUPtr(mOp.getRawInput(0)),
         getCPUPtr(mOp.getRawOutput(0)));
 }
 
+template <>
 void Aidge::LeakyReLUImpl_cpu::backward() {
     // reversing in and out Data for backprop
     const LeakyReLU_Op& op_ = dynamic_cast<const LeakyReLU_Op&>(mOp);
@@ -55,12 +48,10 @@ void Aidge::LeakyReLUImpl_cpu::backward() {
     AIDGE_ASSERT(in0, "missing input #0");
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<LeakyReLUImplForward_cpu>::create({
-        in0->dataType(),
-        out0->dataType()});
+    const auto impl = Registrar<LeakyReLUImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(op_.negativeSlope(),
+    impl.backward(op_.negativeSlope(),
         in0->size(),
         getCPUPtr(in0),
         getCPUPtr(out0));
diff --git a/src/operator/LnImpl.cpp b/src/operator/LnImpl.cpp
index 12885a944be46a977463e900af4047319bb1c8b2..79df733963ea8826439530d3adccde6affc9dfa8 100644
--- a/src/operator/LnImpl.cpp
+++ b/src/operator/LnImpl.cpp
@@ -20,14 +20,9 @@
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 
 #include "aidge/backend/cpu/operator/LnImpl.hpp"
-#include "aidge/backend/cpu/operator/LnImpl_forward_kernels.hpp"
-#include "aidge/backend/cpu/operator/LnImpl_backward_kernels.hpp"
-
-Aidge::Elts_t Aidge::LnImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
+#include "aidge/backend/cpu/operator/LnImpl_kernels.hpp"
 
+template <>
 void Aidge::LnImpl_cpu::forward() {
     const Ln_Op& op_ = static_cast<const Ln_Op&>(mOp);
 	std::shared_ptr<Tensor> in0 = op_.getInput(0);
@@ -35,16 +30,15 @@ void Aidge::LnImpl_cpu::forward() {
     AIDGE_ASSERT(in0, "missing input #0");
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<LnImplForward_cpu>::create({
-        in0->dataType(),
-	    out0->dataType()});
+    const auto impl = Registrar<LnImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(in0->size(),
+    impl.forward(in0->size(),
         getCPUPtr(mOp.getRawInput(0)),
         getCPUPtr(mOp.getRawOutput(0)));
 }
 
+template <>
 void Aidge::LnImpl_cpu::backward() {
     const Ln_Op& op_ = dynamic_cast<const Ln_Op&>(mOp);
 	std::shared_ptr<Tensor> in0  = op_.getInput(0);
@@ -54,12 +48,8 @@ void Aidge::LnImpl_cpu::backward() {
     AIDGE_ASSERT(out0, "missing output #0 for current {} operator", op_.type());
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<LnImplBackward_cpu>::create({
-        in0->dataType(),
-	    gra_int0->dataType(),
-        gra_out0->dataType()        
-    });
+    const auto impl = Registrar<LnImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(gra_int0->size(), getCPUPtr(in0), getCPUPtr(gra_out0), getCPUPtr(gra_int0));
+    impl.backward(gra_int0->size(), getCPUPtr(in0), getCPUPtr(gra_out0), getCPUPtr(gra_int0));
 }
diff --git a/src/operator/MatMulImpl.cpp b/src/operator/MatMulImpl.cpp
index e716726886225f703e7cf482d0bfcfb9ec733948..ccd3265ed230e4f9cdc5ad85785a6473d9f131f0 100644
--- a/src/operator/MatMulImpl.cpp
+++ b/src/operator/MatMulImpl.cpp
@@ -19,17 +19,16 @@
 #include "aidge/utils/Types.h"
 
 #include "aidge/backend/cpu/operator/MatMulImpl.hpp"
-#include "aidge/backend/cpu/operator/MatMulImpl_forward_kernels.hpp"
+#include "aidge/backend/cpu/operator/MatMulImpl_kernels.hpp"
 
+template <>
 void Aidge::MatMulImpl_cpu::forward()
 {
     assert(std::static_pointer_cast<Tensor>(mOp.getRawInput(0)) && "missing input #0");
     assert(std::static_pointer_cast<Tensor>(mOp.getRawInput(1)) && "missing input #1");
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<MatMulImplForward_cpu>::create(
-        {std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dataType(),
-         std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dataType()});
+    const auto impl = Registrar<MatMulImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Compute compatible input dimensions
     std::vector<std::size_t> dims0 = static_cast<const MatMul_Op&>(mOp).getInput(0)->dims();
@@ -91,7 +90,7 @@ void Aidge::MatMulImpl_cpu::forward()
     const std::size_t matrix1Size = k*m;
     const std::size_t matrixOutSize = n*m;
     for (std::size_t stack = 0; stack < nbMatrices;) {
-        kernelFunc(n, k, m,
+        impl.forward(n, k, m,
                     getCPUPtr(mOp.getRawInput(0), offsetIn0*matrix0Size),
                     getCPUPtr(mOp.getRawInput(1), offsetIn1*matrix1Size),
                     getCPUPtr(mOp.getRawOutput(0), offsetOut*matrixOutSize));
@@ -126,3 +125,8 @@ void Aidge::MatMulImpl_cpu::forward()
 //         getCPUPtr(mOp.getRawInput(1)),
 //         getCPUPtr(mOp.getRawOutput(0)));
 // }
+
+template <>
+void Aidge::MatMulImpl_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for MatMul_Op on backend cpu");
+}
diff --git a/src/operator/MaxPoolingImpl.cpp b/src/operator/MaxPoolingImpl.cpp
index 2e6d67abbdd6776a1f75449a0f4562143cbaae87..90075a397be3f082ef95fd4df074c99d926fd385 100644
--- a/src/operator/MaxPoolingImpl.cpp
+++ b/src/operator/MaxPoolingImpl.cpp
@@ -14,32 +14,29 @@
 #include <vector>
 
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
-#include "aidge/backend/cpu/operator/MaxPoolingImpl_forward_kernels.hpp"
+#include "aidge/backend/cpu/operator/MaxPoolingImpl_kernels.hpp"
 #include "aidge/operator/MaxPooling.hpp"
 #include "aidge/utils/Log.hpp"
 #include "aidge/utils/Types.h"
 
-
-Aidge::Elts_t Aidge::MaxPoolingImpl2D_cpu::getNbRequiredProtected(IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
-
+template <>
 void Aidge::MaxPoolingImpl2D_cpu::forward() {
     const auto& op_ = dynamic_cast<const MaxPooling_Op<2>&>(mOp);
     AIDGE_ASSERT(op_.getInput(0), "missing input #0 in MaxPooling Operator.");
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<MaxPoolingImpl2DForward_cpu>::create({
-        op_.getInput(0)->dataType(),
-        op_.getOutput(0)->dataType()
-    });
+    const auto impl = Registrar<MaxPoolingImpl2D_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(op_.strideDims(),
+    impl.forward(op_.strideDims(),
                 op_.kernelDims(),
                 op_.ceilMode(),
                 op_.getInput(0)->template dims<4>(),
                 getCPUPtr(mOp.getRawInput(0)),
                 getCPUPtr(mOp.getRawOutput(0)));
 }
+
+template <>
+void Aidge::MaxPoolingImpl2D_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for MaxPooling_Op<2> on backend cpu");
+}
diff --git a/src/operator/MulImpl.cpp b/src/operator/MulImpl.cpp
index e5fd911cf199edbf98a1ecb343d5904d647d9caa..ea5e3d3ab8ac24934a0cb6f9042858fa094700af 100644
--- a/src/operator/MulImpl.cpp
+++ b/src/operator/MulImpl.cpp
@@ -21,29 +21,20 @@
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 
 #include "aidge/backend/cpu/operator/MulImpl.hpp"
-#include "aidge/backend/cpu/operator/MulImpl_forward_kernels.hpp"
-#include "aidge/backend/cpu/operator/MulImpl_backward_kernels.hpp"
-
-Aidge::Elts_t Aidge::MulImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
+#include "aidge/backend/cpu/operator/MulImpl_kernels.hpp"
 
+template <>
 void Aidge::MulImpl_cpu::forward() {
-    // Find the correct kernel type
-    auto kernelFunc = Registrar<MulImplForward_cpu>::create({
-        std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dataType(),
-        std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->dataType(),
-        std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dataType()});
-
     const std::vector<std::size_t> inputDims0 = getBroadcastedDims(std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dims(),
                                                                    std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dims());
     const std::vector<std::size_t> inputDims1 = getBroadcastedDims(std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dims(),
                                                                    std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->dims());
 
+    // Find the correct kernel type
+    const auto impl = Registrar<MulImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(inputDims0,
+    impl.forward(inputDims0,
         inputDims1,
         std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dims(),
         getCPUPtr(mOp.getRawInput(0)),
@@ -51,8 +42,8 @@ void Aidge::MulImpl_cpu::forward() {
         getCPUPtr(mOp.getRawOutput(0)));
 }
 
+template <>
 void Aidge::MulImpl_cpu::backward() {
-
     const Mul_Op& op_ = dynamic_cast<const Mul_Op&>(mOp);
     
     auto in0 = op_.getInput(0);
@@ -61,13 +52,11 @@ void Aidge::MulImpl_cpu::backward() {
     auto in1grad = op_.getInput(1)->grad();
     auto out0grad = op_.getOutput(0)->grad();
 
-    // Find kernel function
-    auto kernelFunc = Registrar<MulImplBackward_cpu>::create({
-        out0grad->dataType(),
-        in0grad->dataType(),
-        in1grad->dataType()});
+    // Find the correct kernel type
+    const auto impl = Registrar<MulImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
-    kernelFunc(/* input0Length */ in0grad->size(), 
+    // Call kernel
+    impl.backward(/* input0Length */ in0grad->size(), 
                /* input1Length */ in1grad->size(),
                /* grad0Length  */ out0grad->size(),
                /* input0Dims   */ in0->dims(),
@@ -78,4 +67,3 @@ void Aidge::MulImpl_cpu::backward() {
                getCPUPtr(in0grad), 
                getCPUPtr(in1grad));
 }
-
diff --git a/src/operator/PadImpl.cpp b/src/operator/PadImpl.cpp
index b4b52d6be855b6a1f8c0a71a6a9169ee9690f34c..cdae21f8ed2757128f6a36b661b0897a4ba65f89 100644
--- a/src/operator/PadImpl.cpp
+++ b/src/operator/PadImpl.cpp
@@ -16,9 +16,9 @@
 #include "aidge/operator/Conv.hpp"
 
 #include "aidge/backend/cpu/operator/PadImpl.hpp"
-#include "aidge/backend/cpu/operator/PadImpl_forward_kernels.hpp"
+#include "aidge/backend/cpu/operator/PadImpl_kernels.hpp"
 
-Aidge::Elts_t Aidge::PadImpl1D_cpu::getNbRequiredProtected(Aidge::IOIndex_t inputIdx) const {
+Aidge::Elts_t Aidge::Pad_ProdConso_cpu::getNbRequiredProtected(Aidge::IOIndex_t inputIdx) const {
     AIDGE_ASSERT(inputIdx == 0, "input index out of range."
         "{} Operator has only one input", mOp.type());
     (void) inputIdx;
@@ -31,17 +31,16 @@ Aidge::Elts_t Aidge::PadImpl1D_cpu::getNbRequiredProtected(Aidge::IOIndex_t inpu
     return Elts_t::DataElts(outputSize - inputSize);
 }
 
+template <>
 void Aidge::PadImpl1D_cpu::forward() {
     const auto& op_ = dynamic_cast<const Pad_Op<1>&>(mOp);
     AIDGE_ASSERT(op_.getInput(0), "missing input #0 in Pad Operator.");
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<PadImpl1DForward_cpu>::create({
-        op_.getInput(0)->dataType(),
-        op_.getOutput(0)->dataType()});
+    const auto impl = Registrar<PadImpl1D_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-     kernelFunc(op_.beginEndBorders(),
+    impl.forward(op_.beginEndBorders(),
                 op_.borderType(),
                 op_.borderValue(),
                 op_.getInput(0)->template dims<3>(),
@@ -49,32 +48,29 @@ void Aidge::PadImpl1D_cpu::forward() {
                 getCPUPtr(mOp.getRawOutput(0)));
 }
 
-Aidge::Elts_t Aidge::PadImpl2D_cpu::getNbRequiredProtected(Aidge::IOIndex_t inputIdx) const {
-    AIDGE_ASSERT(inputIdx == 0, "input index out of range."
-        "{} Operator has only one input", mOp.type());
-    (void) inputIdx;
-
-    // Padding cannot be in-place!
-    // We must ensure that we do not override data that has not been consummed yet.
-    const auto inputSize = std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->size();
-    const auto outputSize = std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->size();
-    return Elts_t::DataElts(outputSize - inputSize);
+template <>
+void Aidge::PadImpl1D_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for Pad_Op<1> on backend cpu");
 }
 
+template <>
 void Aidge::PadImpl2D_cpu::forward() {
     const auto& op_ = dynamic_cast<const Pad_Op<2>&>(mOp);
     AIDGE_ASSERT(op_.getInput(0), "missing input #0 in Pad Operator.");
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<PadImpl2DForward_cpu>::create({
-        op_.getInput(0)->dataType(),
-        op_.getOutput(0)->dataType()});
+    const auto impl = Registrar<PadImpl2D_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(op_.beginEndBorders(),
+    impl.forward(op_.beginEndBorders(),
                 op_.borderType(),
                 op_.borderValue(),
                 op_.getInput(0)->template dims<4>(),
                 getCPUPtr(mOp.getRawInput(0)),
                 getCPUPtr(mOp.getRawOutput(0)));
 }
+
+template <>
+void Aidge::PadImpl2D_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for Pad_Op<2> on backend cpu");
+}
diff --git a/src/operator/PowImpl.cpp b/src/operator/PowImpl.cpp
index dfdb8fcf358cbacee0725778e88ae1682a1991da..74a7be71e176ba8e1cb8851050e575d6aa7465df 100644
--- a/src/operator/PowImpl.cpp
+++ b/src/operator/PowImpl.cpp
@@ -21,28 +21,20 @@
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 
 #include "aidge/backend/cpu/operator/PowImpl.hpp"
-#include "aidge/backend/cpu/operator/PowImpl_backward_kernels.hpp"
-#include "aidge/backend/cpu/operator/PowImpl_forward_kernels.hpp"
-
-Aidge::Elts_t Aidge::PowImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
+#include "aidge/backend/cpu/operator/PowImpl_kernels.hpp"
 
+template <>
 void Aidge::PowImpl_cpu::forward() {
-    // Find the correct kernel type
-    auto kernelFunc = Registrar<PowImplForward_cpu>::create({
-        std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dataType(),
-        std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->dataType(),
-        std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dataType()});
-
     const std::vector<std::size_t> inputDims0 = getBroadcastedDims(std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dims(),
                                                                    std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dims());
     const std::vector<std::size_t> inputDims1 = getBroadcastedDims(std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dims(),
                                                                    std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->dims());
 
+    // Find the correct kernel type
+    const auto impl = Registrar<PowImpl_cpu>::create(getBestMatch(getRequiredSpec()));
+
     // Call kernel
-    kernelFunc(inputDims0,
+    impl.forward(inputDims0,
         inputDims1,
         std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dims(),
         getCPUPtr(mOp.getRawInput(0)),
@@ -50,26 +42,31 @@ void Aidge::PowImpl_cpu::forward() {
         getCPUPtr(mOp.getRawOutput(0)));
 }
 
+template <>
 void Aidge::PowImpl_cpu::backward() {
-    // Find the correct kernel type
     const Pow_Op& op_ = dynamic_cast<const Pow_Op&>(mOp);
-    auto kernelFunc = Registrar<PowImplBackward_cpu>::create({
-        op_.getOutput(0)->grad()->dataType(),
-        op_.getInput(0)->grad()->dataType(),
-        op_.getInput(1)->grad()->dataType()});
 
-    const std::vector<std::size_t> input0gradDims = getBroadcastedDims(op_.getOutput(0)->grad()->dims(),
-                                                                       op_.getInput(0)->grad()->dims());
-    const std::vector<std::size_t> input1gradDims = getBroadcastedDims(op_.getOutput(0)->grad()->dims(),
-                                                                       op_.getInput(1)->grad()->dims());
+    auto in0 = op_.getInput(0);
+    auto in1 = op_.getInput(1);
+    auto in0grad = op_.getInput(0)->grad();
+    auto in1grad = op_.getInput(1)->grad();
+    auto out0grad = op_.getOutput(0)->grad();
+
+    const std::vector<std::size_t> input0gradDims = getBroadcastedDims(std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->grad()->dims(),
+                                                                       std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->grad()->dims());
+    const std::vector<std::size_t> input1gradDims = getBroadcastedDims(std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->grad()->dims(),
+                                                                       std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->grad()->dims());
+
+    // Find the correct kernel type
+    const auto impl = Registrar<PowImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(input0gradDims,
-               input1gradDims,
-               op_.getOutput(0)->grad()->dims(),
-               getCPUPtr(mOp.getRawInput(0)),
-               getCPUPtr(mOp.getRawInput(1)),
-               getCPUPtr(op_.getOutput(0)->grad()),
-               getCPUPtr(op_.getInput(0)->grad()),
-               getCPUPtr(op_.getInput(1)->grad()));
+    impl.backward(input0gradDims,
+                input1gradDims,
+                out0grad->dims(),
+                getCPUPtr(in0),
+                getCPUPtr(in1),
+                getCPUPtr(out0grad),
+                getCPUPtr(in0grad),
+                getCPUPtr(in1grad));
 }
\ No newline at end of file
diff --git a/src/operator/ReLUImpl.cpp b/src/operator/ReLUImpl.cpp
index 4a0fb9f5d929e2ce731a21b5553e1b9257a32daa..832f91aad347fc081439ec487d06b14b0e2fe8da 100644
--- a/src/operator/ReLUImpl.cpp
+++ b/src/operator/ReLUImpl.cpp
@@ -19,14 +19,9 @@
 #include "aidge/utils/ErrorHandling.hpp"
 
 #include "aidge/backend/cpu/operator/ReLUImpl.hpp"
-#include "aidge/backend/cpu/operator/ReLUImpl_forward_kernels.hpp"
-#include "aidge/backend/cpu/operator/ReLUImpl_backward_kernels.hpp"
-
-Aidge::Elts_t Aidge::ReLUImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
+#include "aidge/backend/cpu/operator/ReLUImpl_kernels.hpp"
 
+template <>
 void Aidge::ReLUImpl_cpu::forward() {
 	const ReLU_Op& op_ = dynamic_cast<const ReLU_Op&>(mOp);
     std::shared_ptr<Tensor> in0 = op_.getInput(0);
@@ -34,16 +29,15 @@ void Aidge::ReLUImpl_cpu::forward() {
     AIDGE_ASSERT(in0, "missing input #0");
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<ReLUImplForward_cpu>::create({
-        in0->dataType(),
-	    out0->dataType()});
+    const auto impl = Registrar<ReLUImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(in0->size(),
+    impl.forward(in0->size(),
         getCPUPtr(mOp.getRawInput(0)),
         getCPUPtr(mOp.getRawOutput(0)));
 }
 
+template <>
 void Aidge::ReLUImpl_cpu::backward() {
     const ReLU_Op& op_ = dynamic_cast<const ReLU_Op&>(mOp);
     std::shared_ptr<Tensor> in0  = op_.getInput(0);
@@ -53,12 +47,8 @@ void Aidge::ReLUImpl_cpu::backward() {
     AIDGE_ASSERT(out0, "missing output #0 for current {} operator", op_.type());
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<ReLUImplBackward_cpu>::create({
-	in0->dataType(),
-        gra_int0->dataType(),
-	gra_out0->dataType()
-    });
+    const auto impl = Registrar<ReLUImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(gra_int0->size(), getCPUPtr(in0), getCPUPtr(gra_out0), getCPUPtr(gra_int0));
+    impl.backward(gra_int0->size(), getCPUPtr(in0), getCPUPtr(gra_out0), getCPUPtr(gra_int0));
 }
diff --git a/src/operator/ReduceMeanImpl.cpp b/src/operator/ReduceMeanImpl.cpp
index b4cd8ffa9b46aaa1c1d7a2eca947ed0254947fef..622672569372ff4e9f135e36255095f4246d5920 100644
--- a/src/operator/ReduceMeanImpl.cpp
+++ b/src/operator/ReduceMeanImpl.cpp
@@ -16,23 +16,29 @@
 
 #include "aidge/utils/Types.h"
 #include "aidge/operator/ReduceMean.hpp"
-#include "aidge/backend/cpu/operator/ReduceMeanImpl_forward_kernels.hpp"
+#include "aidge/backend/cpu/operator/ReduceMeanImpl_kernels.hpp"
 
+template <>
 void Aidge::ReduceMeanImpl_cpu::forward() {
     const ReduceMean_Op& op_ = dynamic_cast<const ReduceMean_Op&>(mOp);
+
     // Find the correct kernel type
-    auto kernelFunc = Registrar<ReduceMeanImplForward_cpu>::create({
-        op_.getInput(0)->dataType(),
-        op_.getOutput(0)->dataType()});
+    const auto impl = Registrar<ReduceMeanImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(op_.axes(),
+    impl.forward(op_.axes(),
                 op_.keepDims(),
                 op_.getInput(0)->dims(),
                 op_.getInput(0)->getImpl()->rawPtr(),
                 op_.getOutput(0)->getImpl()->rawPtr());
 }
 
+template <>
+void Aidge::ReduceMeanImpl_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for ReduceMean_Op on backend cpu");
+}
+
+
 // void Aidge::ReduceMeanImpl1D_cpu::forward() {
 
 //     // Find the correct kernel type
diff --git a/src/operator/ReduceSumImpl.cpp b/src/operator/ReduceSumImpl.cpp
index d9b7eea71c6f6bd078ad6e98f1058ca1dafd1c11..aad0801835a74ecefb046f3dc64729ae1f8bd8bb 100644
--- a/src/operator/ReduceSumImpl.cpp
+++ b/src/operator/ReduceSumImpl.cpp
@@ -16,19 +16,24 @@
 
 #include "aidge/utils/Types.h"
 #include "aidge/operator/ReduceSum.hpp"
-#include "aidge/backend/cpu/operator/ReduceSumImpl_forward_kernels.hpp"
+#include "aidge/backend/cpu/operator/ReduceSumImpl_kernels.hpp"
 
+template <>
 void Aidge::ReduceSumImpl_cpu::forward() {
     const ReduceSum_Op& op_ = dynamic_cast<const ReduceSum_Op&>(mOp);
+
     // Find the correct kernel type
-    auto kernelFunc = Registrar<ReduceSumImplForward_cpu>::create({
-        op_.getInput(0)->dataType(),
-        op_.getOutput(0)->dataType()});
+    const auto impl = Registrar<ReduceSumImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(op_.axes(),
+    impl.forward(op_.axes(),
                 op_.keepDims(),
                 op_.getInput(0)->dims(),
                 op_.getInput(0)->getImpl()->rawPtr(),
                 op_.getOutput(0)->getImpl()->rawPtr());
 }
+
+template <>
+void Aidge::ReduceSumImpl_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for ReduceSum_Op on backend cpu");
+}
diff --git a/src/operator/ScalingImpl.cpp b/src/operator/ScalingImpl.cpp
index db4670836e702f536243aadec36c5ba85b2344c8..1e7a408f267c5eb2d60d188f0ed2ba0394222561 100644
--- a/src/operator/ScalingImpl.cpp
+++ b/src/operator/ScalingImpl.cpp
@@ -17,29 +17,28 @@
 #include "aidge/operator/Scaling.hpp"
 
 #include "aidge/backend/cpu/operator/ScalingImpl.hpp"
-#include "aidge/backend/cpu/operator/ScalingImpl_forward_kernels.hpp"
+#include "aidge/backend/cpu/operator/ScalingImpl_kernels.hpp"
 #include "aidge/utils/Types.h"
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 
-Aidge::Elts_t Aidge::ScalingImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
-
+template <>
 void Aidge::ScalingImpl_cpu::forward() {
     const auto& op_ = dynamic_cast<const Scaling_Op&>(mOp);
     AIDGE_ASSERT(op_.getInput(0), "missing input #0 in Scaling Operator.");
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<ScalingImplForward_cpu>::create({
-        op_.getInput(0)->dataType(),
-        op_.getOutput(0)->dataType()});
+    const auto impl = Registrar<ScalingImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(op_.scalingFactor(),
+    impl.forward(op_.scalingFactor(),
             op_.quantizedNbBits(),
             op_.isOutputUnsigned(),
             op_.getInput(0)->size(),
             getCPUPtr(mOp.getRawInput(0)),
             getCPUPtr(mOp.getRawOutput(0)));
 }
+
+template <>
+void Aidge::ScalingImpl_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for Scaling_Op on backend cpu");
+}
diff --git a/src/operator/SigmoidImpl.cpp b/src/operator/SigmoidImpl.cpp
index ad69935c02e392d7aa1c9601acb827c5baf8970f..cdcbac85df3a38fea9b7100324e0618949262fc9 100644
--- a/src/operator/SigmoidImpl.cpp
+++ b/src/operator/SigmoidImpl.cpp
@@ -20,14 +20,9 @@
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 
 #include "aidge/backend/cpu/operator/SigmoidImpl.hpp"
-#include "aidge/backend/cpu/operator/SigmoidImpl_forward_kernels.hpp"
-#include "aidge/backend/cpu/operator/SigmoidImpl_backward_kernels.hpp"
-
-Aidge::Elts_t Aidge::SigmoidImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
+#include "aidge/backend/cpu/operator/SigmoidImpl_kernels.hpp"
 
+template <>
 void Aidge::SigmoidImpl_cpu::forward() {
 	const Sigmoid_Op& op_ = dynamic_cast<const Sigmoid_Op&>(mOp);
     std::shared_ptr<Tensor> in0 = op_.getInput(0);
@@ -35,16 +30,15 @@ void Aidge::SigmoidImpl_cpu::forward() {
     AIDGE_ASSERT(in0, "missing input #0");
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<SigmoidImplForward_cpu>::create({
-        in0->dataType(),
-	    out0->dataType()});
+    const auto impl = Registrar<SigmoidImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(in0->size(),
+    impl.forward(in0->size(),
         getCPUPtr(mOp.getRawInput(0)),
         getCPUPtr(mOp.getRawOutput(0)));
 }
 
+template <>
 void Aidge::SigmoidImpl_cpu::backward() {
     const Sigmoid_Op& op_ = dynamic_cast<const Sigmoid_Op&>(mOp);
     std::shared_ptr<Tensor> out0  = op_.getOutput(0);
@@ -53,12 +47,8 @@ void Aidge::SigmoidImpl_cpu::backward() {
     AIDGE_ASSERT(out0, "missing output #0 for current {} operator", op_.type());
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<SigmoidImplBackward_cpu>::create({
-        out0->dataType(),
-	gra_int0->dataType(),
-        gra_out0->dataType()        
-    });
+    const auto impl = Registrar<SigmoidImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(gra_int0->size(), getCPUPtr(out0), getCPUPtr(gra_out0), getCPUPtr(gra_int0));
+    impl.backward(gra_int0->size(), getCPUPtr(out0), getCPUPtr(gra_out0), getCPUPtr(gra_int0));
 }
diff --git a/src/operator/SliceImpl.cpp b/src/operator/SliceImpl.cpp
index 8ffe4dcdd97b58758885b013d0c1770bd98a83ba..945c1bc752feb8e6a194b1aff99b26f01a6a0e69 100644
--- a/src/operator/SliceImpl.cpp
+++ b/src/operator/SliceImpl.cpp
@@ -14,27 +14,21 @@
 #include <vector>
 
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
-#include "aidge/backend/cpu/operator/SliceImpl_forward_kernels.hpp"
+#include "aidge/backend/cpu/operator/SliceImpl_kernels.hpp"
 #include "aidge/operator/Slice.hpp"
 #include "aidge/utils/Log.hpp"
 #include "aidge/utils/Types.h"
 
-Aidge::Elts_t Aidge::SliceImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
-
+template <>
 void Aidge::SliceImpl_cpu::forward() {
     const auto& op_ = dynamic_cast<const Slice_Op&>(mOp);
     AIDGE_ASSERT(op_.getInput(0), "missing input #0 in Slice Operator.");
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<SliceImplForward_cpu>::create({
-        op_.getInput(0)->dataType(),
-        op_.getOutput(0)->dataType()});
+    const auto impl = Registrar<SliceImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(op_.starts(),
+    impl.forward(op_.starts(),
             op_.ends(),
             op_.axes(),
             op_.steps(),
@@ -42,3 +36,8 @@ void Aidge::SliceImpl_cpu::forward() {
             getCPUPtr(mOp.getRawInput(0)),
             getCPUPtr(mOp.getRawOutput(0)));
 }
+
+template <>
+void Aidge::SliceImpl_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for Slice_Op on backend cpu");
+}
diff --git a/src/operator/SoftmaxImpl.cpp b/src/operator/SoftmaxImpl.cpp
index 5bc3699e2146e36a63b4a1602ca1cb86e3ff1e2f..8b6933f22f3673476f4a9f1e261fbcdc09857300 100644
--- a/src/operator/SoftmaxImpl.cpp
+++ b/src/operator/SoftmaxImpl.cpp
@@ -20,27 +20,25 @@
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 
 #include "aidge/backend/cpu/operator/SoftmaxImpl.hpp"
-#include "aidge/backend/cpu/operator/SoftmaxImpl_forward_kernels.hpp"
-
-Aidge::Elts_t Aidge::SoftmaxImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
+#include "aidge/backend/cpu/operator/SoftmaxImpl_kernels.hpp"
 
+template <>
 void Aidge::SoftmaxImpl_cpu::forward() {
     const auto& op_ = dynamic_cast<const Softmax_Op&>(mOp);
     AIDGE_ASSERT(!op_.getInput(0)->empty(), "Softmax input empty");
+    std::int32_t axis = (op_.axis() >= 0) ? op_.axis() : op_.getInput(0)->nbDims() + op_.axis();
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<SoftmaxImplForward_cpu>::create({
-        op_.getInput(0)->dataType(),
-        op_.getOutput(0)->dataType()});
-
-    std::int32_t axis = (op_.axis() >= 0) ? op_.axis() : op_.getInput(0)->nbDims() + op_.axis();
+    const auto impl = Registrar<SoftmaxImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(static_cast<std::size_t>(axis), // axisIdx
+    impl.forward(static_cast<std::size_t>(axis), // axisIdx
                std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dims(),
                std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->getImpl()->rawPtr(),
                std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->getImpl()->rawPtr());
 }
+
+template <>
+void Aidge::SoftmaxImpl_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for Softmax_Op on backend cpu");
+}
diff --git a/src/operator/SqrtImpl.cpp b/src/operator/SqrtImpl.cpp
index edb8858fc4ac07fa5725d24688b22d64134afb0e..25bdb42fd5140ef4f64d704fc3a5ccf237f17f81 100644
--- a/src/operator/SqrtImpl.cpp
+++ b/src/operator/SqrtImpl.cpp
@@ -19,30 +19,24 @@
 #include "aidge/utils/Types.h"
 
 #include "aidge/backend/cpu/operator/SqrtImpl.hpp"
-#include "aidge/backend/cpu/operator/SqrtImpl_forward_kernels.hpp"
-#include "aidge/backend/cpu/operator/SqrtImpl_backward_kernels.hpp"
-
-Aidge::Elts_t Aidge::SqrtImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
+#include "aidge/backend/cpu/operator/SqrtImpl_kernels.hpp"
 
+template <>
 void Aidge::SqrtImpl_cpu::forward() {
     std::shared_ptr<Tensor> in0 = std::static_pointer_cast<Tensor>(mOp.getRawInput(0));
     std::shared_ptr<Tensor> out0 = std::static_pointer_cast<Tensor>(mOp.getRawOutput(0));
     AIDGE_ASSERT(in0, "missing input #0");
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<SqrtImplForward_cpu>::create({
-        in0->dataType(),
-        out0->dataType()});
+    const auto impl = Registrar<SqrtImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(in0->size(),
+    impl.forward(in0->size(),
         getCPUPtr(mOp.getRawInput(0)),
         getCPUPtr(mOp.getRawOutput(0)));
 }
 
+template <>
 void Aidge::SqrtImpl_cpu::backward() {
     // reversing in and out Data for backprop
     const Sqrt_Op& op_ = dynamic_cast<const Sqrt_Op&>(mOp);
@@ -51,12 +45,10 @@ void Aidge::SqrtImpl_cpu::backward() {
     AIDGE_ASSERT(out0grad, "missing output #0");
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<SqrtImplForward_cpu>::create({
-        out0grad->dataType(),
-        in0grad->dataType()});
+    const auto impl = Registrar<SqrtImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(out0grad->size(),
+    impl.backward(out0grad->size(),
         getCPUPtr(out0grad),
         getCPUPtr(in0grad));
 }
\ No newline at end of file
diff --git a/src/operator/SubImpl.cpp b/src/operator/SubImpl.cpp
index ffddb59ee3373c4a0a6c2653747744a43fd471d9..d43771b967889183801cb93418c967ce9d9c8453 100644
--- a/src/operator/SubImpl.cpp
+++ b/src/operator/SubImpl.cpp
@@ -21,31 +21,28 @@
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 
 #include "aidge/backend/cpu/operator/SubImpl.hpp"
-#include "aidge/backend/cpu/operator/SubImpl_forward_kernels.hpp"
-
-Aidge::Elts_t Aidge::SubImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
+#include "aidge/backend/cpu/operator/SubImpl_kernels.hpp"
 
+template <>
 void Aidge::SubImpl_cpu::forward() {
-
-    // Find the correct kernel type
-    auto kernelFunc = Registrar<SubImplForward_cpu>::create({
-        std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dataType(),
-        std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->dataType(),
-        std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dataType()});
-
     const std::vector<std::size_t> inputDims0 = getBroadcastedDims(std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dims(),
                                                                    std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dims());
     const std::vector<std::size_t> inputDims1 = getBroadcastedDims(std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dims(),
                                                                    std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->dims());
 
+    // Find the correct kernel type
+    const auto impl = Registrar<SubImpl_cpu>::create(getBestMatch(getRequiredSpec()));
+
     // Call kernel
-    kernelFunc(inputDims0,
+    impl.forward(inputDims0,
         inputDims1,
         std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dims(),
         getCPUPtr(mOp.getRawInput(0)),
         getCPUPtr(mOp.getRawInput(1)),
         getCPUPtr(mOp.getRawOutput(0)));
 }
+
+template <>
+void Aidge::SubImpl_cpu::backward() {
+    AIDGE_THROW_OR_ABORT(std::runtime_error, "Backward not yet implemented for Sub_Op on backend cpu");
+}
diff --git a/src/operator/TanhImpl.cpp b/src/operator/TanhImpl.cpp
index a2469ed9b83679c0edf8d0a761abf9d3d046db6e..ed8dce08b9f710c9e5830b2c72ffef71013edb6e 100644
--- a/src/operator/TanhImpl.cpp
+++ b/src/operator/TanhImpl.cpp
@@ -20,14 +20,9 @@
 #include "aidge/backend/cpu/data/GetCPUPtr.h"
 
 #include "aidge/backend/cpu/operator/TanhImpl.hpp"
-#include "aidge/backend/cpu/operator/TanhImpl_forward_kernels.hpp"
-#include "aidge/backend/cpu/operator/TanhImpl_backward_kernels.hpp"
-
-Aidge::Elts_t Aidge::TanhImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_t /*inputIdx*/) const {
-    // this implementation can be in-place
-    return Elts_t::DataElts(0);
-}
+#include "aidge/backend/cpu/operator/TanhImpl_kernels.hpp"
 
+template <>
 void Aidge::TanhImpl_cpu::forward() {
 	const Tanh_Op& op_ = dynamic_cast<const Tanh_Op&>(mOp);
     std::shared_ptr<Tensor> in0 = op_.getInput(0);
@@ -35,16 +30,15 @@ void Aidge::TanhImpl_cpu::forward() {
     AIDGE_ASSERT(in0, "missing input #0");
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<TanhImplForward_cpu>::create({
-        in0->dataType(),
-	    out0->dataType()});
+    const auto impl = Registrar<TanhImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(in0->size(),
+    impl.forward(in0->size(),
         getCPUPtr(mOp.getRawInput(0)),
         getCPUPtr(mOp.getRawOutput(0)));
 }
 
+template <>
 void Aidge::TanhImpl_cpu::backward() {
     const Tanh_Op& op_ = dynamic_cast<const Tanh_Op&>(mOp);
     std::shared_ptr<Tensor> out0  = op_.getOutput(0);
@@ -53,13 +47,9 @@ void Aidge::TanhImpl_cpu::backward() {
     AIDGE_ASSERT(out0, "missing output #0 for current {} operator", op_.type());
 
     // Find the correct kernel type
-    auto kernelFunc = Registrar<TanhImplBackward_cpu>::create({
-        out0->dataType(),
-	gra_int0->dataType(),
-        gra_out0->dataType()        
-    });
+    const auto impl = Registrar<TanhImpl_cpu>::create(getBestMatch(getRequiredSpec()));
 
     // Call kernel
-    kernelFunc(gra_int0->size(), getCPUPtr(out0), getCPUPtr(gra_out0), getCPUPtr(gra_int0));
+    impl.backward(gra_int0->size(), getCPUPtr(out0), getCPUPtr(gra_out0), getCPUPtr(gra_int0));
 }
 
diff --git a/unit_tests/CMakeLists.txt b/unit_tests/CMakeLists.txt
index 671cdd5ac1262ab61b35a70a234236aff4a3cc15..8178df93beb96a3a7538dae8d9a706380c06ecf8 100644
--- a/unit_tests/CMakeLists.txt
+++ b/unit_tests/CMakeLists.txt
@@ -12,7 +12,7 @@ file(GLOB_RECURSE src_files "*.cpp")
 
 add_executable(tests${module_name} ${src_files})
 
-target_link_libraries(tests${module_name} PUBLIC ${module_name})
+target_link_libraries(tests${module_name} PRIVATE ${module_name})
 
 target_link_libraries(tests${module_name} PRIVATE Catch2::Catch2WithMain)