/*************************************************************************** * GNdarray.cpp - N-dimensional array class * * ----------------------------------------------------------------------- * * copyright (C) 2016 by Juergen Knoedlseder * * ----------------------------------------------------------------------- * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ /** * @file GNdarray.cpp * @brief N-dimensional array class implementation * @author Juergen Knoedlseder */ /* __ Includes ___________________________________________________________ */ #ifdef HAVE_CONFIG_H #include #endif #include "GTools.hpp" #include "GException.hpp" #include "GNdarray.hpp" /* __ Method name definitions ____________________________________________ */ #define G_OP_ADD "GNdarray::operator+=(GNdarray&)" #define G_OP_SUB "GNdarray::operator-=(GNdarray&)" #define G_AT1 "GNdarray::at(int&)" #define G_AT2 "GNdarray::at(int&, int&)" #define G_AT3 "GNdarray::at(int&, int&, int&)" #define G_ATN "GNdarray::at(std::vector&)" /*========================================================================== = = = Constructors/destructors = = = ==========================================================================*/ /***********************************************************************//** * @brief Void constructor ***************************************************************************/ GNdarray::GNdarray(void) { // Initialise class members init_members(); // Return return; } /***********************************************************************//** * @brief 1-dimensional constructor * * @param[in] nx Array dimension. * * Constructs a 1-dimensional array. ***************************************************************************/ GNdarray::GNdarray(const int& nx) { // Initialise class members init_members(); // Set shape m_shape.push_back(nx); // Set strides m_strides.push_back(1); // Initialise data array with zeros m_data.assign(nx, 0.0); // Return return; } /***********************************************************************//** * @brief 2-dimensional constructor * * @param[in] nx Array's first dimension. * @param[in] ny Array's second dimension. * * Constructs a 2-dimensional array. ***************************************************************************/ GNdarray::GNdarray(const int& nx, const int& ny) { // Initialise class members init_members(); // Compute size of array int size = nx * ny; // Set shape m_shape.push_back(nx); m_shape.push_back(ny); // Set strides m_strides.push_back(1); m_strides.push_back(nx); // Initialise data array with zeros m_data.assign(size, 0.0); // Return return; } /***********************************************************************//** * @brief 3-dimensional constructor * * @param[in] nx Array's first dimension. * @param[in] ny Array's second dimension. * @param[in] nz Array's third dimension. * * Constructs a 3-dimensional array. ***************************************************************************/ GNdarray::GNdarray(const int& nx, const int& ny, const int& nz) { // Initialise class members init_members(); // Compute size of array int size = nx * ny * nz; // Set shape m_shape.push_back(nx); m_shape.push_back(ny); m_shape.push_back(nz); // Set strides m_strides.push_back(1); m_strides.push_back(nx); m_strides.push_back(nx*ny); // Initialise data array with zeros m_data.assign(size, 0.0); // Return return; } /***********************************************************************//** * @brief n-dimensional constructor * * @param[in] n Array dimensions. * * Constructs a n-dimensional array. If the array dimension vector @p n is * empty an empty n-dimensional array is constructed (similar to the void * constructor). ***************************************************************************/ GNdarray::GNdarray(const std::vector& n) { // Initialise class members init_members(); // Continue only if there are dimensions in vector if (n.size() > 0) { // Compute size of array int size = n[0]; for (int i = 1; i < n.size(); ++i) { size *= n[i]; } // Set shape m_shape = n; // Set strides int stride = 1; for (int i = 0; i < n.size(); ++i) { m_strides.push_back(stride); stride *= n[i]; } // Initialise data array with zeros m_data.assign(size, 0.0); } // endif: there were dimensions in vector // Return return; } /***********************************************************************//** * @brief Copy constructor * * @param[in] array Array. ***************************************************************************/ GNdarray::GNdarray(const GNdarray& array) { // Initialise class members init_members(); // Copy members copy_members(array); // Return return; } /***********************************************************************//** * @brief Destructor ***************************************************************************/ GNdarray::~GNdarray(void) { // Free members free_members(); // Return return; } /*========================================================================== = = = Operators = = = ==========================================================================*/ /***********************************************************************//** * @brief Assignment operator * * @param[in] array Array. * @return Array. ***************************************************************************/ GNdarray& GNdarray::operator=(const GNdarray& array) { // Execute only if object is not identical if (this != &array) { // Free members free_members(); // Initialise private members init_members(); // Copy members copy_members(array); } // endif: object was not identical // Return this object return *this; } /***********************************************************************//** * @brief Equality operator * * @param[in] array Array. * @return True if arrays are identical. * * Returns true if the arrays have identical shape and content. ***************************************************************************/ bool GNdarray::operator==(const GNdarray& array) const { // Check of arrays have the same shape bool identity = has_same_shape(array); // If the arrays have identical dimensions then check if all elements // are identical. Break as soon as one array element differs. if (identity) { for (int i = 0; i < m_data.size(); ++i) { if (m_data[i] != array.m_data[i]) { identity = false; break; } } } // Return result return identity; } /***********************************************************************//** * @brief Non-equality operator * * @param[in] array Array. * @return True if arrays are different. * * Returns true if the arrays have different shape or content. ***************************************************************************/ bool GNdarray::operator!=(const GNdarray& array) const { // Get negated result of equality operation bool difference = !(this->operator==(array)); // Return result return difference; } /***********************************************************************//** * @brief Unary addition operator * * @param[in] array Array. * @return Array. * * Adds an array to the current array. ***************************************************************************/ GNdarray& GNdarray::operator+=(const GNdarray& array) { // Throw an exception if the arrays have not the same shape require_same_shape(G_OP_ADD, array); // Add elements for (int i = 0; i < m_data.size(); ++i) { m_data[i] += array.m_data[i]; } // Return array return *this; } /***********************************************************************//** * @brief Unary subtraction operator * * @param[in] array Array. * @return Array. * * Subtracts an array from the current array. ***************************************************************************/ GNdarray& GNdarray::operator-=(const GNdarray& array) { // Throw an exception if the arrays have not the same shape require_same_shape(G_OP_SUB, array); // Subtract elements for (int i = 0; i < m_data.size(); ++i) { m_data[i] -= array.m_data[i]; } // Return array return *this; } /***********************************************************************//** * @brief Unary value addition operator * * @param[in] value Value. * @return Array. * * Adds a value to all array elements. ***************************************************************************/ GNdarray& GNdarray::operator+=(const double& value) { // Add value to elements for (int i = 0; i < m_data.size(); ++i) { m_data[i] += value; } // Return array return *this; } /***********************************************************************//** * @brief Unary value subtraction operator * * @param[in] value Value. * @return Array. * * Subtracts a value from all array elements. ***************************************************************************/ GNdarray& GNdarray::operator-=(const double& value) { // Subtract value from elements for (int i = 0; i < m_data.size(); ++i) { m_data[i] -= value; } // Return array return *this; } /***********************************************************************//** * @brief Unary multiplication operator * * @param[in] value Value. * @return Array. * * Multiply all array elements by a value. ***************************************************************************/ GNdarray& GNdarray::operator*=(const double& value) { // Multiply all elements for (int i = 0; i < m_data.size(); ++i) { m_data[i] *= value; } // Return array return *this; } /***********************************************************************//** * @brief Unary division operator * * @param[in] value Value. * @return Array. * * Divide all array elements by a value. ***************************************************************************/ GNdarray& GNdarray::operator/=(const double& value) { // Divide all elements for (int i = 0; i < m_data.size(); ++i) { m_data[i] /= value; } // Return array return *this; } /***********************************************************************//** * @brief Unary minus operator * * @return Array. * * Negate all array elements. ***************************************************************************/ GNdarray GNdarray::operator-(void) const { // Copy array GNdarray result = *this; // Negate all elements for (int i = 0; i < m_data.size(); ++i) { result.m_data[i] = -result.m_data[i]; } // Return array return result; } /*========================================================================== = = = Public methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Clear array ***************************************************************************/ void GNdarray::clear(void) { // Free members free_members(); // Initialise private members init_members(); // Return return; } /***********************************************************************//** * @brief Clone array * * @return Pointer to deep copy of array. ***************************************************************************/ GNdarray* GNdarray::clone(void) const { // Clone array return new GNdarray(*this); } /***********************************************************************//** * @brief 1-dimensional array element access with range checking * (const version) * * @param[in] ix Element index [0,...,shape(0)-1]. * @return Const reference to array element. * * @exception GException::invalid_value * Array is not 1-dimensional. * @exception GException::out_of_range * Element index is out of range. ***************************************************************************/ const double& GNdarray::at(const int& ix) const { // Throw an exception if the array is not 1-dimensional if (m_shape.size() != 1) { std::string msg = "Invalid access of "+ gammalib::str(m_shape.size())+"-dimensional array " "with 1-dimensional access operator."; throw GException::invalid_value(G_AT1, msg); } // Throw an exception if the index is outside the valid range if (ix < 0 || ix >= m_shape[0]) { throw GException::out_of_range(G_AT1, "Array index", ix, m_shape[0]); } // Return array element return (*this)(ix); } /***********************************************************************//** * @brief 2-dimensional array element access with range checking * (const version) * * @param[in] ix Index in first dimension [0,...,shape(0)-1]. * @param[in] iy Index in second dimension [0,...,shape(1)-1]. * @return Const reference to array element. * * @exception GException::invalid_value * Array is not 2-dimensional. * @exception GException::out_of_range * Element index is out of range. ***************************************************************************/ const double& GNdarray::at(const int& ix, const int& iy) const { // Throw an exception if the array is not 2-dimensional if (m_shape.size() != 2) { std::string msg = "Invalid access of "+ gammalib::str(m_shape.size())+"-dimensional array " "with 2-dimensional access operator."; throw GException::invalid_value(G_AT2, msg); } // Throw an exception if the indices are outside the valid range if (ix < 0 || ix >= m_shape[0]) { throw GException::out_of_range(G_AT2, "First array index", ix, m_shape[0]); } if (iy < 0 || iy >= m_shape[1]) { throw GException::out_of_range(G_AT2, "Second array index", iy, m_shape[1]); } // Return array element return (*this)(ix,iy); } /***********************************************************************//** * @brief 3-dimensional array element access with range checking * (const version) * * @param[in] ix Index in first dimension [0,...,shape(0)-1]. * @param[in] iy Index in second dimension [0,...,shape(1)-1]. * @param[in] iz Index in third dimension [0,...,shape(2)-1]. * @return Const reference to array element. * * @exception GException::invalid_value * Array is not 3-dimensional. * @exception GException::out_of_range * Element index is out of range. ***************************************************************************/ const double& GNdarray::at(const int& ix, const int& iy, const int& iz) const { // Throw an exception if the array is not 2-dimensional if (m_shape.size() != 3) { std::string msg = "Invalid access of "+ gammalib::str(m_shape.size())+"-dimensional array " "with 3-dimensional access operator."; throw GException::invalid_value(G_AT3, msg); } // Throw an exception if the indices are outside the valid range if (ix < 0 || ix >= m_shape[0]) { throw GException::out_of_range(G_AT3, "First array index", ix, m_shape[0]); } if (iy < 0 || iy >= m_shape[1]) { throw GException::out_of_range(G_AT3, "Second array index", iy, m_shape[1]); } if (iz < 0 || iz >= m_shape[2]) { throw GException::out_of_range(G_AT3, "Third array index", iz, m_shape[2]); } // Return array element return (*this)(ix,iy,iz); } /***********************************************************************//** * @brief n-dimensional array element access with range checking * (const version) * * @param[in] i Index vector. * @return Const reference to array element. * * @exception GException::invalid_value * Array is not of correct dimension. * @exception GException::out_of_range * Element index is out of range. ***************************************************************************/ const double& GNdarray::at(const std::vector& i) const { // Throw an exception if the array is not 2-dimensional if (m_shape.size() != i.size()) { std::string msg = "Invalid access of "+ gammalib::str(m_shape.size())+"-dimensional array " "with "+ gammalib::str(i.size())+"-dimensional access " "operator."; throw GException::invalid_value(G_ATN, msg); } // Throw an exception if the indices are outside the valid range for (int k = 0; k < m_shape.size(); ++k) { if (i[k] < 0 || i[k] >= m_shape[k]) { throw GException::out_of_range(G_ATN, "Dimension "+ gammalib::str(k)+" index", i[k], m_shape[k]); } } // Return array element return (*this)(i); } /***********************************************************************//** * @brief Print array information * * @param[in] chatter Chattiness. * @return String containing array information. ***************************************************************************/ std::string GNdarray::print(const GChatter& chatter) const { // Initialise result string std::string result = "("; // Continue only if chatter is not silent if (chatter != SILENT) { // Put all elements in string for (int i = 0; i < m_data.size(); ++i) { if (i > 0) { result += ", "; } result += gammalib::str(m_data[i]); } // Append closing parentheses result += ")"; } // endif: chatter was not silent // Return result return result; } /*========================================================================== = = = Private methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Initialise class members ***************************************************************************/ void GNdarray::init_members(void) { // Initialise members m_shape.clear(); m_strides.clear(); m_data.clear(); // Return return; } /***********************************************************************//** * @brief Copy class members * * @param[in] array Array. ***************************************************************************/ void GNdarray::copy_members(const GNdarray& array) { // Copy members m_shape = array.m_shape; m_strides = array.m_strides; m_data = array.m_data; // Return return; } /***********************************************************************//** * @brief Delete class members ***************************************************************************/ void GNdarray::free_members(void) { // Return return; } /***********************************************************************//** * @brief Compute array element index * * @param[in] i Index vector. * @return Array element index. * * Computes the array element index from an index vector. The method assumes * that the index vector has the same dimension as the array. If this * condition is not guaranteed, the condition needs to be checked before * calling the method. ***************************************************************************/ int GNdarray::index(const std::vector& i) const { // Initialise element index int index = 0; // Compute element index for (int k = 0; k < m_shape.size(); ++k) { index += m_strides[k] * i[k]; } // Return index return index; } /***********************************************************************//** * @brief Check if array has the same shape * * @param[in] array Array. * @return True if the array has the same shape. ***************************************************************************/ bool GNdarray::has_same_shape(const GNdarray& array) const { // Check if the array has the same dimension bool identity = m_shape.size() == array.m_shape.size(); // Check if the shape is identical. Break as soon as one shape value // differs. if (identity) { for (int i = 0; i < m_shape.size(); ++i) { if (m_shape[i] != array.m_shape[i]) { identity = false; break; } } } // Return result return identity; } /***********************************************************************//** * @brief Throw exception if array shapes differ * * @param[in] method Method that throws exception. * @param[in] array Array. ***************************************************************************/ void GNdarray::require_same_shape(const std::string& method, const GNdarray& array) const { // If the shape differs then throw an exception if (!has_same_shape(array)) { // Compose exception message std::string msg = "Incompatible array dimensions ("; for (int i = 0; i < m_shape.size(); ++i) { if (i > 0) { msg += ", "; } msg += gammalib::str(m_shape[i]); } msg += ") <=> ("; for (int i = 0; i < array.m_shape.size(); ++i) { if (i > 0) { msg += ", "; } msg += gammalib::str(array.m_shape[i]); } msg += ")."; // Throw exception throw GException::invalid_argument(method, msg); } // endif: array shapes differed // Return return; } /*========================================================================== = = = Friends = = = ==========================================================================*/ /***********************************************************************//** * @brief Computes minimum array element * * @param[in] array Array. * @return Minimum array element. ***************************************************************************/ double min(const GNdarray& array) { // Initialises result double result = 0.0; // Continue only if we have elements if (array.m_data.size() > 0) { // Search for minimum result = array.m_data[0]; for (int i = 1; i < array.m_data.size(); ++i) { if (array.m_data[i] < result) { result = array.m_data[i]; } } } // endif: there were elements // Returns minimum return result; } /***********************************************************************//** * @brief Computes maximum array element * * @param[in] array Array. * @return Maximum array element. ***************************************************************************/ double max(const GNdarray& array) { // Initialises result double result = 0.0; // Continue only if we have elements if (array.m_data.size() > 0) { // Search for maximum result = array.m_data[0]; for (int i = 1; i < array.m_data.size(); ++i) { if (array.m_data[i] > result) { result = array.m_data[i]; } } } // endif: there were elements // Returns maximum return result; } /***********************************************************************//** * @brief Computes array sum * * @param[in] array Array. * @return Sum of array elements. ***************************************************************************/ double sum(const GNdarray& array) { // Compute sum double result = 0.0; for (int i = 0; i < array.m_data.size(); ++i) { result += array.m_data[i]; } // Returns sum return result; } /***********************************************************************//** * @brief Computes arccos of array elements * * @param[in] array Array. * @return Array containing the arccos of every element. ***************************************************************************/ GNdarray acos(const GNdarray& array) { // Initialise result array GNdarray result = array; // Evaluate each array element for (int i = 0; i < array.m_data.size(); ++i) { result.m_data[i] = std::acos(array.m_data[i]); } // Return array return result; } /***********************************************************************//** * @brief Computes acosh of array elements * * @param[in] array Array. * @return Array containing the acosh of every element. ***************************************************************************/ GNdarray acosh(const GNdarray& array) { // Initialise result array GNdarray result = array; // Evaluate each array element for (int i = 0; i < array.m_data.size(); ++i) { result.m_data[i] = acosh(array.m_data[i]); } // Return array return result; } /***********************************************************************//** * @brief Computes arcsin of array elements * * @param[in] array Array. * @return Array containing the arcsin of every element. ***************************************************************************/ GNdarray asin(const GNdarray& array) { // Initialise result array GNdarray result = array; // Evaluate each array element for (int i = 0; i < array.m_data.size(); ++i) { result.m_data[i] = std::asin(array.m_data[i]); } // Return array return result; } /***********************************************************************//** * @brief Computes asinh of array elements * * @param[in] array Array. * @return Array containing the asinh of every element. ***************************************************************************/ GNdarray asinh(const GNdarray& array) { // Initialise result array GNdarray result = array; // Evaluate each array element for (int i = 0; i < array.m_data.size(); ++i) { result.m_data[i] = asinh(array.m_data[i]); } // Return array return result; } /***********************************************************************//** * @brief Computes arctan of array elements * * @param[in] array Array. * @return Array containing the arctan of every element. ***************************************************************************/ GNdarray atan(const GNdarray& array) { // Initialise result array GNdarray result = array; // Evaluate each array element for (int i = 0; i < array.m_data.size(); ++i) { result.m_data[i] = std::atan(array.m_data[i]); } // Return array return result; } /***********************************************************************//** * @brief Computes atanh of array elements * * @param[in] array Array. * @return Array containing the atanh of every element. ***************************************************************************/ GNdarray atanh(const GNdarray& array) { // Initialise result array GNdarray result = array; // Evaluate each array element for (int i = 0; i < array.m_data.size(); ++i) { result.m_data[i] = atanh(array.m_data[i]); } // Return array return result; } /***********************************************************************//** * @brief Computes cosine of array elements * * @param[in] array Array. * @return Array containing the cosine of every element. ***************************************************************************/ GNdarray cos(const GNdarray& array) { // Initialise result array GNdarray result = array; // Evaluate each array element for (int i = 0; i < array.m_data.size(); ++i) { result.m_data[i] = std::cos(array.m_data[i]); } // Return array return result; } /***********************************************************************//** * @brief Computes cosh of array elements * * @param[in] array Array. * @return Array containing the cosh of every element. ***************************************************************************/ GNdarray cosh(const GNdarray& array) { // Initialise result array GNdarray result = array; // Evaluate each array element for (int i = 0; i < array.m_data.size(); ++i) { result.m_data[i] = std::cosh(array.m_data[i]); } // Return array return result; } /***********************************************************************//** * @brief Computes exponential of array elements * * @param[in] array Array. * @return Array containing the exponential of every element. ***************************************************************************/ GNdarray exp(const GNdarray& array) { // Initialise result array GNdarray result = array; // Evaluate each array element for (int i = 0; i < array.m_data.size(); ++i) { result.m_data[i] = std::exp(array.m_data[i]); } // Return array return result; } /***********************************************************************//** * @brief Computes absolute of array elements * * @param[in] array Array. * @return Array containing the absolute of every element. ***************************************************************************/ GNdarray abs(const GNdarray& array) { // Initialise result array GNdarray result = array; // Evaluate each array element for (int i = 0; i < array.m_data.size(); ++i) { result.m_data[i] = std::abs(array.m_data[i]); } // Return array return result; } /***********************************************************************//** * @brief Computes natural logarithm of array elements * * @param[in] array Array. * @return Array containing the natural logarithm of every element. ***************************************************************************/ GNdarray log(const GNdarray& array) { // Initialise result array GNdarray result = array; // Evaluate each array element for (int i = 0; i < array.m_data.size(); ++i) { result.m_data[i] = std::log(array.m_data[i]); } // Return array return result; } /***********************************************************************//** * @brief Computes base10 logarithm of array elements * * @param[in] array Array. * @return Array containing the base10 logarithm of every element. ***************************************************************************/ GNdarray log10(const GNdarray& array) { // Initialise result array GNdarray result = array; // Evaluate each array element for (int i = 0; i < array.m_data.size(); ++i) { result.m_data[i] = std::log10(array.m_data[i]); } // Return array return result; } /***********************************************************************//** * @brief Computes sine of array elements * * @param[in] array Array. * @return Array containing the sine of every element. ***************************************************************************/ GNdarray sin(const GNdarray& array) { // Initialise result array GNdarray result = array; // Evaluate each array element for (int i = 0; i < array.m_data.size(); ++i) { result.m_data[i] = std::sin(array.m_data[i]); } // Return array return result; } /***********************************************************************//** * @brief Computes sinh of array elements * * @param[in] array Array. * @return Array containing the sinh of every element. ***************************************************************************/ GNdarray sinh(const GNdarray& array) { // Initialise result array GNdarray result = array; // Evaluate each array element for (int i = 0; i < array.m_data.size(); ++i) { result.m_data[i] = std::sinh(array.m_data[i]); } // Return array return result; } /***********************************************************************//** * @brief Computes square root of array elements * * @param[in] array Array. * @return Array containing the square root of every element. ***************************************************************************/ GNdarray sqrt(const GNdarray& array) { // Initialise result array GNdarray result = array; // Evaluate each array element for (int i = 0; i < array.m_data.size(); ++i) { result.m_data[i] = std::sqrt(array.m_data[i]); } // Return array return result; } /***********************************************************************//** * @brief Computes tangens of array elements * * @param[in] array Array. * @return Array containing the tangens of every element. ***************************************************************************/ GNdarray tan(const GNdarray& array) { // Initialise result array GNdarray result = array; // Evaluate each array element for (int i = 0; i < array.m_data.size(); ++i) { result.m_data[i] = std::tan(array.m_data[i]); } // Return array return result; } /***********************************************************************//** * @brief Computes tanh of array elements * * @param[in] array Array. * @return Array containing the tanh of every element. ***************************************************************************/ GNdarray tanh(const GNdarray& array) { // Initialise result array GNdarray result = array; // Evaluate each array element for (int i = 0; i < array.m_data.size(); ++i) { result.m_data[i] = std::tanh(array.m_data[i]); } // Return array return result; } /***********************************************************************//** * @brief Computes tanh of array elements * * @param[in] array Array. * @param[in] power Power. * @return Array containing the power of every element. ***************************************************************************/ GNdarray pow(const GNdarray& array, const double& power) { // Initialise result array GNdarray result = array; // Evaluate each array element for (int i = 0; i < array.m_data.size(); ++i) { result.m_data[i] = std::pow(array.m_data[i], power); } // Return array return result; }