diff --git a/src/operator/AddImpl.cpp b/src/operator/AddImpl.cpp
index 36eff221644c00551736c24c70db7b1e3af70dee..851aaa5c6bcd1acc3e8bc17b11dd00143c543b5b 100644
--- a/src/operator/AddImpl.cpp
+++ b/src/operator/AddImpl.cpp
@@ -21,10 +21,10 @@
 #include "aidge/backend/cpu/operator/AddImpl_forward_kernels.hpp"
 
 Aidge::NbElts_t Aidge::AddImpl_cpu::getNbRequiredData(const Aidge::IOIndex_t inputIdx) const {
-    assert(mOp.getInput(inputIdx) && "requires valid input");
+    assert(mOp.getRawInput(inputIdx) && "requires valid input");
 
     // Requires the whole tensors
-    const auto& inputDims = std::static_pointer_cast<Tensor>(mOp.getInput(inputIdx))->dims();
+    const auto& inputDims = std::static_pointer_cast<Tensor>(mOp.getRawInput(inputIdx))->dims();
     return std::accumulate(inputDims.begin(), inputDims.end(), NbElts_t(1), std::multiplies<NbElts_t>());
 }
 
@@ -38,7 +38,7 @@ Aidge::NbElts_t  Aidge::AddImpl_cpu::getRequiredMemory(const Aidge::IOIndex_t ou
     assert(outputIdx == 0 && "operator has only one output");
     (void) outputIdx;
 
-    const auto& outputDims = std::static_pointer_cast<Tensor>(mOp.getOutput(0))->dims();
+    const auto& outputDims = std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dims();
     return std::accumulate(outputDims.begin(), outputDims.end(), NbElts_t(1), std::multiplies<NbElts_t>());
 }
 
@@ -61,25 +61,23 @@ void  Aidge::AddImpl_cpu::updateConsummerProducer() {
 }
 
 void  Aidge::AddImpl_cpu::forward() {
-    assert(mOp.getInput(0) && "missing input in Add operator");
-    DataType datatypeFirstInput = mOp.getInput(0)->dataType();
+    assert(mOp.getRawInput(0) && "missing input in Add operator");
+    DataType datatypeFirstInput = std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dataType();
     for (IOIndex_t i = 1; i < mOp.nbInputs(); ++i) {
-        assert(mOp.getInput(i) && "missing input in Add operator");
-        assert(mOp.getInput(i)->dataType() == datatypeFirstInput);
+        assert(mOp.getRawInput(i) && "missing input in Add operator");
+        assert(std::static_pointer_cast<Tensor>(mOp.getRawInput(i))->dataType() == datatypeFirstInput);
     }
 
     auto kernelFunc = Registrar<AddImplForward_cpu>::create({
         datatypeFirstInput,
-        mOp.getOutput(0)->dataType()});
+        std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dataType()});
 
     std::vector<const void*> opInputs;
     for (IOIndex_t i = 0; i < mOp.nbInputs(); ++i) {
-        opInputs.push_back(mOp.getInput(i)->getImpl()->rawPtr());
+        opInputs.push_back(std::static_pointer_cast<Tensor>(mOp.getRawInput(i))->getImpl()->rawPtr());
     }
 
-    kernelFunc(mOp.getInput(0)->size(),
+    kernelFunc(std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->size(),
                opInputs,
-               mOp.getOutput(0)->getImpl()->rawPtr());
-}
-
-void  Aidge::AddImpl_cpu::backward() { printf("Not implemented yet.\n"); }
\ No newline at end of file
+               std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->getImpl()->rawPtr());
+}
\ No newline at end of file
diff --git a/src/operator/AvgPoolingImpl.cpp b/src/operator/AvgPoolingImpl.cpp
index 299e377a79e46abbda4df2750b5cbbdc9a320c8b..ad236f004ec9d806d43b9549adcec1c094573a1d 100644
--- a/src/operator/AvgPoolingImpl.cpp
+++ b/src/operator/AvgPoolingImpl.cpp
@@ -34,7 +34,7 @@ void Aidge::AvgPoolingImpl2D_cpu::forward() {
 
     // Call kernel
     kernelFunc(dynamic_cast<const AvgPooling_Op<2>&>(mOp).getStaticAttributes(),
-               std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dims<4>(),
+               std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->template dims<4>(),
                std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->getImpl()->rawPtr(),
                std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->getImpl()->rawPtr());
 }
diff --git a/src/operator/BatchNormImpl.cpp b/src/operator/BatchNormImpl.cpp
index 69269175449488a0d621b9bf8c6e403cc458527f..4cfd4b1bef0a027dfd28c95235dd864101ced3c6 100644
--- a/src/operator/BatchNormImpl.cpp
+++ b/src/operator/BatchNormImpl.cpp
@@ -40,7 +40,7 @@ void Aidge::BatchNormImpl2D_cpu::forward() {
 
     // Call kernel
     kernelFunc(dynamic_cast<const BatchNorm_Op<2>&>(mOp).getStaticAttributes(),
-               std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dims<4>(),
+               std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->template dims<4>(),
                std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->getImpl()->rawPtr(),
                std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->getImpl()->rawPtr(),
                std::static_pointer_cast<Tensor>(mOp.getRawInput(2))->getImpl()->rawPtr(),
diff --git a/src/operator/ConcatImpl.cpp b/src/operator/ConcatImpl.cpp
index 5ad46f2ab3e0503445a0500207e2773e3321ff5f..d46054480bab433f3493ffac7fbff48f27f2b570 100644
--- a/src/operator/ConcatImpl.cpp
+++ b/src/operator/ConcatImpl.cpp
@@ -21,10 +21,10 @@
 #include "aidge/backend/cpu/operator/ConcatImpl_forward_kernels.hpp"
 
 Aidge::NbElts_t Aidge::ConcatImpl_cpu::getNbRequiredData(const Aidge::IOIndex_t inputIdx) const {
-    assert(mOp.getInput(inputIdx) && "requires valid input");
+    assert(mOp.getRawInput(inputIdx) && "requires valid input");
 
     // Requires the whole tensors
-    const auto& inputDims = std::static_pointer_cast<Tensor>(mOp.getInput(inputIdx))->dims();
+    const auto& inputDims = std::static_pointer_cast<Tensor>(mOp.getRawInput(inputIdx))->dims();
     return std::accumulate(inputDims.begin(), inputDims.end(), NbElts_t(1), std::multiplies<NbElts_t>());
 }
 
@@ -38,7 +38,7 @@ Aidge::NbElts_t  Aidge::ConcatImpl_cpu::getRequiredMemory(const Aidge::IOIndex_t
     assert(outputIdx == 0 && "operator has only one output");
     (void) outputIdx;
 
-    const auto& outputDims = std::static_pointer_cast<Tensor>(mOp.getOutput(0))->dims();
+    const auto& outputDims = std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dims();
     return std::accumulate(outputDims.begin(), outputDims.end(), NbElts_t(1), std::multiplies<NbElts_t>());
 }
 
@@ -61,29 +61,29 @@ void  Aidge::ConcatImpl_cpu::updateConsummerProducer() {
 }
 
 void  Aidge::ConcatImpl_cpu::forward() {
-    assert(mOp.getInput(0) && "missing input in Concat operator");
-    DataType datatypeFirstInput = mOp.getInput(0)->dataType();
+    assert(std::static_pointer_cast<Tensor>(mOp.getRawInput(0)) && "missing input in Concat operator");
+    DataType datatypeFirstInput = std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dataType();
     for (IOIndex_t i = 1; i < mOp.nbInputs(); ++i) {
-        assert(mOp.getInput(i) && "missing input in Concat operator");
-        assert(mOp.getInput(i)->dataType() == datatypeFirstInput);
+        assert(std::static_pointer_cast<Tensor>(mOp.getRawInput(i)) && "missing input in Concat operator");
+        assert(std::static_pointer_cast<Tensor>(mOp.getRawInput(i))->dataType() == datatypeFirstInput);
     }
 
     auto kernelFunc = Registrar<ConcatImplForward_cpu>::create({
         datatypeFirstInput,
-        mOp.getOutput(0)->dataType()});
+        std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dataType()});
 
     std::vector<const void*> opInputs;
     std::vector<DimSize_t> opInputAxis;
     for (IOIndex_t i = 0; i < mOp.nbInputs(); ++i) {
-        opInputs.push_back(mOp.getInput(i)->getImpl()->rawPtr());
-        opInputAxis.push_back(mOp.getInput(i)->dims()[mOp.template getAttr<DimSize_t>("Axis")]);
+        opInputs.push_back(std::static_pointer_cast<Tensor>(mOp.getRawInput(i))->getImpl()->rawPtr());
+        opInputAxis.push_back(std::static_pointer_cast<Tensor>(mOp.getRawInput(i))->dims()[dynamic_cast<const Concat_Op&>(mOp).template getAttr<DimSize_t>("Axis")]);
     }
 
-    kernelFunc(mOp.getStaticAttributes(),
-               mOp.getInput(0)->dims(),
+    kernelFunc(dynamic_cast<const Concat_Op&>(mOp).getStaticAttributes(),
+               std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dims(),
                opInputAxis,
                opInputs,
-               mOp.getOutput(0)->getImpl()->rawPtr());
+               std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->getImpl()->rawPtr());
 }
 
 void  Aidge::ConcatImpl_cpu::backward() { printf("Not implemented yet.\n"); }
\ No newline at end of file
diff --git a/src/operator/ConvDepthWiseImpl.cpp b/src/operator/ConvDepthWiseImpl.cpp
index 0a0b2dfe2e46c5345ba15a34073076c706960fe4..4a722a5e4b00412617eb998f8cbfb36eb0c46035 100644
--- a/src/operator/ConvDepthWiseImpl.cpp
+++ b/src/operator/ConvDepthWiseImpl.cpp
@@ -31,7 +31,7 @@ void Aidge::ConvDepthWiseImpl2D_cpu::forward() {
     assert(mOp.getRawInput(1) && "missing input #1");
     assert(mOp.getRawInput(2) && "missing input #2");
 
-    assert((mOp.getRawInput(0)->nbDims() == 4) && "support for 4-dimensions tensors only");
+    assert((std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->nbDims() == 4) && "support for 4-dimensions tensors only");
 
     // Find the correct kernel type
     auto kernelFunc =
@@ -41,7 +41,7 @@ void Aidge::ConvDepthWiseImpl2D_cpu::forward() {
                                                                std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dataType()});
 
     // Call kernel
-    kernelFunc(dynamic_cast<const ConvDepthWise_Op<2>&>(mOp).getStaticAttributes(), std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dims<4>(),
+    kernelFunc(dynamic_cast<const ConvDepthWise_Op<2>&>(mOp).getStaticAttributes(), std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->template dims<4>(),
                std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->getImpl()->rawPtr(),
                std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->getImpl()->rawPtr(),
                std::static_pointer_cast<Tensor>(mOp.getRawInput(2))->getImpl()->rawPtr(),
diff --git a/src/operator/ConvImpl.cpp b/src/operator/ConvImpl.cpp
index e3a47ed9d2e6d233ea91276019e0c5d2cd3d2d2a..87b54afbfd0b4c2d3bb57812d07575bc0e255626 100644
--- a/src/operator/ConvImpl.cpp
+++ b/src/operator/ConvImpl.cpp
@@ -40,7 +40,7 @@ void Aidge::ConvImpl2D_cpu::forward() {
                                                       std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dataType()});
 
     // Call kernel
-    kernelFunc(dynamic_cast<const Conv_Op<2>&>(mOp).getStaticAttributes(), std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dims<4>(),
+    kernelFunc(dynamic_cast<const Conv_Op<2>&>(mOp).getStaticAttributes(), std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->template dims<4>(),
                std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->getImpl()->rawPtr(), std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->getImpl()->rawPtr(),
                std::static_pointer_cast<Tensor>(mOp.getRawInput(2))->getImpl()->rawPtr(), std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->getImpl()->rawPtr());
 }
diff --git a/src/operator/DivImpl.cpp b/src/operator/DivImpl.cpp
index 346b6d2c863281ad72d48dadbfffb45f620053a9..2e913df1a4c42a2c6132a4096a92c1ab0eeab0c0 100644
--- a/src/operator/DivImpl.cpp
+++ b/src/operator/DivImpl.cpp
@@ -27,15 +27,6 @@ Aidge::NbElts_t Aidge::DivImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_
 }
 
 void Aidge::DivImpl_cpu::forward() {
-    assert(mOp.getRawInput(0) && "missing input #0");
-    assert(mOp.getRawInput(1) && "missing input #1");
-
-    assert(((std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->size() == 1) ||
-            (std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->size() == std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->size()) ||
-            (std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->nbDims() == 1 && std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->size() == std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dims()[std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->nbDims()-1])
-           ) &&
-           "input #1 must either be a tensor of size 1, the number of channels of input # or the same size of input #0");
-
     // Find the correct kernel type
     auto kernelFunc = Registrar<DivImplForward_cpu>::create({
         std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dataType(),
diff --git a/src/operator/FCImpl.cpp b/src/operator/FCImpl.cpp
index 159769a5c489c1b5df98be749fd8bc13eef4fff0..1e5450d330ee89bdceb30aca846800d7764ca911 100644
--- a/src/operator/FCImpl.cpp
+++ b/src/operator/FCImpl.cpp
@@ -38,7 +38,7 @@ void Aidge::FCImpl_cpu::forward()
     // if (std::static_pointer_cast<Tensor>(mOp.getRawInput(0)->nbDims() == 4) {
     //     kernelFunc(
     //         mOp.getStaticAttributes(),
-    //         std::static_pointer_cast<Tensor>(std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dims<4>(),
+    //         std::static_pointer_cast<Tensor>(std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->template dims<4>(),
     //         std::static_pointer_cast<Tensor>(mOp.getRawInput(0)->getImpl()->rawPtr(),
     //         mOp.mInputs[1]->getImpl()->rawPtr(),
     //         mOp.mInputs[2]->getImpl()->rawPtr(),
diff --git a/src/operator/MatMulImpl.cpp b/src/operator/MatMulImpl.cpp
index f47447a805b1b2e5e532afb4377ffa24f441ea0b..0ad9bd6ceb4ac27c35e72e21477864980bcecfd9 100644
--- a/src/operator/MatMulImpl.cpp
+++ b/src/operator/MatMulImpl.cpp
@@ -36,7 +36,7 @@ void Aidge::MatMulImpl_cpu::forward()
     // if (mOp.getInput(0)->nbDims() == 4) {
     //     kernelFunc(
     //         mOp.getStaticAttributes(),
-    //         std::static_pointer_cast<Tensor>(mOp.getInput(0))->dims<4>(),
+    //         std::static_pointer_cast<Tensor>(mOp.getInput(0))->template dims<4>(),
     //         mOp.getInput(0))->getImpl()->rawPtr(),
     //         mOp.mInputs[1]->getImpl()->rawPtr(),
     //         mOp.mInputs[2]->getImpl()->rawPtr(),
diff --git a/src/operator/MaxPoolingImpl.cpp b/src/operator/MaxPoolingImpl.cpp
index 77bc5e311cbdc5f06f3e70ccd547e3e732fa645e..00a279707424e0fce4eb0af07cdade80fe2dffd9 100644
--- a/src/operator/MaxPoolingImpl.cpp
+++ b/src/operator/MaxPoolingImpl.cpp
@@ -34,7 +34,7 @@ void Aidge::MaxPoolingImpl2D_cpu::forward() {
 
     // Call kernel
     kernelFunc(dynamic_cast<const MaxPooling_Op<2>&>(mOp).getStaticAttributes(),
-               std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dims<4>(),
+               std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->template dims<4>(),
                std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->getImpl()->rawPtr(),
                std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->getImpl()->rawPtr());
 }
diff --git a/src/operator/MulImpl.cpp b/src/operator/MulImpl.cpp
index f72e09fdeba22e447271487fb8539f74492855cc..dfd33445a527459245137f4ca6e6d5e8f416d82c 100644
--- a/src/operator/MulImpl.cpp
+++ b/src/operator/MulImpl.cpp
@@ -27,15 +27,6 @@ Aidge::NbElts_t Aidge::MulImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_
 }
 
 void Aidge::MulImpl_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");
-
-    assert(((std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->size() == 1) ||
-            (std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->size() == std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->size()) ||
-            (std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->nbDims() == 1 && std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->size() == std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dims()[std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->nbDims()-1])
-           ) &&
-           "input #1 must either be a tensor of size 1, the number of channels of input # or the same size of input #0");
-
     // Find the correct kernel type
     auto kernelFunc = Registrar<MulImplForward_cpu>::create({
         std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dataType(),
@@ -43,8 +34,8 @@ void Aidge::MulImpl_cpu::forward() {
         std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dataType()});
 
     // Call kernel
-    kernelFunc(std::static_pointer_cast<Tensor>(mOp.getInput(0))->size(),
-        std::static_pointer_cast<Tensor>(mOp.getInput(1))->size(),
+    kernelFunc(std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->size(),
+        std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->size(),
         std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->getImpl()->rawPtr(),
         std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->getImpl()->rawPtr(),
         std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->getImpl()->rawPtr());
diff --git a/src/operator/PadImpl.cpp b/src/operator/PadImpl.cpp
index 353d11acb6e5b3cbd122000e25ec39fac032d378..a5bf1c52ba39a2f805f2b28562cbab19191ac754 100644
--- a/src/operator/PadImpl.cpp
+++ b/src/operator/PadImpl.cpp
@@ -41,7 +41,7 @@ void Aidge::PadImpl2D_cpu::forward() {
 
     // Call kernel
     kernelFunc(dynamic_cast<const Pad_Op<2>&>(mOp).getStaticAttributes(),
-                        std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dims<4>(),
+                        std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->template dims<4>(),
                         std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->getImpl()->rawPtr(),
                         std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->getImpl()->rawPtr());
 }
diff --git a/src/operator/PowImpl.cpp b/src/operator/PowImpl.cpp
index 57856543dfbd3d63bba40ae30da0b1cf3990eb19..30fafa9b3d060eada9468fc9a2bf94ec6aeab3c0 100644
--- a/src/operator/PowImpl.cpp
+++ b/src/operator/PowImpl.cpp
@@ -27,15 +27,6 @@ Aidge::NbElts_t Aidge::PowImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_
 }
 
 void Aidge::PowImpl_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");
-
-    assert(((std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->size() == 1) ||
-            (std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->size() == std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->size()) ||
-            (std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->nbDims() == 1 && std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->size() == std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dims()[std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->nbDims()-1])
-           ) &&
-           "input #1 must either be a tensor of size 1, the number of channels of input # or the same size of input #0");
-
     // Find the correct kernel type
     auto kernelFunc = Registrar<PowImplForward_cpu>::create({
         std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dataType(),
@@ -47,5 +38,5 @@ void Aidge::PowImpl_cpu::forward() {
         std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->size(),
         std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->getImpl()->rawPtr(),
         std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->getImpl()->rawPtr(),
-        std::static_pointer_cast<Tensor>(mOp.getOutput(0))->getImpl()->rawPtr());
+        std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->getImpl()->rawPtr());
 }
diff --git a/src/operator/ProducerImpl.cpp b/src/operator/ProducerImpl.cpp
index 404d95ef685fea3c5796e396a2c5e17c60ce53bc..827f969e4d636862919e07b2f9457bed3e19e0f3 100644
--- a/src/operator/ProducerImpl.cpp
+++ b/src/operator/ProducerImpl.cpp
@@ -26,7 +26,7 @@ Aidge::DimSize_t Aidge::ProducerImpl_cpu::getNbProducedData(
     assert(outputIdx == 0 && "operator has only one output");
     (void) outputIdx;
 
-    return std::static_pointer_cast<Tensor>(mOp.getOutput(0))->size();
+    return std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->size();
 }
 
 void Aidge::ProducerImpl_cpu::forward()
diff --git a/src/operator/ReLUImpl.cpp b/src/operator/ReLUImpl.cpp
index 1c00d00ca024e18f9cfa578abad7cd2d007978cc..2819bb864cb552adfcd1c52e57b734fa0a9d0ce1 100644
--- a/src/operator/ReLUImpl.cpp
+++ b/src/operator/ReLUImpl.cpp
@@ -32,7 +32,7 @@ void Aidge::ReLUImpl_cpu::forward() {
     // Find the correct kernel type
     auto kernelFunc = Registrar<ReLUImplForward_cpu>::create({
         std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dataType(),
-        std::static_pointer_cast<Tensor>(mOp.getOutput(0))->dataType()});
+        std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->dataType()});
 
     // Call kernel
     kernelFunc(std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->size(),
diff --git a/src/operator/ScalingImpl.cpp b/src/operator/ScalingImpl.cpp
index 03411d907fed11771dd3a45ae130e7bf7dd943ea..c2d2b17245811ab20de310e4656bc67b1a28b257 100644
--- a/src/operator/ScalingImpl.cpp
+++ b/src/operator/ScalingImpl.cpp
@@ -35,7 +35,7 @@ void Aidge::ScalingImpl_cpu::forward() {
 
     // Call kernel
     kernelFunc(dynamic_cast<const Scaling_Op&>(mOp).getStaticAttributes(),
-        std::static_pointer_cast<Tensor>(std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->size(),
+        std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->size(),
         std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->getImpl()->rawPtr(),
         std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->getImpl()->rawPtr());
 }
diff --git a/src/operator/SliceImpl.cpp b/src/operator/SliceImpl.cpp
index 4baaef6087ad611f041b6d11fb3a6a0b5b3b63a1..3ae56e1a4f613a4188dc51659853e07674e74768 100644
--- a/src/operator/SliceImpl.cpp
+++ b/src/operator/SliceImpl.cpp
@@ -24,10 +24,10 @@
 
 
 Aidge::NbElts_t Aidge::SliceImpl_cpu<1>::getNbRequiredData(const Aidge::IOIndex_t /*inputIdx*/) const {
-    assert(mOp.getInput(0) && "requires valid input");
+    assert(std::static_pointer_cast<Tensor>(mOp.getRawInput(0)) && "requires valid input");
 
     // Requires the whole tensors
-    return mOp.getInput(0)->dims<1>()[0];
+    return std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->template dims<1>()[0];
 }
 
 Aidge::NbElts_t Aidge::SliceImpl_cpu<1>::getNbRequiredProtected(const Aidge::IOIndex_t /*inputIdx*/) const { return 0; }
@@ -36,7 +36,7 @@ Aidge::NbElts_t Aidge::SliceImpl_cpu<1>::getRequiredMemory(const Aidge::IOIndex_
                                const std::vector<Aidge::DimSize_t>& inputsSize) const {
     (void)outputIdx;
     (void)inputsSize;
-    return mOp.getOutput(0)->dims<1>()[0];
+    return std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->template dims<1>()[0];
 }
 
 Aidge::NbElts_t Aidge::SliceImpl_cpu<1>::getNbConsumedData(const Aidge::IOIndex_t /*inputIdx*/) const {
@@ -56,17 +56,17 @@ void Aidge::SliceImpl_cpu<1>::updateConsummerProducer() {
 
 void Aidge::SliceImpl_cpu<1>::forward() {
     // FIXME: uncomment the following code once memory handling will work
-    assert(mOp.getInput(0) && "missing input #0");
+    assert(std::static_pointer_cast<Tensor>(mOp.getRawInput(0)) && "missing input #0");
 
     // Find the correct kernel type
     auto kernelFunc = Registrar<SliceImplForward_cpu<1>>::create(
-            {mOp.getInput(0)->dataType()});
+            {std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dataType()});
 
     // Call kernel
-    kernelFunc(mOp.getStaticAttributes(),
-                mOp.getInput(0)->template dims<1>(),
-                mOp.getInput(0)->getImpl()->rawPtr(),
-                mOp.getOutput(0)->getImpl()->rawPtr()
+    kernelFunc(dynamic_cast<const Slice_Op<1>&>(mOp).getStaticAttributes(),
+                std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->template dims<1>(),
+                std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->getImpl()->rawPtr(),
+                std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->getImpl()->rawPtr()
             );
 
     // each input is consumed by the minimum amount for a forward pass
@@ -80,10 +80,10 @@ void Aidge::SliceImpl_cpu<1>::backward() { printf("Not implemented yet.\n"); }
 /////////////////////////////////////////////////////////////////////////
 
 Aidge::NbElts_t Aidge::SliceImpl_cpu<2>::getNbRequiredData(const Aidge::IOIndex_t /*inputIdx*/) const {
-    assert(mOp.getInput(0) && "requires valid input");
+    assert(std::static_pointer_cast<Tensor>(mOp.getRawInput(0)) && "requires valid input");
 
     // Requires the whole tensors
-    const auto& inputDims = mOp.getInput(0)->dims<2>();
+    const auto& inputDims = std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->template dims<2>();
     return inputDims[0]*inputDims[1];
 }
 
@@ -93,7 +93,7 @@ Aidge::NbElts_t Aidge::SliceImpl_cpu<2>::getRequiredMemory(const Aidge::IOIndex_
                                const std::vector<Aidge::DimSize_t>& inputsSize) const {
     (void)outputIdx;
     (void)inputsSize;
-    const auto& outputDims = mOp.getOutput(0)->dims<2>();
+    const auto& outputDims = std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->template dims<2>();
     return outputDims[0]*outputDims[1];
 }
 
@@ -114,17 +114,17 @@ void Aidge::SliceImpl_cpu<2>::updateConsummerProducer() {
 
 void Aidge::SliceImpl_cpu<2>::forward() {
     // FIXME: uncomment the following code once memory handling will work
-    assert(mOp.getInput(0) && "missing input #0");
+    assert(std::static_pointer_cast<Tensor>(mOp.getRawInput(0)) && "missing input #0");
 
     // Find the correct kernel type
     auto kernelFunc = Registrar<SliceImplForward_cpu<2>>::create(
-            {mOp.getInput(0)->dataType()});
+            {std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dataType()});
 
     // Call kernel
-    kernelFunc(mOp.getStaticAttributes(),
-                mOp.getInput(0)->template dims<2>(),
-                mOp.getInput(0)->getImpl()->rawPtr(),
-                mOp.getOutput(0)->getImpl()->rawPtr()
+    kernelFunc(dynamic_cast<const Slice_Op<2>&>(mOp).getStaticAttributes(),
+                std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->template dims<2>(),
+                std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->getImpl()->rawPtr(),
+                std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->getImpl()->rawPtr()
             );
 
     // each input is consumed by the minimum amount for a forward pass
@@ -138,10 +138,10 @@ void Aidge::SliceImpl_cpu<2>::backward() { printf("Not implemented yet.\n"); }
 ////////////////////////////////////////////////////////////////////////////
 
 Aidge::NbElts_t Aidge::SliceImpl_cpu<3>::getNbRequiredData(const Aidge::IOIndex_t /*inputIdx*/) const {
-    assert(mOp.getInput(0) && "requires valid input");
+    assert(std::static_pointer_cast<Tensor>(mOp.getRawInput(0)) && "requires valid input");
 
     // Requires the whole tensors
-    const auto& inputDims = mOp.getInput(0)->dims<3>();
+    const auto& inputDims = std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->template dims<3>();
 
     return std::accumulate(inputDims.begin(), inputDims.end(), static_cast<NbElts_t>(1),
                             std::multiplies<NbElts_t>());
@@ -153,7 +153,7 @@ Aidge::NbElts_t Aidge::SliceImpl_cpu<3>::getRequiredMemory(const Aidge::IOIndex_
                                const std::vector<Aidge::DimSize_t>& inputsSize) const {
     (void)outputIdx;
     (void)inputsSize;
-    const auto& outputDims = mOp.getOutput(0)->dims<3>();
+    const auto& outputDims = std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->template dims<3>();
     return std::accumulate(outputDims.begin(), outputDims.end(), static_cast<NbElts_t>(1),
                             std::multiplies<NbElts_t>());
 }
@@ -175,17 +175,17 @@ void Aidge::SliceImpl_cpu<3>::updateConsummerProducer() {
 
 void Aidge::SliceImpl_cpu<3>::forward() {
     // FIXME: uncomment the following code once memory handling will work
-    assert(mOp.getInput(0) && "missing input #0");
+    assert(std::static_pointer_cast<Tensor>(mOp.getRawInput(0)) && "missing input #0");
 
     // Find the correct kernel type
     auto kernelFunc = Registrar<SliceImplForward_cpu<3>>::create(
-            {mOp.getInput(0)->dataType()});
+            {std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dataType()});
 
     // Call kernel
-    kernelFunc(mOp.getStaticAttributes(),
-                mOp.getInput(0)->template dims<3>(),
-                mOp.getInput(0)->getImpl()->rawPtr(),
-                mOp.getOutput(0)->getImpl()->rawPtr()
+    kernelFunc(dynamic_cast<const Slice_Op<3>&>(mOp).getStaticAttributes(),
+                std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->template dims<3>(),
+                std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->getImpl()->rawPtr(),
+                std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->getImpl()->rawPtr()
             );
 
     // each input is consumed by the minimum amount for a forward pass
@@ -199,10 +199,10 @@ void Aidge::SliceImpl_cpu<3>::backward() { printf("Not implemented yet.\n"); }
 //////////////////////////////////////////////////////////////////////////////
 
 Aidge::NbElts_t Aidge::SliceImpl_cpu<4>::getNbRequiredData(const Aidge::IOIndex_t /*inputIdx*/) const {
-    assert(mOp.getInput(0) && "requires valid input");
+    assert(std::static_pointer_cast<Tensor>(mOp.getRawInput(0)) && "requires valid input");
 
     // Requires the whole tensors
-    const auto& inputDims = mOp.getInput(0)->dims<4>();
+    const auto& inputDims = std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->template dims<4>();
 
     return std::accumulate(inputDims.begin(), inputDims.end(), static_cast<NbElts_t>(1),
                             std::multiplies<NbElts_t>());
@@ -214,7 +214,7 @@ Aidge::NbElts_t Aidge::SliceImpl_cpu<4>::getRequiredMemory(const Aidge::IOIndex_
                                const std::vector<Aidge::DimSize_t>& inputsSize) const {
     (void)outputIdx;
     (void)inputsSize;
-    const auto& outputDims = mOp.getOutput(0)->template dims<4>();
+    const auto& outputDims = std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->template dims<4>();
     return std::accumulate(outputDims.begin(), outputDims.end(), static_cast<NbElts_t>(1),
                             std::multiplies<NbElts_t>());
 }
@@ -236,17 +236,17 @@ void Aidge::SliceImpl_cpu<4>::updateConsummerProducer() {
 
 void Aidge::SliceImpl_cpu<4>::forward() {
     // FIXME: uncomment the following code once memory handling will work
-    assert(mOp.getInput(0) && "missing input #0");
+    assert(std::static_pointer_cast<Tensor>(mOp.getRawInput(0)) && "missing input #0");
 
     // Find the correct kernel type
     auto kernelFunc = Registrar<SliceImplForward_cpu<4>>::create(
-            {mOp.getInput(0)->dataType()});
+            {std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dataType()});
 
     // Call kernel
-    kernelFunc(mOp.getStaticAttributes(),
-                mOp.getInput(0)->template dims<4>(),
-                mOp.getInput(0)->getImpl()->rawPtr(),
-                mOp.getOutput(0)->getImpl()->rawPtr()
+    kernelFunc(dynamic_cast<const Slice_Op<4>&>(mOp).getStaticAttributes(),
+                std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->template dims<4>(),
+                std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->getImpl()->rawPtr(),
+                std::static_pointer_cast<Tensor>(mOp.getRawOutput(0))->getImpl()->rawPtr()
             );
 
     // each input is consumed by the minimum amount for a forward pass
diff --git a/src/operator/SubImpl.cpp b/src/operator/SubImpl.cpp
index 6fbe0cdafefa0146fc97d038cd70a90abcd4071a..7d33d935f8aed4eef741ef81e316f387ad676abe 100644
--- a/src/operator/SubImpl.cpp
+++ b/src/operator/SubImpl.cpp
@@ -27,14 +27,6 @@ Aidge::NbElts_t Aidge::SubImpl_cpu::getNbRequiredProtected(const Aidge::IOIndex_
 }
 
 void Aidge::SubImpl_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");
-
-    assert(((std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->size() == 1) ||
-            (std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->size() == std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->size()) ||
-            (std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->nbDims() == 1 && std::static_pointer_cast<Tensor>(mOp.getRawInput(1))->size() == std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->dims()[std::static_pointer_cast<Tensor>(mOp.getRawInput(0))->nbDims()-1])
-           ) &&
-           "input #1 must either be a tensor of size 1, the number of channels of input # or the same size of input #0");
 
     // Find the correct kernel type
     auto kernelFunc = Registrar<SubImplForward_cpu>::create({