From f1d564040ab11e2cc0cb44ac23315d3b36c9625c Mon Sep 17 00:00:00 2001
From: Gallas Gaye <gallasko@gmail.com>
Date: Fri, 21 Feb 2025 10:22:47 +0100
Subject: [PATCH 1/4] feat: Add reshape export op

---
 aidge_export_cpp/kernels/reshape.hpp          | 27 +++++++++++++++++++
 aidge_export_cpp/operators.py                 | 13 +++++++++
 .../configuration/reshape_config.jinja        |  8 ++++++
 .../kernel_forward/reshape_forward.jinja      |  6 +++++
 4 files changed, 54 insertions(+)
 create mode 100644 aidge_export_cpp/kernels/reshape.hpp
 create mode 100644 aidge_export_cpp/templates/configuration/reshape_config.jinja
 create mode 100644 aidge_export_cpp/templates/kernel_forward/reshape_forward.jinja

diff --git a/aidge_export_cpp/kernels/reshape.hpp b/aidge_export_cpp/kernels/reshape.hpp
new file mode 100644
index 0000000..a5828da
--- /dev/null
+++ b/aidge_export_cpp/kernels/reshape.hpp
@@ -0,0 +1,27 @@
+#ifndef __AIDGE_EXPORT_CPP_KERNELS_RESHAPE__
+#define __AIDGE_EXPORT_CPP_KERNELS_RESHAPE__
+
+#include "network/typedefs.hpp"
+
+// Generic function for reshape and activation
+
+template<int M,
+         typename Input_T, typename Output_T>
+__attribute__((always_inline)) inline
+void reshape_forward (
+    const Input_T* __restrict, // First input is useless as it only dictate the resulting layout of the reshape
+    const Input_T* __restrict inputs2,
+    Output_T* __restrict outputs)
+{
+    // If inputs and outputs pointers are the same, the memory manager has already optimized this function so it is a no-op !
+    if (inputs2 == outputs)
+        return;
+
+    // A reshape in c++ world should equal to a Noop
+    // We only need to copy the input buffer to the output
+    for (int m = 0; m < M; ++m) {
+        outputs[m] = inputs2[m];
+    }
+}
+
+#endif  // __AIDGE_EXPORT_CPP_KERNELS_RESHAPE__
\ No newline at end of file
diff --git a/aidge_export_cpp/operators.py b/aidge_export_cpp/operators.py
index 54c3805..59ce94a 100644
--- a/aidge_export_cpp/operators.py
+++ b/aidge_export_cpp/operators.py
@@ -94,6 +94,19 @@ class ReLUCPP(ExportNodeCpp):
             str(ROOT / "kernels" / "rescaling.hpp")
         ]
 
+@ExportLibCpp.register("Reshape", aidge_core.ImplSpec(aidge_core.IOSpec(aidge_core.dtype.float32)))
+class ReshapeCPP(ExportNodeCpp):
+    def __init__(self, node, mem_info):
+        super().__init__(node, mem_info)
+        self.config_template = str(
+            ROOT / "templates" / "configuration" / "reshape_config.jinja")
+        self.forward_template = str(
+            ROOT / "templates" / "kernel_forward" / "reshape_forward.jinja")
+        self.include_list = []
+        self.kernels_to_copy = [
+            str(ROOT / "kernels" / "reshape.hpp"),
+        ]
+
 @ExportLibCpp.register("Conv2D", aidge_core.ImplSpec(aidge_core.IOSpec(aidge_core.dtype.float32)))
 class ConvCPP(ExportNodeCpp):
     def __init__(self, node, mem_info):
diff --git a/aidge_export_cpp/templates/configuration/reshape_config.jinja b/aidge_export_cpp/templates/configuration/reshape_config.jinja
new file mode 100644
index 0000000..041cf8a
--- /dev/null
+++ b/aidge_export_cpp/templates/configuration/reshape_config.jinja
@@ -0,0 +1,8 @@
+{#- For name header -#}
+#ifndef {{ name|upper }}_LAYER_H
+#define {{ name|upper }}_LAYER_H
+
+{% include "./_def_io.jinja" %}
+{% include "./_meminfo.jinja" %}
+{# For layer configuration -#}
+#define {{ name|upper }}_NB_ELTS {{ in_dims[0]|join('*') }}
diff --git a/aidge_export_cpp/templates/kernel_forward/reshape_forward.jinja b/aidge_export_cpp/templates/kernel_forward/reshape_forward.jinja
new file mode 100644
index 0000000..f9752bc
--- /dev/null
+++ b/aidge_export_cpp/templates/kernel_forward/reshape_forward.jinja
@@ -0,0 +1,6 @@
+{% filter indent(width=4, first=False) %}
+{% include "./_mem_offset.jinja" %}
+reshape_forward<{{name|upper}}_NB_ELTS>
+                 ({{in_name[0]}}, {{in_name[1]}}, {{out_name[0]}});
+{% include "./_save_outputs.jinja" %}
+{% endfilter %}
-- 
GitLab


From 534f5b3381aa7c1f9b088af1d28e97923de492cf Mon Sep 17 00:00:00 2001
From: Gallas Gaye <gallasko@gmail.com>
Date: Fri, 21 Feb 2025 10:38:36 +0100
Subject: [PATCH 2/4] fix: Add Matmul export operator back

Matmul op was removed and obsolote.
Fixed wrong hyperparameters names in jinja
Fixed impl in operator.py
---
 aidge_export_cpp/operators.py                     | 15 +++++++++++++++
 .../templates/configuration/matmul_config.jinja   |  9 ++++++---
 .../templates/kernel_forward/matmul_forward.jinja |  6 +++++-
 3 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/aidge_export_cpp/operators.py b/aidge_export_cpp/operators.py
index 59ce94a..0790877 100644
--- a/aidge_export_cpp/operators.py
+++ b/aidge_export_cpp/operators.py
@@ -107,6 +107,21 @@ class ReshapeCPP(ExportNodeCpp):
             str(ROOT / "kernels" / "reshape.hpp"),
         ]
 
+@ExportLibCpp.register("MatMul", aidge_core.ImplSpec(aidge_core.IOSpec(aidge_core.dtype.float32)))
+class MatMulCPP(ExportNodeCpp):
+    def __init__(self, node, mem_info):
+        super().__init__(node, mem_info)
+        self.attributes["activation"] = "Linear"
+        self.attributes["rescaling"] = "NoScaling"
+        self.config_template = str(
+            ROOT / "templates" / "configuration" / "matmul_config.jinja")
+        self.forward_template = str(
+            ROOT / "templates" / "kernel_forward" / "matmul_forward.jinja")
+        self.include_list = []
+        self.kernels_to_copy = [
+            str(ROOT / "kernels" / "matmul.hpp"),
+        ]
+
 @ExportLibCpp.register("Conv2D", aidge_core.ImplSpec(aidge_core.IOSpec(aidge_core.dtype.float32)))
 class ConvCPP(ExportNodeCpp):
     def __init__(self, node, mem_info):
diff --git a/aidge_export_cpp/templates/configuration/matmul_config.jinja b/aidge_export_cpp/templates/configuration/matmul_config.jinja
index fece988..38316f2 100644
--- a/aidge_export_cpp/templates/configuration/matmul_config.jinja
+++ b/aidge_export_cpp/templates/configuration/matmul_config.jinja
@@ -2,10 +2,13 @@
 #ifndef {{ name|upper }}_LAYER_H
 #define {{ name|upper }}_LAYER_H
 
+{% include "./_def_io.jinja" %}
+{% include "./_meminfo.jinja" %}
+
 {# For layer configuration -#}
-#define {{ name|upper }}_M {{ inputs_dims[0][0] }}
-#define {{ name|upper }}_K {{ inputs_dims[0][1] }}
-#define {{ name|upper }}_N {{ inputs_dims[1][1] }}
+#define {{ name|upper }}_M {{ in_dims[0][0] }}
+#define {{ name|upper }}_K {{ in_dims[0][1] }}
+#define {{ name|upper }}_N {{ in_dims[1][1] }}
 #define {{ name|upper }}_ACTIVATION {{ activation }}
 static const {{ rescaling }} {{ name|upper }}_RESCALING = {};
 
diff --git a/aidge_export_cpp/templates/kernel_forward/matmul_forward.jinja b/aidge_export_cpp/templates/kernel_forward/matmul_forward.jinja
index ce80ffd..64b3df3 100644
--- a/aidge_export_cpp/templates/kernel_forward/matmul_forward.jinja
+++ b/aidge_export_cpp/templates/kernel_forward/matmul_forward.jinja
@@ -1,5 +1,9 @@
+{% filter indent(width=4, first=False) %}
+{% include "./_mem_offset.jinja" %}
 matmul_forward<{{name|upper}}_M,
                {{name|upper}}_K,
                {{name|upper}}_N,
                {{name|upper}}_ACTIVATION>
-               ({{inputs1_name}}, {{inputs2_name}}, {{outputs_name}}, {{name|upper}}_RESCALING);
\ No newline at end of file
+               ({{in_name[0]}}, {{in_name[1]}}, {{out_name[0]}}, {{name|upper}}_RESCALING);
+{% include "./_save_outputs.jinja" %}
+{% endfilter %}
-- 
GitLab


From b957fede729a835fc0fbeff9e5e797580afe7a58 Mon Sep 17 00:00:00 2001
From: Gallas Gaye <gallasko@gmail.com>
Date: Fri, 21 Feb 2025 10:43:48 +0100
Subject: [PATCH 3/4] chore: Refactor duplicate code in operators

---
 aidge_export_cpp/operators.py | 161 ++++++++++++++--------------------
 1 file changed, 64 insertions(+), 97 deletions(-)

diff --git a/aidge_export_cpp/operators.py b/aidge_export_cpp/operators.py
index 0790877..346928f 100644
--- a/aidge_export_cpp/operators.py
+++ b/aidge_export_cpp/operators.py
@@ -122,6 +122,27 @@ class MatMulCPP(ExportNodeCpp):
             str(ROOT / "kernels" / "matmul.hpp"),
         ]
 
+def _setup_conv2D(conv):
+    """Common setup code for convolutions: Conv2D and PaddedConv2D."""
+
+    # If biases are not provided we set it as nullptr instead of None
+    if (len(conv.attributes["in_name"]) > 2 and conv.attributes["in_name"][2] is None):
+        conv.attributes["in_name"][2] = "nullptr"
+
+    conv.attributes["activation"] = "Linear"
+    conv.attributes["rescaling"] = "NoScaling"
+    conv.config_template = str(
+        ROOT / "templates" / "configuration" / "convolution_config.jinja")
+    conv.forward_template = str(
+        ROOT / "templates" / "kernel_forward" / "convolution_forward.jinja")
+    conv.include_list = []
+    conv.kernels_to_copy = [
+        str(ROOT / "kernels" / "convolution.hpp"),
+        str(ROOT / "kernels" / "macs.hpp"),
+        str(ROOT / "kernels" / "activation.hpp"),
+        str(ROOT / "kernels" / "rescaling.hpp")
+    ]
+
 @ExportLibCpp.register("Conv2D", aidge_core.ImplSpec(aidge_core.IOSpec(aidge_core.dtype.float32)))
 class ConvCPP(ExportNodeCpp):
     def __init__(self, node, mem_info):
@@ -129,19 +150,8 @@ class ConvCPP(ExportNodeCpp):
         # No padding with Conv
         # Use PaddedConv to add padding attribute
         self.attributes["padding"] = [0, 0]
-        self.attributes["activation"] = "Linear"
-        self.attributes["rescaling"] = "NoScaling"
-        self.config_template = str(
-            ROOT / "templates" / "configuration" / "convolution_config.jinja")
-        self.forward_template = str(
-            ROOT / "templates" / "kernel_forward" / "convolution_forward.jinja")
-        self.include_list = []
-        self.kernels_to_copy = [
-            str(ROOT / "kernels" / "convolution.hpp"),
-            str(ROOT / "kernels" / "macs.hpp"),
-            str(ROOT / "kernels" / "activation.hpp"),
-            str(ROOT / "kernels" / "rescaling.hpp")
-        ]
+
+        _setup_conv2D(self)
 
 @ExportLibCpp.register_metaop("PaddedConv2D", aidge_core.ImplSpec(aidge_core.IOSpec(aidge_core.dtype.float32)))
 class PaddedConvCPP(ExportNodeCpp):
@@ -159,74 +169,60 @@ class PaddedConvCPP(ExportNodeCpp):
                 ).attr.stride_dims
                 self.attributes["dilation_dims"] = n.get_operator(
                 ).attr.dilation_dims
-        self.attributes["activation"] = "Linear"
-        self.attributes["rescaling"] = "NoScaling"
-        self.config_template = str(
-            ROOT / "templates" / "configuration" / "convolution_config.jinja")
-        self.forward_template = str(
-            ROOT / "templates" / "kernel_forward" / "convolution_forward.jinja")
-        self.include_list = []
-        self.kernels_to_copy = [
-            str(ROOT / "kernels" / "convolution.hpp"),
-            str(ROOT / "kernels" / "macs.hpp"),
-            str(ROOT / "kernels" / "activation.hpp"),
-            str(ROOT / "kernels" / "rescaling.hpp")
-        ]
+
+        _setup_conv2D(self)
+
+def _setup_elemwise_op(elemwise, op):
+    """Common code (template and kernel setup) shared across all the different elementWise operator (Add, Sub,...)."""
+
+    elemwise.attributes["elemwise_op"] = op
+    elemwise.attributes["activation"] = "Linear"
+    elemwise.attributes["rescaling"] = "NoScaling"
+    elemwise.config_template = str(
+        ROOT / "templates" / "configuration" / "elemwise_config.jinja")
+    elemwise.forward_template = str(
+        ROOT / "templates" / "kernel_forward" / "elemwise_forward.jinja")
+    elemwise.include_list = []
+    elemwise.kernels_to_copy = [
+        str(ROOT / "kernels" / "elemwise.hpp"),
+        str(ROOT / "kernels" / "activation.hpp"),
+        str(ROOT / "kernels" / "rescaling.hpp")
+    ]
 
 @ExportLibCpp.register("Add", aidge_core.ImplSpec(aidge_core.IOSpec(aidge_core.dtype.float32)))
 class AddCPP(ExportNodeCpp):
     def __init__(self, node, mem_info):
         super().__init__(node, mem_info)
-        self.attributes["elemwise_op"] = "Add"
-        self.attributes["activation"] = "Linear"
-        self.attributes["rescaling"] = "NoScaling"
-        self.config_template = str(
-            ROOT / "templates" / "configuration" / "elemwise_config.jinja")
-        self.forward_template = str(
-            ROOT / "templates" / "kernel_forward" / "elemwise_forward.jinja")
-        self.include_list = []
-        self.kernels_to_copy = [
-            str(ROOT / "kernels" / "elemwise.hpp"),
-            str(ROOT / "kernels" / "activation.hpp"),
-            str(ROOT / "kernels" / "rescaling.hpp")
-        ]
+
+        _setup_elemwise_op(self, "Add")
 
 @ExportLibCpp.register("Sub", aidge_core.ImplSpec(aidge_core.IOSpec(aidge_core.dtype.float32)))
 class SubCPP(ExportNodeCpp):
     def __init__(self, node, mem_info):
         super().__init__(node, mem_info)
-        self.attributes["elemwise_op"] = "Sub"
-        self.attributes["activation"] = "Linear"
-        self.attributes["rescaling"] = "NoScaling"
-        self.config_template = str(
-            ROOT / "templates" / "configuration" / "elemwise_config.jinja")
-        self.forward_template = str(
-            ROOT / "templates" / "kernel_forward" / "elemwise_forward.jinja")
-        self.include_list = []
-        self.kernels_to_copy = [
-            str(ROOT / "kernels" / "elemwise.hpp"),
-            str(ROOT / "kernels" / "activation.hpp"),
-            str(ROOT / "kernels" / "rescaling.hpp")
-        ]
 
+        _setup_elemwise_op(self, "Sub")
 
 @ExportLibCpp.register("Mul", aidge_core.ImplSpec(aidge_core.IOSpec(aidge_core.dtype.float32)))
 class MulCPP(ExportNodeCpp):
     def __init__(self, node, mem_info):
         super().__init__(node, mem_info)
-        self.attributes["elemwise_op"] = "Mul"
-        self.attributes["activation"] = "Linear"
-        self.attributes["rescaling"] = "NoScaling"
-        self.config_template = str(
-            ROOT / "templates" / "configuration" / "elemwise_config.jinja")
-        self.forward_template = str(
-            ROOT / "templates" / "kernel_forward" / "elemwise_forward.jinja")
-        self.include_list = []
-        self.kernels_to_copy = [
-            str(ROOT / "kernels" / "elemwise.hpp"),
-            str(ROOT / "kernels" / "activation.hpp"),
-            str(ROOT / "kernels" / "rescaling.hpp")
-        ]
+
+        _setup_elemwise_op(self, "Mul")
+
+def _setup_pooling(pooling):
+    """Common code (template and kernel setup) shared across all the different pooling operator."""
+
+    pooling.config_template = str(
+        ROOT / "templates" / "configuration" / "pooling_config.jinja")
+    pooling.forward_template = str(
+        ROOT / "templates" / "kernel_forward" / "pooling_forward.jinja")
+    pooling.include_list = []
+    pooling.kernels_to_copy = [
+        str(ROOT / "kernels" / "pooling.hpp"),
+        str(ROOT / "kernels" / "activation.hpp"),
+        str(ROOT / "kernels" / "rescaling.hpp")
+    ]
 
 @ExportLibCpp.register("MaxPooling2D", aidge_core.ImplSpec(aidge_core.IOSpec(aidge_core.dtype.float32)))
 class MaxPoolCPP(ExportNodeCpp):
@@ -239,17 +235,7 @@ class MaxPoolCPP(ExportNodeCpp):
         self.attributes["pool_type"] = "Max"
         self.attributes["activation"] = "Linear"
 
-        self.config_template = str(
-            ROOT / "templates" / "configuration" / "pooling_config.jinja")
-        self.forward_template = str(
-            ROOT / "templates" / "kernel_forward" / "pooling_forward.jinja")
-        self.include_list = []
-        self.kernels_to_copy = [
-            str(ROOT / "kernels" / "pooling.hpp"),
-            str(ROOT / "kernels" / "activation.hpp"),
-            str(ROOT / "kernels" / "rescaling.hpp")
-        ]
-
+        _setup_pooling(self)
 
 @ExportLibCpp.register_metaop("PaddedMaxPooling2D", aidge_core.ImplSpec(aidge_core.IOSpec(aidge_core.dtype.float32)))
 class PaddedMaxPoolCPP(ExportNodeCpp):
@@ -267,17 +253,7 @@ class PaddedMaxPoolCPP(ExportNodeCpp):
         self.attributes["pool_type"] = "Max"
         self.attributes["activation"] = "Linear"
 
-        self.config_template = str(
-            ROOT / "templates" / "configuration" / "pooling_config.jinja")
-        self.forward_template = str(
-            ROOT / "templates" / "kernel_forward" / "pooling_forward.jinja")
-        self.include_list = []
-        self.kernels_to_copy = [
-            str(ROOT / "kernels" / "pooling.hpp"),
-            str(ROOT / "kernels" / "activation.hpp"),
-            str(ROOT / "kernels" / "rescaling.hpp")
-        ]
-
+        _setup_pooling(self)
 
 @ExportLibCpp.register("GlobalAveragePooling", aidge_core.ImplSpec(aidge_core.IOSpec(aidge_core.dtype.float32)))
 class GlobalAveragePoolCPP(ExportNodeCpp):
@@ -295,16 +271,7 @@ class GlobalAveragePoolCPP(ExportNodeCpp):
         self.attributes["pool_type"] = "Average"
         self.attributes["activation"] = "Linear"
 
-        self.config_template = str(
-            ROOT / "templates" / "configuration" / "pooling_config.jinja")
-        self.forward_template = str(
-            ROOT / "templates" / "kernel_forward" / "pooling_forward.jinja")
-        self.include_list = []
-        self.kernels_to_copy = [
-            str(ROOT / "kernels" / "pooling.hpp"),
-            str(ROOT / "kernels" / "activation.hpp"),
-            str(ROOT / "kernels" / "rescaling.hpp")
-        ]
+        _setup_pooling(self)
 
 @ExportLibCpp.register("FC", aidge_core.ImplSpec(aidge_core.IOSpec(aidge_core.dtype.float32)))
 class FcCPP(ExportNodeCpp):
-- 
GitLab


From e98d3eeee881e880f0f33304c6e2ddebc36dd0fa Mon Sep 17 00:00:00 2001
From: Gallas Gaye <gallasko@gmail.com>
Date: Fri, 21 Feb 2025 10:46:39 +0100
Subject: [PATCH 4/4] fix: conv2D export failure when no biases given

---
 aidge_export_cpp/kernels/convolution.hpp | 45 ++++++++++++++++++++++--
 1 file changed, 43 insertions(+), 2 deletions(-)

diff --git a/aidge_export_cpp/kernels/convolution.hpp b/aidge_export_cpp/kernels/convolution.hpp
index efc7ee7..6ea9f05 100644
--- a/aidge_export_cpp/kernels/convolution.hpp
+++ b/aidge_export_cpp/kernels/convolution.hpp
@@ -65,8 +65,8 @@ void convolution_forward(
                 int oOffset = NB_OUTPUTS * oPos;
 
                 // <--
-
-                Bias_T weightedSum = biases[output];
+                // Check if the biases are defined
+                Bias_T weightedSum = biases ? biases[output] : 0;
 
                 for (int sy = 0; sy < KERNEL_HEIGHT; ++sy) {
                     if ((PADDING_Y != 0
@@ -116,4 +116,45 @@ void convolution_forward(
     }
 }
 
+// Template specialization when biases are not given to the convolution
+template<int NB_CHANNELS,
+         int CHANNELS_HEIGHT, int CHANNELS_WIDTH,
+         int NB_OUTPUTS,
+         int OUTPUTS_HEIGHT, int OUTPUTS_WIDTH,
+         int PADDING_Y, int PADDING_X,
+         int STRIDE_Y, int STRIDE_X,
+         int DILATION_Y, int DILATION_X,
+         int KERNEL_HEIGHT, int KERNEL_WIDTH,
+         ActivationFunction_T ACTIVATION,
+         typename Input_T, typename Output_T,
+         typename Weight_T,
+         typename Rescaling_T>
+__attribute__((always_inline)) inline
+void convolution_forward(
+    const Input_T* __restrict inputs,
+    Output_T* __restrict outputs,
+    const Weight_T* __restrict weights,
+    std::nullptr_t __restrict,
+    const Rescaling_T& __restrict rescaling)
+{
+    const float* b = nullptr;
+
+    convolution_forward<NB_CHANNELS,
+                        CHANNELS_HEIGHT,
+                        CHANNELS_WIDTH,
+                        NB_OUTPUTS,
+                        OUTPUTS_HEIGHT,
+                        OUTPUTS_WIDTH,
+                        PADDING_Y,
+                        PADDING_X,
+                        STRIDE_Y,
+                        STRIDE_X,
+                        DILATION_Y,
+                        DILATION_X,
+                        KERNEL_HEIGHT,
+                        KERNEL_WIDTH,
+                        ACTIVATION>
+                        (inputs, outputs, weights, b, rescaling);
+}
+
 #endif  // __AIDGE_EXPORT_CPP_KERNELS_CONVOLUTION__
-- 
GitLab