From 6b4e0fc3ca0a4cec35764dc96782e55cf18cc210 Mon Sep 17 00:00:00 2001
From: Jerome Hue <jerome.hue@cea.fr>
Date: Mon, 25 Nov 2024 17:29:19 +0100
Subject: [PATCH 01/12] feat: Add CPU backend for heaviside operator

---
 include/aidge/backend/cpu.hpp          |  1 +
 unit_tests/operator/Test_Heaviside.cpp | 86 ++++++++++++++++++++++++++
 2 files changed, 87 insertions(+)
 create mode 100644 unit_tests/operator/Test_Heaviside.cpp

diff --git a/include/aidge/backend/cpu.hpp b/include/aidge/backend/cpu.hpp
index 5db19a2b..17f3b832 100644
--- a/include/aidge/backend/cpu.hpp
+++ b/include/aidge/backend/cpu.hpp
@@ -36,6 +36,7 @@
 #include "aidge/backend/cpu/operator/GlobalAveragePoolingImpl.hpp"
 #include "aidge/backend/cpu/operator/HeavisideImpl.hpp"
 #include "aidge/backend/cpu/operator/LRNImpl.hpp"
+#include "aidge/backend/cpu/operator/HeavisideImpl.hpp"
 #include "aidge/backend/cpu/operator/LeakyReLUImpl.hpp"
 #include "aidge/backend/cpu/operator/LnImpl.hpp"
 #include "aidge/backend/cpu/operator/MatMulImpl.hpp"
diff --git a/unit_tests/operator/Test_Heaviside.cpp b/unit_tests/operator/Test_Heaviside.cpp
new file mode 100644
index 00000000..1a11ff2b
--- /dev/null
+++ b/unit_tests/operator/Test_Heaviside.cpp
@@ -0,0 +1,86 @@
+#include <aidge/operator/Heaviside.hpp>
+#include <aidge/utils/TensorUtils.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <memory>
+#include <cstdlib>
+#include <random>
+
+#include "aidge/data/Tensor.hpp"
+#include "aidge/graph/Node.hpp"
+#include "aidge/operator/AvgPooling.hpp"
+
+namespace Aidge
+{
+
+TEST_CASE("[cpu/operator] Heaviside(forward)", "[Heaviside][CPU]") {
+
+    // Create a random number generator
+    std::random_device rd;
+    std::mt19937 gen(rd());
+    std::uniform_real_distribution<float> valueDist(-1.0f, 1.0f);
+    std::uniform_int_distribution<std::size_t> dimSizeDist(std::size_t(2), std::size_t(10));
+    std::uniform_int_distribution<std::size_t> nbDimsDist(std::size_t(1), std::size_t(5));
+    std::uniform_int_distribution<int> boolDist(0,1);
+
+    SECTION("1D Tensor") {
+
+        std::shared_ptr<Tensor> input0 = std::make_shared<Tensor>(Array1D<float,10> {
+            {0, 1, 2,-3, 4,-5,-6, 7, 8, 9}
+        });
+        std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>(Array1D<float,10> {
+            {0.5, 1, 1, 0, 1, 0, 0, 1, 1, 1}
+        });
+
+        std::shared_ptr<Aidge::Node> hs = Aidge::Heaviside(0.5);
+        auto op = std::static_pointer_cast<Aidge::OperatorTensor>(hs -> getOperator());
+        op->associateInput(0, input0);
+        op->setBackend("cpu");
+        op->setDataType(DataType::Float32);
+
+        op->forward();
+        REQUIRE(approxEq<float>(*op->getOutput(0),*expectedOutput));
+    }
+
+    SECTION("Random Tensor")
+    {
+        auto dims = std::vector<std::size_t>();
+        auto nbDims = nbDimsDist(gen);
+
+        for (std::size_t i = 0; i < nbDims; ++i) {
+            dims.push_back(dimSizeDist(gen));
+        }
+
+        auto numberOfElements = std::accumulate(dims.cbegin(), dims.cend(), std::size_t(1), std::multiplies<std::size_t>());
+        float* array0 = new float[numberOfElements];
+        float* array1 = new float[numberOfElements];
+
+        for(auto i = 0u; i < numberOfElements; ++i)
+        {
+            array0[i] = valueDist(gen);
+            array1[i] = array0[i] > 0 ? 1 : (array0[i] == 0 ? 0.5 : 0);
+        }
+
+        auto T0 = std::make_shared<Tensor>();
+        T0->setDataType(DataType::Float32);
+        T0->setBackend("cpu");
+
+        auto T1 = std::make_shared<Tensor>();
+        T1->setDataType(DataType::Float32);
+        T1->setBackend("cpu");
+
+        T0->resize(dims);
+        T0->getImpl()->setRawPtr(array0, numberOfElements);
+        T1->resize(dims);
+        T1->getImpl()->setRawPtr(array1, numberOfElements);
+
+        std::shared_ptr<Aidge::Node> hs = Aidge::Heaviside(0.5);
+        auto op = std::static_pointer_cast<Aidge::OperatorTensor>(hs -> getOperator());
+        op->associateInput(0, T0);
+        op->setBackend("cpu");
+        op->setDataType(DataType::Float32);
+        op->forward();
+
+        REQUIRE(approxEq<float>(*T0, *T1));
+    }
+}
+}
-- 
GitLab


From 134704eddd0917fb1195a3faac110c0d42058e97 Mon Sep 17 00:00:00 2001
From: Jerome Hue <jerome.hue@cea.fr>
Date: Mon, 25 Nov 2024 17:45:11 +0100
Subject: [PATCH 02/12] chore: format and clean files

---
 unit_tests/operator/Test_Heaviside.cpp | 86 --------------------------
 1 file changed, 86 deletions(-)
 delete mode 100644 unit_tests/operator/Test_Heaviside.cpp

diff --git a/unit_tests/operator/Test_Heaviside.cpp b/unit_tests/operator/Test_Heaviside.cpp
deleted file mode 100644
index 1a11ff2b..00000000
--- a/unit_tests/operator/Test_Heaviside.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-#include <aidge/operator/Heaviside.hpp>
-#include <aidge/utils/TensorUtils.hpp>
-#include <catch2/catch_test_macros.hpp>
-#include <memory>
-#include <cstdlib>
-#include <random>
-
-#include "aidge/data/Tensor.hpp"
-#include "aidge/graph/Node.hpp"
-#include "aidge/operator/AvgPooling.hpp"
-
-namespace Aidge
-{
-
-TEST_CASE("[cpu/operator] Heaviside(forward)", "[Heaviside][CPU]") {
-
-    // Create a random number generator
-    std::random_device rd;
-    std::mt19937 gen(rd());
-    std::uniform_real_distribution<float> valueDist(-1.0f, 1.0f);
-    std::uniform_int_distribution<std::size_t> dimSizeDist(std::size_t(2), std::size_t(10));
-    std::uniform_int_distribution<std::size_t> nbDimsDist(std::size_t(1), std::size_t(5));
-    std::uniform_int_distribution<int> boolDist(0,1);
-
-    SECTION("1D Tensor") {
-
-        std::shared_ptr<Tensor> input0 = std::make_shared<Tensor>(Array1D<float,10> {
-            {0, 1, 2,-3, 4,-5,-6, 7, 8, 9}
-        });
-        std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>(Array1D<float,10> {
-            {0.5, 1, 1, 0, 1, 0, 0, 1, 1, 1}
-        });
-
-        std::shared_ptr<Aidge::Node> hs = Aidge::Heaviside(0.5);
-        auto op = std::static_pointer_cast<Aidge::OperatorTensor>(hs -> getOperator());
-        op->associateInput(0, input0);
-        op->setBackend("cpu");
-        op->setDataType(DataType::Float32);
-
-        op->forward();
-        REQUIRE(approxEq<float>(*op->getOutput(0),*expectedOutput));
-    }
-
-    SECTION("Random Tensor")
-    {
-        auto dims = std::vector<std::size_t>();
-        auto nbDims = nbDimsDist(gen);
-
-        for (std::size_t i = 0; i < nbDims; ++i) {
-            dims.push_back(dimSizeDist(gen));
-        }
-
-        auto numberOfElements = std::accumulate(dims.cbegin(), dims.cend(), std::size_t(1), std::multiplies<std::size_t>());
-        float* array0 = new float[numberOfElements];
-        float* array1 = new float[numberOfElements];
-
-        for(auto i = 0u; i < numberOfElements; ++i)
-        {
-            array0[i] = valueDist(gen);
-            array1[i] = array0[i] > 0 ? 1 : (array0[i] == 0 ? 0.5 : 0);
-        }
-
-        auto T0 = std::make_shared<Tensor>();
-        T0->setDataType(DataType::Float32);
-        T0->setBackend("cpu");
-
-        auto T1 = std::make_shared<Tensor>();
-        T1->setDataType(DataType::Float32);
-        T1->setBackend("cpu");
-
-        T0->resize(dims);
-        T0->getImpl()->setRawPtr(array0, numberOfElements);
-        T1->resize(dims);
-        T1->getImpl()->setRawPtr(array1, numberOfElements);
-
-        std::shared_ptr<Aidge::Node> hs = Aidge::Heaviside(0.5);
-        auto op = std::static_pointer_cast<Aidge::OperatorTensor>(hs -> getOperator());
-        op->associateInput(0, T0);
-        op->setBackend("cpu");
-        op->setDataType(DataType::Float32);
-        op->forward();
-
-        REQUIRE(approxEq<float>(*T0, *T1));
-    }
-}
-}
-- 
GitLab


From 6bdcbbeddba449b4eb7aaebead9a1aceb089baa7 Mon Sep 17 00:00:00 2001
From: Jerome Hue <jerome.hue@cea.fr>
Date: Tue, 3 Dec 2024 17:38:12 +0100
Subject: [PATCH 03/12] save changes - wip tests for leaky

---
 src/operator/SubImpl.cpp                  |    3 +
 unit_tests/operator/Test_MetaOperator.cpp | 1035 ++++++++++++---------
 2 files changed, 614 insertions(+), 424 deletions(-)

diff --git a/src/operator/SubImpl.cpp b/src/operator/SubImpl.cpp
index e36abe2a..5c842c26 100644
--- a/src/operator/SubImpl.cpp
+++ b/src/operator/SubImpl.cpp
@@ -29,6 +29,9 @@ void Aidge::SubImpl_cpu::forward() {
 
     // Find the correct kernel type
     const auto impl = Registrar<SubImpl_cpu>::create(getBestMatch(getRequiredSpec()));
+    Log::info("Sub Operator Kernel");
+    op_.getInput(0)->print();
+    op_.getInput(1)->print();
 
     // Call kernel
     impl.forward(op_.getInput(0)->dims(),
diff --git a/unit_tests/operator/Test_MetaOperator.cpp b/unit_tests/operator/Test_MetaOperator.cpp
index 271a1e2f..9c25795f 100644
--- a/unit_tests/operator/Test_MetaOperator.cpp
+++ b/unit_tests/operator/Test_MetaOperator.cpp
@@ -14,7 +14,6 @@
 #include <cstdlib>
 #include <memory>
 
-#include "aidge/utils/TensorUtils.hpp"
 #include "aidge/backend/cpu/operator/ConvImpl.hpp"
 #include "aidge/backend/cpu/operator/PadImpl.hpp"
 #include "aidge/data/Tensor.hpp"
@@ -23,56 +22,60 @@
 #include "aidge/operator/MetaOperatorDefs.hpp"
 #include "aidge/operator/Pad.hpp"
 #include "aidge/operator/Pop.hpp"
-#include "aidge/scheduler/SequentialScheduler.hpp"
+#include "aidge/operator/Stack.hpp"
 #include "aidge/scheduler/ParallelScheduler.hpp"
+#include "aidge/scheduler/SequentialScheduler.hpp"
+#include "aidge/utils/TensorUtils.hpp"
 
 using namespace Aidge;
 
 TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
   SECTION("PaddedConv(forward)") {
-    std::shared_ptr<Tensor> myWeights = std::make_shared<Tensor>(
-            Array4D<double, 4, 3, 3, 3>{{{{{6.20986394e-01, 1.19775136e-03, 7.22876095e-02},
-                                          {1.16492919e-01, 8.21634093e-02, 1.17413265e-01},
-                                          {2.23743494e-01, 3.99495413e-01, 5.55552411e-01}},
-                                         {{6.64970077e-01, 9.62199940e-01, 4.87531967e-01},
-                                          {6.12586558e-01, 8.09918671e-02, 8.40649383e-01},
-                                          {4.15264406e-01, 8.28247138e-01, 1.52301135e-01}},
-                                         {{1.76992844e-02, 7.78697112e-01, 8.14531592e-01},
-                                          {1.36960611e-01, 4.64806728e-01, 4.85150000e-01},
-                                          {4.34776520e-01, 9.51740977e-01, 9.05793799e-01}}},
-
-                                        {{{1.71925246e-02, 1.91082720e-01, 3.67982644e-01},
-                                          {1.56806559e-01, 6.22280998e-01, 3.15827594e-01},
-                                          {6.04359038e-01, 2.83095947e-01, 6.11168892e-01}},
-                                         {{2.76942832e-01, 1.89768419e-01, 8.07988176e-01},
-                                          {1.67925807e-01, 2.68356150e-01, 6.28875602e-01},
-                                          {1.69093357e-04, 9.64788636e-01, 7.29254981e-01}},
-                                         {{6.34030122e-01, 1.32087038e-01, 3.33857107e-01},
-                                          {7.63047502e-01, 5.12539506e-02, 9.77400493e-01},
-                                          {8.06151288e-01, 2.60237147e-01, 3.93729313e-01}}},
-
-                                        {{{5.84605240e-01, 4.74648725e-01, 8.54111741e-01},
-                                          {7.10897067e-02, 5.02579011e-01, 3.35236224e-01},
-                                          {9.08637408e-01, 8.02903830e-01, 2.83929907e-01}},
-                                         {{3.68206999e-01, 9.18579021e-02, 7.33168098e-01},
-                                          {1.59875539e-01, 9.13163381e-01, 3.59806060e-01},
-                                          {1.41295882e-01, 7.00312185e-01, 5.63728289e-01}},
-                                         {{9.39513546e-01, 1.91704891e-01, 1.11454944e-01},
-                                          {5.46298282e-01, 2.89698587e-01, 2.62612651e-01},
-                                          {1.18554992e-01, 4.32147376e-02, 7.53016994e-01}}},
-
-                                        {{{9.53179175e-01, 2.05041054e-02, 1.11318451e-01},
-                                          {8.67878485e-01, 2.93263422e-01, 8.03912714e-01},
-                                          {8.93620255e-01, 1.37831128e-01, 3.83640583e-01}},
-                                         {{3.96020188e-01, 6.24959320e-01, 1.90709175e-01},
-                                          {5.80538620e-01, 6.63031275e-01, 2.07247191e-01},
-                                          {5.65672171e-01, 5.57014317e-01, 9.26909496e-01}},
-                                         {{3.43901418e-01, 4.47741636e-01, 6.59249367e-01},
-                                          {7.34639028e-01, 2.84957200e-02, 9.70225217e-01},
-                                          {1.33578790e-02, 6.12054702e-01, 9.36685235e-02}}}}});
+    std::shared_ptr<Tensor> myWeights =
+        std::make_shared<Tensor>(Array4D<double, 4, 3, 3, 3>{
+            {{{{6.20986394e-01, 1.19775136e-03, 7.22876095e-02},
+               {1.16492919e-01, 8.21634093e-02, 1.17413265e-01},
+               {2.23743494e-01, 3.99495413e-01, 5.55552411e-01}},
+              {{6.64970077e-01, 9.62199940e-01, 4.87531967e-01},
+               {6.12586558e-01, 8.09918671e-02, 8.40649383e-01},
+               {4.15264406e-01, 8.28247138e-01, 1.52301135e-01}},
+              {{1.76992844e-02, 7.78697112e-01, 8.14531592e-01},
+               {1.36960611e-01, 4.64806728e-01, 4.85150000e-01},
+               {4.34776520e-01, 9.51740977e-01, 9.05793799e-01}}},
+
+             {{{1.71925246e-02, 1.91082720e-01, 3.67982644e-01},
+               {1.56806559e-01, 6.22280998e-01, 3.15827594e-01},
+               {6.04359038e-01, 2.83095947e-01, 6.11168892e-01}},
+              {{2.76942832e-01, 1.89768419e-01, 8.07988176e-01},
+               {1.67925807e-01, 2.68356150e-01, 6.28875602e-01},
+               {1.69093357e-04, 9.64788636e-01, 7.29254981e-01}},
+              {{6.34030122e-01, 1.32087038e-01, 3.33857107e-01},
+               {7.63047502e-01, 5.12539506e-02, 9.77400493e-01},
+               {8.06151288e-01, 2.60237147e-01, 3.93729313e-01}}},
+
+             {{{5.84605240e-01, 4.74648725e-01, 8.54111741e-01},
+               {7.10897067e-02, 5.02579011e-01, 3.35236224e-01},
+               {9.08637408e-01, 8.02903830e-01, 2.83929907e-01}},
+              {{3.68206999e-01, 9.18579021e-02, 7.33168098e-01},
+               {1.59875539e-01, 9.13163381e-01, 3.59806060e-01},
+               {1.41295882e-01, 7.00312185e-01, 5.63728289e-01}},
+              {{9.39513546e-01, 1.91704891e-01, 1.11454944e-01},
+               {5.46298282e-01, 2.89698587e-01, 2.62612651e-01},
+               {1.18554992e-01, 4.32147376e-02, 7.53016994e-01}}},
+
+             {{{9.53179175e-01, 2.05041054e-02, 1.11318451e-01},
+               {8.67878485e-01, 2.93263422e-01, 8.03912714e-01},
+               {8.93620255e-01, 1.37831128e-01, 3.83640583e-01}},
+              {{3.96020188e-01, 6.24959320e-01, 1.90709175e-01},
+               {5.80538620e-01, 6.63031275e-01, 2.07247191e-01},
+               {5.65672171e-01, 5.57014317e-01, 9.26909496e-01}},
+              {{3.43901418e-01, 4.47741636e-01, 6.59249367e-01},
+               {7.34639028e-01, 2.84957200e-02, 9.70225217e-01},
+               {1.33578790e-02, 6.12054702e-01, 9.36685235e-02}}}}});
     std::shared_ptr<Tensor> myBias = std::make_shared<Tensor>(
-            Array1D<double, 4>{{0.16884905, 0.27994487, 0.57227465, 0.06435205}});
-    std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(Array4D<double, 2, 3, 5, 5>{
+        Array1D<double, 4>{{0.16884905, 0.27994487, 0.57227465, 0.06435205}});
+    std::shared_ptr<Tensor> myInput =
+        std::make_shared<Tensor>(Array4D<double, 2, 3, 5, 5>{
             // NCHW
             {{{{0.43224481, 0.9047832, 0.18402257, 0.06162838, 0.52490127},
                {0.27773404, 0.55402353, 0.9485062, 0.31197083, 0.80328607},
@@ -110,61 +113,63 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
                {0.02332061, 0.74270703, 0.59415632, 0.08195934, 0.46295434},
                {0.71426058, 0.85032931, 0.90750818, 0.28768431, 0.4401146}}}}});
 
-    std::shared_ptr<Tensor> myOutput = std::make_shared<Tensor>(
-            Array4D<double, 2, 4, 5, 5>{{{{{3.40294218, 3.74021220, 4.02050114, 4.07054710, 2.46286273},
-                {4.61770582, 6.70517588, 6.50356627, 6.29688787, 3.53332567},
-                {5.47480106, 5.92094421, 6.64605665, 7.95090199, 4.28721523},
-                {4.01485729, 6.06748962, 7.52447891, 7.37980652, 5.28401136},
-                {2.83065438, 3.62033439, 3.56222963, 5.56103945, 3.23335814}},
-
-                {{3.30230498, 4.92814112, 4.34710836, 3.96262765, 2.97987890},
-                {4.49693012, 6.68929291, 5.53603029, 5.68874264, 4.28756475},
-                {4.20528078, 6.82776880, 6.70569849, 7.12809610, 4.40845442},
-                {4.31169367, 6.73352146, 6.30962515, 7.45826864, 4.99164438},
-                {2.18136287, 4.28968000, 4.20080042, 4.89814138, 2.87394023}},
-
-                {{3.54787683, 4.35851812, 4.63881302, 4.23359537, 3.16992092},
-                {5.25099468, 7.54282856, 6.69849157, 5.64309788, 4.56919575},
-                {4.71914101, 7.52830601, 6.71450949, 7.81113863, 5.84658146},
-                {4.97893143, 7.39293909, 6.89905310, 8.14430809, 5.62998581},
-                {2.79735112, 4.80967140, 5.57630205, 5.38828325, 4.57078695}},
-
-                {{3.03048635, 5.04540300, 4.21824932, 4.87323284, 2.35113740},
-                {4.45167351, 6.47721338, 7.40922976, 6.70445728, 3.60700107},
-                {3.77927423, 6.82826376, 7.41777134, 7.57402420, 5.13131523},
-                {4.08747244, 7.07994175, 7.57206821, 8.51897335, 5.26987123},
-                {2.34426999, 4.60127831, 4.86486769, 6.01579571, 3.97803569}}},
-
-
-                {{{3.84700942, 4.25972605, 3.05269003, 3.78043652, 2.08771229},
-                {6.00459957, 6.05633259, 4.45951605, 4.54089880, 4.03066444},
-                {5.41579390, 7.29543972, 6.18680000, 5.58812714, 3.45964241},
-                {6.04531050, 7.70924091, 5.52207708, 5.02131319, 4.09403706},
-                {3.18092418, 4.45422697, 4.04294252, 3.86577177, 2.18776536}},
-
-                {{4.02600670, 4.27603531, 3.81011319, 4.03631020, 2.57254648},
-                {5.33471155, 5.72588634, 5.12079763, 5.11733150, 3.76836705},
-                {5.62947607, 5.92492962, 6.24170446, 6.44130468, 3.44276404},
-                {5.38414621, 6.02679539, 5.88985586, 5.90263271, 3.15044069},
-                {3.31261086, 4.44371319, 3.47660780, 4.15411520, 1.48961508}},
-
-                {{3.95879412, 4.17324543, 3.70114422, 3.27447152, 3.09713888},
-                {5.78258181, 6.57920837, 4.99913597, 6.20961237, 4.98552179},
-                {5.84685421, 7.19971228, 6.66386652, 6.68013430, 4.90963316},
-                {5.24417877, 7.06430531, 6.58512402, 6.02492285, 4.48986387},
-                {3.64294529, 5.00678444, 5.04760027, 4.72895622, 2.67990756}},
-
-                {{3.48610687, 4.12853813, 4.07563591, 3.51327014, 2.44217038},
-                {4.80529881, 7.33211374, 5.14774036, 4.77281189, 4.44612408},
-                {5.11703110, 7.55168772, 7.14374542, 6.43696356, 4.10621357},
-                {5.41270018, 6.85949135, 6.73503923, 5.74601364, 4.46150303},
-                {3.16612267, 4.38248920, 5.23248482, 4.21292210, 2.86031270}}}}});
+    std::shared_ptr<Tensor> myOutput =
+        std::make_shared<Tensor>(Array4D<double, 2, 4, 5, 5>{
+            {{{{3.40294218, 3.74021220, 4.02050114, 4.07054710, 2.46286273},
+               {4.61770582, 6.70517588, 6.50356627, 6.29688787, 3.53332567},
+               {5.47480106, 5.92094421, 6.64605665, 7.95090199, 4.28721523},
+               {4.01485729, 6.06748962, 7.52447891, 7.37980652, 5.28401136},
+               {2.83065438, 3.62033439, 3.56222963, 5.56103945, 3.23335814}},
+
+              {{3.30230498, 4.92814112, 4.34710836, 3.96262765, 2.97987890},
+               {4.49693012, 6.68929291, 5.53603029, 5.68874264, 4.28756475},
+               {4.20528078, 6.82776880, 6.70569849, 7.12809610, 4.40845442},
+               {4.31169367, 6.73352146, 6.30962515, 7.45826864, 4.99164438},
+               {2.18136287, 4.28968000, 4.20080042, 4.89814138, 2.87394023}},
+
+              {{3.54787683, 4.35851812, 4.63881302, 4.23359537, 3.16992092},
+               {5.25099468, 7.54282856, 6.69849157, 5.64309788, 4.56919575},
+               {4.71914101, 7.52830601, 6.71450949, 7.81113863, 5.84658146},
+               {4.97893143, 7.39293909, 6.89905310, 8.14430809, 5.62998581},
+               {2.79735112, 4.80967140, 5.57630205, 5.38828325, 4.57078695}},
+
+              {{3.03048635, 5.04540300, 4.21824932, 4.87323284, 2.35113740},
+               {4.45167351, 6.47721338, 7.40922976, 6.70445728, 3.60700107},
+               {3.77927423, 6.82826376, 7.41777134, 7.57402420, 5.13131523},
+               {4.08747244, 7.07994175, 7.57206821, 8.51897335, 5.26987123},
+               {2.34426999, 4.60127831, 4.86486769, 6.01579571, 3.97803569}}},
+
+             {{{3.84700942, 4.25972605, 3.05269003, 3.78043652, 2.08771229},
+               {6.00459957, 6.05633259, 4.45951605, 4.54089880, 4.03066444},
+               {5.41579390, 7.29543972, 6.18680000, 5.58812714, 3.45964241},
+               {6.04531050, 7.70924091, 5.52207708, 5.02131319, 4.09403706},
+               {3.18092418, 4.45422697, 4.04294252, 3.86577177, 2.18776536}},
+
+              {{4.02600670, 4.27603531, 3.81011319, 4.03631020, 2.57254648},
+               {5.33471155, 5.72588634, 5.12079763, 5.11733150, 3.76836705},
+               {5.62947607, 5.92492962, 6.24170446, 6.44130468, 3.44276404},
+               {5.38414621, 6.02679539, 5.88985586, 5.90263271, 3.15044069},
+               {3.31261086, 4.44371319, 3.47660780, 4.15411520, 1.48961508}},
+
+              {{3.95879412, 4.17324543, 3.70114422, 3.27447152, 3.09713888},
+               {5.78258181, 6.57920837, 4.99913597, 6.20961237, 4.98552179},
+               {5.84685421, 7.19971228, 6.66386652, 6.68013430, 4.90963316},
+               {5.24417877, 7.06430531, 6.58512402, 6.02492285, 4.48986387},
+               {3.64294529, 5.00678444, 5.04760027, 4.72895622, 2.67990756}},
+
+              {{3.48610687, 4.12853813, 4.07563591, 3.51327014, 2.44217038},
+               {4.80529881, 7.33211374, 5.14774036, 4.77281189, 4.44612408},
+               {5.11703110, 7.55168772, 7.14374542, 6.43696356, 4.10621357},
+               {5.41270018, 6.85949135, 6.73503923, 5.74601364, 4.46150303},
+               {3.16612267, 4.38248920, 5.23248482, 4.21292210,
+                2.86031270}}}}});
 
     std::shared_ptr<Node> myConv = Conv<2>(3, 4, {3, 3}, "myconv");
-    auto convOp = std::static_pointer_cast<OperatorTensor>(myConv->getOperator());
+    auto convOp =
+        std::static_pointer_cast<OperatorTensor>(myConv->getOperator());
 
     std::shared_ptr<Node> myPad =
-            Pad<2>({1, 1, 1, 1}, "myPad", PadBorderType::Constant, 0.0);
+        Pad<2>({1, 1, 1, 1}, "myPad", PadBorderType::Constant, 0.0);
     auto padOp = std::static_pointer_cast<OperatorTensor>(myPad->getOperator());
 
     convOp->setInput(1, myWeights);
@@ -180,343 +185,525 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
 
     myPad->forward();
     myConv->forward();
-    convOp -> getOutput(0) -> print();
+    convOp->getOutput(0)->print();
 
-    double* computedOutput = static_cast<double*>(convOp->getOutput(0)->getImpl()->rawPtr());
-    double* expectedOutput = static_cast<double*>(myOutput->getImpl()->rawPtr());
+    double *computedOutput =
+        static_cast<double *>(convOp->getOutput(0)->getImpl()->rawPtr());
+    double *expectedOutput =
+        static_cast<double *>(myOutput->getImpl()->rawPtr());
     for (std::size_t i = 0; i < myOutput->size(); ++i) {
-        REQUIRE(std::abs(computedOutput[i] - expectedOutput[i]) < 1e-5);
+      REQUIRE(std::abs(computedOutput[i] - expectedOutput[i]) < 1e-5);
     }
 
     std::shared_ptr<Node> myPaddedConv =
-            PaddedConv(3, 4, {3, 3}, "myPaddedConv", {1, 1}, {1, 1, 1, 1});
+        PaddedConv(3, 4, {3, 3}, "myPaddedConv", {1, 1}, {1, 1, 1, 1});
+  }
+  SECTION("LSTM(forward)") {
+    auto pop = Pop();
+    auto myLSTM = LSTM(32, 64, 0, true, "ltsm");
+    auto op = std::dynamic_pointer_cast<MetaOperator_Op>(myLSTM->getOperator());
+
+    auto microGraph = op->getMicroGraph();
+    microGraph->save("lstm", false, true);
+
+    REQUIRE(myLSTM->nbInputs() == 3 + 8 + 8);
+    REQUIRE(myLSTM->inputCategory(0) == InputCategory::Data);
+    for (size_t i = 1; i < 9; ++i) {
+      REQUIRE(myLSTM->inputCategory(i) == InputCategory::Param);
+    }
+    for (size_t i = 9; i < 17; ++i) {
+      REQUIRE(myLSTM->inputCategory(i) == InputCategory::OptionalParam);
+    }
+    REQUIRE(myLSTM->nbOutputs() == 2);
+
+    std::shared_ptr<Tensor> myInput =
+        std::make_shared<Tensor>(Array2D<float, 16, 32>{});
+    std::shared_ptr<Tensor> myInit =
+        std::make_shared<Tensor>(Array2D<float, 32, 64>{});
+    std::shared_ptr<Tensor> myInitW =
+        std::make_shared<Tensor>(Array2D<float, 64, 32>{});
+    std::shared_ptr<Tensor> myInitR =
+        std::make_shared<Tensor>(Array2D<float, 64, 64>{});
+
+    pop->addChild(myLSTM, 0, 0);
+    pop->getOperator()->associateInput(0, myInput);
+    op->associateInput(17, myInit);
+    op->associateInput(18, myInit);
+
+    // Weights X
+    myLSTM->input(1).first->getOperator()->setOutput(0, myInitW);
+    myLSTM->input(2).first->getOperator()->setOutput(0, myInitW);
+    myLSTM->input(3).first->getOperator()->setOutput(0, myInitW);
+    myLSTM->input(4).first->getOperator()->setOutput(0, myInitW);
+    // Weights H
+    myLSTM->input(5).first->getOperator()->setOutput(0, myInitR);
+    myLSTM->input(6).first->getOperator()->setOutput(0, myInitR);
+    myLSTM->input(7).first->getOperator()->setOutput(0, myInitR);
+    myLSTM->input(8).first->getOperator()->setOutput(0, myInitR);
+
+    auto g = getConnectedGraphView(myLSTM);
+    g->setDataType(DataType::Float32);
+    g->setBackend("cpu");
+
+    auto scheduler = SequentialScheduler(g);
+    scheduler.forward(true);
+
+    g->save("lstm_outside_dims", true, true);
+
+    microGraph->save("lstm_dims", true, true);
+    REQUIRE(op->dimsForwarded());
+
+    auto microGraphScheduler = std::dynamic_pointer_cast<MetaOperator_Op>(op)
+                                   ->getMicroGraphScheduler();
+    microGraphScheduler->saveSchedulingDiagram("lstm_scheduling");
+
+    REQUIRE(op->getNbConsumedData(0).data == 512);
+    REQUIRE(op->getNbConsumedData(1).data == 32768);
+    REQUIRE(op->getNbProducedData(0).data == 34816);
+    REQUIRE(op->getNbProducedData(1).data == 34816);
+    REQUIRE(microGraphScheduler->getStaticScheduling(0).size() == 26);
+    REQUIRE(microGraphScheduler->getStaticScheduling(1).size() == 24);
+    REQUIRE(microGraphScheduler->getStaticScheduling(15).size() == 24);
   }
-    SECTION("LSTM(forward)") {
-        auto pop = Pop();
-        auto myLSTM = LSTM(32, 64, 0, true, "ltsm");
-        auto op = std::dynamic_pointer_cast<MetaOperator_Op>(myLSTM->getOperator());
-
-        auto microGraph = op->getMicroGraph();
-        microGraph->save("lstm", false, true);
-
-        REQUIRE(myLSTM->nbInputs() == 3 + 8 + 8);
-        REQUIRE(myLSTM->inputCategory(0) == InputCategory::Data);
-        for (size_t i = 1; i < 9; ++i) {
-            REQUIRE(myLSTM->inputCategory(i) == InputCategory::Param);
-        }
-        for (size_t i = 9; i < 17; ++i) {
-            REQUIRE(myLSTM->inputCategory(i) == InputCategory::OptionalParam);
-        }
-        REQUIRE(myLSTM->nbOutputs() == 2);
-
-        std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
-            Array2D<float, 16, 32>{});
-        std::shared_ptr<Tensor> myInit = std::make_shared<Tensor>(
-            Array2D<float, 32, 64>{});
-        std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
-            Array2D<float, 64, 32>{});
-        std::shared_ptr<Tensor> myInitR = std::make_shared<Tensor>(
-            Array2D<float, 64, 64>{});
-
-        pop->addChild(myLSTM, 0, 0);
-        pop->getOperator()->associateInput(0, myInput);
-        op->associateInput(17, myInit);
-        op->associateInput(18, myInit);
-
-        // Weights X
-        myLSTM->input(1).first->getOperator()->setOutput(0, myInitW);
-        myLSTM->input(2).first->getOperator()->setOutput(0, myInitW);
-        myLSTM->input(3).first->getOperator()->setOutput(0, myInitW);
-        myLSTM->input(4).first->getOperator()->setOutput(0, myInitW);
-        // Weights H
-        myLSTM->input(5).first->getOperator()->setOutput(0, myInitR);
-        myLSTM->input(6).first->getOperator()->setOutput(0, myInitR);
-        myLSTM->input(7).first->getOperator()->setOutput(0, myInitR);
-        myLSTM->input(8).first->getOperator()->setOutput(0, myInitR);
-
-        auto g = getConnectedGraphView(myLSTM);
-        g->setDataType(DataType::Float32);
-        g->setBackend("cpu");
-
-        auto scheduler = SequentialScheduler(g);
-        scheduler.forward(true);
-
-        g->save("lstm_outside_dims", true, true);
-
-        microGraph->save("lstm_dims", true, true);
-        REQUIRE(op->dimsForwarded());
-
-        auto microGraphScheduler = std::dynamic_pointer_cast<MetaOperator_Op>(op)->getMicroGraphScheduler();
-        microGraphScheduler->saveSchedulingDiagram("lstm_scheduling");
-
-        REQUIRE(op->getNbConsumedData(0).data == 512);
-        REQUIRE(op->getNbConsumedData(1).data == 32768);
-        REQUIRE(op->getNbProducedData(0).data == 34816);
-        REQUIRE(op->getNbProducedData(1).data == 34816);
-        REQUIRE(microGraphScheduler->getStaticScheduling(0).size() == 26);
-        REQUIRE(microGraphScheduler->getStaticScheduling(1).size() == 24);
-        REQUIRE(microGraphScheduler->getStaticScheduling(15).size() == 24);
+  SECTION("LSTM(forward_values)") {
+    auto myLSTM = LSTM(2, 3, 0, true, "ltsm");
+    auto op = std::static_pointer_cast<OperatorTensor>(myLSTM->getOperator());
+
+    auto microGraph =
+        std::dynamic_pointer_cast<MetaOperator_Op>(op)->getMicroGraph();
+    microGraph->save("lstm", false, false);
+
+    REQUIRE(myLSTM->nbInputs() == 3 + 8 + 8);
+    REQUIRE(myLSTM->inputCategory(0) == InputCategory::Data);
+    for (size_t i = 1; i < 9; ++i) {
+      REQUIRE(myLSTM->inputCategory(i) == InputCategory::Param);
     }
-    SECTION("LSTM(forward_values)") {
-        auto myLSTM = LSTM(2, 3, 0, true, "ltsm");
-        auto op = std::static_pointer_cast<OperatorTensor>(myLSTM->getOperator());
-
-        auto microGraph = std::dynamic_pointer_cast<MetaOperator_Op>(op)->getMicroGraph();
-        microGraph->save("lstm", false, false);
-
-        REQUIRE(myLSTM->nbInputs() == 3 + 8 + 8);
-        REQUIRE(myLSTM->inputCategory(0) == InputCategory::Data);
-        for (size_t i = 1; i < 9; ++i) {
-            REQUIRE(myLSTM->inputCategory(i) == InputCategory::Param);
-        }
-        for (size_t i = 9; i < 17; ++i) {
-            REQUIRE(myLSTM->inputCategory(i) == InputCategory::OptionalParam);
-        }
-        REQUIRE(myLSTM->nbOutputs() == 2);
-
-        std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
-            Array2D<float, 3, 2>{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}}});
-        std::shared_ptr<Tensor> myInit = std::make_shared<Tensor>(
-            Array2D<float, 3, 3>{{{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
-        std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
-            Array2D<float, 3, 2>{{{0.1, 0.1}, {0.1, 0.1}, {0.1, 0.1}}});
-        std::shared_ptr<Tensor> myInitR = std::make_shared<Tensor>(
-            Array2D<float, 3, 3>{{{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
-
-        op->associateInput(0, myInput);
-        op->associateInput(17, myInit);
-        op->associateInput(18, myInit);
-
-        // Weights X
-        myLSTM->input(1).first->getOperator()->setOutput(0, myInitW);
-        myLSTM->input(2).first->getOperator()->setOutput(0, myInitW);
-        myLSTM->input(3).first->getOperator()->setOutput(0, myInitW);
-        myLSTM->input(4).first->getOperator()->setOutput(0, myInitW);
-        // Weights H
-        myLSTM->input(5).first->getOperator()->setOutput(0, myInitR);
-        myLSTM->input(6).first->getOperator()->setOutput(0, myInitR);
-        myLSTM->input(7).first->getOperator()->setOutput(0, myInitR);
-        myLSTM->input(8).first->getOperator()->setOutput(0, myInitR);
-
-        auto g = getConnectedGraphView(myLSTM);
-        g->setDataType(DataType::Float32);
-        g->setBackend("cpu");
-
-        auto scheduler = SequentialScheduler(g);
-        scheduler.forward();
-
-        microGraph->save("lstm_values_dims", false, true);
-
-        std::shared_ptr<Tensor> myHiddenState = std::make_shared<Tensor>(
-                Array2D<float, 3, 3>{{{0.0952412, 0.0952412, 0.0952412},
-                                     {0.25606447, 0.25606447, 0.25606447},
-                                     {0.40323776, 0.40323776, 0.40323776}}});
-
-
-        auto microGraphScheduler = std::dynamic_pointer_cast<MetaOperator_Op>(op)->getMicroGraphScheduler();
-        microGraphScheduler->saveSchedulingDiagram("lstm_values_scheduling");
-
-        op->getOutput(0)->print();
-        myHiddenState->print();
-
-        REQUIRE(approxEq<float>(*(op->getOutput(0)), *myHiddenState));
+    for (size_t i = 9; i < 17; ++i) {
+      REQUIRE(myLSTM->inputCategory(i) == InputCategory::OptionalParam);
     }
-    SECTION("LSTM(forward_values_seq)") {
-        auto pop = Pop();
-        auto myLSTM = LSTM(2, 3, 2, true, "ltsm");
-        auto myGraph = Sequential({pop, myLSTM});
-        auto op = std::static_pointer_cast<OperatorTensor>(myLSTM->getOperator());
-
-        REQUIRE(myLSTM->nbInputs() == 3 + 8 + 8);
-        REQUIRE(myLSTM->inputCategory(0) == InputCategory::Data);
-        for (size_t i = 1; i < 9; ++i) {
-            REQUIRE(myLSTM->inputCategory(i) == InputCategory::Param);
-        }
-        for (size_t i = 9; i < 17; ++i) {
-            REQUIRE(myLSTM->inputCategory(i) == InputCategory::OptionalParam);
-        }
-        REQUIRE(myLSTM->nbOutputs() == 2);
-
-        std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
-            Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}}, {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
-        std::shared_ptr<Tensor> myInit = std::make_shared<Tensor>(
-            Array2D<float, 3, 3>{{{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
-        std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
-            Array2D<float, 3, 2>{{{0.1, 0.1}, {0.1, 0.1}, {0.1, 0.1}}});
-        std::shared_ptr<Tensor> myInitR = std::make_shared<Tensor>(
-            Array2D<float, 3, 3>{{{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
-
-        pop->getOperator()->associateInput(0, myInput);
-        op->associateInput(17, myInit);
-        op->associateInput(18, myInit);
-
-        // Weights X
-        myLSTM->input(1).first->getOperator()->setOutput(0, myInitW);
-        myLSTM->input(2).first->getOperator()->setOutput(0, myInitW);
-        myLSTM->input(3).first->getOperator()->setOutput(0, myInitW);
-        myLSTM->input(4).first->getOperator()->setOutput(0, myInitW);
-        // Weights H
-        myLSTM->input(5).first->getOperator()->setOutput(0, myInitR);
-        myLSTM->input(6).first->getOperator()->setOutput(0, myInitR);
-        myLSTM->input(7).first->getOperator()->setOutput(0, myInitR);
-        myLSTM->input(8).first->getOperator()->setOutput(0, myInitR);
-
-        auto g = getConnectedGraphView(myLSTM);
-        g->compile("cpu", DataType::Float32);
-
-        g->save("lstm_seq", true, true);
-
-        auto scheduler = SequentialScheduler(g);
-        scheduler.forward();
-        scheduler.saveSchedulingDiagram("lstm_seq_schedule");
-
-        std::shared_ptr<Tensor> myHiddenState = std::make_shared<Tensor>(
-                Array2D<float, 3, 3>{{{0.24439372, 0.24439372, 0.24439372},
-                                     {0.49801484, 0.49801484, 0.49801484},
-                                     {0.67162132, 0.67162132, 0.67162132}}});
-
-        myGraph->save("lstm_seq_mygraph", true, true);
-
-        op->getOutput(0)->print();
-        myHiddenState->print();
-
-        REQUIRE(approxEq<float>(*(op->getOutput(0)), *myHiddenState));
+    REQUIRE(myLSTM->nbOutputs() == 2);
+
+    std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
+        Array2D<float, 3, 2>{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}}});
+    std::shared_ptr<Tensor> myInit =
+        std::make_shared<Tensor>(Array2D<float, 3, 3>{
+            {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
+    std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
+        Array2D<float, 3, 2>{{{0.1, 0.1}, {0.1, 0.1}, {0.1, 0.1}}});
+    std::shared_ptr<Tensor> myInitR =
+        std::make_shared<Tensor>(Array2D<float, 3, 3>{
+            {{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
+
+    op->associateInput(0, myInput);
+    op->associateInput(17, myInit);
+    op->associateInput(18, myInit);
+
+    // Weights X
+    myLSTM->input(1).first->getOperator()->setOutput(0, myInitW);
+    myLSTM->input(2).first->getOperator()->setOutput(0, myInitW);
+    myLSTM->input(3).first->getOperator()->setOutput(0, myInitW);
+    myLSTM->input(4).first->getOperator()->setOutput(0, myInitW);
+    // Weights H
+    myLSTM->input(5).first->getOperator()->setOutput(0, myInitR);
+    myLSTM->input(6).first->getOperator()->setOutput(0, myInitR);
+    myLSTM->input(7).first->getOperator()->setOutput(0, myInitR);
+    myLSTM->input(8).first->getOperator()->setOutput(0, myInitR);
+
+    auto g = getConnectedGraphView(myLSTM);
+    g->setDataType(DataType::Float32);
+    g->setBackend("cpu");
+
+    auto scheduler = SequentialScheduler(g);
+    scheduler.forward();
+
+    microGraph->save("lstm_values_dims", false, true);
+
+    std::shared_ptr<Tensor> myHiddenState = std::make_shared<Tensor>(
+        Array2D<float, 3, 3>{{{0.0952412, 0.0952412, 0.0952412},
+                              {0.25606447, 0.25606447, 0.25606447},
+                              {0.40323776, 0.40323776, 0.40323776}}});
+
+    auto microGraphScheduler = std::dynamic_pointer_cast<MetaOperator_Op>(op)
+                                   ->getMicroGraphScheduler();
+    microGraphScheduler->saveSchedulingDiagram("lstm_values_scheduling");
+
+    op->getOutput(0)->print();
+    myHiddenState->print();
+
+    REQUIRE(approxEq<float>(*(op->getOutput(0)), *myHiddenState));
+  }
+  SECTION("LSTM(forward_values_seq)") {
+    auto pop = Pop();
+    auto myLSTM = LSTM(2, 3, 2, true, "ltsm");
+    auto myGraph = Sequential({pop, myLSTM});
+    auto op = std::static_pointer_cast<OperatorTensor>(myLSTM->getOperator());
+
+    REQUIRE(myLSTM->nbInputs() == 3 + 8 + 8);
+    REQUIRE(myLSTM->inputCategory(0) == InputCategory::Data);
+    for (size_t i = 1; i < 9; ++i) {
+      REQUIRE(myLSTM->inputCategory(i) == InputCategory::Param);
+    }
+    for (size_t i = 9; i < 17; ++i) {
+      REQUIRE(myLSTM->inputCategory(i) == InputCategory::OptionalParam);
     }
-    SECTION("LSTM(forward_values_seq_flatten)(sequential)") {
-        auto pop = Pop();
-        auto myLSTM = LSTM(2, 3, 2, true, "ltsm");
-        auto op = std::static_pointer_cast<MetaOperator_Op>(myLSTM->getOperator());
-
-        // Here we test LSTM as it is was flatten in the graph.
-        // We just borrow its micro-graph into our larger myGraph graph.
-        auto myGraph = std::make_shared<GraphView>();
-        pop->addChild(op->getMicroGraph()->getOrderedInputs()[0].first, 0, 0);
-        myGraph->add(op->getMicroGraph());
-        myGraph->add(pop);
-
-        REQUIRE(myLSTM->nbInputs() == 3 + 8 + 8);
-        REQUIRE(myLSTM->inputCategory(0) == InputCategory::Data);
-        for (size_t i = 1; i < 9; ++i) {
-            REQUIRE(myLSTM->inputCategory(i) == InputCategory::Param);
-        }
-        for (size_t i = 9; i < 17; ++i) {
-            REQUIRE(myLSTM->inputCategory(i) == InputCategory::OptionalParam);
-        }
-        REQUIRE(myLSTM->nbOutputs() == 2);
-
-        std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
-            Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}}, {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
-        std::shared_ptr<Tensor> myInit = std::make_shared<Tensor>(
-            Array2D<float, 3, 3>{{{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
-        std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
-            Array2D<float, 3, 2>{{{0.1, 0.1}, {0.1, 0.1}, {0.1, 0.1}}});
-        std::shared_ptr<Tensor> myInitR = std::make_shared<Tensor>(
-            Array2D<float, 3, 3>{{{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
-
-        pop->getOperator()->associateInput(0, myInput);
-        op->associateInput(17, myInit);
-        op->associateInput(18, myInit);
-
-        // Weights X
-        auto prodX = Producer(myInitW);
-        prodX->addChild(op->getMicroGraph()->getOrderedInputs()[1].first, 0, 1);
-        prodX->addChild(op->getMicroGraph()->getOrderedInputs()[2].first, 0, 1);
-        prodX->addChild(op->getMicroGraph()->getOrderedInputs()[3].first, 0, 1);
-        prodX->addChild(op->getMicroGraph()->getOrderedInputs()[4].first, 0, 1);
-        // Weights H
-        auto prodH = Producer(myInitR);
-        prodH->addChild(op->getMicroGraph()->getOrderedInputs()[5].first, 0, 1);
-        prodH->addChild(op->getMicroGraph()->getOrderedInputs()[6].first, 0, 1);
-        prodH->addChild(op->getMicroGraph()->getOrderedInputs()[7].first, 0, 1);
-        prodH->addChild(op->getMicroGraph()->getOrderedInputs()[8].first, 0, 1);
-        myGraph->add({prodX, prodH});
-
-        myGraph->setDataType(DataType::Float32);
-        myGraph->setBackend("cpu");
-        myGraph->save("lstm_seq_flatten", true, true);
-
-        std::shared_ptr<Tensor> myHiddenState = std::make_shared<Tensor>(
-                Array2D<float, 3, 3>{{{0.24439372, 0.24439372, 0.24439372},
-                                     {0.49801484, 0.49801484, 0.49801484},
-                                     {0.67162132, 0.67162132, 0.67162132}}});
-
-        auto scheduler = SequentialScheduler(myGraph);
-        scheduler.generateScheduling();
-        scheduler.saveStaticSchedulingDiagram("lstm_static_schedule");
-        scheduler.forward(true);
-        scheduler.saveSchedulingDiagram("lstm_seq_flatten_schedule_seq");
-
-        op->getOutput(0)->print();
-        myHiddenState->print();
-
-        REQUIRE(approxEq<float>(*(op->getOutput(0)), *myHiddenState));
+    REQUIRE(myLSTM->nbOutputs() == 2);
+
+    std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
+        Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
+                                 {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
+    std::shared_ptr<Tensor> myInit =
+        std::make_shared<Tensor>(Array2D<float, 3, 3>{
+            {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
+    std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
+        Array2D<float, 3, 2>{{{0.1, 0.1}, {0.1, 0.1}, {0.1, 0.1}}});
+    std::shared_ptr<Tensor> myInitR =
+        std::make_shared<Tensor>(Array2D<float, 3, 3>{
+            {{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
+
+    pop->getOperator()->associateInput(0, myInput);
+    op->associateInput(17, myInit);
+    op->associateInput(18, myInit);
+
+    // Weights X
+    myLSTM->input(1).first->getOperator()->setOutput(0, myInitW);
+    myLSTM->input(2).first->getOperator()->setOutput(0, myInitW);
+    myLSTM->input(3).first->getOperator()->setOutput(0, myInitW);
+    myLSTM->input(4).first->getOperator()->setOutput(0, myInitW);
+    // Weights H
+    myLSTM->input(5).first->getOperator()->setOutput(0, myInitR);
+    myLSTM->input(6).first->getOperator()->setOutput(0, myInitR);
+    myLSTM->input(7).first->getOperator()->setOutput(0, myInitR);
+    myLSTM->input(8).first->getOperator()->setOutput(0, myInitR);
+
+    auto g = getConnectedGraphView(myLSTM);
+    g->compile("cpu", DataType::Float32);
+
+    g->save("lstm_seq", true, true);
+
+    auto scheduler = SequentialScheduler(g);
+    scheduler.forward();
+    scheduler.saveSchedulingDiagram("lstm_seq_schedule");
+
+    std::shared_ptr<Tensor> myHiddenState = std::make_shared<Tensor>(
+        Array2D<float, 3, 3>{{{0.24439372, 0.24439372, 0.24439372},
+                              {0.49801484, 0.49801484, 0.49801484},
+                              {0.67162132, 0.67162132, 0.67162132}}});
+
+    myGraph->save("lstm_seq_mygraph", true, true);
+
+    op->getOutput(0)->print();
+    myHiddenState->print();
+
+    REQUIRE(approxEq<float>(*(op->getOutput(0)), *myHiddenState));
+  }
+  SECTION("LSTM(forward_values_seq_flatten)(sequential)") {
+    auto pop = Pop();
+    auto myLSTM = LSTM(2, 3, 2, true, "ltsm");
+    auto op = std::static_pointer_cast<MetaOperator_Op>(myLSTM->getOperator());
+
+    // Here we test LSTM as it is was flatten in the graph.
+    // We just borrow its micro-graph into our larger myGraph graph.
+    auto myGraph = std::make_shared<GraphView>();
+    pop->addChild(op->getMicroGraph()->getOrderedInputs()[0].first, 0, 0);
+    myGraph->add(op->getMicroGraph());
+    myGraph->add(pop);
+
+    REQUIRE(myLSTM->nbInputs() == 3 + 8 + 8);
+    REQUIRE(myLSTM->inputCategory(0) == InputCategory::Data);
+    for (size_t i = 1; i < 9; ++i) {
+      REQUIRE(myLSTM->inputCategory(i) == InputCategory::Param);
     }
-    SECTION("LSTM(forward_values_seq_flatten)(parallel)") {
-        auto pop = Pop();
-        auto myLSTM = LSTM(2, 3, 2, true, "ltsm");
-        auto op = std::static_pointer_cast<MetaOperator_Op>(myLSTM->getOperator());
-
-        // Here we test LSTM as it is was flatten in the graph.
-        // We just borrow its micro-graph into our larger myGraph graph.
-        auto myGraph = std::make_shared<GraphView>();
-        pop->addChild(op->getMicroGraph()->getOrderedInputs()[0].first, 0, 0);
-        myGraph->add(op->getMicroGraph());
-        myGraph->add(pop);
-
-        REQUIRE(myLSTM->nbInputs() == 3 + 8 + 8);
-        REQUIRE(myLSTM->inputCategory(0) == InputCategory::Data);
-        for (size_t i = 1; i < 9; ++i) {
-            REQUIRE(myLSTM->inputCategory(i) == InputCategory::Param);
-        }
-        for (size_t i = 9; i < 17; ++i) {
-            REQUIRE(myLSTM->inputCategory(i) == InputCategory::OptionalParam);
-        }
-        REQUIRE(myLSTM->nbOutputs() == 2);
-
-        std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
-            Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}}, {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
-        std::shared_ptr<Tensor> myInit = std::make_shared<Tensor>(
-            Array2D<float, 3, 3>{{{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
-        std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
-            Array2D<float, 3, 2>{{{0.1, 0.1}, {0.1, 0.1}, {0.1, 0.1}}});
-        std::shared_ptr<Tensor> myInitR = std::make_shared<Tensor>(
-            Array2D<float, 3, 3>{{{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
-
-        pop->getOperator()->associateInput(0, myInput);
-        op->associateInput(17, myInit);
-        op->associateInput(18, myInit);
-
-        // Weights X
-        auto prodX = Producer(myInitW);
-        prodX->addChild(op->getMicroGraph()->getOrderedInputs()[1].first, 0, 1);
-        prodX->addChild(op->getMicroGraph()->getOrderedInputs()[2].first, 0, 1);
-        prodX->addChild(op->getMicroGraph()->getOrderedInputs()[3].first, 0, 1);
-        prodX->addChild(op->getMicroGraph()->getOrderedInputs()[4].first, 0, 1);
-        // Weights H
-        auto prodH = Producer(myInitR);
-        prodH->addChild(op->getMicroGraph()->getOrderedInputs()[5].first, 0, 1);
-        prodH->addChild(op->getMicroGraph()->getOrderedInputs()[6].first, 0, 1);
-        prodH->addChild(op->getMicroGraph()->getOrderedInputs()[7].first, 0, 1);
-        prodH->addChild(op->getMicroGraph()->getOrderedInputs()[8].first, 0, 1);
-        myGraph->add({prodX, prodH});
-
-        myGraph->setDataType(DataType::Float32);
-        myGraph->setBackend("cpu");
-        myGraph->save("lstm_seq_flatten", true, true);
-
-        std::shared_ptr<Tensor> myHiddenState = std::make_shared<Tensor>(
-                Array2D<float, 3, 3>{{{0.24439372, 0.24439372, 0.24439372},
-                                     {0.49801484, 0.49801484, 0.49801484},
-                                     {0.67162132, 0.67162132, 0.67162132}}});
-
-        auto scheduler = ParallelScheduler(myGraph);
-        scheduler.generateScheduling();
-        scheduler.forward(true);
-        scheduler.saveSchedulingDiagram("lstm_seq_flatten_schedule_par");
-
-        op->getOutput(0)->print();
-        myHiddenState->print();
-
-        REQUIRE(approxEq<float>(*(op->getOutput(0)), *myHiddenState));
+    for (size_t i = 9; i < 17; ++i) {
+      REQUIRE(myLSTM->inputCategory(i) == InputCategory::OptionalParam);
     }
-}
\ No newline at end of file
+    REQUIRE(myLSTM->nbOutputs() == 2);
+
+    std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
+        Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
+                                 {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
+    std::shared_ptr<Tensor> myInit =
+        std::make_shared<Tensor>(Array2D<float, 3, 3>{
+            {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
+    std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
+        Array2D<float, 3, 2>{{{0.1, 0.1}, {0.1, 0.1}, {0.1, 0.1}}});
+    std::shared_ptr<Tensor> myInitR =
+        std::make_shared<Tensor>(Array2D<float, 3, 3>{
+            {{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
+
+    pop->getOperator()->associateInput(0, myInput);
+    op->associateInput(17, myInit);
+    op->associateInput(18, myInit);
+
+    // Weights X
+    auto prodX = Producer(myInitW);
+    prodX->addChild(op->getMicroGraph()->getOrderedInputs()[1].first, 0, 1);
+    prodX->addChild(op->getMicroGraph()->getOrderedInputs()[2].first, 0, 1);
+    prodX->addChild(op->getMicroGraph()->getOrderedInputs()[3].first, 0, 1);
+    prodX->addChild(op->getMicroGraph()->getOrderedInputs()[4].first, 0, 1);
+    // Weights H
+    auto prodH = Producer(myInitR);
+    prodH->addChild(op->getMicroGraph()->getOrderedInputs()[5].first, 0, 1);
+    prodH->addChild(op->getMicroGraph()->getOrderedInputs()[6].first, 0, 1);
+    prodH->addChild(op->getMicroGraph()->getOrderedInputs()[7].first, 0, 1);
+    prodH->addChild(op->getMicroGraph()->getOrderedInputs()[8].first, 0, 1);
+    myGraph->add({prodX, prodH});
+
+    myGraph->setDataType(DataType::Float32);
+    myGraph->setBackend("cpu");
+    myGraph->save("lstm_seq_flatten", true, true);
+
+    std::shared_ptr<Tensor> myHiddenState = std::make_shared<Tensor>(
+        Array2D<float, 3, 3>{{{0.24439372, 0.24439372, 0.24439372},
+                              {0.49801484, 0.49801484, 0.49801484},
+                              {0.67162132, 0.67162132, 0.67162132}}});
+
+    auto scheduler = SequentialScheduler(myGraph);
+    scheduler.generateScheduling();
+    scheduler.saveStaticSchedulingDiagram("lstm_static_schedule");
+    scheduler.forward(true);
+    scheduler.saveSchedulingDiagram("lstm_seq_flatten_schedule_seq");
+
+    op->getOutput(0)->print();
+    myHiddenState->print();
+
+    REQUIRE(approxEq<float>(*(op->getOutput(0)), *myHiddenState));
+  }
+  SECTION("LSTM(forward_values_seq_flatten)(parallel)") {
+    auto pop = Pop();
+    auto myLSTM = LSTM(2, 3, 2, true, "ltsm");
+    auto op = std::static_pointer_cast<MetaOperator_Op>(myLSTM->getOperator());
+
+    // Here we test LSTM as it is was flatten in the graph.
+    // We just borrow its micro-graph into our larger myGraph graph.
+    auto myGraph = std::make_shared<GraphView>();
+    pop->addChild(op->getMicroGraph()->getOrderedInputs()[0].first, 0, 0);
+    myGraph->add(op->getMicroGraph());
+    myGraph->add(pop);
+
+    REQUIRE(myLSTM->nbInputs() == 3 + 8 + 8);
+    REQUIRE(myLSTM->inputCategory(0) == InputCategory::Data);
+    for (size_t i = 1; i < 9; ++i) {
+      REQUIRE(myLSTM->inputCategory(i) == InputCategory::Param);
+    }
+    for (size_t i = 9; i < 17; ++i) {
+      REQUIRE(myLSTM->inputCategory(i) == InputCategory::OptionalParam);
+    }
+    REQUIRE(myLSTM->nbOutputs() == 2);
+
+    std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
+        Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
+                                 {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
+    std::shared_ptr<Tensor> myInit =
+        std::make_shared<Tensor>(Array2D<float, 3, 3>{
+            {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
+    std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
+        Array2D<float, 3, 2>{{{0.1, 0.1}, {0.1, 0.1}, {0.1, 0.1}}});
+    std::shared_ptr<Tensor> myInitR =
+        std::make_shared<Tensor>(Array2D<float, 3, 3>{
+            {{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
+
+    pop->getOperator()->associateInput(0, myInput);
+    op->associateInput(17, myInit);
+    op->associateInput(18, myInit);
+
+    // Weights X
+    auto prodX = Producer(myInitW);
+    prodX->addChild(op->getMicroGraph()->getOrderedInputs()[1].first, 0, 1);
+    prodX->addChild(op->getMicroGraph()->getOrderedInputs()[2].first, 0, 1);
+    prodX->addChild(op->getMicroGraph()->getOrderedInputs()[3].first, 0, 1);
+    prodX->addChild(op->getMicroGraph()->getOrderedInputs()[4].first, 0, 1);
+    // Weights H
+    auto prodH = Producer(myInitR);
+    prodH->addChild(op->getMicroGraph()->getOrderedInputs()[5].first, 0, 1);
+    prodH->addChild(op->getMicroGraph()->getOrderedInputs()[6].first, 0, 1);
+    prodH->addChild(op->getMicroGraph()->getOrderedInputs()[7].first, 0, 1);
+    prodH->addChild(op->getMicroGraph()->getOrderedInputs()[8].first, 0, 1);
+    myGraph->add({prodX, prodH});
+
+    myGraph->setDataType(DataType::Float32);
+    myGraph->setBackend("cpu");
+    myGraph->save("lstm_seq_flatten", true, true);
+
+    std::shared_ptr<Tensor> myHiddenState = std::make_shared<Tensor>(
+        Array2D<float, 3, 3>{{{0.24439372, 0.24439372, 0.24439372},
+                              {0.49801484, 0.49801484, 0.49801484},
+                              {0.67162132, 0.67162132, 0.67162132}}});
+
+    auto scheduler = ParallelScheduler(myGraph);
+    scheduler.generateScheduling();
+    scheduler.forward(true);
+    scheduler.saveSchedulingDiagram("lstm_seq_flatten_schedule_par");
+
+    op->getOutput(0)->print();
+    myHiddenState->print();
+
+    REQUIRE(approxEq<float>(*(op->getOutput(0)), *myHiddenState));
+  }
+
+  SECTION("Leaky") {
+
+    // First we need to test the spikes without pop operator.
+
+    // TODO: Compare with real result
+    auto pop = Pop();
+    auto myLeaky = Leaky("leaky");
+    auto op = std::static_pointer_cast<MetaOperator_Op>(myLeaky->getOperator());
+
+    // Here we test LSTM as it is was flatten in the graph.
+    // We just borrow its micro-graph into our larger myGraph graph.
+    auto myGraph = std::make_shared<GraphView>();
+    pop->addChild(op->getMicroGraph()->getOrderedInputs()[0].first, 0, 0);
+    myGraph->add(op->getMicroGraph());
+    myGraph->add(pop);
+
+    // 3 outputs
+    REQUIRE(myLeaky->nbInputs() == 3);
+    REQUIRE(myLeaky->inputCategory(0) == InputCategory::Data);
+    REQUIRE(myLeaky->nbOutputs() == 3);
+
+    std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
+        Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
+                                 {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
+
+    std::shared_ptr<Tensor> myInit =
+        std::make_shared<Tensor>(Array2D<float, 3, 3>{
+            {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
+    std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
+        Array2D<float, 3, 2>{{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}});
+    std::shared_ptr<Tensor> myInitR =
+        std::make_shared<Tensor>(Array2D<float, 3, 3>{
+            {{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
+
+    // Compute expected output
+    // for i in range(num_steps)
+
+    pop->getOperator()->associateInput(0, myInput);
+
+    myLeaky->input(1).first->getOperator()->setOutput(0, myInitW);
+    myLeaky->input(2).first->getOperator()->setOutput(0, myInitW);
+
+    myGraph->setDataType(DataType::Float32);
+    myGraph->setBackend("cpu");
+
+    auto scheduler = SequentialScheduler(myGraph);
+    scheduler.generateScheduling();
+    REQUIRE_NOTHROW(scheduler.forward(true));
+
+    op->getOutput(0)->print();
+  }
+
+  SECTION("Leaky1") {
+    auto myLeaky = Leaky("leaky");
+    auto op = std::static_pointer_cast<MetaOperator_Op>(myLeaky->getOperator());
+
+    // Here we test LSTM as it is was flatten in the graph.
+    // We just borrow its micro-graph into our larger myGraph graph.
+    auto myGraph = std::make_shared<GraphView>();
+    myGraph->add(op->getMicroGraph());
+
+    // 3 outputs
+    REQUIRE(myLeaky->nbInputs() == 3);
+    REQUIRE(myLeaky->inputCategory(0) == InputCategory::Data);
+    REQUIRE(myLeaky->nbOutputs() == 3);
+
+    std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
+        Array2D<float, 3, 2>{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}}});
+
+    std::shared_ptr<Tensor> myInit =
+        std::make_shared<Tensor>(Array2D<float, 3, 3>{
+            {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
+    std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
+        Array2D<float, 3, 2>{{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}});
+    std::shared_ptr<Tensor> myInitR =
+        std::make_shared<Tensor>(Array2D<float, 3, 3>{
+            {{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
+
+    op->associateInput(0, myInput);
+    myLeaky->input(1).first->getOperator()->setOutput(0, myInitW);
+    myLeaky->input(2).first->getOperator()->setOutput(0, myInitW);
+
+    myGraph->setDataType(DataType::Float32);
+    myGraph->setBackend("cpu");
+
+    auto scheduler = SequentialScheduler(myGraph);
+    scheduler.generateScheduling();
+
+    // Results Looking good.
+    REQUIRE_NOTHROW(scheduler.forward(true));
+    op->getOutput(0)->print();
+
+    REQUIRE_NOTHROW(scheduler.forward(true));
+    op->getOutput(0)->print();
+
+    Log::info("Test Done.");
+  }
+
+  SECTION("Leaky2") {
+    auto myLeaky = Leaky("leaky");
+    auto op = std::static_pointer_cast<MetaOperator_Op>(myLeaky->getOperator());
+    //auto stack = Stack(2);
+    auto stack = Pop("popout");
+    auto pop = Pop("popinput");
+
+    // Here we test LSTM as it is was flatten in the graph.
+    // We just borrow its micro-graph into our larger myGraph graph.
+    auto myGraph = std::make_shared<GraphView>();
+
+    pop->addChild(op->getMicroGraph()->getOrderedInputs()[0].first, 0, 0);
+    op->getMicroGraph()->getOrderedOutputs()[0].first->addChild(stack, 0, 0);
+    for(auto node : op->getMicroGraph()->getOrderedOutputs())
+    {
+        Log::info("name  of output {}", node.first->name());
+    }
+
+    myGraph->add(pop);
+    myGraph->add(op->getMicroGraph());
+    myGraph->add(stack);
+    myGraph->save("mg", true, true);
+
+    // 3 outputs
+    REQUIRE(myLeaky->nbInputs() == 3);
+    REQUIRE(myLeaky->inputCategory(0) == InputCategory::Data);
+    REQUIRE(myLeaky->nbOutputs() == 2);
+
+    std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
+        Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
+                                 {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
+
+    std::shared_ptr<Tensor> myInit =
+        std::make_shared<Tensor>(Array2D<float, 3, 3>{
+            {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
+    std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
+        Array2D<float, 3, 2>{{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}});
+    std::shared_ptr<Tensor> myInitR =
+        std::make_shared<Tensor>(Array2D<float, 3, 3>{
+            {{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
+
+    pop->getOperator()->associateInput(0, myInput);
+
+    //myLeaky->input(1).first->getOperator()->setOutput(0, myInitW);
+    //myLeaky->input(2).first->getOperator()->setOutput(0, myInitW);
+    op->associateInput(1, myInitW);
+    op->associateInput(2, myInitW);
+
+    myGraph->setDataType(DataType::Float32);
+    myGraph->setBackend("cpu");
+    myGraph->forwardDims();
+
+    auto scheduler = SequentialScheduler(myGraph);
+    scheduler.generateScheduling();
+
+    scheduler.saveStaticSchedulingDiagram("leaky");
+    scheduler.graphView()->save("schedgraph");
+
+    auto microGraph = op->getMicroGraph();
+    microGraph->save("Micro", false, false);
+
+    REQUIRE_NOTHROW(scheduler.forward(true));
+
+    auto sop = std::static_pointer_cast<OperatorTensor>(stack->getOperator());
+    sop->getOutput(0)->print();
+    op->getOutput(0)->print();
+    Log::info("Test Done.");
+  }
+}
-- 
GitLab


From e16e98684ad575f3faf5fb6709af3f7aee7e4bfa Mon Sep 17 00:00:00 2001
From: Jerome Hue <jerome.hue@cea.fr>
Date: Thu, 5 Dec 2024 16:08:37 +0100
Subject: [PATCH 04/12] [WIP] chore: Improve leaky tests

---
 unit_tests/operator/Test_MetaOperator.cpp | 152 +++++++++++++++++++++-
 1 file changed, 145 insertions(+), 7 deletions(-)

diff --git a/unit_tests/operator/Test_MetaOperator.cpp b/unit_tests/operator/Test_MetaOperator.cpp
index 9c25795f..3efbdc73 100644
--- a/unit_tests/operator/Test_MetaOperator.cpp
+++ b/unit_tests/operator/Test_MetaOperator.cpp
@@ -22,6 +22,7 @@
 #include "aidge/operator/MetaOperatorDefs.hpp"
 #include "aidge/operator/Pad.hpp"
 #include "aidge/operator/Pop.hpp"
+#include "aidge/operator/Identity.hpp"
 #include "aidge/operator/Stack.hpp"
 #include "aidge/scheduler/ParallelScheduler.hpp"
 #include "aidge/scheduler/SequentialScheduler.hpp"
@@ -642,7 +643,8 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
     auto myLeaky = Leaky("leaky");
     auto op = std::static_pointer_cast<MetaOperator_Op>(myLeaky->getOperator());
     //auto stack = Stack(2);
-    auto stack = Pop("popout");
+    auto mem_rec = Stack(3, "mem_rec");
+    auto spk_rec = Stack(3, "spk_rec");
     auto pop = Pop("popinput");
 
     // Here we test LSTM as it is was flatten in the graph.
@@ -650,7 +652,9 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
     auto myGraph = std::make_shared<GraphView>();
 
     pop->addChild(op->getMicroGraph()->getOrderedInputs()[0].first, 0, 0);
-    op->getMicroGraph()->getOrderedOutputs()[0].first->addChild(stack, 0, 0);
+    // 0 for mem 1 for stack
+    op->getMicroGraph()->getOrderedOutputs()[1].first->addChild(mem_rec, 0, 0);
+    op->getMicroGraph()->getOrderedOutputs()[0].first->addChild(spk_rec, 0, 0);
     for(auto node : op->getMicroGraph()->getOrderedOutputs())
     {
         Log::info("name  of output {}", node.first->name());
@@ -658,13 +662,15 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
 
     myGraph->add(pop);
     myGraph->add(op->getMicroGraph());
-    myGraph->add(stack);
+    myGraph->add(mem_rec);
+    myGraph->add(spk_rec);
     myGraph->save("mg", true, true);
 
     // 3 outputs
     REQUIRE(myLeaky->nbInputs() == 3);
     REQUIRE(myLeaky->inputCategory(0) == InputCategory::Data);
-    REQUIRE(myLeaky->nbOutputs() == 2);
+    // Two spikes connected to nothing, + the Add node real output
+    REQUIRE(myLeaky->nbOutputs() == 4);
 
     std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
         Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
@@ -701,9 +707,141 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
 
     REQUIRE_NOTHROW(scheduler.forward(true));
 
-    auto sop = std::static_pointer_cast<OperatorTensor>(stack->getOperator());
-    sop->getOutput(0)->print();
-    op->getOutput(0)->print();
+    auto mem_op = std::static_pointer_cast<OperatorTensor>(mem_rec->getOperator());
+    auto spk_op = std::static_pointer_cast<OperatorTensor>(spk_rec->getOperator());
+    Log::info("Output of stack node and spike : ");
+    mem_op->getOutput(0)->print();
+    Log::info("Output of stack node and spike : ");
+    spk_op->getOutput(0)->print();
     Log::info("Test Done.");
   }
+
+  //SECTION("Accumulate")
+  //{
+  //  auto meta = Accumulate(2, "leaky");
+  //  auto op = std::static_pointer_cast<MetaOperator_Op>(meta->getOperator());
+
+  //  //auto stack = Stack(2);
+  //  //auto stack = Stack(3,"popout");
+  //  auto stack = Identity("popout");
+  //  auto pop = Pop("pop_input");
+
+  //  //auto myGraph = std::make_shared<GraphView>();
+  //  //pop->addChild(op->getMicroGraph()->getOrderedInputs()[0].first, 0, 0);
+  //  //Log::info("added child : {}", op->getMicroGraph()->getOrderedInputs()[0].first->name());
+  //  //op->getMicroGraph()->getOrderedOutputs()[0].first->addChild(stack, 0, 0);
+
+
+
+  //  //myGraph->add(pop);
+  //  //myGraph->add(op->getMicroGraph());
+  //  //myGraph->add(meta);
+  //  //myGraph->add(stack);
+  //  //myGraph->save("mg", true, true);
+
+  //  // 3 outputs
+  //  REQUIRE(meta->nbInputs() == 2);
+  //  REQUIRE(meta->nbOutputs() == 1);
+
+  //  std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
+  //      Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
+  //                               {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
+
+  //  std::shared_ptr<Tensor> myInit =
+  //      std::make_shared<Tensor>(Array2D<float, 3, 3>{
+  //          {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
+
+  // std::shared_ptr<Tensor> myInit2 =
+  //      std::make_shared<Tensor>(Array2D<float, 1, 2>{{{0.0, 0.0}}});
+
+  //  std::shared_ptr<Tensor> myInit3 =
+  //      std::make_shared<Tensor>(Array2D<float, 3, 2>{
+  //          {{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}});
+
+  //  std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
+  //      Array2D<float, 3, 2>{{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}});
+
+  //  std::shared_ptr<Tensor> myInitR =
+  //      std::make_shared<Tensor>(Array2D<float, 3, 3>{
+  //          {{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
+
+
+  //  //pop->getOperator()->associateInput(0, myInput);
+  //  //op->associateInput(1, myInitW);
+
+  //  pop->addChild(meta, 0, 0);
+  //  meta->addChild(stack, 0, 0);
+  //  pop->getOperator()->associateInput(0, myInput);
+  //  op->associateInput(1, myInit3);
+  //  //op->associateInput(18, meta);
+
+  //  //myGraph->setDataType(DataType::Float32);
+  //  //myGraph->setBackend("cpu");
+  //  //myGraph->forwardDims();
+
+  //  //auto scheduler = SequentialScheduler(myGraph);
+  //  //scheduler.generateScheduling();
+  //  //scheduler.graphView()->save("scheduled_graph");
+
+  //  //REQUIRE_NOTHROW(scheduler.forward(true));
+
+  //  //auto sop = std::static_pointer_cast<OperatorTensor>(stack->getOperator());
+  //  //sop->getOutput(0)->print();
+  //  //op->getOutput(0)->print();
+  //  //Log::info("Test Done.");
+
+  //  auto g = getConnectedGraphView(meta);
+  //  g->setDataType(DataType::Float32);
+  //  g->setBackend("cpu");
+
+  //  g->forwardDims({}, true);
+  //  g->save("connected");
+
+  //  auto scheduler = SequentialScheduler(g);
+  //  scheduler.forward();
+
+
+  //}
+
+  //SECTION("Issue")
+  //{
+
+  //  std::shared_ptr<Tensor> Input = std::make_shared<Tensor>(
+  //      Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
+  //                               {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
+
+  //  std::shared_ptr<Tensor> MemInit =
+  //      std::make_shared<Tensor>(Array2D<float, 3, 2>{
+  //          {{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}});
+
+  //  auto meta = Accumulate(2, "accumulate");
+  //  auto op = std::static_pointer_cast<MetaOperator_Op>(meta->getOperator());
+  //  auto pop_i = Pop("pop_input");
+  //  auto pop_o = Identity("pop_output");
+
+  //  pop_i->getOperator()->associateInput(0, Input);
+  //  pop_i->addChild(op->getMicroGraph()->getOrderedInputs()[0].first, 0, 0);
+  //  op->getMicroGraph()->getOrderedOutputs()[0].first->addChild(pop_o, 0, 0);
+
+  //  op->associateInput(1, MemInit);
+
+  //  // Build the graph.
+  //  auto myGraph = std::make_shared<GraphView>();
+  //  myGraph->add(pop_i);
+  //  myGraph->add(op->getMicroGraph());
+  //  myGraph->add(pop_o);
+  //  myGraph->compile("cpu", DataType::Float32);
+
+  //  // Schedule and run
+  //  auto scheduler = SequentialScheduler(myGraph);
+  //  scheduler.generateScheduling();
+  //  scheduler.graphView()->save("scheduled_graph");
+  //  REQUIRE_NOTHROW(scheduler.forward(true));
+
+  //  // Print output
+  //  std::static_pointer_cast<OperatorTensor>(pop_o->getOperator())->getOutput(0)->print();
+  //  // Print spike 
+  //  Log::info("Spike : ");
+  //  op->getOutput(1)->print();
+  //}
 }
-- 
GitLab


From 57b7de20259130453e46866550615d7a841aa351 Mon Sep 17 00:00:00 2001
From: Jerome Hue <jerome.hue@cea.fr>
Date: Thu, 5 Dec 2024 17:59:01 +0100
Subject: [PATCH 05/12] chore: Improve Leaky Tests

---
 unit_tests/operator/Test_MetaOperator.cpp | 267 +++-------------------
 1 file changed, 38 insertions(+), 229 deletions(-)

diff --git a/unit_tests/operator/Test_MetaOperator.cpp b/unit_tests/operator/Test_MetaOperator.cpp
index 3efbdc73..b3450d27 100644
--- a/unit_tests/operator/Test_MetaOperator.cpp
+++ b/unit_tests/operator/Test_MetaOperator.cpp
@@ -541,110 +541,15 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
     REQUIRE(approxEq<float>(*(op->getOutput(0)), *myHiddenState));
   }
 
-  SECTION("Leaky") {
+  SECTION("Leaky(forward)") {
 
-    // First we need to test the spikes without pop operator.
+    const auto nbTimeSteps = 2;
 
-    // TODO: Compare with real result
-    auto pop = Pop();
-    auto myLeaky = Leaky("leaky");
-    auto op = std::static_pointer_cast<MetaOperator_Op>(myLeaky->getOperator());
-
-    // Here we test LSTM as it is was flatten in the graph.
-    // We just borrow its micro-graph into our larger myGraph graph.
-    auto myGraph = std::make_shared<GraphView>();
-    pop->addChild(op->getMicroGraph()->getOrderedInputs()[0].first, 0, 0);
-    myGraph->add(op->getMicroGraph());
-    myGraph->add(pop);
-
-    // 3 outputs
-    REQUIRE(myLeaky->nbInputs() == 3);
-    REQUIRE(myLeaky->inputCategory(0) == InputCategory::Data);
-    REQUIRE(myLeaky->nbOutputs() == 3);
-
-    std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
-        Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
-                                 {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
-
-    std::shared_ptr<Tensor> myInit =
-        std::make_shared<Tensor>(Array2D<float, 3, 3>{
-            {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
-    std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
-        Array2D<float, 3, 2>{{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}});
-    std::shared_ptr<Tensor> myInitR =
-        std::make_shared<Tensor>(Array2D<float, 3, 3>{
-            {{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
-
-    // Compute expected output
-    // for i in range(num_steps)
-
-    pop->getOperator()->associateInput(0, myInput);
-
-    myLeaky->input(1).first->getOperator()->setOutput(0, myInitW);
-    myLeaky->input(2).first->getOperator()->setOutput(0, myInitW);
-
-    myGraph->setDataType(DataType::Float32);
-    myGraph->setBackend("cpu");
-
-    auto scheduler = SequentialScheduler(myGraph);
-    scheduler.generateScheduling();
-    REQUIRE_NOTHROW(scheduler.forward(true));
-
-    op->getOutput(0)->print();
-  }
-
-  SECTION("Leaky1") {
-    auto myLeaky = Leaky("leaky");
-    auto op = std::static_pointer_cast<MetaOperator_Op>(myLeaky->getOperator());
-
-    // Here we test LSTM as it is was flatten in the graph.
-    // We just borrow its micro-graph into our larger myGraph graph.
-    auto myGraph = std::make_shared<GraphView>();
-    myGraph->add(op->getMicroGraph());
-
-    // 3 outputs
-    REQUIRE(myLeaky->nbInputs() == 3);
-    REQUIRE(myLeaky->inputCategory(0) == InputCategory::Data);
-    REQUIRE(myLeaky->nbOutputs() == 3);
-
-    std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
-        Array2D<float, 3, 2>{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}}});
-
-    std::shared_ptr<Tensor> myInit =
-        std::make_shared<Tensor>(Array2D<float, 3, 3>{
-            {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
-    std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
-        Array2D<float, 3, 2>{{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}});
-    std::shared_ptr<Tensor> myInitR =
-        std::make_shared<Tensor>(Array2D<float, 3, 3>{
-            {{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
-
-    op->associateInput(0, myInput);
-    myLeaky->input(1).first->getOperator()->setOutput(0, myInitW);
-    myLeaky->input(2).first->getOperator()->setOutput(0, myInitW);
-
-    myGraph->setDataType(DataType::Float32);
-    myGraph->setBackend("cpu");
-
-    auto scheduler = SequentialScheduler(myGraph);
-    scheduler.generateScheduling();
-
-    // Results Looking good.
-    REQUIRE_NOTHROW(scheduler.forward(true));
-    op->getOutput(0)->print();
-
-    REQUIRE_NOTHROW(scheduler.forward(true));
-    op->getOutput(0)->print();
-
-    Log::info("Test Done.");
-  }
-
-  SECTION("Leaky2") {
-    auto myLeaky = Leaky("leaky");
+    auto myLeaky = Leaky(nbTimeSteps, 1.0, 0.9, "leaky");
     auto op = std::static_pointer_cast<MetaOperator_Op>(myLeaky->getOperator());
     //auto stack = Stack(2);
-    auto mem_rec = Stack(3, "mem_rec");
-    auto spk_rec = Stack(3, "spk_rec");
+    auto mem_rec = Stack(nbTimeSteps, "mem_rec");
+    auto spk_rec = Stack(nbTimeSteps, "spk_rec");
     auto pop = Pop("popinput");
 
     // Here we test LSTM as it is was flatten in the graph.
@@ -676,6 +581,37 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
         Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
                                  {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
 
+    std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>(
+        Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
+                                 {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
+
+    float array[12] = {1,2,3,4,5,6,2,3,4,5,6,7};
+    float* result = new float[expectedOutput->size()];
+
+    // Elements popped at each time step
+    auto nbElements = expectedOutput->size() / nbTimeSteps;
+
+    // Init
+    for(int i = 0; i < nbElements; ++i)
+    {
+        result[i] = array[i];
+    }
+
+    // Reccurence
+    for(int i = 1 ; i < nbTimeSteps; ++i)
+    {
+        auto offset = nbElements * i;
+        auto prev = nbElements * (i-1);
+        for(int j = 0; j < nbElements; ++j)
+        {
+            result[offset+j] = result[prev+j] * 0.9 + array[offset+j];
+        }
+    }
+
+
+    expectedOutput->getImpl()->setRawPtr(result, expectedOutput->size());
+    expectedOutput->print();
+
     std::shared_ptr<Tensor> myInit =
         std::make_shared<Tensor>(Array2D<float, 3, 3>{
             {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
@@ -707,141 +643,14 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
 
     REQUIRE_NOTHROW(scheduler.forward(true));
 
+    // TODO: Naming is wrong
     auto mem_op = std::static_pointer_cast<OperatorTensor>(mem_rec->getOperator());
     auto spk_op = std::static_pointer_cast<OperatorTensor>(spk_rec->getOperator());
     Log::info("Output of stack node and spike : ");
     mem_op->getOutput(0)->print();
+    REQUIRE(approxEq<float>(*(spk_op->getOutput(0)), *(expectedOutput)));
     Log::info("Output of stack node and spike : ");
     spk_op->getOutput(0)->print();
     Log::info("Test Done.");
   }
-
-  //SECTION("Accumulate")
-  //{
-  //  auto meta = Accumulate(2, "leaky");
-  //  auto op = std::static_pointer_cast<MetaOperator_Op>(meta->getOperator());
-
-  //  //auto stack = Stack(2);
-  //  //auto stack = Stack(3,"popout");
-  //  auto stack = Identity("popout");
-  //  auto pop = Pop("pop_input");
-
-  //  //auto myGraph = std::make_shared<GraphView>();
-  //  //pop->addChild(op->getMicroGraph()->getOrderedInputs()[0].first, 0, 0);
-  //  //Log::info("added child : {}", op->getMicroGraph()->getOrderedInputs()[0].first->name());
-  //  //op->getMicroGraph()->getOrderedOutputs()[0].first->addChild(stack, 0, 0);
-
-
-
-  //  //myGraph->add(pop);
-  //  //myGraph->add(op->getMicroGraph());
-  //  //myGraph->add(meta);
-  //  //myGraph->add(stack);
-  //  //myGraph->save("mg", true, true);
-
-  //  // 3 outputs
-  //  REQUIRE(meta->nbInputs() == 2);
-  //  REQUIRE(meta->nbOutputs() == 1);
-
-  //  std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
-  //      Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
-  //                               {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
-
-  //  std::shared_ptr<Tensor> myInit =
-  //      std::make_shared<Tensor>(Array2D<float, 3, 3>{
-  //          {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
-
-  // std::shared_ptr<Tensor> myInit2 =
-  //      std::make_shared<Tensor>(Array2D<float, 1, 2>{{{0.0, 0.0}}});
-
-  //  std::shared_ptr<Tensor> myInit3 =
-  //      std::make_shared<Tensor>(Array2D<float, 3, 2>{
-  //          {{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}});
-
-  //  std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
-  //      Array2D<float, 3, 2>{{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}});
-
-  //  std::shared_ptr<Tensor> myInitR =
-  //      std::make_shared<Tensor>(Array2D<float, 3, 3>{
-  //          {{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
-
-
-  //  //pop->getOperator()->associateInput(0, myInput);
-  //  //op->associateInput(1, myInitW);
-
-  //  pop->addChild(meta, 0, 0);
-  //  meta->addChild(stack, 0, 0);
-  //  pop->getOperator()->associateInput(0, myInput);
-  //  op->associateInput(1, myInit3);
-  //  //op->associateInput(18, meta);
-
-  //  //myGraph->setDataType(DataType::Float32);
-  //  //myGraph->setBackend("cpu");
-  //  //myGraph->forwardDims();
-
-  //  //auto scheduler = SequentialScheduler(myGraph);
-  //  //scheduler.generateScheduling();
-  //  //scheduler.graphView()->save("scheduled_graph");
-
-  //  //REQUIRE_NOTHROW(scheduler.forward(true));
-
-  //  //auto sop = std::static_pointer_cast<OperatorTensor>(stack->getOperator());
-  //  //sop->getOutput(0)->print();
-  //  //op->getOutput(0)->print();
-  //  //Log::info("Test Done.");
-
-  //  auto g = getConnectedGraphView(meta);
-  //  g->setDataType(DataType::Float32);
-  //  g->setBackend("cpu");
-
-  //  g->forwardDims({}, true);
-  //  g->save("connected");
-
-  //  auto scheduler = SequentialScheduler(g);
-  //  scheduler.forward();
-
-
-  //}
-
-  //SECTION("Issue")
-  //{
-
-  //  std::shared_ptr<Tensor> Input = std::make_shared<Tensor>(
-  //      Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
-  //                               {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
-
-  //  std::shared_ptr<Tensor> MemInit =
-  //      std::make_shared<Tensor>(Array2D<float, 3, 2>{
-  //          {{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}});
-
-  //  auto meta = Accumulate(2, "accumulate");
-  //  auto op = std::static_pointer_cast<MetaOperator_Op>(meta->getOperator());
-  //  auto pop_i = Pop("pop_input");
-  //  auto pop_o = Identity("pop_output");
-
-  //  pop_i->getOperator()->associateInput(0, Input);
-  //  pop_i->addChild(op->getMicroGraph()->getOrderedInputs()[0].first, 0, 0);
-  //  op->getMicroGraph()->getOrderedOutputs()[0].first->addChild(pop_o, 0, 0);
-
-  //  op->associateInput(1, MemInit);
-
-  //  // Build the graph.
-  //  auto myGraph = std::make_shared<GraphView>();
-  //  myGraph->add(pop_i);
-  //  myGraph->add(op->getMicroGraph());
-  //  myGraph->add(pop_o);
-  //  myGraph->compile("cpu", DataType::Float32);
-
-  //  // Schedule and run
-  //  auto scheduler = SequentialScheduler(myGraph);
-  //  scheduler.generateScheduling();
-  //  scheduler.graphView()->save("scheduled_graph");
-  //  REQUIRE_NOTHROW(scheduler.forward(true));
-
-  //  // Print output
-  //  std::static_pointer_cast<OperatorTensor>(pop_o->getOperator())->getOutput(0)->print();
-  //  // Print spike 
-  //  Log::info("Spike : ");
-  //  op->getOutput(1)->print();
-  //}
 }
-- 
GitLab


From 5282cb87703f5ae83fe9dd70e9dc70cbdfc252d1 Mon Sep 17 00:00:00 2001
From: Jerome Hue <jerome.hue@cea.fr>
Date: Thu, 5 Dec 2024 19:08:14 +0100
Subject: [PATCH 06/12] Reset node as output

---
 unit_tests/operator/Test_MetaOperator.cpp | 121 +++++++++++++++-------
 1 file changed, 84 insertions(+), 37 deletions(-)

diff --git a/unit_tests/operator/Test_MetaOperator.cpp b/unit_tests/operator/Test_MetaOperator.cpp
index b3450d27..effb95bd 100644
--- a/unit_tests/operator/Test_MetaOperator.cpp
+++ b/unit_tests/operator/Test_MetaOperator.cpp
@@ -9,10 +9,12 @@
  *
  ********************************************************************************/
 
+#include <aidge/filler/Filler.hpp>
 #include <catch2/catch_test_macros.hpp>
 #include <cmath>
 #include <cstdlib>
 #include <memory>
+#include <random>
 
 #include "aidge/backend/cpu/operator/ConvImpl.hpp"
 #include "aidge/backend/cpu/operator/PadImpl.hpp"
@@ -543,7 +545,28 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
 
   SECTION("Leaky(forward)") {
 
-    const auto nbTimeSteps = 2;
+    std::random_device rd;
+    std::mt19937 gen(rd());
+    std::uniform_real_distribution<float> valueDist(0.1f, 1.1f); // Random float distribution between 0 and 1
+    std::uniform_int_distribution<std::size_t> dimSizeDist(std::size_t(2), std::size_t(4));
+    std::uniform_int_distribution<std::size_t> nbDimsDist(std::size_t(3), std::size_t(3));
+    std::uniform_int_distribution<int> boolDist(0,1);
+
+    const std::size_t nbDims = nbDimsDist(gen);
+    Log::info("Nbdims : {}", nbDims);
+    std::vector<std::size_t> dims;
+    for (std::size_t i = 0; i < nbDims; ++i) {
+        dims.push_back(dimSizeDist(gen));
+    }
+    Log::info("timesteps : {}", dims[0]);
+    Log::info("dimensions : ");
+    for(auto dim: dims) {
+        Log::info("{}", dim);
+    }
+
+
+    const auto nbTimeSteps = dims[0];
+
 
     auto myLeaky = Leaky(nbTimeSteps, 1.0, 0.9, "leaky");
     auto op = std::static_pointer_cast<MetaOperator_Op>(myLeaky->getOperator());
@@ -581,76 +604,100 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
         Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
                                  {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
 
-    std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>(
-        Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
-                                 {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
+    //std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>(
+    //    Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
+    //                             {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
+
 
-    float array[12] = {1,2,3,4,5,6,2,3,4,5,6,7};
-    float* result = new float[expectedOutput->size()];
+    // Generate input 
+    std::shared_ptr<Tensor> T0 = std::make_shared<Tensor>();
+    T0->setDataType(DataType::Float32);
+    T0->setBackend("cpu");
+
+    std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>();
+    expectedOutput->setDataType(DataType::Float32);
+    expectedOutput->setBackend("cpu");
+
+    
+    const auto nb_elements = std::accumulate(dims.cbegin(), dims.cend(), std::size_t(1), std::multiplies<std::size_t>());
+    float* input = new float[nb_elements];
+    float* result = new float[nb_elements];
+
+    for (std::size_t i = 0; i < nb_elements; ++i) {
+        input[i] = valueDist(gen);
+    }
+    T0->resize(dims);
+    T0->getImpl()->setRawPtr(input, nb_elements);
+    T0->print();
 
     // Elements popped at each time step
-    auto nbElements = expectedOutput->size() / nbTimeSteps;
+    auto nbElementsPerTimeStep = nb_elements / dims[0];
 
     // Init
-    for(int i = 0; i < nbElements; ++i)
+    for(int i = 0; i < nbElementsPerTimeStep; ++i)
     {
-        result[i] = array[i];
+        result[i] = input[i];
     }
 
     // Reccurence
-    for(int i = 1 ; i < nbTimeSteps; ++i)
+    for(int i = 1 ; i < dims[0]; ++i)
     {
-        auto offset = nbElements * i;
-        auto prev = nbElements * (i-1);
-        for(int j = 0; j < nbElements; ++j)
+        auto offset = nbElementsPerTimeStep * i;
+        auto prev = nbElementsPerTimeStep * (i-1);
+        for(int j = 0; j < nbElementsPerTimeStep; ++j)
         {
-            result[offset+j] = result[prev+j] * 0.9 + array[offset+j];
+            auto reset = (result[prev+j] > 1.0 ? 1 : 0);
+            result[offset+j] = result[prev+j] * 0.9 + input[offset+j] - reset; 
+
         }
     }
 
 
-    expectedOutput->getImpl()->setRawPtr(result, expectedOutput->size());
+    expectedOutput->resize(dims);
+    expectedOutput->getImpl()->setRawPtr(result, nb_elements);
+    Log::info("Expected ouptut : ");
     expectedOutput->print();
 
     std::shared_ptr<Tensor> myInit =
         std::make_shared<Tensor>(Array2D<float, 3, 3>{
             {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
+
+    auto initMemdims = std::vector<std::size_t>(dims.begin()+1, dims.end());
+    Log::info("dimensions : ");
+    for(auto dim: initMemdims) {
+            Log::info("{}", dim);
+    }
     std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
         Array2D<float, 3, 2>{{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}});
-    std::shared_ptr<Tensor> myInitR =
-        std::make_shared<Tensor>(Array2D<float, 3, 3>{
-            {{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
 
-    pop->getOperator()->associateInput(0, myInput);
+    std::shared_ptr<Tensor> myInitR = std::make_shared<Tensor>(initMemdims);
+    myInitR->setDataType(DataType::Float32);
+    myInitR->setBackend("cpu");
+    uniformFiller<float>(myInitR, 0, 0);
 
-    //myLeaky->input(1).first->getOperator()->setOutput(0, myInitW);
-    //myLeaky->input(2).first->getOperator()->setOutput(0, myInitW);
-    op->associateInput(1, myInitW);
-    op->associateInput(2, myInitW);
+    pop->getOperator()->associateInput(0, T0);
+    op->associateInput(1, myInitR);
+    op->associateInput(2, myInitR);
 
-    myGraph->setDataType(DataType::Float32);
-    myGraph->setBackend("cpu");
-    myGraph->forwardDims();
+    myGraph->compile("cpu", DataType::Float32);
 
     auto scheduler = SequentialScheduler(myGraph);
-    scheduler.generateScheduling();
-
-    scheduler.saveStaticSchedulingDiagram("leaky");
-    scheduler.graphView()->save("schedgraph");
-
-    auto microGraph = op->getMicroGraph();
-    microGraph->save("Micro", false, false);
-
+    REQUIRE_NOTHROW(scheduler.generateScheduling());
     REQUIRE_NOTHROW(scheduler.forward(true));
 
     // TODO: Naming is wrong
     auto mem_op = std::static_pointer_cast<OperatorTensor>(mem_rec->getOperator());
     auto spk_op = std::static_pointer_cast<OperatorTensor>(spk_rec->getOperator());
-    Log::info("Output of stack node and spike : ");
-    mem_op->getOutput(0)->print();
+    //Log::info("Output of stack node and spike : ");
+    //mem_op->getOutput(0)->print();
+    Log::info("----- Input -----");
+    T0->print();
+    Log::info("----- Results (expected) -----");
+    expectedOutput->print();
+    Log::info("----- Results -----");
+    spk_op->getOutput(0)->print();
     REQUIRE(approxEq<float>(*(spk_op->getOutput(0)), *(expectedOutput)));
     Log::info("Output of stack node and spike : ");
-    spk_op->getOutput(0)->print();
     Log::info("Test Done.");
   }
 }
-- 
GitLab


From 4b89662afe461aef77f8684dcdd06dd44862c49b Mon Sep 17 00:00:00 2001
From: Jerome Hue <jerome.hue@cea.fr>
Date: Mon, 9 Dec 2024 18:45:59 +0100
Subject: [PATCH 07/12] chore: format files and reflect changes to Leaky
 constructor

---
 unit_tests/operator/Test_MetaOperator.cpp | 1180 +++++++++++----------
 1 file changed, 626 insertions(+), 554 deletions(-)

diff --git a/unit_tests/operator/Test_MetaOperator.cpp b/unit_tests/operator/Test_MetaOperator.cpp
index effb95bd..17064385 100644
--- a/unit_tests/operator/Test_MetaOperator.cpp
+++ b/unit_tests/operator/Test_MetaOperator.cpp
@@ -20,11 +20,11 @@
 #include "aidge/backend/cpu/operator/PadImpl.hpp"
 #include "aidge/data/Tensor.hpp"
 #include "aidge/operator/Conv.hpp"
+#include "aidge/operator/Identity.hpp"
 #include "aidge/operator/MetaOperator.hpp"
 #include "aidge/operator/MetaOperatorDefs.hpp"
 #include "aidge/operator/Pad.hpp"
 #include "aidge/operator/Pop.hpp"
-#include "aidge/operator/Identity.hpp"
 #include "aidge/operator/Stack.hpp"
 #include "aidge/scheduler/ParallelScheduler.hpp"
 #include "aidge/scheduler/SequentialScheduler.hpp"
@@ -33,52 +33,53 @@
 using namespace Aidge;
 
 TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
-  SECTION("PaddedConv(forward)") {
-    std::shared_ptr<Tensor> myWeights =
-        std::make_shared<Tensor>(Array4D<double, 4, 3, 3, 3>{
-            {{{{6.20986394e-01, 1.19775136e-03, 7.22876095e-02},
-               {1.16492919e-01, 8.21634093e-02, 1.17413265e-01},
-               {2.23743494e-01, 3.99495413e-01, 5.55552411e-01}},
-              {{6.64970077e-01, 9.62199940e-01, 4.87531967e-01},
-               {6.12586558e-01, 8.09918671e-02, 8.40649383e-01},
-               {4.15264406e-01, 8.28247138e-01, 1.52301135e-01}},
-              {{1.76992844e-02, 7.78697112e-01, 8.14531592e-01},
-               {1.36960611e-01, 4.64806728e-01, 4.85150000e-01},
-               {4.34776520e-01, 9.51740977e-01, 9.05793799e-01}}},
-
-             {{{1.71925246e-02, 1.91082720e-01, 3.67982644e-01},
-               {1.56806559e-01, 6.22280998e-01, 3.15827594e-01},
-               {6.04359038e-01, 2.83095947e-01, 6.11168892e-01}},
-              {{2.76942832e-01, 1.89768419e-01, 8.07988176e-01},
-               {1.67925807e-01, 2.68356150e-01, 6.28875602e-01},
-               {1.69093357e-04, 9.64788636e-01, 7.29254981e-01}},
-              {{6.34030122e-01, 1.32087038e-01, 3.33857107e-01},
-               {7.63047502e-01, 5.12539506e-02, 9.77400493e-01},
-               {8.06151288e-01, 2.60237147e-01, 3.93729313e-01}}},
-
-             {{{5.84605240e-01, 4.74648725e-01, 8.54111741e-01},
-               {7.10897067e-02, 5.02579011e-01, 3.35236224e-01},
-               {9.08637408e-01, 8.02903830e-01, 2.83929907e-01}},
-              {{3.68206999e-01, 9.18579021e-02, 7.33168098e-01},
-               {1.59875539e-01, 9.13163381e-01, 3.59806060e-01},
-               {1.41295882e-01, 7.00312185e-01, 5.63728289e-01}},
-              {{9.39513546e-01, 1.91704891e-01, 1.11454944e-01},
-               {5.46298282e-01, 2.89698587e-01, 2.62612651e-01},
-               {1.18554992e-01, 4.32147376e-02, 7.53016994e-01}}},
-
-             {{{9.53179175e-01, 2.05041054e-02, 1.11318451e-01},
-               {8.67878485e-01, 2.93263422e-01, 8.03912714e-01},
-               {8.93620255e-01, 1.37831128e-01, 3.83640583e-01}},
-              {{3.96020188e-01, 6.24959320e-01, 1.90709175e-01},
-               {5.80538620e-01, 6.63031275e-01, 2.07247191e-01},
-               {5.65672171e-01, 5.57014317e-01, 9.26909496e-01}},
-              {{3.43901418e-01, 4.47741636e-01, 6.59249367e-01},
-               {7.34639028e-01, 2.84957200e-02, 9.70225217e-01},
-               {1.33578790e-02, 6.12054702e-01, 9.36685235e-02}}}}});
-    std::shared_ptr<Tensor> myBias = std::make_shared<Tensor>(
-        Array1D<double, 4>{{0.16884905, 0.27994487, 0.57227465, 0.06435205}});
-    std::shared_ptr<Tensor> myInput =
-        std::make_shared<Tensor>(Array4D<double, 2, 3, 5, 5>{
+    SECTION("PaddedConv(forward)") {
+        std::shared_ptr<Tensor> myWeights =
+            std::make_shared<Tensor>(Array4D<double, 4, 3, 3, 3>{
+                {{{{6.20986394e-01, 1.19775136e-03, 7.22876095e-02},
+                   {1.16492919e-01, 8.21634093e-02, 1.17413265e-01},
+                   {2.23743494e-01, 3.99495413e-01, 5.55552411e-01}},
+                  {{6.64970077e-01, 9.62199940e-01, 4.87531967e-01},
+                   {6.12586558e-01, 8.09918671e-02, 8.40649383e-01},
+                   {4.15264406e-01, 8.28247138e-01, 1.52301135e-01}},
+                  {{1.76992844e-02, 7.78697112e-01, 8.14531592e-01},
+                   {1.36960611e-01, 4.64806728e-01, 4.85150000e-01},
+                   {4.34776520e-01, 9.51740977e-01, 9.05793799e-01}}},
+
+                 {{{1.71925246e-02, 1.91082720e-01, 3.67982644e-01},
+                   {1.56806559e-01, 6.22280998e-01, 3.15827594e-01},
+                   {6.04359038e-01, 2.83095947e-01, 6.11168892e-01}},
+                  {{2.76942832e-01, 1.89768419e-01, 8.07988176e-01},
+                   {1.67925807e-01, 2.68356150e-01, 6.28875602e-01},
+                   {1.69093357e-04, 9.64788636e-01, 7.29254981e-01}},
+                  {{6.34030122e-01, 1.32087038e-01, 3.33857107e-01},
+                   {7.63047502e-01, 5.12539506e-02, 9.77400493e-01},
+                   {8.06151288e-01, 2.60237147e-01, 3.93729313e-01}}},
+
+                 {{{5.84605240e-01, 4.74648725e-01, 8.54111741e-01},
+                   {7.10897067e-02, 5.02579011e-01, 3.35236224e-01},
+                   {9.08637408e-01, 8.02903830e-01, 2.83929907e-01}},
+                  {{3.68206999e-01, 9.18579021e-02, 7.33168098e-01},
+                   {1.59875539e-01, 9.13163381e-01, 3.59806060e-01},
+                   {1.41295882e-01, 7.00312185e-01, 5.63728289e-01}},
+                  {{9.39513546e-01, 1.91704891e-01, 1.11454944e-01},
+                   {5.46298282e-01, 2.89698587e-01, 2.62612651e-01},
+                   {1.18554992e-01, 4.32147376e-02, 7.53016994e-01}}},
+
+                 {{{9.53179175e-01, 2.05041054e-02, 1.11318451e-01},
+                   {8.67878485e-01, 2.93263422e-01, 8.03912714e-01},
+                   {8.93620255e-01, 1.37831128e-01, 3.83640583e-01}},
+                  {{3.96020188e-01, 6.24959320e-01, 1.90709175e-01},
+                   {5.80538620e-01, 6.63031275e-01, 2.07247191e-01},
+                   {5.65672171e-01, 5.57014317e-01, 9.26909496e-01}},
+                  {{3.43901418e-01, 4.47741636e-01, 6.59249367e-01},
+                   {7.34639028e-01, 2.84957200e-02, 9.70225217e-01},
+                   {1.33578790e-02, 6.12054702e-01, 9.36685235e-02}}}}});
+        std::shared_ptr<Tensor> myBias =
+            std::make_shared<Tensor>(Array1D<double, 4>{
+                {0.16884905, 0.27994487, 0.57227465, 0.06435205}});
+        std::shared_ptr<Tensor> myInput = std::make_shared<
+            Tensor>(Array4D<double, 2, 3, 5, 5>{
             // NCHW
             {{{{0.43224481, 0.9047832, 0.18402257, 0.06162838, 0.52490127},
                {0.27773404, 0.55402353, 0.9485062, 0.31197083, 0.80328607},
@@ -114,10 +115,14 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
                {0.95873236, 0.6742374, 0.55679676, 0.6323497, 0.34072958},
                {0.49694061, 0.79173045, 0.19738225, 0.14755281, 0.80818177},
                {0.02332061, 0.74270703, 0.59415632, 0.08195934, 0.46295434},
-               {0.71426058, 0.85032931, 0.90750818, 0.28768431, 0.4401146}}}}});
-
-    std::shared_ptr<Tensor> myOutput =
-        std::make_shared<Tensor>(Array4D<double, 2, 4, 5, 5>{
+               {0.71426058,
+                0.85032931,
+                0.90750818,
+                0.28768431,
+                0.4401146}}}}});
+
+        std::shared_ptr<Tensor> myOutput = std::make_shared<
+            Tensor>(Array4D<double, 2, 4, 5, 5>{
             {{{{3.40294218, 3.74021220, 4.02050114, 4.07054710, 2.46286273},
                {4.61770582, 6.70517588, 6.50356627, 6.29688787, 3.53332567},
                {5.47480106, 5.92094421, 6.64605665, 7.95090199, 4.28721523},
@@ -164,540 +169,607 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
                {4.80529881, 7.33211374, 5.14774036, 4.77281189, 4.44612408},
                {5.11703110, 7.55168772, 7.14374542, 6.43696356, 4.10621357},
                {5.41270018, 6.85949135, 6.73503923, 5.74601364, 4.46150303},
-               {3.16612267, 4.38248920, 5.23248482, 4.21292210,
+               {3.16612267,
+                4.38248920,
+                5.23248482,
+                4.21292210,
                 2.86031270}}}}});
 
-    std::shared_ptr<Node> myConv = Conv<2>(3, 4, {3, 3}, "myconv");
-    auto convOp =
-        std::static_pointer_cast<OperatorTensor>(myConv->getOperator());
+        std::shared_ptr<Node> myConv = Conv<2>(3, 4, {3, 3}, "myconv");
+        auto convOp =
+            std::static_pointer_cast<OperatorTensor>(myConv->getOperator());
 
-    std::shared_ptr<Node> myPad =
-        Pad<2>({1, 1, 1, 1}, "myPad", PadBorderType::Constant, 0.0);
-    auto padOp = std::static_pointer_cast<OperatorTensor>(myPad->getOperator());
+        std::shared_ptr<Node> myPad =
+            Pad<2>({1, 1, 1, 1}, "myPad", PadBorderType::Constant, 0.0);
+        auto padOp =
+            std::static_pointer_cast<OperatorTensor>(myPad->getOperator());
 
-    convOp->setInput(1, myWeights);
-    convOp->setInput(2, myBias);
+        convOp->setInput(1, myWeights);
+        convOp->setInput(2, myBias);
 
-    myPad->addChild(myConv, 0, 0);
-    padOp->setInput(0, myInput);
+        myPad->addChild(myConv, 0, 0);
+        padOp->setInput(0, myInput);
 
-    padOp->setDataType(DataType::Float64);
-    padOp->setBackend("cpu");
-    convOp->setDataType(DataType::Float64);
-    convOp->setBackend("cpu");
+        padOp->setDataType(DataType::Float64);
+        padOp->setBackend("cpu");
+        convOp->setDataType(DataType::Float64);
+        convOp->setBackend("cpu");
 
-    myPad->forward();
-    myConv->forward();
-    convOp->getOutput(0)->print();
+        myPad->forward();
+        myConv->forward();
+        convOp->getOutput(0)->print();
 
-    double *computedOutput =
-        static_cast<double *>(convOp->getOutput(0)->getImpl()->rawPtr());
-    double *expectedOutput =
-        static_cast<double *>(myOutput->getImpl()->rawPtr());
-    for (std::size_t i = 0; i < myOutput->size(); ++i) {
-      REQUIRE(std::abs(computedOutput[i] - expectedOutput[i]) < 1e-5);
-    }
+        double *computedOutput =
+            static_cast<double *>(convOp->getOutput(0)->getImpl()->rawPtr());
+        double *expectedOutput =
+            static_cast<double *>(myOutput->getImpl()->rawPtr());
+        for (std::size_t i = 0; i < myOutput->size(); ++i) {
+            REQUIRE(std::abs(computedOutput[i] - expectedOutput[i]) < 1e-5);
+        }
 
-    std::shared_ptr<Node> myPaddedConv =
-        PaddedConv(3, 4, {3, 3}, "myPaddedConv", {1, 1}, {1, 1, 1, 1});
-  }
-  SECTION("LSTM(forward)") {
-    auto pop = Pop();
-    auto myLSTM = LSTM(32, 64, 0, true, "ltsm");
-    auto op = std::dynamic_pointer_cast<MetaOperator_Op>(myLSTM->getOperator());
-
-    auto microGraph = op->getMicroGraph();
-    microGraph->save("lstm", false, true);
-
-    REQUIRE(myLSTM->nbInputs() == 3 + 8 + 8);
-    REQUIRE(myLSTM->inputCategory(0) == InputCategory::Data);
-    for (size_t i = 1; i < 9; ++i) {
-      REQUIRE(myLSTM->inputCategory(i) == InputCategory::Param);
-    }
-    for (size_t i = 9; i < 17; ++i) {
-      REQUIRE(myLSTM->inputCategory(i) == InputCategory::OptionalParam);
-    }
-    REQUIRE(myLSTM->nbOutputs() == 2);
-
-    std::shared_ptr<Tensor> myInput =
-        std::make_shared<Tensor>(Array2D<float, 16, 32>{});
-    std::shared_ptr<Tensor> myInit =
-        std::make_shared<Tensor>(Array2D<float, 32, 64>{});
-    std::shared_ptr<Tensor> myInitW =
-        std::make_shared<Tensor>(Array2D<float, 64, 32>{});
-    std::shared_ptr<Tensor> myInitR =
-        std::make_shared<Tensor>(Array2D<float, 64, 64>{});
-
-    pop->addChild(myLSTM, 0, 0);
-    pop->getOperator()->associateInput(0, myInput);
-    op->associateInput(17, myInit);
-    op->associateInput(18, myInit);
-
-    // Weights X
-    myLSTM->input(1).first->getOperator()->setOutput(0, myInitW);
-    myLSTM->input(2).first->getOperator()->setOutput(0, myInitW);
-    myLSTM->input(3).first->getOperator()->setOutput(0, myInitW);
-    myLSTM->input(4).first->getOperator()->setOutput(0, myInitW);
-    // Weights H
-    myLSTM->input(5).first->getOperator()->setOutput(0, myInitR);
-    myLSTM->input(6).first->getOperator()->setOutput(0, myInitR);
-    myLSTM->input(7).first->getOperator()->setOutput(0, myInitR);
-    myLSTM->input(8).first->getOperator()->setOutput(0, myInitR);
-
-    auto g = getConnectedGraphView(myLSTM);
-    g->setDataType(DataType::Float32);
-    g->setBackend("cpu");
-
-    auto scheduler = SequentialScheduler(g);
-    scheduler.forward(true);
-
-    g->save("lstm_outside_dims", true, true);
-
-    microGraph->save("lstm_dims", true, true);
-    REQUIRE(op->dimsForwarded());
-
-    auto microGraphScheduler = std::dynamic_pointer_cast<MetaOperator_Op>(op)
-                                   ->getMicroGraphScheduler();
-    microGraphScheduler->saveSchedulingDiagram("lstm_scheduling");
-
-    REQUIRE(op->getNbConsumedData(0).data == 512);
-    REQUIRE(op->getNbConsumedData(1).data == 32768);
-    REQUIRE(op->getNbProducedData(0).data == 34816);
-    REQUIRE(op->getNbProducedData(1).data == 34816);
-    REQUIRE(microGraphScheduler->getStaticScheduling(0).size() == 26);
-    REQUIRE(microGraphScheduler->getStaticScheduling(1).size() == 24);
-    REQUIRE(microGraphScheduler->getStaticScheduling(15).size() == 24);
-  }
-  SECTION("LSTM(forward_values)") {
-    auto myLSTM = LSTM(2, 3, 0, true, "ltsm");
-    auto op = std::static_pointer_cast<OperatorTensor>(myLSTM->getOperator());
-
-    auto microGraph =
-        std::dynamic_pointer_cast<MetaOperator_Op>(op)->getMicroGraph();
-    microGraph->save("lstm", false, false);
-
-    REQUIRE(myLSTM->nbInputs() == 3 + 8 + 8);
-    REQUIRE(myLSTM->inputCategory(0) == InputCategory::Data);
-    for (size_t i = 1; i < 9; ++i) {
-      REQUIRE(myLSTM->inputCategory(i) == InputCategory::Param);
-    }
-    for (size_t i = 9; i < 17; ++i) {
-      REQUIRE(myLSTM->inputCategory(i) == InputCategory::OptionalParam);
-    }
-    REQUIRE(myLSTM->nbOutputs() == 2);
-
-    std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
-        Array2D<float, 3, 2>{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}}});
-    std::shared_ptr<Tensor> myInit =
-        std::make_shared<Tensor>(Array2D<float, 3, 3>{
-            {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
-    std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
-        Array2D<float, 3, 2>{{{0.1, 0.1}, {0.1, 0.1}, {0.1, 0.1}}});
-    std::shared_ptr<Tensor> myInitR =
-        std::make_shared<Tensor>(Array2D<float, 3, 3>{
-            {{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
-
-    op->associateInput(0, myInput);
-    op->associateInput(17, myInit);
-    op->associateInput(18, myInit);
-
-    // Weights X
-    myLSTM->input(1).first->getOperator()->setOutput(0, myInitW);
-    myLSTM->input(2).first->getOperator()->setOutput(0, myInitW);
-    myLSTM->input(3).first->getOperator()->setOutput(0, myInitW);
-    myLSTM->input(4).first->getOperator()->setOutput(0, myInitW);
-    // Weights H
-    myLSTM->input(5).first->getOperator()->setOutput(0, myInitR);
-    myLSTM->input(6).first->getOperator()->setOutput(0, myInitR);
-    myLSTM->input(7).first->getOperator()->setOutput(0, myInitR);
-    myLSTM->input(8).first->getOperator()->setOutput(0, myInitR);
-
-    auto g = getConnectedGraphView(myLSTM);
-    g->setDataType(DataType::Float32);
-    g->setBackend("cpu");
-
-    auto scheduler = SequentialScheduler(g);
-    scheduler.forward();
-
-    microGraph->save("lstm_values_dims", false, true);
-
-    std::shared_ptr<Tensor> myHiddenState = std::make_shared<Tensor>(
-        Array2D<float, 3, 3>{{{0.0952412, 0.0952412, 0.0952412},
-                              {0.25606447, 0.25606447, 0.25606447},
-                              {0.40323776, 0.40323776, 0.40323776}}});
-
-    auto microGraphScheduler = std::dynamic_pointer_cast<MetaOperator_Op>(op)
-                                   ->getMicroGraphScheduler();
-    microGraphScheduler->saveSchedulingDiagram("lstm_values_scheduling");
-
-    op->getOutput(0)->print();
-    myHiddenState->print();
-
-    REQUIRE(approxEq<float>(*(op->getOutput(0)), *myHiddenState));
-  }
-  SECTION("LSTM(forward_values_seq)") {
-    auto pop = Pop();
-    auto myLSTM = LSTM(2, 3, 2, true, "ltsm");
-    auto myGraph = Sequential({pop, myLSTM});
-    auto op = std::static_pointer_cast<OperatorTensor>(myLSTM->getOperator());
-
-    REQUIRE(myLSTM->nbInputs() == 3 + 8 + 8);
-    REQUIRE(myLSTM->inputCategory(0) == InputCategory::Data);
-    for (size_t i = 1; i < 9; ++i) {
-      REQUIRE(myLSTM->inputCategory(i) == InputCategory::Param);
-    }
-    for (size_t i = 9; i < 17; ++i) {
-      REQUIRE(myLSTM->inputCategory(i) == InputCategory::OptionalParam);
+        std::shared_ptr<Node> myPaddedConv =
+            PaddedConv(3, 4, {3, 3}, "myPaddedConv", {1, 1}, {1, 1, 1, 1});
     }
-    REQUIRE(myLSTM->nbOutputs() == 2);
-
-    std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
-        Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
-                                 {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
-    std::shared_ptr<Tensor> myInit =
-        std::make_shared<Tensor>(Array2D<float, 3, 3>{
-            {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
-    std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
-        Array2D<float, 3, 2>{{{0.1, 0.1}, {0.1, 0.1}, {0.1, 0.1}}});
-    std::shared_ptr<Tensor> myInitR =
-        std::make_shared<Tensor>(Array2D<float, 3, 3>{
-            {{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
-
-    pop->getOperator()->associateInput(0, myInput);
-    op->associateInput(17, myInit);
-    op->associateInput(18, myInit);
-
-    // Weights X
-    myLSTM->input(1).first->getOperator()->setOutput(0, myInitW);
-    myLSTM->input(2).first->getOperator()->setOutput(0, myInitW);
-    myLSTM->input(3).first->getOperator()->setOutput(0, myInitW);
-    myLSTM->input(4).first->getOperator()->setOutput(0, myInitW);
-    // Weights H
-    myLSTM->input(5).first->getOperator()->setOutput(0, myInitR);
-    myLSTM->input(6).first->getOperator()->setOutput(0, myInitR);
-    myLSTM->input(7).first->getOperator()->setOutput(0, myInitR);
-    myLSTM->input(8).first->getOperator()->setOutput(0, myInitR);
-
-    auto g = getConnectedGraphView(myLSTM);
-    g->compile("cpu", DataType::Float32);
-
-    g->save("lstm_seq", true, true);
-
-    auto scheduler = SequentialScheduler(g);
-    scheduler.forward();
-    scheduler.saveSchedulingDiagram("lstm_seq_schedule");
-
-    std::shared_ptr<Tensor> myHiddenState = std::make_shared<Tensor>(
-        Array2D<float, 3, 3>{{{0.24439372, 0.24439372, 0.24439372},
-                              {0.49801484, 0.49801484, 0.49801484},
-                              {0.67162132, 0.67162132, 0.67162132}}});
-
-    myGraph->save("lstm_seq_mygraph", true, true);
-
-    op->getOutput(0)->print();
-    myHiddenState->print();
-
-    REQUIRE(approxEq<float>(*(op->getOutput(0)), *myHiddenState));
-  }
-  SECTION("LSTM(forward_values_seq_flatten)(sequential)") {
-    auto pop = Pop();
-    auto myLSTM = LSTM(2, 3, 2, true, "ltsm");
-    auto op = std::static_pointer_cast<MetaOperator_Op>(myLSTM->getOperator());
-
-    // Here we test LSTM as it is was flatten in the graph.
-    // We just borrow its micro-graph into our larger myGraph graph.
-    auto myGraph = std::make_shared<GraphView>();
-    pop->addChild(op->getMicroGraph()->getOrderedInputs()[0].first, 0, 0);
-    myGraph->add(op->getMicroGraph());
-    myGraph->add(pop);
-
-    REQUIRE(myLSTM->nbInputs() == 3 + 8 + 8);
-    REQUIRE(myLSTM->inputCategory(0) == InputCategory::Data);
-    for (size_t i = 1; i < 9; ++i) {
-      REQUIRE(myLSTM->inputCategory(i) == InputCategory::Param);
-    }
-    for (size_t i = 9; i < 17; ++i) {
-      REQUIRE(myLSTM->inputCategory(i) == InputCategory::OptionalParam);
+    SECTION("LSTM(forward)") {
+        auto pop = Pop();
+        auto myLSTM = LSTM(32, 64, 0, true, "ltsm");
+        auto op =
+            std::dynamic_pointer_cast<MetaOperator_Op>(myLSTM->getOperator());
+
+        auto microGraph = op->getMicroGraph();
+        microGraph->save("lstm", false, true);
+
+        REQUIRE(myLSTM->nbInputs() == 3 + 8 + 8);
+        REQUIRE(myLSTM->inputCategory(0) == InputCategory::Data);
+        for (size_t i = 1; i < 9; ++i) {
+            REQUIRE(myLSTM->inputCategory(i) == InputCategory::Param);
+        }
+        for (size_t i = 9; i < 17; ++i) {
+            REQUIRE(myLSTM->inputCategory(i) == InputCategory::OptionalParam);
+        }
+        REQUIRE(myLSTM->nbOutputs() == 2);
+
+        std::shared_ptr<Tensor> myInput =
+            std::make_shared<Tensor>(Array2D<float, 16, 32>{});
+        std::shared_ptr<Tensor> myInit =
+            std::make_shared<Tensor>(Array2D<float, 32, 64>{});
+        std::shared_ptr<Tensor> myInitW =
+            std::make_shared<Tensor>(Array2D<float, 64, 32>{});
+        std::shared_ptr<Tensor> myInitR =
+            std::make_shared<Tensor>(Array2D<float, 64, 64>{});
+
+        pop->addChild(myLSTM, 0, 0);
+        pop->getOperator()->associateInput(0, myInput);
+        op->associateInput(17, myInit);
+        op->associateInput(18, myInit);
+
+        // Weights X
+        myLSTM->input(1).first->getOperator()->setOutput(0, myInitW);
+        myLSTM->input(2).first->getOperator()->setOutput(0, myInitW);
+        myLSTM->input(3).first->getOperator()->setOutput(0, myInitW);
+        myLSTM->input(4).first->getOperator()->setOutput(0, myInitW);
+        // Weights H
+        myLSTM->input(5).first->getOperator()->setOutput(0, myInitR);
+        myLSTM->input(6).first->getOperator()->setOutput(0, myInitR);
+        myLSTM->input(7).first->getOperator()->setOutput(0, myInitR);
+        myLSTM->input(8).first->getOperator()->setOutput(0, myInitR);
+
+        auto g = getConnectedGraphView(myLSTM);
+        g->setDataType(DataType::Float32);
+        g->setBackend("cpu");
+
+        auto scheduler = SequentialScheduler(g);
+        scheduler.forward(true);
+
+        g->save("lstm_outside_dims", true, true);
+
+        microGraph->save("lstm_dims", true, true);
+        REQUIRE(op->dimsForwarded());
+
+        auto microGraphScheduler =
+            std::dynamic_pointer_cast<MetaOperator_Op>(op)
+                ->getMicroGraphScheduler();
+        microGraphScheduler->saveSchedulingDiagram("lstm_scheduling");
+
+        REQUIRE(op->getNbConsumedData(0).data == 512);
+        REQUIRE(op->getNbConsumedData(1).data == 32768);
+        REQUIRE(op->getNbProducedData(0).data == 34816);
+        REQUIRE(op->getNbProducedData(1).data == 34816);
+        REQUIRE(microGraphScheduler->getStaticScheduling(0).size() == 26);
+        REQUIRE(microGraphScheduler->getStaticScheduling(1).size() == 24);
+        REQUIRE(microGraphScheduler->getStaticScheduling(15).size() == 24);
     }
-    REQUIRE(myLSTM->nbOutputs() == 2);
-
-    std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
-        Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
-                                 {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
-    std::shared_ptr<Tensor> myInit =
-        std::make_shared<Tensor>(Array2D<float, 3, 3>{
-            {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
-    std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
-        Array2D<float, 3, 2>{{{0.1, 0.1}, {0.1, 0.1}, {0.1, 0.1}}});
-    std::shared_ptr<Tensor> myInitR =
-        std::make_shared<Tensor>(Array2D<float, 3, 3>{
-            {{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
-
-    pop->getOperator()->associateInput(0, myInput);
-    op->associateInput(17, myInit);
-    op->associateInput(18, myInit);
-
-    // Weights X
-    auto prodX = Producer(myInitW);
-    prodX->addChild(op->getMicroGraph()->getOrderedInputs()[1].first, 0, 1);
-    prodX->addChild(op->getMicroGraph()->getOrderedInputs()[2].first, 0, 1);
-    prodX->addChild(op->getMicroGraph()->getOrderedInputs()[3].first, 0, 1);
-    prodX->addChild(op->getMicroGraph()->getOrderedInputs()[4].first, 0, 1);
-    // Weights H
-    auto prodH = Producer(myInitR);
-    prodH->addChild(op->getMicroGraph()->getOrderedInputs()[5].first, 0, 1);
-    prodH->addChild(op->getMicroGraph()->getOrderedInputs()[6].first, 0, 1);
-    prodH->addChild(op->getMicroGraph()->getOrderedInputs()[7].first, 0, 1);
-    prodH->addChild(op->getMicroGraph()->getOrderedInputs()[8].first, 0, 1);
-    myGraph->add({prodX, prodH});
-
-    myGraph->setDataType(DataType::Float32);
-    myGraph->setBackend("cpu");
-    myGraph->save("lstm_seq_flatten", true, true);
-
-    std::shared_ptr<Tensor> myHiddenState = std::make_shared<Tensor>(
-        Array2D<float, 3, 3>{{{0.24439372, 0.24439372, 0.24439372},
-                              {0.49801484, 0.49801484, 0.49801484},
-                              {0.67162132, 0.67162132, 0.67162132}}});
-
-    auto scheduler = SequentialScheduler(myGraph);
-    scheduler.generateScheduling();
-    scheduler.saveStaticSchedulingDiagram("lstm_static_schedule");
-    scheduler.forward(true);
-    scheduler.saveSchedulingDiagram("lstm_seq_flatten_schedule_seq");
-
-    op->getOutput(0)->print();
-    myHiddenState->print();
-
-    REQUIRE(approxEq<float>(*(op->getOutput(0)), *myHiddenState));
-  }
-  SECTION("LSTM(forward_values_seq_flatten)(parallel)") {
-    auto pop = Pop();
-    auto myLSTM = LSTM(2, 3, 2, true, "ltsm");
-    auto op = std::static_pointer_cast<MetaOperator_Op>(myLSTM->getOperator());
-
-    // Here we test LSTM as it is was flatten in the graph.
-    // We just borrow its micro-graph into our larger myGraph graph.
-    auto myGraph = std::make_shared<GraphView>();
-    pop->addChild(op->getMicroGraph()->getOrderedInputs()[0].first, 0, 0);
-    myGraph->add(op->getMicroGraph());
-    myGraph->add(pop);
-
-    REQUIRE(myLSTM->nbInputs() == 3 + 8 + 8);
-    REQUIRE(myLSTM->inputCategory(0) == InputCategory::Data);
-    for (size_t i = 1; i < 9; ++i) {
-      REQUIRE(myLSTM->inputCategory(i) == InputCategory::Param);
+    SECTION("LSTM(forward_values)") {
+        auto myLSTM = LSTM(2, 3, 0, true, "ltsm");
+        auto op =
+            std::static_pointer_cast<OperatorTensor>(myLSTM->getOperator());
+
+        auto microGraph =
+            std::dynamic_pointer_cast<MetaOperator_Op>(op)->getMicroGraph();
+        microGraph->save("lstm", false, false);
+
+        REQUIRE(myLSTM->nbInputs() == 3 + 8 + 8);
+        REQUIRE(myLSTM->inputCategory(0) == InputCategory::Data);
+        for (size_t i = 1; i < 9; ++i) {
+            REQUIRE(myLSTM->inputCategory(i) == InputCategory::Param);
+        }
+        for (size_t i = 9; i < 17; ++i) {
+            REQUIRE(myLSTM->inputCategory(i) == InputCategory::OptionalParam);
+        }
+        REQUIRE(myLSTM->nbOutputs() == 2);
+
+        std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
+            Array2D<float, 3, 2>{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}}});
+        std::shared_ptr<Tensor> myInit =
+            std::make_shared<Tensor>(Array2D<float, 3, 3>{
+                {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
+        std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
+            Array2D<float, 3, 2>{{{0.1, 0.1}, {0.1, 0.1}, {0.1, 0.1}}});
+        std::shared_ptr<Tensor> myInitR =
+            std::make_shared<Tensor>(Array2D<float, 3, 3>{
+                {{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
+
+        op->associateInput(0, myInput);
+        op->associateInput(17, myInit);
+        op->associateInput(18, myInit);
+
+        // Weights X
+        myLSTM->input(1).first->getOperator()->setOutput(0, myInitW);
+        myLSTM->input(2).first->getOperator()->setOutput(0, myInitW);
+        myLSTM->input(3).first->getOperator()->setOutput(0, myInitW);
+        myLSTM->input(4).first->getOperator()->setOutput(0, myInitW);
+        // Weights H
+        myLSTM->input(5).first->getOperator()->setOutput(0, myInitR);
+        myLSTM->input(6).first->getOperator()->setOutput(0, myInitR);
+        myLSTM->input(7).first->getOperator()->setOutput(0, myInitR);
+        myLSTM->input(8).first->getOperator()->setOutput(0, myInitR);
+
+        auto g = getConnectedGraphView(myLSTM);
+        g->setDataType(DataType::Float32);
+        g->setBackend("cpu");
+
+        auto scheduler = SequentialScheduler(g);
+        scheduler.forward();
+
+        microGraph->save("lstm_values_dims", false, true);
+
+        std::shared_ptr<Tensor> myHiddenState = std::make_shared<Tensor>(
+            Array2D<float, 3, 3>{{{0.0952412, 0.0952412, 0.0952412},
+                                  {0.25606447, 0.25606447, 0.25606447},
+                                  {0.40323776, 0.40323776, 0.40323776}}});
+
+        auto microGraphScheduler =
+            std::dynamic_pointer_cast<MetaOperator_Op>(op)
+                ->getMicroGraphScheduler();
+        microGraphScheduler->saveSchedulingDiagram("lstm_values_scheduling");
+
+        op->getOutput(0)->print();
+        myHiddenState->print();
+
+        REQUIRE(approxEq<float>(*(op->getOutput(0)), *myHiddenState));
     }
-    for (size_t i = 9; i < 17; ++i) {
-      REQUIRE(myLSTM->inputCategory(i) == InputCategory::OptionalParam);
+    SECTION("LSTM(forward_values_seq)") {
+        auto pop = Pop();
+        auto myLSTM = LSTM(2, 3, 2, true, "ltsm");
+        auto myGraph = Sequential({pop, myLSTM});
+        auto op =
+            std::static_pointer_cast<OperatorTensor>(myLSTM->getOperator());
+
+        REQUIRE(myLSTM->nbInputs() == 3 + 8 + 8);
+        REQUIRE(myLSTM->inputCategory(0) == InputCategory::Data);
+        for (size_t i = 1; i < 9; ++i) {
+            REQUIRE(myLSTM->inputCategory(i) == InputCategory::Param);
+        }
+        for (size_t i = 9; i < 17; ++i) {
+            REQUIRE(myLSTM->inputCategory(i) == InputCategory::OptionalParam);
+        }
+        REQUIRE(myLSTM->nbOutputs() == 2);
+
+        std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
+            Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
+                                     {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
+        std::shared_ptr<Tensor> myInit =
+            std::make_shared<Tensor>(Array2D<float, 3, 3>{
+                {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
+        std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
+            Array2D<float, 3, 2>{{{0.1, 0.1}, {0.1, 0.1}, {0.1, 0.1}}});
+        std::shared_ptr<Tensor> myInitR =
+            std::make_shared<Tensor>(Array2D<float, 3, 3>{
+                {{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
+
+        pop->getOperator()->associateInput(0, myInput);
+        op->associateInput(17, myInit);
+        op->associateInput(18, myInit);
+
+        // Weights X
+        myLSTM->input(1).first->getOperator()->setOutput(0, myInitW);
+        myLSTM->input(2).first->getOperator()->setOutput(0, myInitW);
+        myLSTM->input(3).first->getOperator()->setOutput(0, myInitW);
+        myLSTM->input(4).first->getOperator()->setOutput(0, myInitW);
+        // Weights H
+        myLSTM->input(5).first->getOperator()->setOutput(0, myInitR);
+        myLSTM->input(6).first->getOperator()->setOutput(0, myInitR);
+        myLSTM->input(7).first->getOperator()->setOutput(0, myInitR);
+        myLSTM->input(8).first->getOperator()->setOutput(0, myInitR);
+
+        auto g = getConnectedGraphView(myLSTM);
+        g->compile("cpu", DataType::Float32);
+
+        g->save("lstm_seq", true, true);
+
+        auto scheduler = SequentialScheduler(g);
+        scheduler.forward();
+        scheduler.saveSchedulingDiagram("lstm_seq_schedule");
+
+        std::shared_ptr<Tensor> myHiddenState = std::make_shared<Tensor>(
+            Array2D<float, 3, 3>{{{0.24439372, 0.24439372, 0.24439372},
+                                  {0.49801484, 0.49801484, 0.49801484},
+                                  {0.67162132, 0.67162132, 0.67162132}}});
+
+        myGraph->save("lstm_seq_mygraph", true, true);
+
+        op->getOutput(0)->print();
+        myHiddenState->print();
+
+        REQUIRE(approxEq<float>(*(op->getOutput(0)), *myHiddenState));
     }
-    REQUIRE(myLSTM->nbOutputs() == 2);
-
-    std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
-        Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
-                                 {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
-    std::shared_ptr<Tensor> myInit =
-        std::make_shared<Tensor>(Array2D<float, 3, 3>{
-            {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
-    std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
-        Array2D<float, 3, 2>{{{0.1, 0.1}, {0.1, 0.1}, {0.1, 0.1}}});
-    std::shared_ptr<Tensor> myInitR =
-        std::make_shared<Tensor>(Array2D<float, 3, 3>{
-            {{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
-
-    pop->getOperator()->associateInput(0, myInput);
-    op->associateInput(17, myInit);
-    op->associateInput(18, myInit);
-
-    // Weights X
-    auto prodX = Producer(myInitW);
-    prodX->addChild(op->getMicroGraph()->getOrderedInputs()[1].first, 0, 1);
-    prodX->addChild(op->getMicroGraph()->getOrderedInputs()[2].first, 0, 1);
-    prodX->addChild(op->getMicroGraph()->getOrderedInputs()[3].first, 0, 1);
-    prodX->addChild(op->getMicroGraph()->getOrderedInputs()[4].first, 0, 1);
-    // Weights H
-    auto prodH = Producer(myInitR);
-    prodH->addChild(op->getMicroGraph()->getOrderedInputs()[5].first, 0, 1);
-    prodH->addChild(op->getMicroGraph()->getOrderedInputs()[6].first, 0, 1);
-    prodH->addChild(op->getMicroGraph()->getOrderedInputs()[7].first, 0, 1);
-    prodH->addChild(op->getMicroGraph()->getOrderedInputs()[8].first, 0, 1);
-    myGraph->add({prodX, prodH});
-
-    myGraph->setDataType(DataType::Float32);
-    myGraph->setBackend("cpu");
-    myGraph->save("lstm_seq_flatten", true, true);
-
-    std::shared_ptr<Tensor> myHiddenState = std::make_shared<Tensor>(
-        Array2D<float, 3, 3>{{{0.24439372, 0.24439372, 0.24439372},
-                              {0.49801484, 0.49801484, 0.49801484},
-                              {0.67162132, 0.67162132, 0.67162132}}});
-
-    auto scheduler = ParallelScheduler(myGraph);
-    scheduler.generateScheduling();
-    scheduler.forward(true);
-    scheduler.saveSchedulingDiagram("lstm_seq_flatten_schedule_par");
-
-    op->getOutput(0)->print();
-    myHiddenState->print();
-
-    REQUIRE(approxEq<float>(*(op->getOutput(0)), *myHiddenState));
-  }
-
-  SECTION("Leaky(forward)") {
-
-    std::random_device rd;
-    std::mt19937 gen(rd());
-    std::uniform_real_distribution<float> valueDist(0.1f, 1.1f); // Random float distribution between 0 and 1
-    std::uniform_int_distribution<std::size_t> dimSizeDist(std::size_t(2), std::size_t(4));
-    std::uniform_int_distribution<std::size_t> nbDimsDist(std::size_t(3), std::size_t(3));
-    std::uniform_int_distribution<int> boolDist(0,1);
-
-    const std::size_t nbDims = nbDimsDist(gen);
-    Log::info("Nbdims : {}", nbDims);
-    std::vector<std::size_t> dims;
-    for (std::size_t i = 0; i < nbDims; ++i) {
-        dims.push_back(dimSizeDist(gen));
+    SECTION("LSTM(forward_values_seq_flatten)(sequential)") {
+        auto pop = Pop();
+        auto myLSTM = LSTM(2, 3, 2, true, "ltsm");
+        auto op =
+            std::static_pointer_cast<MetaOperator_Op>(myLSTM->getOperator());
+
+        // Here we test LSTM as it is was flatten in the graph.
+        // We just borrow its micro-graph into our larger myGraph graph.
+        auto myGraph = std::make_shared<GraphView>();
+        pop->addChild(op->getMicroGraph()->getOrderedInputs()[0].first, 0, 0);
+        myGraph->add(op->getMicroGraph());
+        myGraph->add(pop);
+
+        REQUIRE(myLSTM->nbInputs() == 3 + 8 + 8);
+        REQUIRE(myLSTM->inputCategory(0) == InputCategory::Data);
+        for (size_t i = 1; i < 9; ++i) {
+            REQUIRE(myLSTM->inputCategory(i) == InputCategory::Param);
+        }
+        for (size_t i = 9; i < 17; ++i) {
+            REQUIRE(myLSTM->inputCategory(i) == InputCategory::OptionalParam);
+        }
+        REQUIRE(myLSTM->nbOutputs() == 2);
+
+        std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
+            Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
+                                     {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
+        std::shared_ptr<Tensor> myInit =
+            std::make_shared<Tensor>(Array2D<float, 3, 3>{
+                {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
+        std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
+            Array2D<float, 3, 2>{{{0.1, 0.1}, {0.1, 0.1}, {0.1, 0.1}}});
+        std::shared_ptr<Tensor> myInitR =
+            std::make_shared<Tensor>(Array2D<float, 3, 3>{
+                {{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
+
+        pop->getOperator()->associateInput(0, myInput);
+        op->associateInput(17, myInit);
+        op->associateInput(18, myInit);
+
+        // Weights X
+        auto prodX = Producer(myInitW);
+        prodX->addChild(op->getMicroGraph()->getOrderedInputs()[1].first,
+                        0,
+                        1);
+        prodX->addChild(op->getMicroGraph()->getOrderedInputs()[2].first,
+                        0,
+                        1);
+        prodX->addChild(op->getMicroGraph()->getOrderedInputs()[3].first,
+                        0,
+                        1);
+        prodX->addChild(op->getMicroGraph()->getOrderedInputs()[4].first,
+                        0,
+                        1);
+        // Weights H
+        auto prodH = Producer(myInitR);
+        prodH->addChild(op->getMicroGraph()->getOrderedInputs()[5].first,
+                        0,
+                        1);
+        prodH->addChild(op->getMicroGraph()->getOrderedInputs()[6].first,
+                        0,
+                        1);
+        prodH->addChild(op->getMicroGraph()->getOrderedInputs()[7].first,
+                        0,
+                        1);
+        prodH->addChild(op->getMicroGraph()->getOrderedInputs()[8].first,
+                        0,
+                        1);
+        myGraph->add({prodX, prodH});
+
+        myGraph->setDataType(DataType::Float32);
+        myGraph->setBackend("cpu");
+        myGraph->save("lstm_seq_flatten", true, true);
+
+        std::shared_ptr<Tensor> myHiddenState = std::make_shared<Tensor>(
+            Array2D<float, 3, 3>{{{0.24439372, 0.24439372, 0.24439372},
+                                  {0.49801484, 0.49801484, 0.49801484},
+                                  {0.67162132, 0.67162132, 0.67162132}}});
+
+        auto scheduler = SequentialScheduler(myGraph);
+        scheduler.generateScheduling();
+        scheduler.saveStaticSchedulingDiagram("lstm_static_schedule");
+        scheduler.forward(true);
+        scheduler.saveSchedulingDiagram("lstm_seq_flatten_schedule_seq");
+
+        op->getOutput(0)->print();
+        myHiddenState->print();
+
+        REQUIRE(approxEq<float>(*(op->getOutput(0)), *myHiddenState));
     }
-    Log::info("timesteps : {}", dims[0]);
-    Log::info("dimensions : ");
-    for(auto dim: dims) {
-        Log::info("{}", dim);
+    SECTION("LSTM(forward_values_seq_flatten)(parallel)") {
+        auto pop = Pop();
+        auto myLSTM = LSTM(2, 3, 2, true, "ltsm");
+        auto op =
+            std::static_pointer_cast<MetaOperator_Op>(myLSTM->getOperator());
+
+        // Here we test LSTM as it is was flatten in the graph.
+        // We just borrow its micro-graph into our larger myGraph graph.
+        auto myGraph = std::make_shared<GraphView>();
+        pop->addChild(op->getMicroGraph()->getOrderedInputs()[0].first, 0, 0);
+        myGraph->add(op->getMicroGraph());
+        myGraph->add(pop);
+
+        REQUIRE(myLSTM->nbInputs() == 3 + 8 + 8);
+        REQUIRE(myLSTM->inputCategory(0) == InputCategory::Data);
+        for (size_t i = 1; i < 9; ++i) {
+            REQUIRE(myLSTM->inputCategory(i) == InputCategory::Param);
+        }
+        for (size_t i = 9; i < 17; ++i) {
+            REQUIRE(myLSTM->inputCategory(i) == InputCategory::OptionalParam);
+        }
+        REQUIRE(myLSTM->nbOutputs() == 2);
+
+        std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
+            Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
+                                     {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
+        std::shared_ptr<Tensor> myInit =
+            std::make_shared<Tensor>(Array2D<float, 3, 3>{
+                {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
+        std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
+            Array2D<float, 3, 2>{{{0.1, 0.1}, {0.1, 0.1}, {0.1, 0.1}}});
+        std::shared_ptr<Tensor> myInitR =
+            std::make_shared<Tensor>(Array2D<float, 3, 3>{
+                {{0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}, {0.1, 0.1, 0.1}}});
+
+        pop->getOperator()->associateInput(0, myInput);
+        op->associateInput(17, myInit);
+        op->associateInput(18, myInit);
+
+        // Weights X
+        auto prodX = Producer(myInitW);
+        prodX->addChild(op->getMicroGraph()->getOrderedInputs()[1].first,
+                        0,
+                        1);
+        prodX->addChild(op->getMicroGraph()->getOrderedInputs()[2].first,
+                        0,
+                        1);
+        prodX->addChild(op->getMicroGraph()->getOrderedInputs()[3].first,
+                        0,
+                        1);
+        prodX->addChild(op->getMicroGraph()->getOrderedInputs()[4].first,
+                        0,
+                        1);
+        // Weights H
+        auto prodH = Producer(myInitR);
+        prodH->addChild(op->getMicroGraph()->getOrderedInputs()[5].first,
+                        0,
+                        1);
+        prodH->addChild(op->getMicroGraph()->getOrderedInputs()[6].first,
+                        0,
+                        1);
+        prodH->addChild(op->getMicroGraph()->getOrderedInputs()[7].first,
+                        0,
+                        1);
+        prodH->addChild(op->getMicroGraph()->getOrderedInputs()[8].first,
+                        0,
+                        1);
+        myGraph->add({prodX, prodH});
+
+        myGraph->setDataType(DataType::Float32);
+        myGraph->setBackend("cpu");
+        myGraph->save("lstm_seq_flatten", true, true);
+
+        std::shared_ptr<Tensor> myHiddenState = std::make_shared<Tensor>(
+            Array2D<float, 3, 3>{{{0.24439372, 0.24439372, 0.24439372},
+                                  {0.49801484, 0.49801484, 0.49801484},
+                                  {0.67162132, 0.67162132, 0.67162132}}});
+
+        auto scheduler = ParallelScheduler(myGraph);
+        scheduler.generateScheduling();
+        scheduler.forward(true);
+        scheduler.saveSchedulingDiagram("lstm_seq_flatten_schedule_par");
+
+        op->getOutput(0)->print();
+        myHiddenState->print();
+
+        REQUIRE(approxEq<float>(*(op->getOutput(0)), *myHiddenState));
     }
 
+    SECTION("Leaky(forward)(fixed)") {
 
-    const auto nbTimeSteps = dims[0];
-
+        std::shared_ptr<Tensor> input = std::make_shared<Tensor>(
+            Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
+                                     {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
 
-    auto myLeaky = Leaky(nbTimeSteps, 1.0, 0.9, "leaky");
-    auto op = std::static_pointer_cast<MetaOperator_Op>(myLeaky->getOperator());
-    //auto stack = Stack(2);
-    auto mem_rec = Stack(nbTimeSteps, "mem_rec");
-    auto spk_rec = Stack(nbTimeSteps, "spk_rec");
-    auto pop = Pop("popinput");
 
-    // Here we test LSTM as it is was flatten in the graph.
-    // We just borrow its micro-graph into our larger myGraph graph.
-    auto myGraph = std::make_shared<GraphView>();
+        constexpr auto beta = 0.9;
+        constexpr auto threshold  = 1.0;
+        auto pop = Pop("pop");
+        auto leaky = Leaky(2, beta, threshold, "leaky");
 
-    pop->addChild(op->getMicroGraph()->getOrderedInputs()[0].first, 0, 0);
-    // 0 for mem 1 for stack
-    op->getMicroGraph()->getOrderedOutputs()[1].first->addChild(mem_rec, 0, 0);
-    op->getMicroGraph()->getOrderedOutputs()[0].first->addChild(spk_rec, 0, 0);
-    for(auto node : op->getMicroGraph()->getOrderedOutputs())
-    {
-        Log::info("name  of output {}", node.first->name());
+        REQUIRE(true);
     }
 
-    myGraph->add(pop);
-    myGraph->add(op->getMicroGraph());
-    myGraph->add(mem_rec);
-    myGraph->add(spk_rec);
-    myGraph->save("mg", true, true);
-
-    // 3 outputs
-    REQUIRE(myLeaky->nbInputs() == 3);
-    REQUIRE(myLeaky->inputCategory(0) == InputCategory::Data);
-    // Two spikes connected to nothing, + the Add node real output
-    REQUIRE(myLeaky->nbOutputs() == 4);
-
-    std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
-        Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
-                                 {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
-
-    //std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>(
-    //    Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
-    //                             {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
-
-
-    // Generate input 
-    std::shared_ptr<Tensor> T0 = std::make_shared<Tensor>();
-    T0->setDataType(DataType::Float32);
-    T0->setBackend("cpu");
-
-    std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>();
-    expectedOutput->setDataType(DataType::Float32);
-    expectedOutput->setBackend("cpu");
-
-    
-    const auto nb_elements = std::accumulate(dims.cbegin(), dims.cend(), std::size_t(1), std::multiplies<std::size_t>());
-    float* input = new float[nb_elements];
-    float* result = new float[nb_elements];
-
-    for (std::size_t i = 0; i < nb_elements; ++i) {
-        input[i] = valueDist(gen);
-    }
-    T0->resize(dims);
-    T0->getImpl()->setRawPtr(input, nb_elements);
-    T0->print();
+    SECTION("Leaky(forward)") {
+
+        std::random_device rd;
+        std::mt19937 gen(rd());
+        std::uniform_real_distribution<float> valueDist(
+            0.1f,
+            1.1f); // Random float distribution between 0 and 1
+        std::uniform_int_distribution<std::size_t> dimSizeDist(std::size_t(2),
+                                                               std::size_t(4));
+        std::uniform_int_distribution<std::size_t> nbDimsDist(std::size_t(3),
+                                                              std::size_t(3));
+        std::uniform_int_distribution<int> boolDist(0, 1);
+
+        const std::size_t nbDims = nbDimsDist(gen);
+        Log::info("Nbdims : {}", nbDims);
+        std::vector<std::size_t> dims;
+        for (std::size_t i = 0; i < nbDims; ++i) {
+            dims.push_back(dimSizeDist(gen));
+        }
+        Log::info("timesteps : {}", dims[0]);
+        Log::info("dimensions : ");
+        for (auto dim : dims) {
+            Log::info("{}", dim);
+        }
 
-    // Elements popped at each time step
-    auto nbElementsPerTimeStep = nb_elements / dims[0];
+        const auto nbTimeSteps = dims[0];
+
+        auto myLeaky = Leaky(nbTimeSteps, 0.9, 1.0, "leaky");
+        auto op =
+            std::static_pointer_cast<MetaOperator_Op>(myLeaky->getOperator());
+        // auto stack = Stack(2);
+        auto mem_rec = Stack(nbTimeSteps, "mem_rec");
+        auto spk_rec = Stack(nbTimeSteps, "spk_rec");
+        auto pop = Pop("popinput");
+
+        // Here we test LSTM as it is was flatten in the graph.
+        // We just borrow its micro-graph into our larger myGraph graph.
+        auto myGraph = std::make_shared<GraphView>();
+
+        pop->addChild(op->getMicroGraph()->getOrderedInputs()[0].first, 0, 0);
+        // 0 for mem 1 for stack
+        op->getMicroGraph()->getOrderedOutputs()[1].first->addChild(mem_rec,
+                                                                    0,
+                                                                    0);
+        op->getMicroGraph()->getOrderedOutputs()[0].first->addChild(spk_rec,
+                                                                    0,
+                                                                    0);
+        for (auto node : op->getMicroGraph()->getOrderedOutputs()) {
+            Log::info("name  of output {}", node.first->name());
+        }
 
-    // Init
-    for(int i = 0; i < nbElementsPerTimeStep; ++i)
-    {
-        result[i] = input[i];
-    }
+        myGraph->add(pop);
+        myGraph->add(op->getMicroGraph());
+        myGraph->add(mem_rec);
+        myGraph->add(spk_rec);
+        myGraph->save("mg", true, true);
+
+        // 3 outputs
+        REQUIRE(myLeaky->nbInputs() == 3);
+        REQUIRE(myLeaky->inputCategory(0) == InputCategory::Data);
+        // Two spikes connected to nothing, + the Add node real output
+        REQUIRE(myLeaky->nbOutputs() == 4);
+
+        std::shared_ptr<Tensor> myInput = std::make_shared<Tensor>(
+            Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
+                                     {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
+
+        // std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>(
+        //     Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
+        //                              {{2.0, 3.0}, {4.0, 5.0},
+        //                              {6.0, 7.0}}}});
+
+        // Generate input
+        std::shared_ptr<Tensor> T0 = std::make_shared<Tensor>();
+        T0->setDataType(DataType::Float32);
+        T0->setBackend("cpu");
+
+        std::shared_ptr<Tensor> expectedOutput = std::make_shared<Tensor>();
+        expectedOutput->setDataType(DataType::Float32);
+        expectedOutput->setBackend("cpu");
+
+        const auto nb_elements =
+            std::accumulate(dims.cbegin(),
+                            dims.cend(),
+                            std::size_t(1),
+                            std::multiplies<std::size_t>());
+        float *input = new float[nb_elements];
+        float *result = new float[nb_elements];
+
+        for (std::size_t i = 0; i < nb_elements; ++i) {
+            input[i] = valueDist(gen);
+        }
+        T0->resize(dims);
+        T0->getImpl()->setRawPtr(input, nb_elements);
+        T0->print();
 
-    // Reccurence
-    for(int i = 1 ; i < dims[0]; ++i)
-    {
-        auto offset = nbElementsPerTimeStep * i;
-        auto prev = nbElementsPerTimeStep * (i-1);
-        for(int j = 0; j < nbElementsPerTimeStep; ++j)
-        {
-            auto reset = (result[prev+j] > 1.0 ? 1 : 0);
-            result[offset+j] = result[prev+j] * 0.9 + input[offset+j] - reset; 
+        // Elements popped at each time step
+        auto nbElementsPerTimeStep = nb_elements / dims[0];
 
+        // Init
+        for (int i = 0; i < nbElementsPerTimeStep; ++i) {
+            result[i] = input[i];
         }
-    }
 
+        // Reccurence
+        for (int i = 1; i < dims[0]; ++i) {
+            auto offset = nbElementsPerTimeStep * i;
+            auto prev = nbElementsPerTimeStep * (i - 1);
+            for (int j = 0; j < nbElementsPerTimeStep; ++j) {
+                auto reset = (result[prev + j] > 1.0 ? 1 : 0);
+                result[offset + j] =
+                    result[prev + j] * 0.9 + input[offset + j] - reset;
+            }
+        }
 
-    expectedOutput->resize(dims);
-    expectedOutput->getImpl()->setRawPtr(result, nb_elements);
-    Log::info("Expected ouptut : ");
-    expectedOutput->print();
+        expectedOutput->resize(dims);
+        expectedOutput->getImpl()->setRawPtr(result, nb_elements);
+        Log::info("Expected ouptut : ");
+        expectedOutput->print();
 
-    std::shared_ptr<Tensor> myInit =
-        std::make_shared<Tensor>(Array2D<float, 3, 3>{
-            {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
+        std::shared_ptr<Tensor> myInit =
+            std::make_shared<Tensor>(Array2D<float, 3, 3>{
+                {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}});
 
-    auto initMemdims = std::vector<std::size_t>(dims.begin()+1, dims.end());
-    Log::info("dimensions : ");
-    for(auto dim: initMemdims) {
+        auto initMemdims =
+            std::vector<std::size_t>(dims.begin() + 1, dims.end());
+        Log::info("dimensions : ");
+        for (auto dim : initMemdims) {
             Log::info("{}", dim);
+        }
+        std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
+            Array2D<float, 3, 2>{{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}});
+
+        std::shared_ptr<Tensor> myInitR =
+            std::make_shared<Tensor>(initMemdims);
+        myInitR->setDataType(DataType::Float32);
+        myInitR->setBackend("cpu");
+        uniformFiller<float>(myInitR, 0, 0);
+
+        pop->getOperator()->associateInput(0, T0);
+        op->associateInput(1, myInitR);
+        op->associateInput(2, myInitR);
+
+        myGraph->compile("cpu", DataType::Float32);
+
+        auto scheduler = SequentialScheduler(myGraph);
+        REQUIRE_NOTHROW(scheduler.generateScheduling());
+        REQUIRE_NOTHROW(scheduler.forward(true));
+
+        // TODO: Naming is wrong
+        auto mem_op =
+            std::static_pointer_cast<OperatorTensor>(mem_rec->getOperator());
+        auto spk_op =
+            std::static_pointer_cast<OperatorTensor>(spk_rec->getOperator());
+        // Log::info("Output of stack node and spike : ");
+        // mem_op->getOutput(0)->print();
+        Log::info("----- Input -----");
+        T0->print();
+        Log::info("----- Results (expected) -----");
+        expectedOutput->print();
+        Log::info("----- Results -----");
+        spk_op->getOutput(0)->print();
+        REQUIRE(approxEq<float>(*(spk_op->getOutput(0)), *(expectedOutput)));
+        Log::info("Output of stack node and spike : ");
+        Log::info("Test Done.");
     }
-    std::shared_ptr<Tensor> myInitW = std::make_shared<Tensor>(
-        Array2D<float, 3, 2>{{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}});
-
-    std::shared_ptr<Tensor> myInitR = std::make_shared<Tensor>(initMemdims);
-    myInitR->setDataType(DataType::Float32);
-    myInitR->setBackend("cpu");
-    uniformFiller<float>(myInitR, 0, 0);
-
-    pop->getOperator()->associateInput(0, T0);
-    op->associateInput(1, myInitR);
-    op->associateInput(2, myInitR);
-
-    myGraph->compile("cpu", DataType::Float32);
-
-    auto scheduler = SequentialScheduler(myGraph);
-    REQUIRE_NOTHROW(scheduler.generateScheduling());
-    REQUIRE_NOTHROW(scheduler.forward(true));
-
-    // TODO: Naming is wrong
-    auto mem_op = std::static_pointer_cast<OperatorTensor>(mem_rec->getOperator());
-    auto spk_op = std::static_pointer_cast<OperatorTensor>(spk_rec->getOperator());
-    //Log::info("Output of stack node and spike : ");
-    //mem_op->getOutput(0)->print();
-    Log::info("----- Input -----");
-    T0->print();
-    Log::info("----- Results (expected) -----");
-    expectedOutput->print();
-    Log::info("----- Results -----");
-    spk_op->getOutput(0)->print();
-    REQUIRE(approxEq<float>(*(spk_op->getOutput(0)), *(expectedOutput)));
-    Log::info("Output of stack node and spike : ");
-    Log::info("Test Done.");
-  }
 }
-- 
GitLab


From 8f01fb74d0ff506a8b51efab4cb076e2aac2a0eb Mon Sep 17 00:00:00 2001
From: Jerome Hue <jerome.hue@cea.fr>
Date: Tue, 10 Dec 2024 17:57:15 +0100
Subject: [PATCH 08/12] Add a new test for a full nn using leaky neuron

---
 src/operator/SubImpl.cpp                  |   3 -
 unit_tests/operator/Test_MetaOperator.cpp | 112 ++++++++++++++++++++--
 2 files changed, 102 insertions(+), 13 deletions(-)

diff --git a/src/operator/SubImpl.cpp b/src/operator/SubImpl.cpp
index 5c842c26..e36abe2a 100644
--- a/src/operator/SubImpl.cpp
+++ b/src/operator/SubImpl.cpp
@@ -29,9 +29,6 @@ void Aidge::SubImpl_cpu::forward() {
 
     // Find the correct kernel type
     const auto impl = Registrar<SubImpl_cpu>::create(getBestMatch(getRequiredSpec()));
-    Log::info("Sub Operator Kernel");
-    op_.getInput(0)->print();
-    op_.getInput(1)->print();
 
     // Call kernel
     impl.forward(op_.getInput(0)->dims(),
diff --git a/unit_tests/operator/Test_MetaOperator.cpp b/unit_tests/operator/Test_MetaOperator.cpp
index 17064385..cf663dbb 100644
--- a/unit_tests/operator/Test_MetaOperator.cpp
+++ b/unit_tests/operator/Test_MetaOperator.cpp
@@ -10,16 +10,18 @@
  ********************************************************************************/
 
 #include <aidge/filler/Filler.hpp>
+#include <aidge/operator/FC.hpp>
 #include <catch2/catch_test_macros.hpp>
 #include <cmath>
 #include <cstdlib>
 #include <memory>
 #include <random>
 
-#include "aidge/backend/cpu/operator/ConvImpl.hpp"
+#include "aidge/backend/cpu/operator/ConvImpl.hpp" 
 #include "aidge/backend/cpu/operator/PadImpl.hpp"
 #include "aidge/data/Tensor.hpp"
 #include "aidge/operator/Conv.hpp"
+#include "aidge/operator/FC.hpp"
 #include "aidge/operator/Identity.hpp"
 #include "aidge/operator/MetaOperator.hpp"
 #include "aidge/operator/MetaOperatorDefs.hpp"
@@ -29,6 +31,7 @@
 #include "aidge/scheduler/ParallelScheduler.hpp"
 #include "aidge/scheduler/SequentialScheduler.hpp"
 #include "aidge/utils/TensorUtils.hpp"
+#include "aidge/filler/Filler.hpp"
 
 using namespace Aidge;
 
@@ -211,6 +214,7 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
             PaddedConv(3, 4, {3, 3}, "myPaddedConv", {1, 1}, {1, 1, 1, 1});
     }
     SECTION("LSTM(forward)") {
+
         auto pop = Pop();
         auto myLSTM = LSTM(32, 64, 0, true, "ltsm");
         auto op =
@@ -279,6 +283,7 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
         REQUIRE(microGraphScheduler->getStaticScheduling(1).size() == 24);
         REQUIRE(microGraphScheduler->getStaticScheduling(15).size() == 24);
     }
+
     SECTION("LSTM(forward_values)") {
         auto myLSTM = LSTM(2, 3, 0, true, "ltsm");
         auto op =
@@ -348,6 +353,7 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
 
         REQUIRE(approxEq<float>(*(op->getOutput(0)), *myHiddenState));
     }
+
     SECTION("LSTM(forward_values_seq)") {
         auto pop = Pop();
         auto myLSTM = LSTM(2, 3, 2, true, "ltsm");
@@ -413,6 +419,7 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
 
         REQUIRE(approxEq<float>(*(op->getOutput(0)), *myHiddenState));
     }
+
     SECTION("LSTM(forward_values_seq_flatten)(sequential)") {
         auto pop = Pop();
         auto myLSTM = LSTM(2, 3, 2, true, "ltsm");
@@ -592,18 +599,103 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
     }
 
     SECTION("Leaky(forward)(fixed)") {
+        
+        constexpr auto inChannels = 10;
+        constexpr auto outChannels = 5;
 
-        std::shared_ptr<Tensor> input = std::make_shared<Tensor>(
-            Array3D<float, 2, 3, 2>{{{{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}},
-                                     {{2.0, 3.0}, {4.0, 5.0}, {6.0, 7.0}}}});
-
-
-        constexpr auto beta = 0.9;
+        constexpr auto beta = 0.95;
         constexpr auto threshold  = 1.0;
-        auto pop = Pop("pop");
-        auto leaky = Leaky(2, beta, threshold, "leaky");
+        constexpr auto nbTimeSteps = 2;
+
+
+        auto myWeights = std::make_shared<Tensor>(Array2D<float, outChannels, inChannels>{{
+            {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0},
+            {1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1},
+            {0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 0.1, 0.2, 0.3, 0.4},
+            {0.4, 0.3, 0.2, 0.1, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5},
+            {0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0},
+        }});
+
+        auto myWeights2 = std::make_shared<Tensor>(Array2D<float, inChannels, outChannels>{{
+            {0.1, 0.2, 0.3, 0.4, 0.5},
+            {0.6, 0.7, 0.8, 0.9, 1.0},
+            {1.0, 0.9, 0.8, 0.7, 0.6}, 
+            {0.5, 0.4, 0.3, 0.2, 0.1},
+            {0.5, 0.6, 0.7, 0.8, 0.9}, 
+            {1.0, 0.1, 0.2, 0.3, 0.4},
+            {0.4, 0.3, 0.2, 0.1, 0.0},
+            {0.1, 0.2, 0.3, 0.4, 0.5},
+            {0.9, 0.8, 0.7, 0.6, 0.5},
+            {0.4, 0.3, 0.2, 0.1, 0.0},
+        }});
+
+                auto myInput = std::make_shared<Tensor>(Array2D<float, 2, 10>{{
+            {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0},
+            {1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1},
+        }});
+
+        // py/snn Torch computed result, output of fc1 at time step 1
+        auto expectedOutputlif1ts1 = std::make_shared<Tensor>(Array2D<float,2,5>{{
+            {3.850, 2.2000, 2.6500, 1.5000, 1.6500},
+            {2.200, 3.8500, 3.4000, 1.2500, 3.3000},
+        }});
+
+        auto expectedOutputfc2ts1 = std::make_shared<Tensor>(Array2D<float,2,10>{{
+            {1.5000, 4.0000, 4.0000, 1.5000, 3.5000, 2.0000, 1.0000, 1.5000, 3.5000, 1.0000},
+            {1.5000, 4.0000, 4.0000, 1.5000, 3.5000, 2.0000, 1.0000, 1.5000, 3.5000, 1.0000},
+        }});
+
+        auto expectedOutputlif1ts2 = std::make_shared<Tensor>(Array2D<float,2,5>{{
+            {6.5075, 3.2900, 4.1675, 1.9250, 2.2175},
+            {3.2900, 6.5075, 5.6300, 1.4375, 5.4350},
+        }});
+
+        // NOTE: Same output as before, because for all channels, we have a potential higher than threshold.
+        // Thus the lif neuron fires at every timestep for every channel.
+        auto expectedOutputfc2ts2 = std::make_shared<Tensor>(Array2D<float,2,10>{{
+            {1.5000, 4.0000, 4.0000, 1.5000, 3.5000, 2.0000, 1.0000, 1.5000, 3.5000, 1.0000},
+            {1.5000, 4.0000, 4.0000, 1.5000, 3.5000, 2.0000, 1.0000, 1.5000, 3.5000, 1.0000},
+        }});
+
+        auto init = std::make_shared<Tensor>(Array2D<float, 2, 5>{});
+        uniformFiller<float>(init, 0.0, 0.0);
+
+
+        auto fc1 = FC(inChannels, outChannels, true, "myfc");
+        auto fc2 = FC(outChannels, inChannels, true, "fc2");
+        // NOTE: Account for init step by adding 1 to the max timestep parameter.
+        auto lif1 = Leaky(nbTimeSteps+1, beta, threshold, "leaky");
+
+        // associateInput() does not work
+        fc1->input(1).first->getOperator()->setOutput(0, myWeights);
+        fc2->input(1).first->getOperator()->setOutput(0, myWeights2);
+
+        auto fc1Op = std::static_pointer_cast<OperatorTensor>(fc1->getOperator());
+        auto lif1Op = std::static_pointer_cast<MetaOperator_Op>(lif1->getOperator());
+        auto fc2Op = std::static_pointer_cast<OperatorTensor>(fc2->getOperator());
+
+        fc1Op->associateInput(0, myInput);
+        lif1Op->associateInput(1, init);
+        lif1Op->associateInput(2, init);
+
+        fc1->addChild(lif1, 0, 0);
+        lif1->addChild(fc2, 1, 0);
+
+
+        auto g = std::make_shared<GraphView>();
+        g->add({fc1, lif1, fc2});
+        g->compile("cpu", DataType::Float32);
+        auto scheduler = SequentialScheduler(g);
 
-        REQUIRE(true);
+        // Forward 1 (simulate timestep 0)
+        scheduler.forward(true);
+        REQUIRE(approxEq<float>(*(lif1Op->getOutput(0)), *(expectedOutputlif1ts1)));
+        REQUIRE(approxEq<float>(*(fc2Op->getOutput(0)), *(expectedOutputfc2ts1)));
+
+        // Forward 1 (simulate timestep 1)
+        scheduler.forward(true);
+        REQUIRE(approxEq<float>(*(lif1Op->getOutput(0)), *(expectedOutputlif1ts2)));
+        REQUIRE(approxEq<float>(*(fc2Op->getOutput(0)), *(expectedOutputfc2ts2)));
     }
 
     SECTION("Leaky(forward)") {
-- 
GitLab


From 623c55c6f42ab15208c711c130bbc0b86da30895 Mon Sep 17 00:00:00 2001
From: Jerome Hue <jerome.hue@cea.fr>
Date: Wed, 18 Dec 2024 14:35:43 +0100
Subject: [PATCH 09/12] chore: Remove debug prints

---
 unit_tests/operator/Test_MetaOperator.cpp | 17 ++---------------
 1 file changed, 2 insertions(+), 15 deletions(-)

diff --git a/unit_tests/operator/Test_MetaOperator.cpp b/unit_tests/operator/Test_MetaOperator.cpp
index cf663dbb..86e64c57 100644
--- a/unit_tests/operator/Test_MetaOperator.cpp
+++ b/unit_tests/operator/Test_MetaOperator.cpp
@@ -847,21 +847,8 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
         REQUIRE_NOTHROW(scheduler.generateScheduling());
         REQUIRE_NOTHROW(scheduler.forward(true));
 
-        // TODO: Naming is wrong
-        auto mem_op =
-            std::static_pointer_cast<OperatorTensor>(mem_rec->getOperator());
-        auto spk_op =
+        auto memOp =
             std::static_pointer_cast<OperatorTensor>(spk_rec->getOperator());
-        // Log::info("Output of stack node and spike : ");
-        // mem_op->getOutput(0)->print();
-        Log::info("----- Input -----");
-        T0->print();
-        Log::info("----- Results (expected) -----");
-        expectedOutput->print();
-        Log::info("----- Results -----");
-        spk_op->getOutput(0)->print();
-        REQUIRE(approxEq<float>(*(spk_op->getOutput(0)), *(expectedOutput)));
-        Log::info("Output of stack node and spike : ");
-        Log::info("Test Done.");
+        REQUIRE(approxEq<float>(*(memOp->getOutput(0)), *(expectedOutput)));
     }
 }
-- 
GitLab


From 948d024bcbb8b6af6cf355cd974219a6c5dc2972 Mon Sep 17 00:00:00 2001
From: Jerome Hue <jerome.hue@cea.fr>
Date: Fri, 31 Jan 2025 13:09:35 +0100
Subject: [PATCH 10/12] Format files

---
 include/aidge/backend/cpu.hpp             |   1 -
 unit_tests/operator/Test_MetaOperator.cpp | 166 ++++++++++++++--------
 2 files changed, 107 insertions(+), 60 deletions(-)

diff --git a/include/aidge/backend/cpu.hpp b/include/aidge/backend/cpu.hpp
index 17f3b832..5db19a2b 100644
--- a/include/aidge/backend/cpu.hpp
+++ b/include/aidge/backend/cpu.hpp
@@ -36,7 +36,6 @@
 #include "aidge/backend/cpu/operator/GlobalAveragePoolingImpl.hpp"
 #include "aidge/backend/cpu/operator/HeavisideImpl.hpp"
 #include "aidge/backend/cpu/operator/LRNImpl.hpp"
-#include "aidge/backend/cpu/operator/HeavisideImpl.hpp"
 #include "aidge/backend/cpu/operator/LeakyReLUImpl.hpp"
 #include "aidge/backend/cpu/operator/LnImpl.hpp"
 #include "aidge/backend/cpu/operator/MatMulImpl.hpp"
diff --git a/unit_tests/operator/Test_MetaOperator.cpp b/unit_tests/operator/Test_MetaOperator.cpp
index 86e64c57..d954add4 100644
--- a/unit_tests/operator/Test_MetaOperator.cpp
+++ b/unit_tests/operator/Test_MetaOperator.cpp
@@ -17,9 +17,10 @@
 #include <memory>
 #include <random>
 
-#include "aidge/backend/cpu/operator/ConvImpl.hpp" 
+#include "aidge/backend/cpu/operator/ConvImpl.hpp"
 #include "aidge/backend/cpu/operator/PadImpl.hpp"
 #include "aidge/data/Tensor.hpp"
+#include "aidge/filler/Filler.hpp"
 #include "aidge/operator/Conv.hpp"
 #include "aidge/operator/FC.hpp"
 #include "aidge/operator/Identity.hpp"
@@ -31,7 +32,6 @@
 #include "aidge/scheduler/ParallelScheduler.hpp"
 #include "aidge/scheduler/SequentialScheduler.hpp"
 #include "aidge/utils/TensorUtils.hpp"
-#include "aidge/filler/Filler.hpp"
 
 using namespace Aidge;
 
@@ -599,80 +599,125 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
     }
 
     SECTION("Leaky(forward)(fixed)") {
-        
+
         constexpr auto inChannels = 10;
         constexpr auto outChannels = 5;
 
         constexpr auto beta = 0.95;
-        constexpr auto threshold  = 1.0;
+        constexpr auto threshold = 1.0;
         constexpr auto nbTimeSteps = 2;
 
-
-        auto myWeights = std::make_shared<Tensor>(Array2D<float, outChannels, inChannels>{{
-            {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0},
-            {1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1},
-            {0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 0.1, 0.2, 0.3, 0.4},
-            {0.4, 0.3, 0.2, 0.1, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5},
-            {0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0},
-        }});
-
-        auto myWeights2 = std::make_shared<Tensor>(Array2D<float, inChannels, outChannels>{{
-            {0.1, 0.2, 0.3, 0.4, 0.5},
-            {0.6, 0.7, 0.8, 0.9, 1.0},
-            {1.0, 0.9, 0.8, 0.7, 0.6}, 
-            {0.5, 0.4, 0.3, 0.2, 0.1},
-            {0.5, 0.6, 0.7, 0.8, 0.9}, 
-            {1.0, 0.1, 0.2, 0.3, 0.4},
-            {0.4, 0.3, 0.2, 0.1, 0.0},
-            {0.1, 0.2, 0.3, 0.4, 0.5},
-            {0.9, 0.8, 0.7, 0.6, 0.5},
-            {0.4, 0.3, 0.2, 0.1, 0.0},
-        }});
-
-                auto myInput = std::make_shared<Tensor>(Array2D<float, 2, 10>{{
+        auto myWeights =
+            std::make_shared<Tensor>(Array2D<float, outChannels, inChannels>{{
+                {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0},
+                {1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1},
+                {0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 0.1, 0.2, 0.3, 0.4},
+                {0.4, 0.3, 0.2, 0.1, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5},
+                {0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0},
+            }});
+
+        auto myWeights2 =
+            std::make_shared<Tensor>(Array2D<float, inChannels, outChannels>{{
+                {0.1, 0.2, 0.3, 0.4, 0.5},
+                {0.6, 0.7, 0.8, 0.9, 1.0},
+                {1.0, 0.9, 0.8, 0.7, 0.6},
+                {0.5, 0.4, 0.3, 0.2, 0.1},
+                {0.5, 0.6, 0.7, 0.8, 0.9},
+                {1.0, 0.1, 0.2, 0.3, 0.4},
+                {0.4, 0.3, 0.2, 0.1, 0.0},
+                {0.1, 0.2, 0.3, 0.4, 0.5},
+                {0.9, 0.8, 0.7, 0.6, 0.5},
+                {0.4, 0.3, 0.2, 0.1, 0.0},
+            }});
+
+        auto myInput = std::make_shared<Tensor>(Array2D<float, 2, 10>{{
             {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0},
             {1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1},
         }});
 
         // py/snn Torch computed result, output of fc1 at time step 1
-        auto expectedOutputlif1ts1 = std::make_shared<Tensor>(Array2D<float,2,5>{{
-            {3.850, 2.2000, 2.6500, 1.5000, 1.6500},
-            {2.200, 3.8500, 3.4000, 1.2500, 3.3000},
-        }});
-
-        auto expectedOutputfc2ts1 = std::make_shared<Tensor>(Array2D<float,2,10>{{
-            {1.5000, 4.0000, 4.0000, 1.5000, 3.5000, 2.0000, 1.0000, 1.5000, 3.5000, 1.0000},
-            {1.5000, 4.0000, 4.0000, 1.5000, 3.5000, 2.0000, 1.0000, 1.5000, 3.5000, 1.0000},
-        }});
-
-        auto expectedOutputlif1ts2 = std::make_shared<Tensor>(Array2D<float,2,5>{{
-            {6.5075, 3.2900, 4.1675, 1.9250, 2.2175},
-            {3.2900, 6.5075, 5.6300, 1.4375, 5.4350},
-        }});
-
-        // NOTE: Same output as before, because for all channels, we have a potential higher than threshold.
-        // Thus the lif neuron fires at every timestep for every channel.
-        auto expectedOutputfc2ts2 = std::make_shared<Tensor>(Array2D<float,2,10>{{
-            {1.5000, 4.0000, 4.0000, 1.5000, 3.5000, 2.0000, 1.0000, 1.5000, 3.5000, 1.0000},
-            {1.5000, 4.0000, 4.0000, 1.5000, 3.5000, 2.0000, 1.0000, 1.5000, 3.5000, 1.0000},
-        }});
+        auto expectedOutputlif1ts1 =
+            std::make_shared<Tensor>(Array2D<float, 2, 5>{{
+                {3.850, 2.2000, 2.6500, 1.5000, 1.6500},
+                {2.200, 3.8500, 3.4000, 1.2500, 3.3000},
+            }});
+
+        auto expectedOutputfc2ts1 =
+            std::make_shared<Tensor>(Array2D<float, 2, 10>{{
+                {1.5000,
+                 4.0000,
+                 4.0000,
+                 1.5000,
+                 3.5000,
+                 2.0000,
+                 1.0000,
+                 1.5000,
+                 3.5000,
+                 1.0000},
+                {1.5000,
+                 4.0000,
+                 4.0000,
+                 1.5000,
+                 3.5000,
+                 2.0000,
+                 1.0000,
+                 1.5000,
+                 3.5000,
+                 1.0000},
+            }});
+
+        auto expectedOutputlif1ts2 =
+            std::make_shared<Tensor>(Array2D<float, 2, 5>{{
+                {6.5075, 3.2900, 4.1675, 1.9250, 2.2175},
+                {3.2900, 6.5075, 5.6300, 1.4375, 5.4350},
+            }});
+
+        // NOTE: Same output as before, because for all channels, we have a
+        // potential higher than threshold. Thus the lif neuron fires at every
+        // timestep for every channel.
+        auto expectedOutputfc2ts2 =
+            std::make_shared<Tensor>(Array2D<float, 2, 10>{{
+                {1.5000,
+                 4.0000,
+                 4.0000,
+                 1.5000,
+                 3.5000,
+                 2.0000,
+                 1.0000,
+                 1.5000,
+                 3.5000,
+                 1.0000},
+                {1.5000,
+                 4.0000,
+                 4.0000,
+                 1.5000,
+                 3.5000,
+                 2.0000,
+                 1.0000,
+                 1.5000,
+                 3.5000,
+                 1.0000},
+            }});
 
         auto init = std::make_shared<Tensor>(Array2D<float, 2, 5>{});
         uniformFiller<float>(init, 0.0, 0.0);
 
-
         auto fc1 = FC(inChannels, outChannels, true, "myfc");
         auto fc2 = FC(outChannels, inChannels, true, "fc2");
-        // NOTE: Account for init step by adding 1 to the max timestep parameter.
-        auto lif1 = Leaky(nbTimeSteps+1, beta, threshold, "leaky");
+        // NOTE: Account for init step by adding 1 to the max timestep
+        // parameter.
+        auto lif1 = Leaky(nbTimeSteps + 1, beta, threshold, "leaky");
 
         // associateInput() does not work
         fc1->input(1).first->getOperator()->setOutput(0, myWeights);
         fc2->input(1).first->getOperator()->setOutput(0, myWeights2);
 
-        auto fc1Op = std::static_pointer_cast<OperatorTensor>(fc1->getOperator());
-        auto lif1Op = std::static_pointer_cast<MetaOperator_Op>(lif1->getOperator());
-        auto fc2Op = std::static_pointer_cast<OperatorTensor>(fc2->getOperator());
+        auto fc1Op =
+            std::static_pointer_cast<OperatorTensor>(fc1->getOperator());
+        auto lif1Op =
+            std::static_pointer_cast<MetaOperator_Op>(lif1->getOperator());
+        auto fc2Op =
+            std::static_pointer_cast<OperatorTensor>(fc2->getOperator());
 
         fc1Op->associateInput(0, myInput);
         lif1Op->associateInput(1, init);
@@ -681,7 +726,6 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
         fc1->addChild(lif1, 0, 0);
         lif1->addChild(fc2, 1, 0);
 
-
         auto g = std::make_shared<GraphView>();
         g->add({fc1, lif1, fc2});
         g->compile("cpu", DataType::Float32);
@@ -689,13 +733,17 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
 
         // Forward 1 (simulate timestep 0)
         scheduler.forward(true);
-        REQUIRE(approxEq<float>(*(lif1Op->getOutput(0)), *(expectedOutputlif1ts1)));
-        REQUIRE(approxEq<float>(*(fc2Op->getOutput(0)), *(expectedOutputfc2ts1)));
+        REQUIRE(approxEq<float>(*(lif1Op->getOutput(0)),
+                                *(expectedOutputlif1ts1)));
+        REQUIRE(
+            approxEq<float>(*(fc2Op->getOutput(0)), *(expectedOutputfc2ts1)));
 
         // Forward 1 (simulate timestep 1)
         scheduler.forward(true);
-        REQUIRE(approxEq<float>(*(lif1Op->getOutput(0)), *(expectedOutputlif1ts2)));
-        REQUIRE(approxEq<float>(*(fc2Op->getOutput(0)), *(expectedOutputfc2ts2)));
+        REQUIRE(approxEq<float>(*(lif1Op->getOutput(0)),
+                                *(expectedOutputlif1ts2)));
+        REQUIRE(
+            approxEq<float>(*(fc2Op->getOutput(0)), *(expectedOutputfc2ts2)));
     }
 
     SECTION("Leaky(forward)") {
-- 
GitLab


From 4ca093a23785f30b7fa77f543c77a91ca49bd63c Mon Sep 17 00:00:00 2001
From: Jerome Hue <jerome.hue@cea.fr>
Date: Fri, 31 Jan 2025 13:41:15 +0100
Subject: [PATCH 11/12] Rearrange includes

---
 unit_tests/operator/Test_MetaOperator.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/unit_tests/operator/Test_MetaOperator.cpp b/unit_tests/operator/Test_MetaOperator.cpp
index d954add4..81f57b13 100644
--- a/unit_tests/operator/Test_MetaOperator.cpp
+++ b/unit_tests/operator/Test_MetaOperator.cpp
@@ -9,14 +9,13 @@
  *
  ********************************************************************************/
 
-#include <aidge/filler/Filler.hpp>
-#include <aidge/operator/FC.hpp>
-#include <catch2/catch_test_macros.hpp>
 #include <cmath>
 #include <cstdlib>
 #include <memory>
 #include <random>
 
+#include <catch2/catch_test_macros.hpp>
+
 #include "aidge/backend/cpu/operator/ConvImpl.hpp"
 #include "aidge/backend/cpu/operator/PadImpl.hpp"
 #include "aidge/data/Tensor.hpp"
-- 
GitLab


From b36e1bcd59d637805cce04a95c975aaed7e14448 Mon Sep 17 00:00:00 2001
From: Jerome Hue <jerome.hue@cea.fr>
Date: Fri, 31 Jan 2025 14:43:08 +0100
Subject: [PATCH 12/12] randomize beta in leaky  unit test

---
 unit_tests/operator/Test_MetaOperator.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/unit_tests/operator/Test_MetaOperator.cpp b/unit_tests/operator/Test_MetaOperator.cpp
index 81f57b13..23bacda5 100644
--- a/unit_tests/operator/Test_MetaOperator.cpp
+++ b/unit_tests/operator/Test_MetaOperator.cpp
@@ -757,6 +757,7 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
         std::uniform_int_distribution<std::size_t> nbDimsDist(std::size_t(3),
                                                               std::size_t(3));
         std::uniform_int_distribution<int> boolDist(0, 1);
+        std::uniform_real_distribution<float> betaDist(0,1);
 
         const std::size_t nbDims = nbDimsDist(gen);
         Log::info("Nbdims : {}", nbDims);
@@ -771,8 +772,9 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
         }
 
         const auto nbTimeSteps = dims[0];
+        const auto beta = betaDist(gen); 
 
-        auto myLeaky = Leaky(nbTimeSteps, 0.9, 1.0, "leaky");
+        auto myLeaky = Leaky(nbTimeSteps, beta, 1.0, "leaky");
         auto op =
             std::static_pointer_cast<MetaOperator_Op>(myLeaky->getOperator());
         // auto stack = Stack(2);
@@ -856,7 +858,7 @@ TEST_CASE("[cpu/operator] MetaOperator", "[MetaOperator][CPU]") {
             for (int j = 0; j < nbElementsPerTimeStep; ++j) {
                 auto reset = (result[prev + j] > 1.0 ? 1 : 0);
                 result[offset + j] =
-                    result[prev + j] * 0.9 + input[offset + j] - reset;
+                    result[prev + j] * beta + input[offset + j] - reset;
             }
         }
 
-- 
GitLab