diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 0000000000000000000000000000000000000000..82e90519cc6546e5fa2c2dfa76bc32893d7cad64
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,3 @@
+# Version 0.1.0 (January 23, 2024)
+
+Initial release
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f8dbe375e217020a4c4570bd67c1b466e6593130..a0d70035e0e150ec33dc4806bd02632debbf0a42 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -66,16 +66,39 @@ endif()
 
 target_compile_features(${module_name} PRIVATE cxx_std_14)
 
+if (DOSANITIZE STREQUAL "ON")
+set(SANITIZE_FLAGS -fsanitize=address,leak,undefined,float-divide-by-zero -fno-omit-frame-pointer)
+#TODO sanitizer seems buggy in some situations with msvc, leading to linker errors, temporarily inactivating it
+#set(SANITIZE_MSVC_FLAGS)
+set(SANITIZE_MSVC_FLAGS /fsanitize=address)
+target_compile_definitions(${module_name} PUBLIC _DISABLE_VECTOR_ANNOTATION)
+else()
+set(SANITIZE_FLAGS)
+set(SANITIZE_MSVC_FLAGS)
+endif()
+
+set(STRICT_ALIASING_FLAGS -fstrict-aliasing -Wstrict-aliasing=2)
+
 # -fvisibility=hidden required by pybind11
 target_compile_options(${module_name} PUBLIC
     $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
     -fvisibility=hidden>)
 target_compile_options(${module_name} PRIVATE
-    $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
-    -Wall -Wextra -Wold-style-cast -Winline -pedantic -Werror=narrowing -Wshadow $<$<BOOL:${WERROR}>:-Werror>>)
+$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
+-Wall -Wextra -Wold-style-cast -Winline -pedantic -Werror=narrowing -Wshadow $<$<BOOL:${WERROR}>:-Werror> ${SANITIZE_FLAGS}>)
 target_compile_options(${module_name} PRIVATE
-    $<$<CXX_COMPILER_ID:MSVC>:
-    /W4>)
+$<$<CXX_COMPILER_ID:GNU>:${STRICT_ALIASING_FLAGS}>)
+target_compile_options(${module_name} PRIVATE
+$<$<CXX_COMPILER_ID:MSVC>:
+/W4 /wd4477 /DWIN32 /D_WINDOWS /GR /EHsc /MP /Zc:__cplusplus /Zc:preprocessor /permissive- ${SANITIZE_MSVC_FLAGS}>)
+if (DOSANITIZE STREQUAL "ON")
+    target_compile_options(${module_name} PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/MDd>)
+endif()
+# TODO FIXME: I'm not sure it's a good idea to propagate this option but, at this point, it was the only way that worked to silence C4477
+target_compile_options(${module_name} PUBLIC $<$<CXX_COMPILER_ID:MSVC>: /wd4477>)
+
+target_link_options(${module_name} PUBLIC $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:${SANITIZE_FLAGS}>)
+#target_link_options(${module_name} PUBLIC $<$<CXX_COMPILER_ID:MSVC>:${SANITIZE_MSVC_FLAGS}>)
 
 if(CMAKE_COMPILER_IS_GNUCXX AND COVERAGE)
     append_coverage_compiler_flags()
diff --git a/include/aidge/data/Tensor.hpp b/include/aidge/data/Tensor.hpp
index b43406b5bc38a101e1bb56af3d483789f8a774f7..658c0b497d9753f1bdfd42a274dbb48970cb6d6b 100644
--- a/include/aidge/data/Tensor.hpp
+++ b/include/aidge/data/Tensor.hpp
@@ -468,7 +468,7 @@ class Tensor : public Data,
 
 
     std::string toString() const {
-        AIDGE_ASSERT(mImpl && mImpl->hostPtr() != nullptr, "tensor should have a valid host pointer");
+        AIDGE_ASSERT(mImpl && (dims().empty() || (dims() == std::vector<DimSize_t>({0})) || (mImpl->hostPtr() != nullptr)), "tensor should have a valid host pointer");
 
         // TODO: move lambda elsewhere?
         auto ptrToString = [](DataType dt, void* ptr, size_t idx) {
@@ -548,7 +548,7 @@ class Tensor : public Data,
         } else {
             res += "{";
             for (DimSize_t j = 0; j < dims()[0]; ++j) {
-                res += " " + ptrToString(mDataType, mImpl->hostPtr(mImplOffset), j) + ((j < dims()[0]-1) ? "," : "");
+                res += " " + ptrToString(mDataType, mImpl->hostPtr(mImplOffset), j) + ((j < dims()[0]-1) ? "," : " ");
             }
         }
         res += "}";
diff --git a/include/aidge/graph/Node.hpp b/include/aidge/graph/Node.hpp
index 5ae4eb5d893244fa842e6bb0435c0a8ab3bc0ac5..de2a7b6aae5357d9a1304ec2b718a475abc1ea43 100644
--- a/include/aidge/graph/Node.hpp
+++ b/include/aidge/graph/Node.hpp
@@ -140,7 +140,7 @@ public:
 
   /**
    * @brief List of pair <Parent, ID of the data intput>. When an input is not
-   * linked to any Parent, the pair is <nullptr, gk_IODefaultIndex>. 
+   * linked to any Parent, the pair is <nullptr, gk_IODefaultIndex>.
    * Data inputs exclude inputs expecting parameters (weights or bias).
    * @return std::vector<std::pair<std::shared_ptr<Node>, IOIndex_t>>
    */
diff --git a/python_binding/graph/pybind_GraphView.cpp b/python_binding/graph/pybind_GraphView.cpp
index 8e0da01c89767844040fcbc7b48e727800436daa..eb26538a5db1eb40fdcb8a2e409067483d4a7d68 100644
--- a/python_binding/graph/pybind_GraphView.cpp
+++ b/python_binding/graph/pybind_GraphView.cpp
@@ -71,15 +71,19 @@ void init_GraphView(py::module& m) {
                                    const IOIndex_t,
                                    IOIndex_t)) &
                     GraphView::addChild,
-               py::arg("toOtherNode"), py::arg("fromOutNode") = nullptr,
-               py::arg("fromTensor") = 0U, py::arg("toTensor") = gk_IODefaultIndex,
+               py::arg("to_other_node"), py::arg("from_out_node") = nullptr,
+               py::arg("from_tensor") = 0U, py::arg("to_tensor") = gk_IODefaultIndex,
           R"mydelimiter(
           Include a Node to the current GraphView object.
 
-          :param other_node: Node to add
-          :type oth_Node: Node
-          :param includeLearnableParameter: include non-data inputs, like weights and biases. Default True.
-          :type includeLearnableParameter
+          :param to_other_node: Node to add
+          :type to_other_node: Node
+          :param from_out_node: Node inside the GraphView the new Node will be linked to (it will become a parent of the new Node). If the GraphView only has one output Node, then default to this Node.
+          :type from_out_node: Node
+          :param from_tensor: Ouput Tensor ID of the already included Node. Default to 0.
+          :type from_tensor: int
+          :param to_tensor: Input Tensor ID of the new Node. Default to gk_IODefaultIndex, meaning first available data input for the Node.
+          :type to_tensor: int
           )mydelimiter")
 
           .def_static("replace", &GraphView::replace, py::arg("old_nodes"), py::arg("new_nodes"),
diff --git a/python_binding/graph/pybind_Node.cpp b/python_binding/graph/pybind_Node.cpp
index 1f655b50a38dddf597f51879411535ff655ed694..83f5688fa3d9e459a364ee3e74975a23d09c236c 100644
--- a/python_binding/graph/pybind_Node.cpp
+++ b/python_binding/graph/pybind_Node.cpp
@@ -63,12 +63,20 @@ void init_Node(py::module& m) {
     )mydelimiter")
 
     .def("add_child",
-        (void (Node::*)(std::shared_ptr<GraphView>, const IOIndex_t,
-                        std::pair<std::shared_ptr<Node>, IOIndex_t>)) &
-                Node::addChild,
+        [](Node &self, std::shared_ptr<GraphView> other_graph, const IOIndex_t out_id=0,
+                        py::object other_in_id = py::none()) {
+            std::pair<NodePtr, IOIndex_t> cpp_other_in_id;
+            // Note: PyBind on windows does not support conversion of nullptr -> std::shared_ptr, using this trampoline to change the default arg to a py::none(). If signature change, we would be able to directly bind the function.
+
+            if (other_in_id.is_none()) {
+                cpp_other_in_id = std::pair<NodePtr, IOIndex_t>(nullptr, gk_IODefaultIndex);
+            }else{
+                cpp_other_in_id = other_in_id.cast<std::pair<NodePtr, IOIndex_t>>();
+            }
+            self.addChild(other_graph, out_id, cpp_other_in_id);
+        },
         py::arg("other_graph"), py::arg("out_id") = 0,
-        py::arg("other_in_id") =
-                std::pair<std::shared_ptr<Node>, IOIndex_t>(nullptr, gk_IODefaultIndex),
+        py::arg("other_in_id") = py::none(),
                R"mydelimiter(
     Link a Node from a specific GraphView to the current Node.
 
diff --git a/setup.ps1 b/setup.ps1
index 61324cf4a7d64094f5ead498adf64719c3290f06..9a110380b91b06cbb45017612e1350fb3bca49c6 100644
--- a/setup.ps1
+++ b/setup.ps1
@@ -40,7 +40,7 @@ if ($install_reqs)
 mkdir -Force build_cpp
 mkdir -Force $env:AIDGE_INSTALL_PATH
 Set-Location build_cpp
-cmake -DCMAKE_INSTALL_PREFIX:PATH=$env:AIDGE_INSTALL_PATH -DCMAKE_BUILD_TYPE=Debug ..
+cmake -DCMAKE_INSTALL_PREFIX:PATH=$env:AIDGE_INSTALL_PATH -DCMAKE_BUILD_TYPE=Debug -DDOSANITIZE=OFF ..
 if(!$?) { $lastError = $LASTEXITCODE; Set-Location $PSScriptRoot; Exit $lastError }
 cmake --build . -j2
 if(!$?) { $lastError = $LASTEXITCODE; Set-Location $PSScriptRoot; Exit $lastError }
diff --git a/unit_tests/CMakeLists.txt b/unit_tests/CMakeLists.txt
index 5ccfa3832a8ce2522f18ab07e11a78cf8b462a40..806f62d47dcad02614a18d0d7f6e51042b164cc8 100644
--- a/unit_tests/CMakeLists.txt
+++ b/unit_tests/CMakeLists.txt
@@ -14,6 +14,47 @@ file(GLOB_RECURSE src_files "*.cpp")
 
 add_executable(tests${module_name} ${src_files})
 
+target_compile_features(tests${module_name} PRIVATE cxx_std_14)
+
+set(FORCE_CI TRUE)
+if (NOT(FORCE_CI))
+
+if (DOSANITIZE STREQUAL "ON")
+set(SANITIZE_FLAGS -fsanitize=address,leak,undefined,float-divide-by-zero -fno-omit-frame-pointer)
+#TODO sanitizer seems buggy in some situations with msvc, leading to linker errors, temporarily inactivating it
+#set(SANITIZE_MSVC_FLAGS)
+set(SANITIZE_MSVC_FLAGS /fsanitize=address)
+target_compile_definitions(tests${module_name} PUBLIC _DISABLE_VECTOR_ANNOTATION)
+else()
+set(SANITIZE_FLAGS)
+set(SANITIZE_MSVC_FLAGS)
+endif()
+
+set(STRICT_ALIASING_FLAGS -fstrict-aliasing -Wstrict-aliasing=2)
+
+# -fvisibility=hidden required by pybind11
+target_compile_options(tests${module_name} PUBLIC
+    $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
+    -fvisibility=hidden>)
+target_compile_options(tests${module_name} PRIVATE
+$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
+-Wall -Wextra -Wold-style-cast -Winline -pedantic -Werror=narrowing -Wshadow $<$<BOOL:${WERROR}>:-Werror> ${SANITIZE_FLAGS}>)
+target_compile_options(tests${module_name} PRIVATE
+$<$<CXX_COMPILER_ID:GNU>:${STRICT_ALIASING_FLAGS}>)
+target_compile_options(${module_name} PRIVATE
+$<$<CXX_COMPILER_ID:MSVC>:
+/W4 /DWIN32 /D_WINDOWS /GR /EHsc /MP /Zc:__cplusplus /Zc:preprocessor /permissive- ${SANITIZE_MSVC_FLAGS}>)
+if (DOSANITIZE STREQUAL "ON")
+  target_compile_options(${module_name} PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/MDd>)
+endif()
+# TODO FIXME: I'm not sure it's a good idea to propagate this option but, at this point, it was the only way that worked to silence C4477
+target_compile_options(${module_name} PUBLIC $<$<CXX_COMPILER_ID:MSVC>: /wd4477>)
+
+target_link_options(tests${module_name} PUBLIC $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:${SANITIZE_FLAGS}>)
+#target_link_options(tests${module_name} PUBLIC $<$<CXX_COMPILER_ID:MSVC>:${SANITIZE_MSVC_FLAGS}>)
+
+endif()
+
 target_link_libraries(tests${module_name} PUBLIC ${module_name})
 
 target_link_libraries(tests${module_name} PRIVATE Catch2::Catch2WithMain)