diff --git a/aidge_export_cpp/kernels/pad.hpp b/aidge_export_cpp/kernels/pad.hpp index dc7629fc571ac722ed8d13937b454e96fcd59e21..158c9359c934f2735c12e47cbd186a4f30715554 100644 --- a/aidge_export_cpp/kernels/pad.hpp +++ b/aidge_export_cpp/kernels/pad.hpp @@ -2,10 +2,7 @@ #define __AIDGE_EXPORT_CPP_KERNELS_PAD2D__ #include "network/typedefs.hpp" -#include "kernels/rescaling.hpp" #include "network/utils.hpp" -#include "kernels/macs.hpp" -#include "kernels/activation.hpp" // Todo add border value and border type (Reflect, Constant, Wrap...) and add the two missing pad value (bottom and right) @@ -21,20 +18,20 @@ void convolution_forward( Output_T* __restrict outputs ) { - const I *input = static_cast<const I *>(input_); - O *output = static_cast<O *>(output_); + const unsigned int oySize = CHANNELS_HEIGHT + PADDING_Y + PADDING_Y; + const unsigned int oxSize = CHANNELS_WIDTH + PADDING_X + PADDING_X; - const std::size_t oySize = CHANNELS_HEIGHT + PADDING_Y + PADDING_Y; - const std::size_t oxSize = CHANNELS_WIDTH + PADDING_X + PADDING_X; - - for (std::uint32_t oy = 0; oy < oySize; ++oy) { - for (std::uint32_t ox = 0; ox < oxSize; ++ox) { + for (unsigned int oy = 0; oy < oySize; ++oy) { + for (unsigned int ox = 0; ox < oxSize; ++ox) { if (oy < PADDING_Y or oy >= CHANNELS_HEIGHT + PADDING_Y or ox < PADDING_X or ox >= CHANNELS_WIDTH + PADDING_X) { outputs[oy * oySize + ox] = 0.0f; } + else + { + outputs[oy * oySize + ox] = inputs[(oy - PADDING_Y) * CHANNELS_HEIGHT + (ox - PADDING_X)]; + } - outputs[oy * oySize + ox] = input[(oy - PADDING_Y) * CHANNELS_HEIGHT + (ox - PADDING_X)]; } } } diff --git a/aidge_export_cpp/kernels/pooling.hpp b/aidge_export_cpp/kernels/pooling.hpp index 478b6a58aed45e2bce0ed1683ad113f9c7a8bffb..667e1a98cc3542cf598d223d59a767dccad6c651 100644 --- a/aidge_export_cpp/kernels/pooling.hpp +++ b/aidge_export_cpp/kernels/pooling.hpp @@ -7,7 +7,7 @@ #include <stdexcept> -template<int NB_CHANNELS, +template<int NB_CHANNELS, int CHANNELS_HEIGHT, int CHANNELS_WIDTH, int NB_OUTPUTS, int OUTPUTS_HEIGHT, int OUTPUTS_WIDTH, @@ -17,7 +17,7 @@ template<int NB_CHANNELS, Pooling_T POOLING_TYPE, ActivationFunction_T ACTIVATION, typename Input_T, typename Output_T> -__attribute__((always_inline)) inline +__attribute__((always_inline)) inline void pooling_forward( const Input_T* __restrict inputs, Output_T* __restrict outputs) @@ -32,7 +32,7 @@ void pooling_forward( : max(PADDING_Y - (oy * STRIDE_Y), 0); const int syMax = (PADDING_Y == 0 && OUTPUTS_HEIGHT == OUTPUTS_HEIGHT_NOPAD) ? POOL_HEIGHT - : clamp(CHANNELS_HEIGHT + PADDING_Y - (oy * STRIDE_Y), + : clamp(CHANNELS_HEIGHT + PADDING_Y - (oy * STRIDE_Y), 0, POOL_HEIGHT); const int iy = (oy * STRIDE_Y) - PADDING_Y; @@ -45,7 +45,7 @@ void pooling_forward( const int sxMax = (PADDING_X == 0 && OUTPUTS_WIDTH == OUTPUTS_WIDTH_NOPAD) ? POOL_WIDTH - : clamp(CHANNELS_WIDTH + PADDING_X - (ox * STRIDE_X), + : clamp(CHANNELS_WIDTH + PADDING_X - (ox * STRIDE_X), 0, POOL_WIDTH); const int ix = (ox * STRIDE_X) - PADDING_X; diff --git a/aidge_export_cpp/operators.py b/aidge_export_cpp/operators.py index 0e3749a3592cce219f9ce8fe6888b42fea587ed1..c89236bf7b452c370d6dc6577b61a9e0b7aa9cdc 100644 --- a/aidge_export_cpp/operators.py +++ b/aidge_export_cpp/operators.py @@ -353,14 +353,10 @@ class BatchNorm2DCPP(ExportNodeCpp): class Concat(ExportNodeCpp): def __init__(self, node, mem_info): super().__init__(node, mem_info) - - print(node.get_operator()) - print(dir(node.get_operator())) - self.attributes["nb_in"] = node.get_operator().nb_inputs() self.attributes["axis"] = node.get_operator().attr.axis - self.config_template = str(ROOT / "templates" / "configuration" / "concat.jinja") - self.forward_template = str(ROOT / "templates" / "forward_call" / "concat.jinja") + self.config_template = str(ROOT / "templates" / "configuration" / "concat_config.jinja") + self.forward_template = str(ROOT / "templates" / "kernel_forward" / "concat_forward.jinja") self.include_list = [] self.kernels_to_copy = [ str(ROOT / "kernels" / "concat.hpp"), diff --git a/aidge_export_cpp/templates/configuration/batchnorm_config.jinja b/aidge_export_cpp/templates/configuration/batchnorm_config.jinja index bc01e3b964d3741548bb3249d88aa9034ecaa343..ae7ef5760a63689d11f6d7369e387b55b7cb3d15 100644 --- a/aidge_export_cpp/templates/configuration/batchnorm_config.jinja +++ b/aidge_export_cpp/templates/configuration/batchnorm_config.jinja @@ -1,6 +1,7 @@ {#- For name header -#} #ifndef {{ name|upper }}_LAYER_H #define {{ name|upper }}_LAYER_H +#include "kernels/rescaling.hpp" {# For layer configuration -#} {% include "./_def_io.jinja" %} diff --git a/aidge_export_cpp/templates/configuration/concat.jinja b/aidge_export_cpp/templates/configuration/concat_config.jinja similarity index 63% rename from aidge_export_cpp/templates/configuration/concat.jinja rename to aidge_export_cpp/templates/configuration/concat_config.jinja index 8aa63156a2d890bbfb6f0c7ddce700917ccef83b..1a6637e9094c12fc04d47da4bddcee160f3c7a56 100644 --- a/aidge_export_cpp/templates/configuration/concat.jinja +++ b/aidge_export_cpp/templates/configuration/concat_config.jinja @@ -2,14 +2,15 @@ #ifndef {{ name|upper }}_LAYER_H #define {{ name|upper }}_LAYER_H +{% include "./_def_io.jinja" %} {% include "./_meminfo.jinja" %} // Attributes #define {{ name|upper }}_NB_INPUTS {{ nb_in }} #define {{ name|upper }}_AXIS {{ axis }} {%- for i in range(nb_in) %} -#define {{ name|upper }}_INPUT_{{i}}_SIZE {{ in_chan[i] * in_height[i] * in_width[i] }} +#define {{ name|upper }}_INPUT_{{i}}_SIZE {{ in_dims[i]|join('*') }} {%- endfor %} -#define {{ name|upper }}_OUTPUT_SIZE {{ out_chan[0] * out_height[0] * out_width[0] }} +#define {{ name|upper }}_OUTPUT_SIZE {{ out_dims[0]|join('*')}} #endif /* {{ name|upper }}_LAYER_H */ diff --git a/aidge_export_cpp/templates/kernel_forward/concat.jinja b/aidge_export_cpp/templates/kernel_forward/concat_forward.jinja similarity index 86% rename from aidge_export_cpp/templates/kernel_forward/concat.jinja rename to aidge_export_cpp/templates/kernel_forward/concat_forward.jinja index 46fe87e43c51c672b3d74bfbaabbb21aaac12ee7..a2f48e9b5aa563924a346eabfaec67b0c30ef38f 100644 --- a/aidge_export_cpp/templates/kernel_forward/concat.jinja +++ b/aidge_export_cpp/templates/kernel_forward/concat_forward.jinja @@ -1,6 +1,6 @@ {% filter indent(width=4, first=False) %} {% include "./_mem_offset.jinja" %} -float* {{ name|upper }}_INPUTS[] = { +const float* {{ name|upper }}_INPUTS[] = { {%- for i in range(nb_in) -%} {{ in_name[i] }}{{ ", " if not loop.last else "" }} {%- endfor -%} @@ -12,7 +12,7 @@ unsigned int {{ name|upper }}_SIZES[] = { {%- endfor -%} }; -aidge_concat<float, {{ nb_in }}> ( +concat_forward<float, {{ nb_in }}> ( {{name|upper}}_AXIS, {{ name|upper }}_INPUTS, {{ name|upper }}_SIZES, diff --git a/aidge_export_cpp/unit_tests/test_export.py b/aidge_export_cpp/unit_tests/test_export.py index 20421adbe3ab90f1eba1a4df03ab141c21dc0c2d..273c9b4b0481771a2f90a8a6818787e37d28efd4 100644 --- a/aidge_export_cpp/unit_tests/test_export.py +++ b/aidge_export_cpp/unit_tests/test_export.py @@ -3,6 +3,8 @@ import aidge_core import aidge_backend_cpu import aidge_export_cpp import numpy as np +import operator +from functools import reduce import subprocess import re @@ -31,19 +33,8 @@ def initFiller(model): aidge_core.constant_filler(value, 0.01) else: pass -import math - -def normalize_random_tensor(randList): - for index in np.ndindex(randList.shape): - randList[index] = (math.floor(randList[index] * 21) - 10) / 10 - - return aidge_core.Tensor(randList.astype(np.float32)) - -import numpy as np -import operator -from functools import reduce -def np_init(shape, dtype=np.float32): +def _np_init(shape, dtype=np.float32): """ Generates a NumPy array with the given shape, filled with random values between -1 and 1 with a step of 0.1. @@ -56,71 +47,18 @@ def np_init(shape, dtype=np.float32): data = (np.random.randint(0, 21, size=total_elements) - 10) / 10.0 return data.reshape(shape).astype(dtype) -def unit_test_export(graph_view, op_name, in_dims): - # Initialize parameters (weights and biases) - - graph_view.compile("cpu", aidge_core.dtype.float32, dims=in_dims) - - for node in graph_view.get_nodes(): - if node.type() == "Producer": - prod_op = node.get_operator() - value = prod_op.get_output(0) - - # rand_tensor = aidge_core.Tensor(np_init(value.dims())) - # rand_tensor.set_backend(value.backend()) - # value = rand_tensor - - print(value) - - aidge_core.constant_filler(value, 0.01) - - - scheduler = aidge_core.SequentialScheduler(graph_view) - - in_tensor = [aidge_core.Tensor(np_init(in_dim)) for in_dim in in_dims] - - scheduler.forward(data=in_tensor) - - export_folder = op_name + "_temp_test" - # Export the model in C++ standalone - aidge_core.export_utils.scheduler_export( - scheduler, - export_folder, - aidge_export_cpp.ExportLibCpp, - memory_manager=aidge_core.mem_info.generate_optimized_memory_info, - memory_manager_args={"stats_folder": f"{export_folder}/stats", "wrapping": False } - ) - aidge_core.export_utils.generate_main_compare_cpp(export_folder, graph_view) - print("COMPILATION") - - try: - for std_line in run_command(["make"], cwd=export_folder): - print(std_line, end="") - except subprocess.CalledProcessError as e: - print(f"An error occurred: {e}\nFailed to generate export.") - raise SystemExit(1) - print("RUN EXPORT") - pattern = r"Number of equal outputs: (\d+) / (\d+)" - comparison_matched = False - result = False - try: - for std_line in run_command(["./bin/run_export"], cwd=export_folder): - print(std_line, end="") - matches = re.findall(pattern, std_line) - if matches: - if comparison_matched: - raise RuntimeError("Two comparison matched found!") - else: - expected, infered = map(int, matches[0]) - result = (expected == infered) - comparison_matched = True - except subprocess.CalledProcessError as e: - print(f"An error occurred: {e}\nFailed to run export for comparison.") - raise SystemExit(1) - if not comparison_matched: - raise RuntimeError("No comparison matched found!") +def _np_init_ones(shape, default_value=0.01, dtype=np.float32): + """ + Generates a NumPy array with the given shape, filled with random values between -1 and 1 + with a step of 0.1. - return result + :param shape: Tuple of dimensions for the array + :param dtype: Data type of the output array (default: np.float32) + :return: A NumPy array with the given shape and dtype + """ + total_elements = reduce(operator.mul, shape, 1) + data = np.ones(total_elements) * default_value + return data.reshape(shape).astype(dtype) class test_operator_export(unittest.TestCase): @@ -133,7 +71,7 @@ class test_operator_export(unittest.TestCase): def tearDown(self): pass - def unit_test_export(self, graph_view, op_name, in_dims): + def unit_test_export(self, graph_view, op_name, in_dims, random_inputs=True, random_weights=True, default_value=0.01): """ TODO: * Handle multiple dataformat @@ -146,9 +84,27 @@ class test_operator_export(unittest.TestCase): 4- Retrieve standard output and using regex to now if the results are the same """ graph_view.compile("cpu", aidge_core.dtype.float32, dims=in_dims) + + for node in graph_view.get_nodes(): + if node.type() == "Producer": + prod_op = node.get_operator() + value = prod_op.get_output(0) + + if (random_weights): + tensor = aidge_core.Tensor(_np_init(value.dims())) + + node.get_operator().set_output(0, tensor) + else: + aidge_core.constant_filler(value, default_value) + + scheduler = aidge_core.SequentialScheduler(graph_view) - in_tensor = [aidge_core.Tensor(np.random.random(in_dim).astype(np.float32)) for in_dim in in_dims] + if (random_inputs): + in_tensor = [aidge_core.Tensor(_np_init(in_dim)) for in_dim in in_dims] + else: + in_tensor = [aidge_core.Tensor(_np_init_ones(in_dim, default_value)) for in_dim in in_dims] + scheduler.forward(data=in_tensor) # Note the convention ``<op_name>_test`` is useful for gitignore to avoid pushing generated export by accident. @@ -208,7 +164,7 @@ class test_operator_export(unittest.TestCase): aidge_core.Softmax(axis=1, name="sf0") ]) - self.assertTrue(unit_test_export(model, "Softmax", [[1, 10]])) + self.unit_test_export(model, "Softmax", [[1, 10]]) @unittest.skip("Currently this test is failing") def test_export_FC_image_in(self): @@ -226,7 +182,7 @@ class test_operator_export(unittest.TestCase): aidge_core.ReLU(name="relu0") ]) - self.assertTrue(unit_test_export(model, "ReLU", [[1, 10]])) + self.unit_test_export(model, "ReLU", [[1, 10]]) def test_export_add(self): print("Add") @@ -235,7 +191,7 @@ class test_operator_export(unittest.TestCase): aidge_core.Add(name="add") ]) - self.assertTrue(unit_test_export(model, "Add", [[1, 5, 5]])) + self.unit_test_export(model, "Add", [[1, 5, 5]]) def test_export_sub(self): print("Sub") @@ -244,7 +200,7 @@ class test_operator_export(unittest.TestCase): aidge_core.Sub(name="sub") ]) - self.assertTrue(unit_test_export(model, "Sub", [[1, 5, 5]])) + self.unit_test_export(model, "Sub", [[1, 5, 5]]) def test_export_mul(self): print("Mul") @@ -253,7 +209,16 @@ class test_operator_export(unittest.TestCase): aidge_core.Mul(name="mul") ]) - self.assertTrue(unit_test_export(model, "Mul", [[1, 5, 5]])) + self.unit_test_export(model, "Mul", [[1, 5, 5]]) + + def test_export_concat(self): + print("Concat") + model = aidge_core.sequential([ + aidge_core.Producer([1, 5, 5], name="producer"), + aidge_core.Concat(nb_inputs=2, axis=1, name="concat") + ]) + + self.unit_test_export(model, "Concat", [[1, 5, 5]]) def test_export_conv2D(self): print("Conv2D") @@ -261,7 +226,7 @@ class test_operator_export(unittest.TestCase): aidge_core.Conv2D(in_channels=3, out_channels=3, kernel_dims=(3, 3), name="conv") ]) - self.assertTrue(unit_test_export(model, "Conv2D", [[1, 3, 12, 12]])) + self.unit_test_export(model, "Conv2D", [[1, 3, 12, 12]], False, False) def test_export_max_pooling(self): print("MaxPooling2D") @@ -269,7 +234,7 @@ class test_operator_export(unittest.TestCase): aidge_core.MaxPooling2D(kernel_dims=(3, 3), name="max_pool") ]) - self.assertTrue(unit_test_export(model, "MaxPooling2D", [[1, 2, 12, 12]])) + self.unit_test_export(model, "MaxPooling2D", [[1, 2, 12, 12]], False, False) def test_export_avg_pooling(self): print("AvgPooling2D") @@ -277,7 +242,7 @@ class test_operator_export(unittest.TestCase): aidge_core.AvgPooling2D(kernel_dims=(3, 3), name="avg_pool") ]) - self.assertTrue(unit_test_export(model, "AvgPooling2D", [[1, 2, 12, 12]])) + self.unit_test_export(model, "AvgPooling2D", [[1, 2, 12, 12]], False, False) def test_export_pad2D(self): print("Pad2D") @@ -285,7 +250,7 @@ class test_operator_export(unittest.TestCase): aidge_core.Pad2D((1, 1, 1, 1), name="pad2d") ]) - self.assertTrue(unit_test_export(model, "Pad2D", [[1, 3, 10, 10]])) + self.unit_test_export(model, "Pad2D", [[1, 1, 10, 10]]) def test_export_batchnorm2D(self): print("BatchNormalization2D") @@ -293,7 +258,8 @@ class test_operator_export(unittest.TestCase): aidge_core.BatchNorm2D(nb_features=10, epsilon=2e-5, name="bn") ]) - self.assertTrue(unit_test_export(model, "BatchNorm2D", [[1, 10, 5, 5]])) + self.unit_test_export(model, "BatchNorm2D", [[1, 1, 5, 5]], False, False) + def test_export_cpp(self): print("Export test to do")