diff --git a/include/aidge/data/Tensor.hpp b/include/aidge/data/Tensor.hpp index 8d9f77bc41b0fa5225e7201d2e4d03eb2ff72502..8f3d7c6b4797b161836f1d65616c1be5417cbab4 100644 --- a/include/aidge/data/Tensor.hpp +++ b/include/aidge/data/Tensor.hpp @@ -12,6 +12,7 @@ #ifndef AIDGE_CORE_DATA_TENSOR_H_ #define AIDGE_CORE_DATA_TENSOR_H_ +#include <algorithm> #include <cstddef> // std::size_t #include <cstring> #include <functional> // std::multiplies @@ -25,6 +26,7 @@ #include "aidge/backend/TensorImpl.hpp" #include "aidge/data/Data.hpp" +#include "aidge/utils/ErrorHandling.hpp" #include "aidge/utils/Registrar.hpp" #include "aidge/utils/Types.h" #include "aidge/utils/ArrayHelpers.hpp" @@ -625,16 +627,19 @@ public: * @return std::vector<DimSize_t> */ 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]; - } - return coordIdx; + return Tensor::getCoord(mDims, flatIdx); } + /** + * @brief From the the 1D contiguous index, return the coordinate of an element in the tensor. + * Beware: do not use this function with the storage index! + * + * @param flatIdx 1D contiguous index of the value considering a flatten, contiguous, tensor. + * @return std::vector<DimSize_t> + */ + static std::vector<std::size_t> + getCoord(const std::vector<Aidge::DimSize_t> &dimensions, std::size_t flattenedIdx); + /** * @brief From the coordinate returns the 1D contiguous index of an element in the tensor. * If the number of coordinates is inferior to the number of dimensions, @@ -647,17 +652,33 @@ public: * @return DimSize_t Contiguous index */ std::size_t getIdx(const std::vector<std::size_t>& coordIdx) const { - AIDGE_ASSERT(coordIdx.size() <= mDims.size(), "Coordinates does not match number of dimensions"); - std::size_t flatIdx = 0; - for(std::size_t i = 0; i < mDims.size(); ++i) { - auto coord = i < coordIdx.size() ? coordIdx[i]: 0; - AIDGE_ASSERT(coord < mDims[i], "Coordinates dimensions does not fit the dimensions of the tensor"); - auto nextDimSize = i + 1 < mDims.size() ? mDims[i + 1]: 1; - flatIdx = (flatIdx + coord) * nextDimSize; - } - return flatIdx; + return Tensor::getIdx(mDims,coordIdx); } + /** + * @brief From the coordinate returns the 1D contiguous index of an element in the tensor. + * If the number of coordinates is inferior to the number of dimensions, + * the remaining coordinates are assumed to be 0. + * Beware: the contiguous index will only correspond to the storage index + * if the tensor is contiguous! + * Note that the coordIdx may be an empty vector. + * + * @param coordIdx Coordinate to an element in the tensor + * @return DimSize_t Contiguous index + */ + static std::size_t getIdx(const std::vector<DimSize_t>& tensorDims, const std::vector<std::size_t>& coords); + + /** + * @brief check if index is in bound of given tensor dimensions + * @warning this function is templated in order to welcome cases like interpolation where indexes are not integers. + * However, the only types accepted are floating, integer & size_t + * @param tensorDims : tensor dimensions + * @param coords : coords of the tensor you want to flattened index of + * @return true if all coords are in bound. False otherwise + */ + template<typename T> + static bool isInBounds(const std::vector<DimSize_t>& tensorDims, const std::vector<T>& coords); + /** * @brief From the coordinate returns the 1D storage index of an element in the tensor. * If the number of coordinates is inferior to the number of dimensions, diff --git a/python_binding/data/pybind_Tensor.cpp b/python_binding/data/pybind_Tensor.cpp index b972c87dcda8f912ff40feef0001b95d5feac71e..a6dcf8aa5d3d9440735cf41bde49abf34a3410b1 100644 --- a/python_binding/data/pybind_Tensor.cpp +++ b/python_binding/data/pybind_Tensor.cpp @@ -329,9 +329,8 @@ void init_Tensor(py::module& m){ .def("capacity", &Tensor::capacity) .def("resize", (void (Tensor::*)(const std::vector<DimSize_t>&, std::vector<DimSize_t>)) &Tensor::resize, py::arg("dims"), py::arg("strides") = std::vector<DimSize_t>()) .def("has_impl", &Tensor::hasImpl) - .def("get_coord", &Tensor::getCoord) - .def("get_idx", &Tensor::getIdx) - .def_static("get_available_backends", &Tensor::getAvailableBackends) + .def("get_coord", (std::vector<std::size_t> (Tensor::*)(const std::size_t) &Tensor::getCoord, py::arg("flatIdx")) + .def("get_idx",(std::size_t (Tensor::*)(const std::vector<std::size_t> &) &Tensor::getIdx, py::arg("coords")) .def("undefined", &Tensor::undefined) .def("cpy_transpose", (void (Tensor::*)(const Tensor& src, const std::vector<DimSize_t>& transpose)) &Tensor::copyTranspose, py::arg("src"), py::arg("transpose")) diff --git a/src/data/Tensor.cpp b/src/data/Tensor.cpp index 3dcdcc65d0ef40b0443eb5b9662111420ce4fb86..5a336e93151fe9347c218325a88bcbda50884dc8 100644 --- a/src/data/Tensor.cpp +++ b/src/data/Tensor.cpp @@ -11,9 +11,11 @@ #include "aidge/data/Tensor.hpp" +#include <algorithm> #include <cstddef> #include <vector> +#include "aidge/data/half.hpp" #include "aidge/utils/ErrorHandling.hpp" #include "aidge/utils/Registrar.hpp" #include "aidge/operator/Abs.hpp" @@ -675,7 +677,58 @@ const Aidge::Tensor& Aidge::Tensor::ref(std::shared_ptr<Tensor>& fallback, std::set<std::string> Aidge::Tensor::getAvailableBackends() { std::set<std::string> backendsList; - for (const auto& tupleKey : Registrar<Tensor>::getKeys()) + for (const auto& tupleKey : Registrar<Tensor>::getKeys()) { backendsList.insert(std::get<0>(tupleKey)); + } return backendsList; } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// COORDINATES MANIPULATION +std::vector<std::size_t> +Aidge::Tensor::getCoord(const std::vector<Aidge::DimSize_t> &tensorDims, + std::size_t flatIdx) { + std::vector<std::size_t> coordIdx(tensorDims.size()); + std::size_t i = tensorDims.size(); + + while (i-- > 0) { + coordIdx[i] = (flatIdx % tensorDims[i]); + flatIdx/=tensorDims[i]; + } + return coordIdx; +} + + +std::size_t Aidge::Tensor::getIdx(const std::vector<Aidge::DimSize_t> &tensorDims, const std::vector<std::size_t>& coordIdx) { + AIDGE_ASSERT(coordIdx.size() <= tensorDims.size(), "Tensor::getIdx(): Coordinates does not match number of dimensions.\n\tCoords : {}\n\tDimensions: {}",coordIdx, tensorDims); + std::size_t flatIdx = 0; + for(std::size_t i = 0; i < tensorDims.size(); ++i) { + auto coord = i < coordIdx.size() ? coordIdx[i]: 0; + AIDGE_ASSERT(coord < tensorDims[i], "Coordinates dimensions does not fit the dimensions of the tensor"); + auto nextDimSize = i + 1 < tensorDims.size() ? tensorDims[i + 1]: 1; + flatIdx = (flatIdx + coord) * nextDimSize; + } + return flatIdx; +} + +template<typename T> +bool Aidge::Tensor::isInBounds(const std::vector<Aidge::DimSize_t>& tensorDims, const std::vector<T>& coords){ + AIDGE_ASSERT(coords.size() == tensorDims.size(), + "Coordinates({}) to compare have not " + "the same number of dimension as tensor dimensions({}), aborting.", + coords, + tensorDims); + T i; + bool isInBound {true}; + for(i = 0 ; i < static_cast<T>(coords.size()) && isInBound; ++i ){ + isInBound = coords[i] >= 0 && coords[i] < static_cast<T>(tensorDims[i]) ; + } + return isInBound; +} +template bool Tensor::isInBounds(const std::vector<DimSize_t>& tensorDims, const std::vector<int16_t>& coords); +template bool Tensor::isInBounds(const std::vector<DimSize_t>& tensorDims, const std::vector<int32_t>& coords); +template bool Tensor::isInBounds(const std::vector<DimSize_t>& tensorDims, const std::vector<int64_t>& coords); +template bool Tensor::isInBounds(const std::vector<DimSize_t>& tensorDims, const std::vector<DimSize_t>& coords); +template bool Tensor::isInBounds(const std::vector<DimSize_t>& tensorDims, const std::vector<float>& coords); +template bool Tensor::isInBounds(const std::vector<DimSize_t>& tensorDims, const std::vector<double>& coords); + diff --git a/unit_tests/data/Test_Tensor.cpp b/unit_tests/data/Test_Tensor.cpp index 4462eb91ed6c6cdfce77b47b6a1a8808eec88423..cebc995d5bd2e0120937b8a81acd773b08cb5861 100644 --- a/unit_tests/data/Test_Tensor.cpp +++ b/unit_tests/data/Test_Tensor.cpp @@ -9,9 +9,9 @@ * ********************************************************************************/ -#include <array> #include <cstddef> // std::size_t #include <cstdint> // std::uint8_t, std::uint16_t, std::int32_t +#include <cstdlib> #include <numeric> // std::accumulate, std::inner_product #include <functional> // std::multiplies #include <random> // std::mt19937, @@ -365,7 +365,40 @@ TEST_CASE("[core/data] Tensor(other)", "[Tensor][extract][zeros][print]") { } } } - + SECTION("Index & coord manipulation"){ + Tensor tensor; + std::vector<DimSize_t> dims {2,2}; + int nbVal = std::accumulate(dims.begin(), + dims.end(), + 1, + std::multiplies<DimSize_t>()); + float* values = static_cast<float*>(malloc(nbVal * sizeof(float))); + values[0] = 0; + values[1] = 1; + values[2] = 2; + values[3] = 3; + tensor.setDataType(DataType::Int32); + tensor.setBackend("cpu"); + tensor.resize(dims); + tensor.getImpl()->setRawPtr(values, 4); + std::vector<std::size_t> coords; + SECTION("getIdx"){ + CHECK(Tensor::getIdx(tensor.dims(), std::vector<std::size_t>({1,1}) ) == 3); + CHECK(Tensor::getIdx(tensor.dims(), std::vector<std::size_t>({1,0}) ) == 2); + // No check to ensure if value is in bounds + CHECK_THROWS(Tensor::getIdx(tensor.dims(), std::vector<std::size_t>({0,2}) )); + } + SECTION("getCoord"){ + CHECK(Tensor::getCoord(tensor.dims(), 3 ) ==std::vector<std::size_t>({1,1})); + CHECK(Tensor::getCoord(tensor.dims(), 2 ) ==std::vector<std::size_t>({1,0})); + } + SECTION("isInBound"){ + CHECK_THROWS(Tensor::isInBounds(dims, std::vector<DimSize_t>({1,2,4,5})) == true); + CHECK(Tensor::isInBounds(dims, std::vector<DimSize_t>({1,2})) == false); + CHECK(Tensor::isInBounds(dims, std::vector<int>({-1,1})) == false); + CHECK(Tensor::isInBounds(dims, std::vector<DimSize_t>({1,1})) == true); + } + } SECTION("Tensor extract") { bool equal;