diff --git a/include/aidge/scheduler/MemoryManager.hpp b/include/aidge/scheduler/MemoryManager.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9f718e8df341b2303c876b903c4e0339461f88b2
--- /dev/null
+++ b/include/aidge/scheduler/MemoryManager.hpp
@@ -0,0 +1,324 @@
+/********************************************************************************
+ * Copyright (c) 2023 CEA-List
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
+
+#ifndef AIDGE_MEMORY_MANAGER_H
+#define AIDGE_MEMORY_MANAGER_H
+
+#include <memory>
+#include <vector>
+#include <map>
+
+#include "aidge/graph/Node.hpp"
+
+namespace Aidge {
+class MemoryManager {
+public:
+    typedef int Clock_T;
+
+    enum OptimizeStrategy {
+        None,
+        OptimizeMaxLifetimeMinSizeFirst,
+        OptimizeMaxLifetimeMaxSizeFirst,
+        OptimizeMaxHoleMaxLifetimeFirst
+    };
+
+    // MemorySpace are contiguous, non-overlapping memory blocks, that can be
+    // re-arranged freely.
+    struct MemorySpace {
+        MemorySpace(Clock_T clock_,
+                    unsigned int offset_,
+                    unsigned int size_,
+                    std::set<std::shared_ptr<Node> > dependencies_
+                        = std::set<std::shared_ptr<Node> >()
+        ):
+            offset(offset_),
+            size(size_),
+            dependencies(dependencies_),
+            allocated(clock_),
+            released(-1) {}
+
+        unsigned int offset;
+        unsigned int size;
+        std::set<std::shared_ptr<Node> > dependencies;
+        Clock_T allocated;
+        Clock_T released;
+    };
+
+    // MemoryPlane belongs to a MemorySpace. Any number of potentially
+    // overlapping planes can be associated to a MemorySpace.
+    // MemoryPlane can be non-contiguous (in case of stride, or wrapping, when
+    // offset + size > memSpace.size).
+    // MemoryPlane cannot be re-arranged inside a MemorySpace.
+    struct MemoryPlane {
+        MemoryPlane(std::shared_ptr<MemorySpace> memSpace_,
+                    Clock_T clock_,
+                    unsigned int offset_,
+                    unsigned int size_,
+                    unsigned int stride_ = 0,
+                    unsigned int length_ = 1,
+                    unsigned int count_ = 1
+        ):
+            memSpace(memSpace_),
+            allocated(clock_),
+            offset(offset_),
+            size(size_),
+            stride(std::max(size_, stride_)),
+            length(length_),
+            count(count_)
+        {
+            assert(offset <= memSpace->size);
+            // The preceding assert should allow offset == memSpace->size (see 
+            // issue #63). This means immediate wrapping.
+            // It appends if the final offset computed in reallocate() is at
+            // the end of the previous memPlane and is also at the end of the
+            // memSpace (in case for example of in-place memory op.).
+            // Instead of bringing the offset back to the beginning of the 
+            // memSpace, we stay attached to this offset in case the memSpace
+            // grows when a new memPlane is added.
+
+            assert(getContiguousOffset() >= memSpace->offset);
+            assert(getWrappedOffset() >= memSpace->offset);
+            assert(getContiguousOffset() + getContiguousSize()
+                <= memSpace->offset + memSpace->size);
+            assert(getWrappedOffset() + getWrappedSize()
+                <= memSpace->offset + memSpace->size);
+        }
+
+        inline unsigned int getSize() const {
+            return stride * length * count;
+        }
+
+        inline unsigned int getUsefulSize() const {
+            return size * length * count;
+        }
+
+        inline unsigned int getContiguousOffset() const {
+            return memSpace->offset + offset;
+        }
+
+        inline unsigned int getContiguousSize() const {
+            return std::min(getSize(), getLimit());
+        }
+
+        inline unsigned int getWrappedOffset() const {
+            return memSpace->offset;
+        }
+
+        inline unsigned int getWrappedSize() const {
+            return getSize() - getContiguousSize();
+        }
+
+        inline unsigned int getFinalOffset() const {
+            return (getWrappedSize() > 0)
+                ? getWrappedOffset() + getWrappedSize()
+                : getContiguousOffset() + getContiguousSize();
+        }
+
+        inline unsigned int getUpperOffset() const {
+            return (getContiguousOffset() + getContiguousSize());
+        }
+
+        // Limit is computed dynamically, as memSpace->size may increase after
+        // the creation of this memory space. This is actually necessary to
+        // ensure that the memory wrapping works correctly, because when 
+        // computing the margin required for the wrapping, it is assumed that
+        // the previous layer wrapping extends to the full memory space size.
+        inline unsigned int getLimit() const {
+            // limit must be a multiple of (stride * length) if count > 1
+            // or stride if length > 1
+            // uses floor() to stay below memSpace->size
+            return (count > 1)
+                ? std::floor((memSpace->size - offset)
+                        / static_cast<double>(stride * length)) * (stride * length)
+                : (length > 1)
+                    ? std::floor((memSpace->size - offset)
+                            / static_cast<double>(stride)) * stride
+                    : memSpace->size - offset;
+        }
+
+        std::shared_ptr<MemorySpace> memSpace;
+        Clock_T allocated;
+        unsigned int offset;
+        unsigned int size;
+        unsigned int stride;
+        unsigned int length;
+        unsigned int count;
+    };
+
+    struct MaxLifetimeMinSizeFirst {
+        MaxLifetimeMinSizeFirst(unsigned int maxLifetime_)
+            : maxLifetime(maxLifetime_) {}
+        const unsigned int maxLifetime;
+
+        bool operator()(const std::shared_ptr<MemorySpace>& p0,
+                        const std::shared_ptr<MemorySpace>& p1);
+    };
+
+    struct MaxLifetimeMaxSizeFirst {
+        MaxLifetimeMaxSizeFirst(unsigned int maxLifetime_)
+            : maxLifetime(maxLifetime_) {}
+        const unsigned int maxLifetime;
+
+        bool operator()(const std::shared_ptr<MemorySpace>& p0,
+                        const std::shared_ptr<MemorySpace>& p1);
+    };
+
+    struct MaxHoleMaxLifetimeFirst {
+        MaxHoleMaxLifetimeFirst(unsigned int maxLifetime_, MemoryManager* inst_)
+            : maxLifetime(maxLifetime_),
+              inst(inst_) {}
+        const unsigned int maxLifetime;
+        MemoryManager* inst;
+
+        bool operator()(const std::shared_ptr<MemorySpace>& p0,
+                        const std::shared_ptr<MemorySpace>& p1);
+    };
+
+    struct CompByNodeName {
+        bool operator()(const std::shared_ptr<Node>& lhs,
+                        const std::shared_ptr<Node>& rhs) const
+        {
+            return lhs->name() < rhs->name();
+        }
+    };
+
+    typedef std::map<std::shared_ptr<Node>, std::vector<MemoryPlane>,
+        CompByNodeName> MemMap_T;
+
+    MemoryManager(): mClock(0) {}
+    /// Generates a new MemorySpace
+    std::shared_ptr<MemorySpace> reserve(unsigned int size,
+                                    const std::set<std::shared_ptr<Node> >&
+                          dependencies = std::set<std::shared_ptr<Node> >());
+    /// Expand an existing MemorySpace, without affecting its MemoryPlane
+    /// This function rebuild the memory stack mMemStack
+    void expand(std::shared_ptr<MemorySpace> memSpace,
+                unsigned int requiredSize);
+    /// Generates a MemoryPlane in a new MemorySpace
+    MemoryPlane allocate(unsigned int size,
+                         const std::set<std::shared_ptr<Node> >&
+                          dependencies = std::set<std::shared_ptr<Node> >(),
+                         unsigned int stride = 0,
+                         unsigned int length = 1,
+                         unsigned int count = 1);
+    /// Generates a MemoryPlane in a new MemorySpace, associated to a Node
+    unsigned int allocate(const std::shared_ptr<Node>& node,
+                          unsigned int size,
+                          const std::set<std::shared_ptr<Node> >&
+                          dependencies = std::set<std::shared_ptr<Node> >(),
+                          unsigned int stride = 0,
+                          unsigned int length = 1,
+                          unsigned int count = 1);
+    bool isWrapAround(std::shared_ptr<MemorySpace> memSpace,
+                      unsigned int offset,
+                      unsigned int size,
+                      unsigned int stride = 0,
+                      unsigned int length = 1,
+                      unsigned int count = 1) const;
+    /// Generate a new MemoryPlane in an existing MemorySpace
+    MemoryPlane reallocate(std::shared_ptr<MemorySpace> memSpace,
+                           unsigned int offset,
+                           unsigned int size,
+                           bool wrapAround,
+                           unsigned int extraSize = 0,
+                           const std::set<std::shared_ptr<Node> >&
+                additionalDependencies = std::set<std::shared_ptr<Node> >(),
+                           unsigned int stride = 0,
+                           unsigned int length = 1,
+                           unsigned int count = 1);
+    /// Generate a new MemoryPlane directly following an existing MemoryPlane
+    /// memPlane with an additionnal offset extraOffset
+    MemoryPlane reallocate(const MemoryPlane& memPlane,
+                           unsigned int extraOffset,
+                           unsigned int size,
+                           bool wrapAround,
+                           unsigned int extraSize = 0,
+                           const std::set<std::shared_ptr<Node> >&
+                additionalDependencies = std::set<std::shared_ptr<Node> >(),
+                           unsigned int stride = 0,
+                           unsigned int length = 1,
+                           unsigned int count = 1);
+    /// Generate a new MemoryPlane in an existing MemorySpace, associated to a 
+    /// Node
+    unsigned int reallocate(std::shared_ptr<MemorySpace> memSpace,
+                            const std::shared_ptr<Node>& node,
+                            unsigned int offset,
+                            unsigned int size,
+                            bool wrapAround,
+                            unsigned int extraSize = 0,
+                            const std::set<std::shared_ptr<Node> >&
+                additionalDependencies = std::set<std::shared_ptr<Node> >(),
+                            unsigned int stride = 0,
+                            unsigned int length = 1,
+                            unsigned int count = 1);
+    /// Generate a new MemoryPlane directly following an existing MemoryPlane
+    /// memPlane with an additionnal offset extraOffset
+    unsigned int reallocate(const MemoryPlane& memPlane,
+                            const std::shared_ptr<Node>& node,
+                            unsigned int extraOffset,
+                            unsigned int size,
+                            bool wrapAround,
+                            unsigned int extraSize = 0,
+                            const std::set<std::shared_ptr<Node> >&
+                additionalDependencies = std::set<std::shared_ptr<Node> >(),
+                            unsigned int stride = 0,
+                            unsigned int length = 1,
+                            unsigned int count = 1);
+
+    unsigned int release(std::shared_ptr<MemorySpace> memSpace);
+    unsigned int release(const std::shared_ptr<Node>& node);
+    unsigned int releaseDependencies(const std::shared_ptr<Node>& node);
+    void optimize(OptimizeStrategy strategy);
+    unsigned int getOffset(const std::shared_ptr<Node>& node,
+                           unsigned int plane = 0) const;
+    unsigned int getSize(const std::shared_ptr<Node>& node,
+                         unsigned int plane) const;
+    unsigned int getSize(const std::shared_ptr<Node>& node) const;
+    unsigned int getNbPlanes(const std::shared_ptr<Node>& node) const;
+    unsigned int getPeakUsage() const;
+    Clock_T getMaxLifetime() const;
+    const std::vector<MemoryPlane>& getPlanes(const std::shared_ptr<Node>& node)
+        const;
+    const MemMap_T& getPlanes() const { return mMemPlanes; }
+    MemMap_T getPlanes(std::shared_ptr<MemorySpace> memSpace) const;
+    unsigned int getNbPlanes(std::shared_ptr<MemorySpace> memSpace) const;
+    Clock_T getCurrentTick() const { return mClock; };
+    void tick();
+    void log(const std::string& fileName) const;
+
+private:
+    /// Find a valid offset in the memory stack that can fit a contiguous chunk
+    /// of memory of size @size
+    unsigned int onStack(unsigned int size);
+    unsigned int offStack(unsigned int offset);
+    std::map<unsigned int, unsigned int> getStack(
+        std::shared_ptr<MemorySpace> memSpace,
+        Clock_T clock) const;
+    std::pair<Clock_T, unsigned int> getMaxHole(
+        std::shared_ptr<MemorySpace> memSpace) const;
+
+    std::map<unsigned int, unsigned int> mMemStack;
+    std::vector<std::shared_ptr<MemorySpace> > mMemSpaces;
+    MemMap_T mMemPlanes;
+    Clock_T mClock;
+};
+}
+
+namespace {
+template <>
+const char* const EnumStrings<Aidge::MemoryManager::OptimizeStrategy>::data[]
+    = {"None",
+       "OptimizeMaxLifetimeMinSizeFirst",
+       "OptimizeMaxLifetimeMaxSizeFirst",
+       "OptimizeMaxHoleMaxLifetimeFirst"};
+}
+
+#endif // AIDGE_MEMORY_MANAGER_H
diff --git a/include/aidge/scheduler/Scheduler.hpp b/include/aidge/scheduler/Scheduler.hpp
index aa63ff7208dbe22cb762f0f8fe1e0ffce9ad856e..06c3b6e9ed23f10fe32aeb807cfef112970897a0 100644
--- a/include/aidge/scheduler/Scheduler.hpp
+++ b/include/aidge/scheduler/Scheduler.hpp
@@ -22,6 +22,7 @@
 #include "aidge/utils/Types.h"
 
 #include "aidge/data/Tensor.hpp"
+#include "aidge/scheduler/MemoryManager.hpp"
 
 namespace Aidge {
 class Node;
@@ -62,6 +63,14 @@ public:
         mStaticSchedule.clear();
         mStaticScheduleStep = 0;
     }
+
+    /**
+     * Generate the memory layout for the current static scheduling.
+     * @param incProducers If true, include the producers in the memory layout.
+     * @param wrapAroundBuffer If true, allow wrapping in memory planes.
+    */
+    MemoryManager generateMemory(bool incProducers = false, bool wrapAroundBuffer = false) const;
+
     /**
      * @brief Place the data tensors inside in the data input tensor of the graphView. In case of multiple data input tensors, they are mapped to producers in the order given by the graph.
      * 
diff --git a/src/scheduler/MemoryManager.cpp b/src/scheduler/MemoryManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9599dbf74f4b1044534b94014e16cebe5731c503
--- /dev/null
+++ b/src/scheduler/MemoryManager.cpp
@@ -0,0 +1,914 @@
+/********************************************************************************
+ * Copyright (c) 2023 CEA-List
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
+
+#include <fmt/format.h>
+
+#include "aidge/scheduler/MemoryManager.hpp"
+#include "aidge/utils/ErrorHandling.hpp"
+
+std::shared_ptr<Aidge::MemoryManager::MemorySpace> Aidge::MemoryManager::reserve(
+    unsigned int size,
+    const std::set<std::shared_ptr<Node> >& dependencies)
+{
+    const unsigned int offset = onStack(size);
+
+    std::shared_ptr<MemorySpace> memSpace
+        = std::make_shared<MemorySpace>(mClock, offset, size, dependencies);
+    mMemSpaces.push_back(memSpace);
+    return memSpace;
+}
+
+void Aidge::MemoryManager::expand(
+    std::shared_ptr<MemorySpace> memSpace,
+    unsigned int requiredSize)
+{
+    assert(std::find(mMemSpaces.begin(), mMemSpaces.end(), memSpace)
+            != mMemSpaces.end());
+
+    memSpace->size = std::max(memSpace->size, requiredSize);
+
+    // Rebuild the stack from the beginning, taking into account the new size.
+    // Everything else stay the same.
+    mMemStack.clear();
+
+    for (Clock_T clock = 0; clock <= mClock; ++clock) {
+        for (std::vector<std::shared_ptr<MemorySpace> >::iterator
+            it = mMemSpaces.begin(), itEnd = mMemSpaces.end(); it != itEnd;
+            ++it)
+        {
+            if ((*it)->allocated == clock)
+                (*it)->offset = onStack((*it)->size);
+        }
+
+        // MemorySpace released at clock are still valid until the next tick;
+        // make sure offStack() only append after all onStack() are done.
+        for (std::vector<std::shared_ptr<MemorySpace> >::iterator
+            it = mMemSpaces.begin(), itEnd = mMemSpaces.end(); it != itEnd;
+            ++it)
+        {
+            if ((*it)->released == clock && (*it)->dependencies.empty())
+                offStack((*it)->offset);
+        }
+    }
+}
+
+Aidge::MemoryManager::MemoryPlane Aidge::MemoryManager::allocate(
+    unsigned int size,
+    const std::set<std::shared_ptr<Node> >& dependencies,
+    unsigned int stride,
+    unsigned int length,
+    unsigned int count)
+{
+    const unsigned int fullSize = std::max(size, stride) * length * count;
+    return MemoryPlane(reserve(fullSize, dependencies),
+                       mClock, 0, size, stride, length, count);
+}
+
+unsigned int Aidge::MemoryManager::allocate(
+    const std::shared_ptr<Node>& node,
+    unsigned int size,
+    const std::set<std::shared_ptr<Node> >& dependencies,
+    unsigned int stride,
+    unsigned int length,
+    unsigned int count)
+{
+    std::map<std::shared_ptr<Node>, std::vector<MemoryPlane> >::iterator it;
+    std::tie(it, std::ignore) = mMemPlanes.insert(std::make_pair(node,
+                                                std::vector<MemoryPlane>()));
+
+    (*it).second.push_back(allocate(size, dependencies, stride, length, count));
+    return ((*it).second.size() - 1);
+}
+
+bool Aidge::MemoryManager::isWrapAround(
+    std::shared_ptr<MemorySpace> memSpace,
+    unsigned int offset,
+    unsigned int size,
+    unsigned int stride,
+    unsigned int length,
+    unsigned int count) const
+{
+    const unsigned int fullSize = std::max(size, stride) * length * count;
+    return (offset + fullSize > memSpace->size);
+}
+
+Aidge::MemoryManager::MemoryPlane Aidge::MemoryManager::reallocate(
+    std::shared_ptr<MemorySpace> memSpace,
+    unsigned int offset,
+    unsigned int size,
+    bool wrapAround,
+    unsigned int extraSize,
+    const std::set<std::shared_ptr<Node> >& additionalDependencies,
+    unsigned int stride,
+    unsigned int length,
+    unsigned int count)
+{
+    const unsigned int fullSize = std::max(size, stride) * length * count;
+    unsigned int requiredSize = offset + fullSize;
+
+    if (wrapAround) {
+        requiredSize = fullSize + extraSize;
+
+        if (count > 1) {
+            // (requiredSize - offset) must be a multiple of (stride * length)
+            requiredSize = offset
+                + std::ceil((requiredSize - offset)
+                    / static_cast<double>(std::max(size, stride) * length))
+                        * (std::max(size, stride) * length);
+        }
+        else if (length > 1) {
+            // (requiredSize - offset) must be a multiple of stride
+            requiredSize = offset
+                + std::ceil((requiredSize - offset)
+                    / static_cast<double>(std::max(size, stride)))
+                        * std::max(size, stride);
+        }
+    }
+
+    if (requiredSize > memSpace->size || memSpace->released >= 0) {
+        // Expand in size and/or duration.
+        // If memSpace was already released, put it back on the stack
+        memSpace->released = -1;
+        expand(memSpace, requiredSize);
+    }
+
+    memSpace->dependencies.insert(additionalDependencies.begin(),
+                                  additionalDependencies.end());
+
+    return MemoryPlane(memSpace, mClock, offset, size, stride, length, count);
+}
+
+Aidge::MemoryManager::MemoryPlane Aidge::MemoryManager::reallocate(
+    const MemoryPlane& memPlane,
+    unsigned int extraOffset,
+    unsigned int size,
+    bool wrapAround,
+    unsigned int extraSize,
+    const std::set<std::shared_ptr<Node> >& additionalDependencies,
+    unsigned int stride,
+    unsigned int length,
+    unsigned int count)
+{
+    const unsigned int initialOffset = memPlane.getFinalOffset()
+        - memPlane.memSpace->offset + extraOffset;
+    const unsigned int fullSize = std::max(size, stride) * length * count;
+    unsigned int requiredSize = initialOffset + fullSize;
+
+    if (wrapAround) {
+        requiredSize = fullSize + extraSize;
+
+        if (count > 1) {
+            // (requiredSize - offset) must be a multiple of (stride * length)
+            requiredSize = initialOffset
+                + std::ceil((requiredSize - initialOffset)
+                    / static_cast<double>(std::max(size, stride) * length))
+                        * (std::max(size, stride) * length);
+        }
+        else if (length > 1) {
+            // (requiredSize - offset) must be a multiple of stride
+            requiredSize = initialOffset
+                + std::ceil((requiredSize - initialOffset)
+                    / static_cast<double>(std::max(size, stride)))
+                        * std::max(size, stride);
+        }
+
+        // Make sure that the intended margin with previous memPlane will be
+        // respected, as it may actually be lower because of the floor()
+        // in the memPlane getLimit() function.
+        if (memPlane.count > 1) {
+            requiredSize = memPlane.offset
+                + std::ceil((requiredSize - memPlane.offset)
+                    / static_cast<double>(memPlane.stride * memPlane.length))
+                        * (memPlane.stride * memPlane.length);
+        }
+        else if (memPlane.length > 1) {
+            requiredSize = memPlane.offset
+                + std::ceil((requiredSize - memPlane.offset)
+                    / static_cast<double>(memPlane.stride))
+                        * memPlane.stride;
+        }
+    }
+
+    if (requiredSize > memPlane.memSpace->size
+        || memPlane.memSpace->released >= 0)
+    {
+        // Expand in size and/or duration.
+        // If memSpace was already released, put it back on the stack
+        memPlane.memSpace->released = -1;
+        expand(memPlane.memSpace, requiredSize);
+    }
+
+    memPlane.memSpace->dependencies.insert(
+        additionalDependencies.begin(),
+        additionalDependencies.end());
+
+    const unsigned int finalOffset = memPlane.getFinalOffset()
+        - memPlane.memSpace->offset + extraOffset;
+
+    return MemoryPlane(memPlane.memSpace, mClock,
+                       finalOffset, size, stride, length, count);
+}
+
+unsigned int Aidge::MemoryManager::reallocate(
+    const MemoryPlane& memPlane,
+    const std::shared_ptr<Node>& node,
+    unsigned int extraOffset,
+    unsigned int size,
+    bool wrapAround,
+    unsigned int extraSize,
+    const std::set<std::shared_ptr<Node> >& additionalDependencies,
+    unsigned int stride,
+    unsigned int length,
+    unsigned int count)
+{
+    std::map<std::shared_ptr<Node>, std::vector<MemoryPlane> >::iterator it;
+    std::tie(it, std::ignore) = mMemPlanes.insert(std::make_pair(node,
+                                                std::vector<MemoryPlane>()));
+
+    (*it).second.push_back(reallocate(memPlane, extraOffset, size, wrapAround,
+                                      extraSize, additionalDependencies,
+                                      stride, length, count));
+    return ((*it).second.size() - 1);
+}
+
+unsigned int Aidge::MemoryManager::reallocate(
+    std::shared_ptr<MemorySpace> memSpace,
+    const std::shared_ptr<Node>& node,
+    unsigned int offset,
+    unsigned int size,
+    bool wrapAround,
+    unsigned int extraSize,
+    const std::set<std::shared_ptr<Node> >& additionalDependencies,
+    unsigned int stride,
+    unsigned int length,
+    unsigned int count)
+{
+    std::map<std::shared_ptr<Node>, std::vector<MemoryPlane> >::iterator it;
+    std::tie(it, std::ignore) = mMemPlanes.insert(std::make_pair(node,
+                                                std::vector<MemoryPlane>()));
+
+    (*it).second.push_back(reallocate(memSpace, offset, size, wrapAround,
+                                      extraSize, additionalDependencies,
+                                      stride, length, count));
+    return ((*it).second.size() - 1);
+}
+
+unsigned int Aidge::MemoryManager::release(std::shared_ptr<MemorySpace> memSpace)
+{
+    if (memSpace->released == -1) {
+        memSpace->released = mClock;
+
+        if (memSpace->dependencies.empty())
+            return offStack(memSpace->offset);
+    }
+
+    return 0;
+}
+
+unsigned int Aidge::MemoryManager::release(const std::shared_ptr<Node>& node)
+{
+    const std::map<std::shared_ptr<Node>, std::vector<MemoryPlane> >
+        ::iterator it = mMemPlanes.find(node);
+
+    if (it == mMemPlanes.end()) {
+        fmt::print("Warning: release(): there is no allocated memory for node {}\n", node->name());
+        return 0;
+    }
+
+    unsigned int releasedMemSize = 0;
+
+    for (std::vector<MemoryPlane>::iterator itPlanes = (*it).second.begin(),
+        itPlanesEnd = (*it).second.end(); itPlanes != itPlanesEnd; ++itPlanes)
+    {
+        releasedMemSize += release((*itPlanes).memSpace);
+    }
+
+    // Remove dependencies
+    releasedMemSize += releaseDependencies(node);
+
+    return releasedMemSize;
+}
+
+unsigned int Aidge::MemoryManager::releaseDependencies(
+    const std::shared_ptr<Node>& node)
+{
+    unsigned int releasedMemSize = 0;
+
+    for (std::vector<std::shared_ptr<MemorySpace> >::iterator
+        it = mMemSpaces.begin(), itEnd = mMemSpaces.end(); it != itEnd;
+        ++it)
+    {
+        if (!(*it)->dependencies.empty()) {
+            (*it)->dependencies.erase(node);
+
+            if ((*it)->released <= mClock
+                && (*it)->dependencies.empty())
+            {
+                (*it)->released = mClock;
+                releasedMemSize += offStack((*it)->offset);
+            }
+        }
+    }
+
+    return releasedMemSize;
+}
+
+bool Aidge::MemoryManager::MaxLifetimeMinSizeFirst::operator()(
+    const std::shared_ptr<MemorySpace>& p0,
+    const std::shared_ptr<MemorySpace>& p1)
+{
+    const Clock_T lifetime0
+        = ((p0->released >= 0) ? p0->released : maxLifetime) - p0->allocated;
+    const Clock_T lifetime1
+        = ((p1->released >= 0) ? p1->released : maxLifetime) - p1->allocated;
+
+    return (lifetime0 > lifetime1
+            || (lifetime0 == lifetime1 && p0->size < p1->size));
+}
+
+bool Aidge::MemoryManager::MaxLifetimeMaxSizeFirst::operator()(
+    const std::shared_ptr<MemorySpace>& p0,
+    const std::shared_ptr<MemorySpace>& p1)
+{
+    const Clock_T lifetime0
+        = ((p0->released >= 0) ? p0->released : maxLifetime) - p0->allocated;
+    const Clock_T lifetime1
+        = ((p1->released >= 0) ? p1->released : maxLifetime) - p1->allocated;
+
+    return (lifetime0 > lifetime1
+            || (lifetime0 == lifetime1 && p0->size > p1->size));
+}
+
+bool Aidge::MemoryManager::MaxHoleMaxLifetimeFirst::operator()(
+    const std::shared_ptr<MemorySpace>& p0,
+    const std::shared_ptr<MemorySpace>& p1)
+{
+    const Clock_T lifetime0
+        = ((p0->released >= 0) ? p0->released : maxLifetime) - p0->allocated;
+    const Clock_T lifetime1
+        = ((p1->released >= 0) ? p1->released : maxLifetime) - p1->allocated;
+
+    const std::pair<Clock_T, unsigned int> maxHole0 = inst->getMaxHole(p0);
+    const std::pair<Clock_T, unsigned int> maxHole1 = inst->getMaxHole(p1);
+
+    return (maxHole0.second > maxHole1.second
+            || (maxHole0.second == maxHole1.second && lifetime0 > lifetime1));
+}
+
+void Aidge::MemoryManager::optimize(OptimizeStrategy strategy) {
+    if (strategy == None)
+        return;
+
+    const unsigned int maxLifetime = getMaxLifetime();
+
+    if (strategy == OptimizeMaxLifetimeMinSizeFirst) {
+        std::stable_sort(mMemSpaces.begin(), mMemSpaces.end(),
+                        MemoryManager::MaxLifetimeMinSizeFirst(maxLifetime));
+    }
+    else if (strategy == OptimizeMaxLifetimeMaxSizeFirst) {
+        std::stable_sort(mMemSpaces.begin(), mMemSpaces.end(),
+                        MemoryManager::MaxLifetimeMaxSizeFirst(maxLifetime));
+    }
+    else if (strategy == OptimizeMaxHoleMaxLifetimeFirst) {
+        std::stable_sort(mMemSpaces.begin(), mMemSpaces.end(),
+                        MemoryManager::MaxHoleMaxLifetimeFirst(maxLifetime, this));
+    }
+
+    std::vector<std::map<unsigned int, unsigned int> > stacks(maxLifetime + 1,
+                                        std::map<unsigned int, unsigned int>());
+
+    for (std::vector<std::shared_ptr<MemorySpace> >::const_iterator
+        it = mMemSpaces.begin(), itEnd = mMemSpaces.end(); it != itEnd; ++it)
+    {
+        const Clock_T maxT = ((*it)->released >= 0
+                                && (*it)->dependencies.empty())
+                                    ? (*it)->released : maxLifetime;
+
+        // Merge stacks over memSpace lifetime
+        std::map<unsigned int, unsigned int> mergedStacks;
+
+        for (Clock_T t = (*it)->allocated; t <= maxT; ++t) {
+            for (std::map<unsigned int, unsigned int>::iterator itMem
+                = stacks[t].begin(), itMemEnd = stacks[t].end();
+                itMem != itMemEnd; ++itMem)
+            {
+                bool newInsert;
+                std::map<unsigned int, unsigned int>::iterator itMergedMem;
+                std::tie(itMergedMem, newInsert) = mergedStacks.insert(
+                    std::make_pair((*itMem).first, (*itMem).second));
+
+                if (!newInsert) {
+                    (*itMergedMem).second = std::max((*itMergedMem).second,
+                                                     (*itMem).second);
+                }
+            }
+        }
+
+        std::map<unsigned int, unsigned int> mergedStack;
+
+        if (!mergedStacks.empty()) {
+            std::map<unsigned int, unsigned int>::iterator itMem
+                = mergedStacks.begin();
+
+            mergedStack.insert(*itMem);
+            ++itMem;
+
+            while (itMem != mergedStacks.end()) {
+                std::map<unsigned int, unsigned int>::reverse_iterator
+                    itMergedMem = mergedStack.rbegin();
+                const unsigned int nextOffset = (*itMergedMem).first
+                                                + (*itMergedMem).second;
+
+                if ((*itMem).first <= nextOffset) {
+                    (*itMergedMem).second
+                        = std::max((*itMem).first + (*itMem).second, nextOffset)
+                            - (*itMergedMem).first;
+                }
+                else
+                    mergedStack.insert(*itMem);
+
+                ++itMem;
+            }
+        }
+
+        // Allocate in merged stack
+        unsigned int offset = 0;
+        std::map<unsigned int, unsigned int>::iterator itMem
+            = mergedStack.begin();
+
+        while (true) {
+            if (itMem == mergedStack.end()
+                || (*itMem).first - offset >= (*it)->size)
+            {
+                mergedStack.insert(std::make_pair(offset, (*it)->size));
+                break;
+            }
+            else {
+                offset = (*itMem).first + (*itMem).second;
+                ++itMem;
+            }
+        }
+
+        (*it)->offset = offset;
+
+        for (Clock_T t = (*it)->allocated; t <= maxT; ++t) {
+            const std::map<unsigned int, unsigned int> stack
+                = getStack((*it), t);
+            stacks[t].insert(stack.begin(), stack.end());
+
+            //stacks[t].insert(std::make_pair(offset, (*it)->size));
+        }
+    }
+}
+
+unsigned int Aidge::MemoryManager::getOffset(const std::shared_ptr<Node>& node,
+                                            unsigned int plane) const
+{
+    const std::map<std::shared_ptr<Node>, std::vector<MemoryPlane> >
+        ::const_iterator it = mMemPlanes.find(node);
+
+    if (it == mMemPlanes.end()) {
+        AIDGE_THROW_OR_ABORT(std::runtime_error,
+            "getOffset(): no memory allocated for node name {}", node->name());
+    }
+
+    if (plane >= (*it).second.size()) {
+        AIDGE_THROW_OR_ABORT(std::runtime_error,
+            "getOffset(): plane out of range for node name {}", node->name());
+    }
+
+    return ((*it).second[plane].memSpace->offset + (*it).second[plane].offset);
+}
+
+unsigned int Aidge::MemoryManager::getSize(const std::shared_ptr<Node>& node,
+                                          unsigned int plane) const
+{
+    const std::map<std::shared_ptr<Node>, std::vector<MemoryPlane> >
+        ::const_iterator it = mMemPlanes.find(node);
+
+    if (it == mMemPlanes.end()) {
+        AIDGE_THROW_OR_ABORT(std::runtime_error,
+            "getSize(): no memory allocated for node name {}", node->name());
+    }
+
+    if (plane >= (*it).second.size()) {
+        AIDGE_THROW_OR_ABORT(std::runtime_error,
+            "getSize(): plane out of range for node name {}", node->name());
+    }
+
+    return (*it).second[plane].getSize();
+}
+
+unsigned int Aidge::MemoryManager::getSize(const std::shared_ptr<Node>& node)
+    const
+{
+    const std::map<std::shared_ptr<Node>, std::vector<MemoryPlane> >
+        ::const_iterator it = mMemPlanes.find(node);
+
+    if (it == mMemPlanes.end()) {
+        AIDGE_THROW_OR_ABORT(std::runtime_error,
+            "getSize(): no memory allocated for node name {}", node->name());
+    }
+
+    unsigned int size = 0;
+
+    for (std::vector<MemoryPlane>::const_iterator itPlanes
+        = (*it).second.begin(), itPlanesEnd = (*it).second.end();
+        itPlanes != itPlanesEnd; ++itPlanes)
+    {
+        size += (*itPlanes).getSize();
+    }
+
+    return size;
+}
+
+unsigned int Aidge::MemoryManager::getNbPlanes(const std::shared_ptr<Node>& node)
+    const
+{
+    const std::map<std::shared_ptr<Node>, std::vector<MemoryPlane> >
+        ::const_iterator it = mMemPlanes.find(node);
+    return (it == mMemPlanes.end()) ? 0 : (*it).second.size();
+}
+
+unsigned int Aidge::MemoryManager::getPeakUsage() const {
+    unsigned int peakUsage = 0;
+
+    for (std::vector<std::shared_ptr<MemorySpace> >::const_iterator
+        it = mMemSpaces.begin(), itEnd = mMemSpaces.end(); it != itEnd; ++it)
+    {
+        peakUsage = std::max(peakUsage,
+                             (*it)->offset + (*it)->size);
+    }
+
+    return peakUsage;
+}
+
+Aidge::MemoryManager::Clock_T Aidge::MemoryManager::getMaxLifetime() const {
+    Clock_T maxLifetime = 0;
+
+    for (std::vector<std::shared_ptr<MemorySpace> >::const_iterator
+        it = mMemSpaces.begin(), itEnd = mMemSpaces.end(); it != itEnd; ++it)
+    {
+        maxLifetime = std::max(maxLifetime,
+            std::max((*it)->allocated, (*it)->released));
+    }
+
+    return maxLifetime;
+}
+
+const std::vector<Aidge::MemoryManager::MemoryPlane>&
+Aidge::MemoryManager::getPlanes(const std::shared_ptr<Node>& node) const
+{
+    const std::map<std::shared_ptr<Node>, std::vector<MemoryPlane> >
+        ::const_iterator it = mMemPlanes.find(node);
+
+    if (it == mMemPlanes.end()) {
+        AIDGE_THROW_OR_ABORT(std::runtime_error,
+            "getSize(): no memory allocated for node name {}", node->name());
+    }
+
+    return (*it).second;
+}
+
+Aidge::MemoryManager::MemMap_T
+Aidge::MemoryManager::getPlanes(std::shared_ptr<MemorySpace> memSpace)
+    const
+{
+    MemMap_T planes;
+
+    for (MemMap_T::const_iterator itNode = mMemPlanes.begin(),
+        itNodeEnd = mMemPlanes.end(); itNode != itNodeEnd; ++itNode)
+    {
+        for (std::vector<MemoryPlane>::const_iterator itPlane
+             = (*itNode).second.begin(), itPlaneEnd = (*itNode).second.end();
+             itPlane != itPlaneEnd; ++itPlane)
+        {
+            if ((*itPlane).memSpace == memSpace) {
+                std::map<std::shared_ptr<Node>, std::vector<MemoryPlane> >
+                    ::iterator it;
+                std::tie(it, std::ignore) = planes.insert(
+                    std::make_pair((*itNode).first,
+                                   std::vector<MemoryPlane>()));
+
+                (*it).second.push_back((*itPlane));
+            }
+        }
+    }
+
+    return planes;
+}
+
+unsigned int Aidge::MemoryManager::getNbPlanes(
+    std::shared_ptr<MemorySpace> memSpace) const
+{
+    unsigned int count = 0;
+
+    for (std::map<std::shared_ptr<Node>, std::vector<MemoryPlane> >
+        ::const_iterator itNode = mMemPlanes.begin(),
+        itNodeEnd = mMemPlanes.end(); itNode != itNodeEnd; ++itNode)
+    {
+        for (std::vector<MemoryPlane>::const_iterator itPlane
+             = (*itNode).second.begin(), itPlaneEnd = (*itNode).second.end();
+             itPlane != itPlaneEnd; ++itPlane)
+        {
+            if ((*itPlane).memSpace == memSpace)
+                ++count;
+        }
+    }
+
+    return count;
+}
+
+void Aidge::MemoryManager::tick()
+{
+    ++mClock;
+}
+
+void Aidge::MemoryManager::log(const std::string& fileName) const
+{
+    auto memData = std::unique_ptr<FILE, decltype(&std::fclose)>(std::fopen(fileName.c_str(), "w"), &std::fclose);
+
+    if (!memData) {
+        AIDGE_THROW_OR_ABORT(std::runtime_error,
+            "Could not create memory layout log file: {}", fileName);
+    }
+
+    auto gnuplot = std::unique_ptr<FILE, decltype(&std::fclose)>(std::fopen((fileName + "_plot.gnu").c_str(), "w"), &std::fclose);
+
+    if (!gnuplot) {
+        AIDGE_THROW_OR_ABORT(std::runtime_error,
+            "Could not create memory layout log file: {}", (fileName + "_plot.gnu"));
+    }
+
+    const Clock_T maxLifetime = getMaxLifetime();
+    const unsigned int peakUsage = getPeakUsage();
+
+    fmt::print(gnuplot.get(), "#!/usr/bin/gnuplot\n");
+    fmt::print(gnuplot.get(), "set term pngcairo size 1280,768 noenhanced\n");
+    fmt::print(gnuplot.get(), "set output \"{}\"\n", fileName + "_plot.png");
+    fmt::print(gnuplot.get(), "set xrange [{}:{}]\n", 0, maxLifetime + 1);
+    fmt::print(gnuplot.get(), "set yrange [{}:{}]\n", 0, 1.05 * (peakUsage / 1024.0));
+    fmt::print(gnuplot.get(), "set xlabel \"Time\"\n");
+    fmt::print(gnuplot.get(), "set ylabel \"Memory usage (KWords)\"\n");
+    fmt::print(gnuplot.get(), "set grid\n");
+    fmt::print(gnuplot.get(), "set xtics 1\n");
+    fmt::print(gnuplot.get(), "unset key\n");
+    fmt::print(gnuplot.get(), "set palette rgbformulae 30,31,32\n");
+    fmt::print(gnuplot.get(), "unset colorbox\n");
+    fmt::print(gnuplot.get(), "N={}\n", mMemPlanes.size() + 1);
+
+    unsigned int objectId = 1;
+    unsigned int labelId = 1;
+
+    for (std::map<std::shared_ptr<Node>, std::vector<MemoryPlane> >
+        ::const_iterator it = mMemPlanes.begin(), itEnd = mMemPlanes.end();
+        it != itEnd; ++it)
+    {
+        const std::string name = (*it).first->name();
+        fmt::print(memData.get(), "{}\n", name);
+
+        double minX = -1;
+        unsigned int maxY = 0;
+
+        for (std::vector<MemoryPlane>::const_iterator itPlanes
+             = (*it).second.begin(), itPlanesBegin = (*it).second.begin(),
+            itPlanesEnd = (*it).second.end(); itPlanes != itPlanesEnd;
+            ++itPlanes)
+        {
+            const unsigned int contiguousOffset
+                = (*itPlanes).getContiguousOffset();
+            const unsigned int contiguousSize = (*itPlanes).getContiguousSize();
+            const unsigned int wrappedOffset = (*itPlanes).getWrappedOffset();
+            const unsigned int wrappedSize = (*itPlanes).getWrappedSize();
+
+            const Clock_T allocated = (*itPlanes).allocated;
+            const Clock_T released = (*itPlanes).memSpace->released;
+            const bool isReleased = (released >= 0
+                                && (*itPlanes).memSpace->dependencies.empty());
+
+            fmt::print(memData.get(), "  {} {} ({:#08x}U) -> {} ({:#08x}U)",
+                (itPlanes - itPlanesBegin), contiguousOffset, contiguousOffset,
+                (contiguousOffset + contiguousSize), (contiguousOffset + contiguousSize));
+
+            if (wrappedSize > 0) {
+                fmt::print(memData.get(), " + {} ({:#08x}U) -> {} ({:#08x}U)",
+                    wrappedOffset, wrappedOffset,
+                    (wrappedOffset + wrappedSize), (wrappedOffset + wrappedSize));
+            }
+
+            fmt::print(memData.get(), " [{}] @ {}", (*itPlanes).getSize(), allocated);
+
+            if (isReleased) {
+                fmt::print(memData.get(), " to {}", released);
+            }
+
+            fmt::print(memData.get(), "\n");
+
+            // Gnuplot
+            const double startX = allocated;
+
+            if (startX < minX || minX < 0) {
+                minX = startX;
+                maxY = contiguousOffset + contiguousSize;
+            }
+
+            if ((*itPlanes).size != (*itPlanes).stride) {
+                for (unsigned int offset = contiguousOffset;
+                    offset < contiguousOffset + contiguousSize;
+                    offset += (*itPlanes).stride)
+                {
+                    fmt::print(gnuplot.get(), "set object {} rectangle from {},{} to {},{} fc palette frac ({} * 1./N)\n",
+                        (allocated * 100 + objectId), startX, (offset / 1024.0),
+                        (((isReleased) ? released : maxLifetime) + 1),
+                        (std::min((offset + (*itPlanes).size),
+                                        contiguousOffset + contiguousSize) / 1024.0),
+                        labelId);
+                    ++objectId;
+                }
+            }
+            else {
+                fmt::print(gnuplot.get(), "set object {} rectangle from {},{} to {},{} fc palette frac ({} * 1./N)\n",
+                    (allocated * 100 + objectId), startX, (contiguousOffset / 1024.0),
+                    (((isReleased) ? released : maxLifetime) + 1),
+                    ((contiguousOffset + contiguousSize) / 1024.0),
+                    labelId);
+                ++objectId;
+            }
+
+            if (wrappedSize > 0) {
+                fmt::print(gnuplot.get(), "set object {} rectangle from {},{} to {},{} fc palette frac ({} * 1./N)\n",
+                    (allocated * 100 + objectId), startX, (wrappedOffset / 1024.0),
+                    (((isReleased) ? released : maxLifetime) + 1),
+                    ((wrappedOffset + contiguousSize) / 1024.0),
+                    labelId);
+                ++objectId;
+
+                fmt::print(gnuplot.get(), "set arrow from {},{} to {},{} nohead\n",
+                    startX, (contiguousOffset / 1024.0),
+                    (startX + 0.1), (contiguousOffset / 1024.0));
+
+                fmt::print(gnuplot.get(), "set arrow from {},{} to {},{} nohead\n",
+                    (startX + 0.05), ((contiguousOffset + contiguousSize) / 1024.0),
+                    (startX + 0.05), (wrappedOffset / 1024.0));
+            }
+        }
+
+        fmt::print(gnuplot.get(), "set label {} '{}' at {},{} rotate by 30 font \",8\" offset char 0.5,0.5\n",
+            labelId, name, minX, (maxY / 1024.0));
+        ++labelId;
+
+        fmt::print(memData.get(), "\n");
+    }
+
+    fmt::print(gnuplot.get(), "set arrow from 0,{} to {},{} nohead lc rgb \"red\"\n",
+        (peakUsage / 1024.0), (maxLifetime + 1),
+        (peakUsage / 1024.0));
+
+    fmt::print(gnuplot.get(), "set label {} 'Peak usage = {} KWords' at 0,{} textcolor rgb \"red\" offset char 0.5,0.5\n",
+        labelId, (peakUsage / 1024.0), (peakUsage / 1024.0));
+
+    fmt::print(gnuplot.get(), "plot 0\n");
+}
+
+unsigned int Aidge::MemoryManager::onStack(unsigned int size)
+{
+    unsigned int offset = 0;
+    std::map<unsigned int, unsigned int>::iterator itMem = mMemStack.begin();
+
+    while (true) {
+        if (itMem == mMemStack.end()
+            || (*itMem).first - offset >= size)
+        {
+            mMemStack.insert(std::make_pair(offset, size));
+            break;
+        }
+        else {
+            offset = (*itMem).first + (*itMem).second;
+            ++itMem;
+        }
+    }
+
+    return offset;
+}
+
+unsigned int Aidge::MemoryManager::offStack(unsigned int offset)
+{
+    std::map<unsigned int, unsigned int>::iterator itMem
+        = mMemStack.find(offset);
+
+    if (itMem == mMemStack.end()) {
+        AIDGE_THROW_OR_ABORT(std::runtime_error,
+            "offStack(): offset not found in stack");
+    }
+    else {
+        const unsigned int size = (*itMem).second;
+        mMemStack.erase(offset);
+        return size;
+    }
+}
+
+std::map<unsigned int, unsigned int> Aidge::MemoryManager::getStack(
+    std::shared_ptr<MemorySpace> memSpace,
+    Clock_T clock) const
+{
+    // Find all planes associated to memSpace and index them by their allocated
+    // value in a map
+    std::map<Clock_T, std::vector<MemoryPlane> > planes;
+
+    for (std::map<std::shared_ptr<Node>, std::vector<MemoryPlane> >
+        ::const_iterator itNode = mMemPlanes.begin(),
+        itNodeEnd = mMemPlanes.end(); itNode != itNodeEnd; ++itNode)
+    {
+        for (std::vector<MemoryPlane>::const_iterator itPlane
+             = (*itNode).second.begin(), itPlaneEnd = (*itNode).second.end();
+             itPlane != itPlaneEnd; ++itPlane)
+        {
+            if ((*itPlane).memSpace == memSpace) {
+                std::map<Clock_T, std::vector<MemoryPlane> >::iterator it;
+                std::tie(it, std::ignore) = planes.insert(
+                    std::make_pair((*itPlane).allocated,
+                                   std::vector<MemoryPlane>()));
+
+                (*it).second.push_back((*itPlane));
+            }
+        }
+    }
+
+    // Find the planes allocated at time clock or the one just before
+    // => obtain all the planes that are considered valid at the time clock
+    Clock_T c = clock;
+    std::map<Clock_T, std::vector<MemoryPlane> >::iterator itPlanes;
+
+    do
+        itPlanes = planes.find(c);
+    while (itPlanes == planes.end() && (c--) > 0);
+
+    assert(itPlanes != planes.end());
+
+    // Fill the stack at time clock
+    std::map<unsigned int, unsigned int> stack;
+
+    for (std::vector<MemoryPlane>::const_iterator
+        it = (*itPlanes).second.begin(), itEnd = (*itPlanes).second.end();
+        it != itEnd; ++it)
+    {
+        stack.insert(std::make_pair((*it).getContiguousOffset(),
+                                    (*it).getContiguousSize()));
+
+        if ((*it).getWrappedSize() > 0) {
+            stack.insert(std::make_pair((*it).getWrappedOffset(),
+                                        (*it).getWrappedSize()));
+        }
+    }
+
+    return stack;
+}
+
+std::pair<Aidge::MemoryManager::Clock_T, unsigned int>
+Aidge::MemoryManager::getMaxHole(std::shared_ptr<MemorySpace> memSpace) const
+{
+    std::map<Clock_T, unsigned int> holesSize;
+
+    for (std::map<std::shared_ptr<Node>, std::vector<MemoryPlane> >
+        ::const_iterator itNode = mMemPlanes.begin(),
+        itNodeEnd = mMemPlanes.end(); itNode != itNodeEnd; ++itNode)
+    {
+        for (std::vector<MemoryPlane>::const_iterator itPlane
+             = (*itNode).second.begin(), itPlaneEnd = (*itNode).second.end();
+             itPlane != itPlaneEnd; ++itPlane)
+        {
+            if ((*itPlane).memSpace == memSpace) {
+                const unsigned int holeSize = memSpace->size
+                    - (*itPlane).getContiguousSize()
+                    - (*itPlane).getWrappedSize();
+
+                std::map<Clock_T, unsigned int>::iterator it;
+                bool newInsert;
+                std::tie(it, newInsert) = holesSize.insert(
+                    std::make_pair((*itPlane).allocated, holeSize));
+
+                if (!newInsert) {
+                    // Another plane exists at the same time, one must substract
+                    // the size of this other plane from the hole size
+                    (*it).second = std::max(0, static_cast<int>((*it).second)
+                        - static_cast<int>((*itPlane).getContiguousSize())
+                        - static_cast<int>((*itPlane).getWrappedSize()));
+                }
+            }
+        }
+    }
+
+    return *std::max_element(holesSize.begin(),
+                             holesSize.end(),
+                             [](const auto& left, const auto& right) {
+                                return std::max(left.second, right.second);
+                             });
+}
diff --git a/src/scheduler/Scheduler.cpp b/src/scheduler/Scheduler.cpp
index 7d2db24eb3637fc7f64d8c9ba16ce388b7e9204e..7562944a98865c21296396473a0b39ec0b591dbc 100644
--- a/src/scheduler/Scheduler.cpp
+++ b/src/scheduler/Scheduler.cpp
@@ -306,6 +306,114 @@ void Aidge::SequentialScheduler::generateScheduling(bool verbose) {
     }
 }
 
+/**
+ * This version is a simplified version without special handling of concatenation.
+*/
+Aidge::MemoryManager Aidge::SequentialScheduler::generateMemory(bool incProducers, bool wrapAroundBuffer) const {
+    MemoryManager memManager;
+
+    for (const auto& shedule : mStaticSchedule) {
+        for (const auto& node : shedule) {
+            if (!incProducers && node->type() == Producer_Op::Type) {
+                memManager.releaseDependencies(node);
+                continue;
+            }
+            
+            const auto childs = node->getChildren();
+            const auto op = std::static_pointer_cast<OperatorTensor>(node->getOperator());
+
+            std::vector<const MemoryManager::MemoryPlane*> wrapAroundMemPlane;
+
+            // Allocate a memory plane for each node's output
+            for (IOIndex_t outputIdx = 0; outputIdx < node->nbOutputs(); ++outputIdx) {
+                const size_t requiredSize = op->getRequiredMemory(outputIdx, {});
+
+                // By default, specifies a fully monolithic memory block
+                size_t size = requiredSize;
+                size_t stride = 0;
+                size_t length = 1;
+                size_t count = 1;
+
+                if (op->getOutput(outputIdx) && op->getOutput(outputIdx)->dims().size() > 3) {
+                    // If it is possible, assume a NCHW layout
+                    size = op->getOutput(outputIdx)->dims().end()[-3];
+                    stride = size;
+                    length = op->getOutput(outputIdx)->dims().end()[-1];
+                    count = op->getOutput(outputIdx)->dims().end()[-2];
+                }
+                
+                // Check if wrap around buffer is possible for this node
+                // (re-using previous node outputs memory for this node outputs).
+                // => only if this node is the only child of its parent(s)
+                size_t wrapAroundSize = 0;
+                size_t wrapAroundExtra = 0;
+                wrapAroundMemPlane.push_back(nullptr);
+
+                // Select the best parent among all allocable nodes for 
+                // reallocation, which is the one with most memory (in order
+                // to minimize the reallocation size).
+                IOIndex_t inputIdx = 0;
+                for (const auto& parent : node->dataInputs()) {
+                    if (parent.first && parent.first->getChildren(parent.second).size() == 1
+                        // there might be no existing plane if the parent was
+                        // not yet scheduled (because it may be a recurrent connection)
+                        && memManager.getNbPlanes(parent.first) >= parent.first->nbOutputs()
+                        // memSpace should not be already released
+                        && memManager.getPlanes(parent.first).end()[-parent.first->nbOutputs()+parent.second].memSpace->released == -1)
+                    {
+                        const bool isWrappable = (op->getNbRequiredProtected(inputIdx) < op->getNbRequiredData(inputIdx));
+                        const MemoryManager::MemoryPlane& memPlane = memManager.getPlanes(parent.first).end()[-parent.first->nbOutputs()+parent.second];
+
+                        if (isWrappable || !memManager.isWrapAround(
+                                    memPlane.memSpace,
+                                    memPlane.getFinalOffset()
+                                        - memPlane.memSpace->offset,
+                                    requiredSize))
+                        {
+                            if (memPlane.getSize() > wrapAroundSize + op->getNbRequiredProtected(inputIdx)
+                                && std::find(wrapAroundMemPlane.begin(), wrapAroundMemPlane.end(), &memPlane) == wrapAroundMemPlane.end())
+                            {
+                                wrapAroundSize = memPlane.getSize() - op->getNbRequiredProtected(inputIdx);
+                                if (requiredSize > wrapAroundSize) {
+                                    wrapAroundExtra = requiredSize - wrapAroundSize;
+                                }
+                                wrapAroundMemPlane[outputIdx] = &memPlane;
+                            }
+
+                            if (wrapAroundExtra == 0) {
+                                break;
+                            }
+                        }
+                    }
+                    ++inputIdx;
+                }
+
+                // MemoryPlane to (re)use
+                const MemoryManager::MemoryPlane& memPlane
+                    = (wrapAroundBuffer && wrapAroundSize > 0)
+                        ? (*wrapAroundMemPlane[outputIdx]) :
+                            memManager.allocate(requiredSize, childs, stride, length, count);
+
+                if (wrapAroundBuffer && wrapAroundSize > 0) {
+                    memManager.reallocate(memPlane,
+                        node, 0,
+                        requiredSize, true, wrapAroundExtra, childs, stride, length, count);
+                }
+                else {
+                    memManager.reallocate(memPlane.memSpace,
+                        node, memPlane.offset,
+                        requiredSize, false, 0, childs, stride, length, count);
+                }
+            }
+
+            memManager.releaseDependencies(node);
+            memManager.tick();
+        }
+    }
+
+    return memManager;
+}
+
 void Aidge::SequentialScheduler::connectInputs(std::vector<std::shared_ptr<Aidge::Tensor>> data){
     // This version of connect inputs only connects tensor inputs in input data producers.
     auto inputNodes = mGraphView->getOrderedInputs();
@@ -362,8 +470,8 @@ void Aidge::SequentialScheduler::forward(bool forwardDims, bool verbose, std::ve
 }
 
 void Aidge::SequentialScheduler::saveSchedulingDiagram(const std::string& fileName) const {
-    FILE* fp = std::fopen((fileName + ".mmd").c_str(), "w");
-    std::fprintf(fp, "gantt\ndateFormat x\naxisFormat %%Q µs\n\n");
+    auto fp = std::unique_ptr<FILE, decltype(&std::fclose)>(std::fopen((fileName + ".mmd").c_str(), "w"), &std::fclose);
+    std::fprintf(fp.get(), "gantt\ndateFormat x\naxisFormat %%Q µs\n\n");
 
     if (!mScheduling.empty()) {
         const std::map<std::shared_ptr<Node>, std::string> namePtrTable
@@ -371,15 +479,14 @@ void Aidge::SequentialScheduler::saveSchedulingDiagram(const std::string& fileNa
         const auto globalStart = mScheduling[0].start;
 
         for (const auto& element : mScheduling) {
-            std::fprintf(fp, "%s :%ld, %ld\n",
+            std::fprintf(fp.get(), "%s :%ld, %ld\n",
                          namePtrTable.at(element.node).c_str(),
                          std::chrono::duration_cast<std::chrono::microseconds>(element.start - globalStart).count(),
                          std::chrono::duration_cast<std::chrono::microseconds>(element.end - globalStart).count());
         }
     }
 
-    std::fprintf(fp, "\n");
-    std::fclose(fp);
+    std::fprintf(fp.get(), "\n");
 }
 
 std::set<std::shared_ptr<Aidge::Node>> Aidge::SequentialScheduler::getConsumers(
diff --git a/unit_tests/scheduler/Test_MemoryManager.cpp b/unit_tests/scheduler/Test_MemoryManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a4941203644b7ba291682f3932926a36fa83b745
--- /dev/null
+++ b/unit_tests/scheduler/Test_MemoryManager.cpp
@@ -0,0 +1,599 @@
+/********************************************************************************
+ * Copyright (c) 2023 CEA-List
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
+
+#include <catch2/catch_test_macros.hpp>
+
+#include "aidge/operator/GenericOperator.hpp"
+#include "aidge/scheduler/MemoryManager.hpp"
+
+using namespace Aidge;
+
+TEST_CASE("allocate1", "[MemoryManager]") {
+    std::shared_ptr<Node> node1
+        = GenericOperator("Fictive", 0, 0, 0, "node1");
+    std::shared_ptr<Node> node2
+        = GenericOperator("Fictive", 0, 0, 0, "node2");
+    std::shared_ptr<Node> node3
+        = GenericOperator("Fictive", 0, 0, 0, "node3");
+    std::shared_ptr<Node> node4
+        = GenericOperator("Fictive", 0, 0, 0, "node4");
+
+    MemoryManager memManager;
+    memManager.allocate(node1, 1024, {node2});
+
+    REQUIRE(memManager.getPeakUsage() == 1024);
+    REQUIRE(memManager.getPlanes(node1).size() == 1);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->allocated == 0);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->offset == 0);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->size == 1024);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node2}));
+    REQUIRE(memManager.getPlanes(node1).back().offset == 0);
+    REQUIRE(memManager.getPlanes(node1).back().size == 1024);
+    REQUIRE(memManager.getPlanes(node1).back().getLimit() == 1024);
+    REQUIRE(memManager.getPlanes(node1).back().count == 1);
+    REQUIRE(memManager.getPlanes(node1).back().length == 1);
+    REQUIRE(memManager.getPlanes(node1).back().stride == 1024);
+
+    memManager.releaseDependencies(node1);
+
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node2}));
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->released == -1);
+
+    memManager.tick();
+    memManager.release(node1);
+
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->allocated == 0);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->released == 1);
+
+    memManager.allocate(node2, 2048, {node3});
+
+    REQUIRE(memManager.getPeakUsage() == 1024 + 2048);
+    REQUIRE(memManager.getPlanes(node2).size() == 1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->allocated == 1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->offset == 1024);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->size == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node3}));
+    REQUIRE(memManager.getPlanes(node2).back().offset == 0);
+    REQUIRE(memManager.getPlanes(node2).back().size == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().getLimit() == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().count == 1);
+    REQUIRE(memManager.getPlanes(node2).back().length == 1);
+    REQUIRE(memManager.getPlanes(node2).back().stride == 2048);
+
+    memManager.releaseDependencies(node2);
+
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node3}));
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->dependencies.empty());
+
+    memManager.tick();
+    memManager.release(node2);
+
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->allocated == 1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->released == 2);
+
+    const std::vector<MemoryManager::MemoryPlane>& memPlanes
+        = memManager.getPlanes(node2);
+
+    REQUIRE(memPlanes.size() == 1);
+
+    memManager.reallocate(memPlanes.back().memSpace,
+                          node3, 512, 2048, false, 0, {node4});
+
+    REQUIRE(memManager.getPeakUsage() == 1024 + 2048 + 512);
+    REQUIRE(memManager.getPlanes(node3).size() == 1);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->allocated == 1);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->offset == 1024);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->size == 2048 + 512);
+    REQUIRE(memManager.getPlanes(node3).back().offset == 512);
+    REQUIRE(memManager.getPlanes(node3).back().size == 2048);
+    REQUIRE(memManager.getPlanes(node3).back().getLimit() == 2048);
+    REQUIRE(memManager.getPlanes(node3).back().count == 1);
+    REQUIRE(memManager.getPlanes(node3).back().length == 1);
+    REQUIRE(memManager.getPlanes(node3).back().stride == 2048);
+
+    memManager.releaseDependencies(node3);
+    memManager.tick();
+    memManager.release(node3);
+
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->allocated == 1);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->released == 3);
+
+    memManager.allocate(node4, 1024);
+
+    REQUIRE(memManager.getPeakUsage() == 1024 + 2048 + 512);
+    REQUIRE(memManager.getPlanes(node4).size() == 1);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->allocated == 3);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->offset == 0);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->size == 1024);
+    REQUIRE(memManager.getPlanes(node4).back().offset == 0);
+    REQUIRE(memManager.getPlanes(node4).back().size == 1024);
+    REQUIRE(memManager.getPlanes(node4).back().getLimit() == 1024);
+    REQUIRE(memManager.getPlanes(node4).back().count == 1);
+    REQUIRE(memManager.getPlanes(node4).back().length == 1);
+    REQUIRE(memManager.getPlanes(node4).back().stride == 1024);
+
+    memManager.releaseDependencies(node4);
+    memManager.tick();
+    memManager.release(node4);
+
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->allocated == 3);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->released == 4);
+
+    memManager.log("MemoryManager_allocate1.log");
+}
+
+TEST_CASE("allocate2", "[MemoryManager]") {
+    std::shared_ptr<Node> node1
+        = GenericOperator("Fictive", 0, 0, 0, "node1");
+    std::shared_ptr<Node> node2
+        = GenericOperator("Fictive", 0, 0, 0, "node2");
+    std::shared_ptr<Node> node3
+        = GenericOperator("Fictive", 0, 0, 0, "node3");
+    std::shared_ptr<Node> node4
+        = GenericOperator("Fictive", 0, 0, 0, "node4");
+
+    MemoryManager memManager;
+    memManager.allocate(node1, 1024, {node2});
+
+    REQUIRE(memManager.getPeakUsage() == 1024);
+    REQUIRE(memManager.getPlanes(node1).size() == 1);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->allocated == 0);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->offset == 0);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->size == 1024);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node2}));
+    REQUIRE(memManager.getPlanes(node1).back().offset == 0);
+    REQUIRE(memManager.getPlanes(node1).back().size == 1024);
+    REQUIRE(memManager.getPlanes(node1).back().getLimit() == 1024);
+    REQUIRE(memManager.getPlanes(node1).back().count == 1);
+    REQUIRE(memManager.getPlanes(node1).back().length == 1);
+    REQUIRE(memManager.getPlanes(node1).back().stride == 1024);
+
+    memManager.releaseDependencies(node1);
+
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node2}));
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->released == -1);
+
+    memManager.tick();
+    memManager.release(node1);
+
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->allocated == 0);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->released == 1);
+
+    memManager.allocate(node2, 2048, {node3});
+
+    REQUIRE(memManager.getPeakUsage() == 1024 + 2048);
+    REQUIRE(memManager.getPlanes(node2).size() == 1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->allocated == 1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->offset == 1024);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->size == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node3}));
+    REQUIRE(memManager.getPlanes(node2).back().offset == 0);
+    REQUIRE(memManager.getPlanes(node2).back().size == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().getLimit() == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().count == 1);
+    REQUIRE(memManager.getPlanes(node2).back().length == 1);
+    REQUIRE(memManager.getPlanes(node2).back().stride == 2048);
+
+    memManager.releaseDependencies(node2);
+
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node3}));
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->dependencies.empty());
+
+    memManager.tick();
+    memManager.release(node2);
+
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->allocated == 1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->released == 2);
+
+    const std::vector<MemoryManager::MemoryPlane>& memPlanes
+        = memManager.getPlanes(node1);
+
+    REQUIRE(memPlanes.size() == 1);
+
+    memManager.reallocate(memPlanes.back().memSpace,
+                          node3, 512, 2048, false, 0, {node4});
+
+    REQUIRE(memManager.getPeakUsage() == 2048 + 2048 + 512);
+    REQUIRE(memManager.getPlanes(node3).size() == 1);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->allocated == 0);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->offset == 0);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->size == 2048 + 512);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node4}));
+    REQUIRE(memManager.getPlanes(node3).back().offset == 512);
+    REQUIRE(memManager.getPlanes(node3).back().size == 2048);
+    REQUIRE(memManager.getPlanes(node3).back().getLimit() == 2048);
+    REQUIRE(memManager.getPlanes(node3).back().count == 1);
+    REQUIRE(memManager.getPlanes(node3).back().length == 1);
+    REQUIRE(memManager.getPlanes(node3).back().stride == 2048);
+
+    // node2 memSpace should have moved
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->allocated == 1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->released == 2);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->offset == 2048 + 512);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->size == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node3}));
+    REQUIRE(memManager.getPlanes(node2).back().offset == 0);
+    REQUIRE(memManager.getPlanes(node2).back().size == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().getLimit() == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().count == 1);
+    REQUIRE(memManager.getPlanes(node2).back().length == 1);
+    REQUIRE(memManager.getPlanes(node2).back().stride == 2048);
+
+    memManager.releaseDependencies(node3);
+
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node4}));
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->dependencies.empty());
+
+    memManager.tick();
+    memManager.release(node3);
+
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->allocated == 0);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->released == 3);
+
+    memManager.allocate(node4, 1024);
+
+    REQUIRE(memManager.getPeakUsage() == 2048 + 2048 + 512);
+    REQUIRE(memManager.getPlanes(node4).size() == 1);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->allocated == 3);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->offset == 2048 + 512);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->size == 1024);
+    REQUIRE(memManager.getPlanes(node4).back().offset == 0);
+    REQUIRE(memManager.getPlanes(node4).back().size == 1024);
+    REQUIRE(memManager.getPlanes(node4).back().getLimit() == 1024);
+    REQUIRE(memManager.getPlanes(node4).back().count == 1);
+    REQUIRE(memManager.getPlanes(node4).back().length == 1);
+    REQUIRE(memManager.getPlanes(node4).back().stride == 1024);
+
+    memManager.releaseDependencies(node4);
+    memManager.tick();
+    memManager.release(node4);
+
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->allocated == 3);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->released == 4);
+
+    memManager.log("MemoryManager_allocate2.log");
+}
+
+TEST_CASE("allocate3", "[MemoryManager]") {
+    std::shared_ptr<Node> node1
+        = GenericOperator("Fictive", 0, 0, 0, "node1");
+    std::shared_ptr<Node> node2
+        = GenericOperator("Fictive", 0, 0, 0, "node2");
+    std::shared_ptr<Node> node3
+        = GenericOperator("Fictive", 0, 0, 0, "node3");
+    std::shared_ptr<Node> node4
+        = GenericOperator("Fictive", 0, 0, 0, "node4");
+
+    MemoryManager memManager;
+    memManager.allocate(node1, 1024, {node2});
+
+    REQUIRE(memManager.getPeakUsage() == 1024);
+    REQUIRE(memManager.getPlanes(node1).size() == 1);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->allocated == 0);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->offset == 0);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->size == 1024);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node2}));
+    REQUIRE(memManager.getPlanes(node1).back().offset == 0);
+    REQUIRE(memManager.getPlanes(node1).back().size == 1024);
+    REQUIRE(memManager.getPlanes(node1).back().getLimit() == 1024);
+    REQUIRE(memManager.getPlanes(node1).back().count == 1);
+    REQUIRE(memManager.getPlanes(node1).back().length == 1);
+    REQUIRE(memManager.getPlanes(node1).back().stride == 1024);
+
+    memManager.releaseDependencies(node1);
+
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node2}));
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->released == -1);
+
+    memManager.tick();
+    memManager.release(node1);
+
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->allocated == 0);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->released == 1);
+
+    memManager.allocate(node2, 2048, {node3});
+
+    REQUIRE(memManager.getPeakUsage() == 1024 + 2048);
+    REQUIRE(memManager.getPlanes(node2).size() == 1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->allocated == 1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->offset == 1024);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->size == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node3}));
+    REQUIRE(memManager.getPlanes(node2).back().offset == 0);
+    REQUIRE(memManager.getPlanes(node2).back().size == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().getLimit() == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().count == 1);
+    REQUIRE(memManager.getPlanes(node2).back().length == 1);
+    REQUIRE(memManager.getPlanes(node2).back().stride == 2048);
+
+    memManager.releaseDependencies(node2);
+
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node3}));
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->dependencies.empty());
+
+    memManager.tick();
+    memManager.release(node2);
+
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->allocated == 1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->released == 2);
+
+    const std::vector<MemoryManager::MemoryPlane>& memPlanes
+        = memManager.getPlanes(node1);
+
+    REQUIRE(memPlanes.size() == 1);
+
+    memManager.reallocate(memPlanes.back().memSpace,
+                          node3, 512, 2048, false);
+
+    REQUIRE(memManager.getPeakUsage() == 2048 + 2048 + 512);
+    REQUIRE(memManager.getPlanes(node3).size() == 1);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->allocated == 0);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->offset == 0);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->size == 2048 + 512);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->dependencies.empty());
+    REQUIRE(memManager.getPlanes(node3).back().offset == 512);
+    REQUIRE(memManager.getPlanes(node3).back().size == 2048);
+    REQUIRE(memManager.getPlanes(node3).back().getLimit() == 2048);
+    REQUIRE(memManager.getPlanes(node3).back().count == 1);
+    REQUIRE(memManager.getPlanes(node3).back().length == 1);
+    REQUIRE(memManager.getPlanes(node3).back().stride == 2048);
+
+    // node2 memSpace should have moved
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->allocated == 1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->released == 2);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->offset == 2048 + 512);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->size == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node3}));
+    REQUIRE(memManager.getPlanes(node2).back().offset == 0);
+    REQUIRE(memManager.getPlanes(node2).back().size == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().getLimit() == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().count == 1);
+    REQUIRE(memManager.getPlanes(node2).back().length == 1);
+    REQUIRE(memManager.getPlanes(node2).back().stride == 2048);
+
+    memManager.releaseDependencies(node3);
+
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->dependencies.empty());
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->dependencies.empty());
+
+    memManager.tick();
+    memManager.release(node3);
+
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->allocated == 0);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->released == 3);
+
+    memManager.reallocate(memPlanes.back().memSpace,
+                          node4, 256, 1024, false);
+
+    REQUIRE(memManager.getPeakUsage() == 2048 + 2048 + 512);
+    REQUIRE(memManager.getPlanes(node4).size() == 1);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->allocated == 0);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->offset == 0);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->size == 2048 + 512);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->dependencies.empty());
+    REQUIRE(memManager.getPlanes(node4).back().offset == 256);
+    REQUIRE(memManager.getPlanes(node4).back().size == 1024);
+    REQUIRE(memManager.getPlanes(node4).back().getLimit() == 2048 + 256);
+    REQUIRE(memManager.getPlanes(node4).back().count == 1);
+    REQUIRE(memManager.getPlanes(node4).back().length == 1);
+    REQUIRE(memManager.getPlanes(node4).back().stride == 1024);
+
+    // node2 memSpace should not have moved
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->allocated == 1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->released == 2);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->offset == 2048 + 512);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->size == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().offset == 0);
+    REQUIRE(memManager.getPlanes(node2).back().size == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().getLimit() == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().count == 1);
+    REQUIRE(memManager.getPlanes(node2).back().length == 1);
+    REQUIRE(memManager.getPlanes(node2).back().stride == 2048);
+
+    memManager.releaseDependencies(node4);
+    memManager.tick();
+    memManager.release(node4);
+
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->allocated == 0);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->released == 4);
+
+    memManager.log("MemoryManager_allocate3.log");
+}
+
+TEST_CASE("allocate3_wrapAround", "[MemoryManager]") {
+    std::shared_ptr<Node> node1
+        = GenericOperator("Fictive", 0, 0, 0, "node1");
+    std::shared_ptr<Node> node2
+        = GenericOperator("Fictive", 0, 0, 0, "node2");
+    std::shared_ptr<Node> node3
+        = GenericOperator("Fictive", 0, 0, 0, "node3");
+    std::shared_ptr<Node> node4
+        = GenericOperator("Fictive", 0, 0, 0, "node4");
+
+    MemoryManager memManager;
+    memManager.allocate(node1, 1024, {node2});
+
+    REQUIRE(memManager.getPeakUsage() == 1024);
+    REQUIRE(memManager.getPlanes(node1).size() == 1);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->allocated == 0);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->offset == 0);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->size == 1024);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node2}));
+    REQUIRE(memManager.getPlanes(node1).back().offset == 0);
+    REQUIRE(memManager.getPlanes(node1).back().size == 1024);
+    REQUIRE(memManager.getPlanes(node1).back().getLimit() == 1024);
+    REQUIRE(memManager.getPlanes(node1).back().count == 1);
+    REQUIRE(memManager.getPlanes(node1).back().length == 1);
+    REQUIRE(memManager.getPlanes(node1).back().stride == 1024);
+
+    memManager.releaseDependencies(node1);
+
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node2}));
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->released == -1);
+
+    memManager.tick();
+    memManager.release(node1);
+
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->allocated == 0);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->released == 1);
+
+    memManager.allocate(node2, 2048, {node3});
+
+    REQUIRE(memManager.getPeakUsage() == 1024 + 2048);
+    REQUIRE(memManager.getPlanes(node2).size() == 1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->allocated == 1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->offset == 1024);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->size == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node3}));
+    REQUIRE(memManager.getPlanes(node2).back().offset == 0);
+    REQUIRE(memManager.getPlanes(node2).back().size == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().getLimit() == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().count == 1);
+    REQUIRE(memManager.getPlanes(node2).back().length == 1);
+    REQUIRE(memManager.getPlanes(node2).back().stride == 2048);
+
+    memManager.releaseDependencies(node2);
+
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node3}));
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node1).back().memSpace->dependencies.empty());
+
+    memManager.tick();
+    memManager.release(node2);
+
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->allocated == 1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->released == 2);
+
+    const std::vector<MemoryManager::MemoryPlane>& memPlanes
+        = memManager.getPlanes(node1);
+
+    REQUIRE(memPlanes.size() == 1);
+
+    memManager.reallocate(memPlanes.back().memSpace,
+                          node3, 512, 2048, true);
+
+    REQUIRE(memManager.getPeakUsage() == 2048 + 2048);
+    REQUIRE(memManager.getPlanes(node3).size() == 1);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->allocated == 0);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->offset == 0);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->size == 2048);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->dependencies.empty());
+    REQUIRE(memManager.getPlanes(node3).back().offset == 512);
+    REQUIRE(memManager.getPlanes(node3).back().size == 2048);
+    REQUIRE(memManager.getPlanes(node3).back().getLimit() == 2048 - 512);
+    REQUIRE(memManager.getPlanes(node3).back().count == 1);
+    REQUIRE(memManager.getPlanes(node3).back().length == 1);
+    REQUIRE(memManager.getPlanes(node3).back().stride == 2048);
+
+    // node2 memSpace should have moved
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->allocated == 1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->released == 2);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->offset == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->size == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->dependencies ==
+        std::set<std::shared_ptr<Node> >({node3}));
+    REQUIRE(memManager.getPlanes(node2).back().offset == 0);
+    REQUIRE(memManager.getPlanes(node2).back().size == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().getLimit() == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().count == 1);
+    REQUIRE(memManager.getPlanes(node2).back().length == 1);
+    REQUIRE(memManager.getPlanes(node2).back().stride == 2048);
+
+    memManager.releaseDependencies(node3);
+
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->dependencies.empty());
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->dependencies.empty());
+
+    memManager.tick();
+    memManager.release(node3);
+
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->allocated == 0);
+    REQUIRE(memManager.getPlanes(node3).back().memSpace->released == 3);
+
+    memManager.reallocate(memPlanes.back().memSpace,
+                          node4, 1024, 1792, true);
+
+    REQUIRE(memManager.getPeakUsage() == 2048 + 2048);
+    REQUIRE(memManager.getPlanes(node4).size() == 1);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->allocated == 0);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->released == -1);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->offset == 0);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->size == 2048);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->dependencies.empty());
+    REQUIRE(memManager.getPlanes(node4).back().offset == 1024);
+    REQUIRE(memManager.getPlanes(node4).back().size == 1792);
+    REQUIRE(memManager.getPlanes(node4).back().getLimit() == 2048 - 1024);
+    REQUIRE(memManager.getPlanes(node4).back().count == 1);
+    REQUIRE(memManager.getPlanes(node4).back().length == 1);
+    REQUIRE(memManager.getPlanes(node4).back().stride == 1792);
+
+    // node2 memSpace should not have moved
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->allocated == 1);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->released == 2);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->offset == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().memSpace->size == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().offset == 0);
+    REQUIRE(memManager.getPlanes(node2).back().size == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().getLimit() == 2048);
+    REQUIRE(memManager.getPlanes(node2).back().count == 1);
+    REQUIRE(memManager.getPlanes(node2).back().length == 1);
+    REQUIRE(memManager.getPlanes(node2).back().stride == 2048);
+
+    memManager.releaseDependencies(node4);
+    memManager.tick();
+    memManager.release(node4);
+
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->allocated == 0);
+    REQUIRE(memManager.getPlanes(node4).back().memSpace->released == 4);
+
+    memManager.log("MemoryManager_allocate3_wrapAround.log");
+}