/* * Copyright (c) 2017 ARM Limited. * * SPDX-License-Identifier: MIT * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #pragma once #include #include #include #include "alloc.hpp" /*****************************************************************************/ /* Padding definitions */ enum PaddingType { PADDING_SAME, PADDING_VALID }; /*****************************************************************************/ /* Shape of a kernel */ struct KernelShape { int n_output_channels, n_rows, n_cols, n_input_channels; int size(void) const { return n_output_channels * n_rows * n_cols * n_input_channels; } }; struct Tensor4DShape { int n_batches, n_rows, n_cols, n_channels; int size() const { return n_batches * n_rows * n_cols * n_channels; } bool TestEq(const Tensor4DShape& other) const { return (n_batches == other.n_batches && n_rows == other.n_rows && n_cols == other.n_cols && n_channels == other.n_channels); } }; template class Tensor4D final { public: Tensor4D(ShapeT shape) : _shape(shape), _data(reinterpret_cast(ALLOCATE(size_bytes()))) { Clear(); } ~Tensor4D() { free(_data); } T* ptr() const { return _data; } const ShapeT& shape() const { return _shape; } size_t size_bytes() const { return _shape.size() * sizeof(T); } bool TestEq(Tensor4D& other) const; T& element(int, int, int, int) const; void Print() const; void Clear() { Fill(static_cast(0)); } void Fill(T val) { for (int i = 0; i < _shape.size(); i++) _data[i] = val; } void TestPattern() { for (int i = 0; i < _shape.size(); i++) _data[i] = static_cast(i); } void Rand(const int seed=2311) { std::mt19937 gen(seed); std::uniform_int_distribution<> dis(-50, +50); for (int i = 0; i < _shape.size(); i++) { _data[i] = static_cast(dis(gen)); } } Tensor4D(const Tensor4D &) = delete; /** Prevent instances of this class from being copied (As this class contains pointers) */ Tensor4D &operator=(const Tensor4D &) = delete; /** Allow instances of this class to be moved */ Tensor4D(Tensor4D &&) = default; /** Allow instances of this class to be moved */ Tensor4D &operator=(Tensor4D &&) = default; private: const ShapeT _shape; T* const _data; }; template <> inline float& Tensor4D::element(int n, int i, int j, int c) const { int index = ((n*_shape.n_rows + i)*_shape.n_cols + j)*_shape.n_channels + c; return _data[index]; } template <> inline float& Tensor4D::element(int oc, int i, int j, int ic) const { int index = ((i*_shape.n_cols + j)*_shape.n_input_channels + ic)*_shape.n_output_channels + oc; return _data[index]; } template <> inline bool Tensor4D::TestEq(Tensor4D& other) const { // Test equivalence, printing errors // First test the shapes are the same if (!_shape.TestEq(other.shape())) { printf("Tensors have different shapes.\n"); return false; } else { int incorrects = 0; for (int n = 0; n < _shape.n_batches; n++) { for (int i = 0; i < _shape.n_rows; i++) { for (int j = 0; j < _shape.n_cols; j++) { for (int c = 0; c < _shape.n_channels; c++) { // Check elements for equivalence const auto a = this->element(n, i, j, c); const auto b = other.element(n, i, j, c); if (a != b) { printf("Difference at element {%d, %d, %d, %d}: %.3f != %.3f\n", n, i, j, c, a, b); if (++incorrects > 100) { printf("More than 100 incorrect values, stopping test.\n"); return false; } } } } } } return incorrects == 0; } } template <> inline void Tensor4D::Print() const { for (int n = 0; n < _shape.n_batches; n++) { for (int c = 0; c < _shape.n_channels; c++) { for (int i = 0; i < _shape.n_rows; i++) { for (int j = 0; j < _shape.n_cols; j++) { printf("%5.2f ", element(n, i, j, c)); } printf("\n"); } printf("\n"); } } } template <> inline void Tensor4D::Print() const { for (int oc = 0; oc < _shape.n_output_channels; oc++) { for (int ic = 0; ic < _shape.n_input_channels; ic++) { for (int i = 0; i < _shape.n_rows; i++) { for (int j = 0; j < _shape.n_cols; j++) { printf("%5.2f ", element(oc, i, j, ic)); } printf("\n"); } printf("\n"); } } }