diff --git a/include/aidge/data/Tensor.hpp b/include/aidge/data/Tensor.hpp
index 3f8b6f5c65a2359c4cb54beedeee38c34cc7a9e0..61f2e0b5175d5294aa15530208517c77255e2b34 100644
--- a/include/aidge/data/Tensor.hpp
+++ b/include/aidge/data/Tensor.hpp
@@ -12,10 +12,12 @@
 #ifndef AIDGE_CORE_DATA_TENSOR_H_
 #define AIDGE_CORE_DATA_TENSOR_H_
 
+#include <cstddef>      // std::size_t
 #include <cstring>
+#include <functional>   // std::multiplies
 #include <set>
 #include <memory>
-#include <numeric>   // std::accumulate
+#include <numeric>      // std::accumulate
 #include <string>
 #include <type_traits>  // std::is_arithmetic
 #include <vector>
@@ -35,15 +37,17 @@ namespace Aidge {
 class Tensor : public Data,
                public Registrable<Tensor, std::tuple<std::string, DataType>, std::shared_ptr<TensorImpl>(DeviceIdx_t device, std::vector<DimSize_t> dims)> {
    private:
-    DataType mDataType; /** enum to specify data type. */
+    DataType mDataType = DataType::Float32; /** enum to specify data type. */
     std::vector<DimSize_t> mDims; /** Dimensions of the tensor. */
     std::vector<DimSize_t> mStrides; /** Stride dimensions of the tensor. */
-    std::shared_ptr<TensorImpl> mImpl; /** Pointer to the actual data implementation. */
+    std::shared_ptr<TensorImpl> mImpl = nullptr; /** Pointer to the actual data implementation. */
     std::size_t mImplOffset = 0;
-    std::shared_ptr<Tensor> mGrad; /** Pointer to the associated gradient Tensor instance. */
+    std::shared_ptr<Tensor> mGrad = nullptr; /** Pointer to the associated gradient Tensor instance. */
 
     // Cached data
-    std::size_t mSize = 0;    /** Number of elements in the Tensor. */
+    /// @brief Number of elements in the Tensor.
+    std::size_t mSize;
+    /// @brief Whether or not data are contiguous in memory.
     bool mContiguous = true;
 
    public:
@@ -51,64 +55,48 @@ class Tensor : public Data,
 
     /**
      * @brief Construct a new empty Tensor object.
-     * @param dataType Sets the type of inserted data.
+     * It has the features of an undefined scalar.
      */
-    Tensor(DataType dataType = DataType::Float32)
+    Tensor(DataType dtype = DataType::Float32)
         : Data(Type),
-          mDataType(dataType)
+          mDataType(dtype),
+          mDims(std::vector<DimSize_t>({})),
+          mStrides({1}),
+          mSize(1)
     {
         // ctor
     }
 
     /**
-     * @brief Construct a new Tensor object from dimensions.
+     * @brief Construct a new Tensor object from an arithmetic parameter.
      *
-     * @param dims dimensions of the tensor
-     * @param dataType datatype of the tensor (default = DataType::Float32)
+     * @tparam T Type of the input parameter.
+     * @tparam VT Decayed type of the input paramter.
+     * @param val Input value.
      */
-    Tensor(const std::vector<DimSize_t>& dims, DataType dataType = DataType::Float32)
+    template<typename T,
+             typename VT = std::enable_if_t<std::is_arithmetic<T>::value, std::decay_t<T>>>
+    Tensor(T val)
         : Data(Type),
-          mDataType(dataType),
-          mDims(dims)
+          mDataType(NativeType<VT>::type),
+          mDims({}),
+          mStrides({1}),
+          mImpl(Registrar<Tensor>::create({"cpu", NativeType<VT>::type})(0, std::vector<std::size_t>())),
+          mSize(1)
     {
-        computeSize();
+        *static_cast<VT*>(mImpl->rawPtr()) = static_cast<VT>(val);
     }
 
     /**
-     * @brief Construct a new Tensor object from another one (shallow copy).
-     * Data memory is not copied, but shared between the new Tensor and the
-     * initial one.
+     * @brief Construct a new Tensor object from dimensions.
      *
-     * @param otherTensor
+     * @param dims dimensions of the tensor
      */
-    Tensor(const Tensor&)            = default;
-    Tensor(Tensor&&)            = default;
-
-    /**
-     * Perform a deep copy of the tensor.
-    */
-    Tensor clone() const {
-        Tensor newTensor(*this);
-        if (!newTensor.isContiguous()) {
-            newTensor.makeContiguous();
-        }
-        else {
-            std::shared_ptr<TensorImpl> newImpl = Registrar<Tensor>::create({mImpl->backend(), mDataType})(mImpl->device().second, mDims);
-            newImpl->copy(mImpl->rawPtr(mImplOffset), mSize);
-            newTensor.setImpl(newImpl);
-        }
-        return newTensor;
-    }
-
-    template<typename T,
-            typename VT = std::enable_if_t<std::is_arithmetic<T>::value, std::decay_t<T>>>
-    Tensor(T val)
-        : Data(Type),
-          mDataType(NativeType<VT>::type),
-          mDims({}), mStrides({1}),
-          mImpl(Registrar<Tensor>::create({"cpu", NativeType<VT>::type})(0, std::vector<std::size_t>())),
-          mSize(1) {
-        *static_cast<VT*>(mImpl->rawPtr()) = static_cast<VT>(val);
+    Tensor(const std::vector<DimSize_t>& dims)
+        : Data(Type)
+    {
+        // set mDims, mStrides, mContiguous, mSize
+        resize(dims);
     }
 
     /**
@@ -123,20 +111,11 @@ class Tensor : public Data,
           mDims({SIZE_0}),
           mStrides({1}),
           mImpl(Registrar<Tensor>::create({"cpu", NativeType<T>::type})(0, {SIZE_0})),
-          mSize(SIZE_0) {
+          mSize(SIZE_0)
+    {
         mImpl->copyFromHost(&arr.data[0], SIZE_0);
     }
 
-    template <typename T, std::size_t SIZE_0>
-    constexpr Tensor &operator=(Array1D<T, SIZE_0> &&arr) {
-        resize({SIZE_0});
-        if (!mImpl) {
-            mImpl = Registrar<Tensor>::create({"cpu", NativeType<T>::type})(0, {SIZE_0});
-        }
-        mImpl->copyFromHost(&arr.data[0], SIZE_0, mImplOffset);
-        return *this;
-    }
-
     /**
      * @brief Construct a new Tensor object from the 2-dimensions Array helper.
      * @tparam T datatype
@@ -154,16 +133,6 @@ class Tensor : public Data,
         mImpl->copyFromHost(&arr.data[0][0], SIZE_0 * SIZE_1);
     }
 
-    template <typename T, std::size_t SIZE_0, std::size_t SIZE_1>
-    constexpr Tensor &operator=(Array2D<T, SIZE_0, SIZE_1> &&arr) {
-        resize({SIZE_0, SIZE_1});
-        if (!mImpl) {
-            mImpl = Registrar<Tensor>::create({"cpu", NativeType<T>::type})(0, {SIZE_0, SIZE_1});
-        }
-        mImpl->copyFromHost(&arr.data[0][0], SIZE_0 * SIZE_1, mImplOffset);
-        return *this;
-    }
-
     /**
      * @brief Construct a new Tensor object from the 3-dimensions Array helper.
      * @tparam T datatype
@@ -182,16 +151,6 @@ class Tensor : public Data,
         mImpl->copyFromHost(&arr.data[0][0][0], SIZE_0 * SIZE_1 * SIZE_2);
     }
 
-    template <typename T, std::size_t SIZE_0, std::size_t SIZE_1, std::size_t SIZE_2>
-    constexpr Tensor &operator=(Array3D<T, SIZE_0, SIZE_1, SIZE_2> &&arr) {
-        resize({SIZE_0, SIZE_1, SIZE_2});
-        if (!mImpl) {
-            mImpl = Registrar<Tensor>::create({"cpu", NativeType<T>::type})(0, {SIZE_0, SIZE_1, SIZE_2});
-        }
-        mImpl->copyFromHost(&arr.data[0][0][0], SIZE_0 * SIZE_1 * SIZE_2, mImplOffset);
-        return *this;
-    }
-
     /**
      * @brief Construct a new Tensor object from the 4-dimensions Array helper.
      * @tparam T datatype
@@ -211,15 +170,19 @@ class Tensor : public Data,
         mImpl->copyFromHost(&arr.data[0][0][0][0], SIZE_0 * SIZE_1 * SIZE_2 * SIZE_3);
     }
 
-    template <typename T, std::size_t SIZE_0, std::size_t SIZE_1, std::size_t SIZE_2, std::size_t SIZE_3>
-    constexpr Tensor &operator=(Array4D<T, SIZE_0, SIZE_1, SIZE_2, SIZE_3> &&arr) {
-        resize({SIZE_0, SIZE_1, SIZE_2, SIZE_3});
-        if (!mImpl) {
-            mImpl = Registrar<Tensor>::create({"cpu", NativeType<T>::type})(0, {SIZE_0, SIZE_1, SIZE_2, SIZE_3});
-        }
-        mImpl->copyFromHost(&arr.data[0][0][0][0], SIZE_0 * SIZE_1 * SIZE_2 * SIZE_3, mImplOffset);
-        return *this;
-    }
+    /**
+     * @brief Copy constructor. Construct a new Tensor object from another one
+     * (shallow copy). Data memory is not copied, but shared between the new
+     * Tensor and the initial one.
+     * @param other
+     */
+    Tensor(const Tensor& other) = default;
+
+    /**
+     * @brief Move constructor.
+     * @param other
+     */
+    Tensor(Tensor&& other) = default;
 
     /**
      * @brief Copy dimensions, datatype and data from another Tensor.
@@ -227,24 +190,32 @@ class Tensor : public Data,
      * existing implementation. Tensor backend/device remain untouched.
      * If current Tensor does not have an implementation, only a shallow copy
      * is performed and the Tensor will share data with t.
-     * @param t other Tensor object.
+     * @param other other Tensor object.
      * @return Tensor&
      */
-    Tensor &operator=(const Tensor &t) {
-        resize(t.dims(), t.strides());
-        setDataType(t.dataType(), false); // do not convert existing data
-        if (t.hasImpl()) {
-            if (hasImpl()) {
-                copyFrom(t);
-            }
-            else {
-                // Perform a shallow copy only
-                setImpl(t.mImpl, t.mImplOffset);
-            }
-        }
-        else {
-            setImpl(nullptr);
-        }
+    Tensor &operator=(const Tensor& other);
+
+    template <typename T, std::size_t SIZE_0>
+    constexpr Tensor &operator=(Array1D<T, SIZE_0> &&arr) {
+        *this = Tensor(std::move(arr));
+        return *this;
+    }
+
+    template <typename T, std::size_t SIZE_0, std::size_t SIZE_1>
+    constexpr Tensor &operator=(Array2D<T, SIZE_0, SIZE_1> &&arr) {
+        *this = Tensor(std::move(arr));
+        return *this;
+    }
+
+    template <typename T, std::size_t SIZE_0, std::size_t SIZE_1, std::size_t SIZE_2>
+    constexpr Tensor &operator=(Array3D<T, SIZE_0, SIZE_1, SIZE_2> &&arr) {
+        *this = Tensor(std::move(arr));
+        return *this;
+    }
+
+    template <typename T, std::size_t SIZE_0, std::size_t SIZE_1, std::size_t SIZE_2, std::size_t SIZE_3>
+    constexpr Tensor &operator=(Array4D<T, SIZE_0, SIZE_1, SIZE_2, SIZE_3> &&arr) {
+        *this = Tensor(std::move(arr));
         return *this;
     }
 
@@ -260,6 +231,23 @@ class Tensor : public Data,
         return *mImpl == *(otherTensor.mImpl);
     }
 
+public:
+    /**
+     * @brief Perform a deep copy of the tensor.
+    */
+    Tensor clone() const {
+        Tensor newTensor(*this);
+        if (!newTensor.isContiguous()) {
+            newTensor.makeContiguous();
+        }
+        else {
+            std::shared_ptr<TensorImpl> newImpl = Registrar<Tensor>::create({mImpl->backend(), mDataType})(mImpl->device().second, mDims);
+            newImpl->copy(mImpl->rawPtr(mImplOffset), mSize);
+            newTensor.setImpl(newImpl);
+        }
+        return newTensor;
+    }
+
     /**
      * @brief Set the backend of the Tensor associated implementation. If there
      * was no previous implementation set, data will be allocated, but it will
@@ -292,12 +280,7 @@ class Tensor : public Data,
      * @brief Get a list of available backends.
      * @return std::set<std::string>
      */
-    static std::set<std::string> getAvailableBackends(){
-        std::set<std::string> backendsList;
-        for(std::tuple<std::string, DataType> tupleKey : Registrar<Tensor>::getKeys())
-            backendsList.insert(std::get<0>(tupleKey));
-        return backendsList;
-    }
+    static std::set<std::string> getAvailableBackends();
 
     /**
      * @brief Get the data type enum.
@@ -369,13 +352,13 @@ class Tensor : public Data,
      * @brief Get dimensions of the Tensor object.
      * @return constexpr const std::vector<DimSize_t>&
      */
-    constexpr const std::vector<DimSize_t> &dims() const { return mDims; }
+    constexpr inline const std::vector<DimSize_t>& dims() const noexcept { return mDims; }
 
     /**
      * @brief Get strides of the Tensor object.
      * @return constexpr const std::vector<DimSize_t>&
      */
-    constexpr const std::vector<DimSize_t> &strides() const { return mStrides; }
+    constexpr inline const std::vector<DimSize_t>& strides() const noexcept { return mStrides; }
 
     /**
      * @brief Return true if Tensor is contiguous in memory.
@@ -424,6 +407,9 @@ class Tensor : public Data,
      * @return false
      */
     bool empty() const { return mDims.empty(); }
+    // bool newempty() const noexcept {
+    //     return mSize == 0;
+    // }
 
     /**
      * @brief Set each element of the tensor to zero.
@@ -464,12 +450,13 @@ class Tensor : public Data,
     inline void print() const { printf("%s\n", toString().c_str()); }
 
     std::shared_ptr<Tensor> grad() {
-        if (!mGrad) {
-            mGrad = std::make_shared<Tensor>(mDataType);
-            mGrad->resize(mDims);
+        // if (!mGrad && mImpl) {
+        //     mGrad = std::make_shared<Tensor>(mDims);
+        //     mGrad->setDataType(mDataType);
+        //     mGrad->setBackend(mImpl->backend());
 
-            if (mImpl) mGrad->setBackend(mImpl->backend());
-        }
+        //     // if (mImpl) mGrad->setBackend(mImpl->backend());
+        // }
 
         return mGrad;
     }
@@ -481,14 +468,14 @@ class Tensor : public Data,
      * @param flatIdx 1D contiguous index of the value considering a flatten, contiguous, tensor.
      * @return std::vector<DimSize_t>
      */
-    std::vector<std::size_t> getCoord(const std::size_t flatIdx) const {
-        std::vector<std::size_t> coordIdx = std::vector<std::size_t>(mDims.size());
-        std::size_t idx = flatIdx;
-        for (std::size_t i = mDims.size() - 1; i > 0; --i){
-            coordIdx[i] = (idx % mDims[i]);
-            idx/=mDims[i];
+    std::vector<std::size_t> getCoord(std::size_t flatIdx) const {
+        std::vector<std::size_t> coordIdx(mDims.size());
+        std::size_t i = mDims.size();
+
+        while (i-- > 0) {
+            coordIdx[i] = (flatIdx % mDims[i]);
+            flatIdx/=mDims[i];
         }
-        coordIdx[0] = idx % mDims[0];
         return coordIdx;
     }
 
@@ -506,7 +493,7 @@ class Tensor : public Data,
         AIDGE_ASSERT(coordIdx.size() <= mDims.size(), "Coordinates does not match number of dimensions");
         std::size_t flatIdx = 0;
         std::size_t i = 0;
-        for(; i < coordIdx.size() - 1; ++i){
+        for(; i < coordIdx.size() - 1; ++i) {
             AIDGE_ASSERT(coordIdx[i] < mDims[i], "Coordinates dimensions does not fit the dimensions of the tensor");
             flatIdx = (flatIdx + coordIdx[i]) * mDims[i + 1];
         }
@@ -522,21 +509,24 @@ class Tensor : public Data,
      * @return DimSize_t Storage index
      */
     std::size_t getStorageIdx(const std::vector<std::size_t>& coordIdx) const {
+        for(std::size_t i = 0; i < coordIdx.size(); ++i) {
+            AIDGE_ASSERT(coordIdx[i] < mDims[i], "Coordinates dimensions does not fit the dimensions of the tensor");
+        }
         AIDGE_ASSERT(coordIdx.size() <= mDims.size(), "Coordinates does not match number of dimensions");
-        return std::inner_product(coordIdx.begin(), coordIdx.end(), mStrides.begin(), DimSize_t(0));
+        return std::inner_product(coordIdx.cbegin(), coordIdx.cend(), mStrides.cbegin(), DimSize_t(0));
     }
 
     /**
      * @brief Returns a sub-tensor with equal or lower number of dimensions.
      *
-     * For instance, ``t.extract({1})`` on a CHW tensor will return the HW tensor
+     * @note For instance, ``t.extract({1})`` on a CHW tensor will return the HW tensor
      * of channel #1.
      * Likewise, ``t.extract({0, 1})`` on a NCHW tensor will return the HW tensor
      * of batch #0 and channel #1.
-     * No memory copy is performed, the returned tensor does not own the memory.
-     * If the number of coordinates matches the number of dimensions, an empty
+     * @note No memory copy is performed, the returned tensor does not own the memory.
+     * @note If the number of coordinates matches the number of dimensions, a scalar
      * tensor is returned.
-     * It current tensor was contiguous, the returned tensor is garanteed to be
+     * @note If current tensor was contiguous, the returned tensor is garanteed to be
      * contiguous as well.
      *
      * @param coordIdx Coordinates of the sub-tensor to extract
@@ -547,6 +537,8 @@ class Tensor : public Data,
     /**
      * @brief Returns a sub-tensor at some coordinate and with some dimension.
      *
+     * @note Data contiguity of the returned Tensor is not guaranted.
+     *
      * @param coordIdx First coordinates of the sub-tensor to extract
      * @param dims Dimensions of the sub-tensor to extract
      * @return Tensor Sub-tensor.
diff --git a/src/data/Tensor.cpp b/src/data/Tensor.cpp
index dbac819cf36f1b14eb3861f6de07a1a50cde3790..0d5359156bb8ff23a5b4bdaea93d30b65f8ba702 100644
--- a/src/data/Tensor.cpp
+++ b/src/data/Tensor.cpp
@@ -9,9 +9,6 @@
  *
  ********************************************************************************/
 
-#include <vector>
-#include <cstddef>
-
 #include "aidge/data/Tensor.hpp"
 
 #include <cstddef>
@@ -21,7 +18,38 @@
 #include "aidge/utils/Registrar.hpp"
 #include "aidge/utils/Types.h"
 
+Aidge::Tensor& Aidge::Tensor::operator=(const Aidge::Tensor& other) {
+    resize(other.dims(), other.strides());
+    setDataType(other.dataType(), false); // do not convert existing data
+    if (other.hasImpl()) {
+        if (hasImpl()) {
+            copyFrom(other);
+        }
+        else {
+            // Perform a shallow copy only
+            setImpl(other.mImpl, other.mImplOffset);
+        }
+    }
+    else {
+        setImpl(nullptr);
+    }
+    return *this;
+}
+
 void Aidge::Tensor::resize(const std::vector<Aidge::DimSize_t> &dims, std::vector<Aidge::DimSize_t> strides) {
+    // TODO: scalar Tensor not handled
+    if (dims.empty()) { // scalar
+        mDims = std::vector<DimSize_t>(0);
+        mStrides = std::vector<DimSize_t>({1});
+        mContiguous = true;
+
+        computeSize();
+        if (mImpl) {
+            mImpl->resize(mDims);
+        }
+        return;
+    }
+
     bool checkContiguous = true;
     if (strides.empty()) {
         strides.resize(dims.size());
@@ -36,7 +64,7 @@ void Aidge::Tensor::resize(const std::vector<Aidge::DimSize_t> &dims, std::vecto
         AIDGE_ASSERT(strides.size() == dims.size(), "Number of strides must match number of dims");
     }
 
-    if (mImpl.use_count() > 1) {
+    if (mImpl && mImpl.use_count() > 1) {
         // Here we could also create a new storage for this tensor in this case
         // But, is it more likely that the user really wants this, or that he did a mistake?
         AIDGE_ASSERT(dims == mDims && strides == mStrides, "Cannot resize Tensor with shared storage");
@@ -48,6 +76,11 @@ void Aidge::Tensor::resize(const std::vector<Aidge::DimSize_t> &dims, std::vecto
         mContiguous = true;
         if (checkContiguous) {
             std::size_t expectedStride = 1;
+            // std::size_t i = dims.size();
+            // while ((i-- > 0) && (strides[i] == expectedStride)) {
+            //     mContiguous&= (strides[i] == expectedStride);
+            //     expectedStride*= dims[i];
+            // }
             for (std::size_t i = dims.size()-1; i > 0; --i) {
                 if (strides[i] != expectedStride) {
                     mContiguous = false;
@@ -153,26 +186,26 @@ std::string Aidge::Tensor::toString() const {
     return res;
 }
 
-Aidge::Tensor Aidge::Tensor::extract(const std::vector<std::size_t>& coordIdx) const {
+Aidge::Tensor Aidge::Tensor::extract(const std::vector<std::size_t>& fixedCoord) const {
     AIDGE_ASSERT(isContiguous(), "Tensor must be contiguous");
-    AIDGE_ASSERT(coordIdx.size() <= mDims.size(), "Number of coordinates is higher than number of dimensions");
+    AIDGE_ASSERT(fixedCoord.size() <= mDims.size(), "Number of coordinates is higher than number of dimensions");
 
     Tensor subTensor(mDataType);
-    subTensor.resize(std::vector<size_t>(mDims.begin() + coordIdx.size(), mDims.end()),
-        std::vector<size_t>(mStrides.begin() + coordIdx.size(), mStrides.end()));
+    subTensor.resize(std::vector<size_t>(mDims.cbegin() + fixedCoord.size(), mDims.cend()),
+        std::vector<size_t>(mStrides.cbegin() + fixedCoord.size(), mStrides.cend()));
     subTensor.setBackend(mImpl->backend(), mImpl->device().second);
-    subTensor.setImpl(mImpl, mImplOffset + getStorageIdx(coordIdx));
+    subTensor.setImpl(mImpl, mImplOffset + getStorageIdx(fixedCoord));
     return subTensor;
 }
 
-Aidge::Tensor Aidge::Tensor::extract(const std::vector<std::size_t>& coordIdx, const std::vector<std::size_t>& dims) const {
+Aidge::Tensor Aidge::Tensor::extract(const std::vector<std::size_t>& startCoord, const std::vector<std::size_t>& dims) const {
     AIDGE_ASSERT(isContiguous(), "Tensor must be contiguous");
-    AIDGE_ASSERT(coordIdx.size() == mDims.size(), "Coordinates does not match number of dimensions");
+    AIDGE_ASSERT(startCoord.size() == mDims.size(), "Coordinates does not match number of dimensions");
 
     Tensor subTensor(mDataType);
     subTensor.resize(dims, mStrides);
     subTensor.setBackend(mImpl->backend(), mImpl->device().second);
-    subTensor.setImpl(mImpl, mImplOffset + getStorageIdx(coordIdx));
+    subTensor.setImpl(mImpl, mImplOffset + getStorageIdx(startCoord));
     return subTensor;
 }
 
@@ -396,3 +429,10 @@ const Aidge::Tensor& Aidge::Tensor::ref(std::shared_ptr<Tensor>& fallback, const
         return *fallback;
     }
 }
+
+std::set<std::string> Aidge::Tensor::getAvailableBackends() {
+    std::set<std::string> backendsList;
+    for(const auto& tupleKey : Registrar<Tensor>::getKeys())
+        backendsList.insert(std::get<0>(tupleKey));
+    return backendsList;
+}