/* * Copyright (c) 2015, M.Naruoka (fenrir) * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the naruoka.org nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef __MATRIX_H #define __MATRIX_H /** @file * @brief Portable matrix library * * This is hand-made matrix library whose features are * 1) to use template for generic primitive type * including not only double for general purpose, * but also int used with fixed float for embedded environment. * 2) to utilize shallow copy for small memory usage, * which is very important for embedded environment. * 3) to use views for transposed and partial matrices * to reduce copies. * 4) to use expression template technique * for matrix multiplying, adding, and subtracting * to eliminate temporary objects. * * Currently it only supports dense matrices, * whose storage is prepared as continuous array, * however it can support sparse matrices by extending * the Array2D class structure. */ #include #include #include #include #include #include #include #include #include "param/complex.h" #include #if (__cplusplus < 201103L) && !defined(noexcept) #define noexcept throw() #endif #if defined(DEBUG) && !defined(throws_when_debug) #define throws_when_debug #else #define throws_when_debug noexcept #endif #if defined(_MSC_VER) #define DELETE_IF_MSC(x) #else #define DELETE_IF_MSC(x) x #endif /** * @brief 2D array abstract class for fixed content * * This class provides basic interface of 2D array, such as row and column numbers, * accessor for element. * * @param T precision, for example, double */ template class Array2D_Frozen{ public: typedef Array2D_Frozen self_t; static const bool writable = false; protected: unsigned int m_rows; ///< Rows unsigned int m_columns; ///< Columns public: typedef T content_t; /** * Constructor of Array2D * * @param rows Rows * @param columns Columns */ Array2D_Frozen(const unsigned int &rows, const unsigned int &columns) noexcept : m_rows(rows), m_columns(columns){} /** * Destructor of Array2D */ virtual ~Array2D_Frozen(){} /** * Return rows * * @return (unsigned int) Rows */ const unsigned int &rows() const noexcept {return m_rows;} /** * Return columns * * @return (int) Columns */ const unsigned int &columns() const noexcept {return m_columns;} /** * Accessor for element * * @param row Row index (the first row is zero) * @param column Column index (the first column is zero) * @return (T) content */ virtual T operator()( const unsigned int &row, const unsigned int &column) const = 0; inline void check_index( const unsigned int &row, const unsigned int &column) const { if(row >= rows()){ throw std::out_of_range("Row index incorrect"); }else if(column >= columns()){ throw std::out_of_range("Column index incorrect"); } } protected: self_t &operator=(const self_t &array); }; /** * @brief 2D array abstract class for changeable content * * @param T precision, for example, double */ template class Array2D : public Array2D_Frozen { public: typedef Array2D self_t; static const bool writable = true; /** * Constructor of Array2D * * @param rows Rows * @param columns Columns */ Array2D(const unsigned int &rows, const unsigned int &columns) noexcept : Array2D_Frozen(rows, columns){} /** * Destructor of Array2D */ virtual ~Array2D(){} /** * Assigner, which performs shallow copy if possible. * * @param array another one * @return (ImplementedT) */ virtual ImplementedT &operator=(const ImplementedT &array) = 0; /** * Accessor for element * * @param row Row index (the first row is zero) * @param column Column index (the first column is zero) * @return (T) content */ using Array2D_Frozen::operator(); virtual T &operator()( const unsigned int &row, const unsigned int &column) = 0; /** * Perform zero clear * */ virtual void clear() = 0; /** * Perform copy * * @param is_deep If true, return deep copy if possible, otherwise return shallow copy (just link). * @return (ImplementedT) Copy */ virtual ImplementedT copy(const bool &is_deep = false) const = 0; }; /** * @brief Array2D whose elements are dense, and are stored in sequential 1D array. * In other words, (i, j) element is mapped to [i * rows + j]. * * @param T precision, for example, double */ template class Array2D_Dense : public Array2D > { public: typedef Array2D_Dense self_t; typedef Array2D super_t; template struct cast_t { typedef Array2D_Dense res_t; }; using super_t::rows; using super_t::columns; protected: static const int offset = (sizeof(T) >= sizeof(int)) ? 1 : ((sizeof(int) + sizeof(T) - 1) / sizeof(T)); int *ref; ///< reference counter TODO alignment? T *values; ///< array for values template ::is_specialized> struct setup_t { static void copy(Array2D_Dense &dest, const T2 *src){ for(int i(dest.rows() * dest.columns() - 1); i >= 0; --i){ dest.values[i] = src[i]; } } static void clear(Array2D_Dense &target){ for(int i(target.rows() * target.columns() - 1); i >= 0; --i){ target.values[i] = T2(); } } }; template struct setup_t { static void copy(Array2D_Dense &dest, const T2 *src){ std::memcpy(dest.values, src, sizeof(T2) * dest.rows() * dest.columns()); } static void clear(Array2D_Dense &target){ std::memset(target.values, 0, sizeof(T2) * target.rows() * target.columns()); } }; public: Array2D_Dense() : super_t(0, 0), ref(NULL), values(NULL) { } /** * Constructor * * @param rows Rows * @param columns Columns */ Array2D_Dense( const unsigned int &rows, const unsigned int &columns) : super_t(rows, columns), ref(reinterpret_cast(new T[offset + (rows * columns)])), values(reinterpret_cast(ref) + offset) { *ref = 1; } /** * Constructor with initializer * * @param rows Rows * @param columns Columns * @param serialized Initializer */ Array2D_Dense( const unsigned int &rows, const unsigned int &columns, const T *serialized) : super_t(rows, columns), ref(reinterpret_cast(new T[offset + (rows * columns)])), values(reinterpret_cast(ref) + offset) { *ref = 1; setup_t::copy(*this, serialized); } /** * Copy constructor, which performs shallow copy. * * @param array another one */ Array2D_Dense(const self_t &array) : super_t(array.m_rows, array.m_columns), ref(array.ref), values(array.values){ if(ref){++(*ref);} } /** * Constructor based on another type array, which performs deep copy. * * @param array another one */ template Array2D_Dense(const Array2D_Frozen &array) : super_t(array.rows(), array.columns()), ref(reinterpret_cast(new T[offset + (array.rows() * array.columns())])), values(reinterpret_cast(ref) + offset) { *ref = 1; T *buf(values); const unsigned int i_end(array.rows()), j_end(array.columns()); for(unsigned int i(0); i < i_end; ++i){ for(unsigned int j(0); j < j_end; ++j){ *(buf++) = array(i, j); } } } /** * Destructor * * The reference counter will be decreased, and when the counter equals to zero, * allocated memory for elements will be deleted. */ ~Array2D_Dense(){ if(ref && ((--(*ref)) <= 0)){ delete [] reinterpret_cast(ref); } } /** * Assigner, which performs shallow copy. * * @param array another one * @return self_t */ self_t &operator=(const self_t &array){ if(this != &array){ if(ref && ((--(*ref)) <= 0)){delete [] reinterpret_cast(ref);} super_t::m_rows = array.m_rows; super_t::m_columns = array.m_columns; if((ref = array.ref)){++(*ref);} values = array.values; } return *this; } /** * Assigner for different type, which performs deep copy. * * @param array another one * @return self_t */ template self_t &operator=(const Array2D_Frozen &array){ return ((*this) = self_t(array)); } protected: inline const T &get( const unsigned int &row, const unsigned int &column) const throws_when_debug { #if defined(DEBUG) super_t::check_index(row, column); #endif return values[(row * columns()) + column]; } public: /** * Accessor for element * * @param row Row index * @param column Column Index * @return (T) Element * @throw std::out_of_range When the indices are out of range */ T operator()( const unsigned int &row, const unsigned int &column) const throws_when_debug { return get(row, column); } T &operator()( const unsigned int &row, const unsigned int &column) throws_when_debug { return const_cast( const_cast(this)->get(row, column)); } void clear(){ setup_t::clear(*this); } /** * Perform copy * * @param is_deep If true, return deep copy, otherwise return shallow copy (just link). * @return (self_t) copy */ self_t copy(const bool &is_deep = false) const { return is_deep ? self_t(rows(), columns(), values) : self_t(*this); } }; /** * @brief special Array2D representing scaled unit * * @param T precision, for example, double */ template class Array2D_ScaledUnit : public Array2D_Frozen { public: typedef Array2D_ScaledUnit self_t; typedef Array2D_Frozen super_t; protected: const T value; ///< scaled unit public: /** * Constructor * * @param rows Rows * @param columns Columns */ Array2D_ScaledUnit(const unsigned int &size, const T &v) : super_t(size, size), value(v){} /** * Accessor for element * * @param row Row index * @param column Column Index * @return (T) Element * @throw std::out_of_range When the indices are out of range */ T operator()( const unsigned int &row, const unsigned int &column) const throws_when_debug { #if defined(DEBUG) super_t::check_index(row, column); #endif return (row == column) ? value : 0; } }; /** * @brief special Array2D representing operation * * @param T precision, for example, double */ template struct Array2D_Operator : public Array2D_Frozen { public: typedef OperatorT op_t; typedef Array2D_Operator self_t; typedef Array2D_Frozen super_t; const op_t op; /** * Constructor * * @param rows Rows * @param columns Columns */ Array2D_Operator( const unsigned int &r, const unsigned int &c, const op_t &_op) : super_t(r, c), op(_op){} /** * Accessor for element * * @param row Row index * @param column Column Index * @return (T) Element * @throw std::out_of_range When the indices are out of range */ T operator()( const unsigned int &row, const unsigned int &column) const throws_when_debug { #if defined(DEBUG) super_t::check_index(row, colunmn); #endif return op(row, column); } }; template struct Array2D_Operator_Binary { typedef LHS_T first_t; typedef LHS_T lhs_t; typedef RHS_T rhs_t; lhs_t lhs; ///< Left hand side value rhs_t rhs; ///< Right hand side value Array2D_Operator_Binary(const lhs_t &_lhs, const rhs_t &_rhs) noexcept : lhs(_lhs), rhs(_rhs) {} }; template struct Array2D_Operator_Multiply_by_Matrix; template struct Array2D_Operator_Multiply_by_Scalar; template struct Array2D_Operator_Add; template struct Array2D_Operator_EntrywiseMultiply; template struct Array2D_Operator_Stack; template struct MatrixValue_Special; template struct MatrixValue { template struct check_complex_t { static const bool hit = false; typedef T2 r_t; typedef Complex c_t; }; template struct check_complex_t > { static const bool hit = true; typedef T2 r_t; typedef Complex c_t; }; static const bool is_complex = check_complex_t::hit; typedef typename check_complex_t::r_t real_t; typedef typename check_complex_t::c_t complex_t; static real_t get_real(const real_t &v) noexcept { return v; } static real_t get_real(const complex_t &v) noexcept { return v.real(); } static complex_t get_sqrt(const real_t &v) noexcept { return (v >= 0) ? complex_t(std::sqrt(v)) : complex_t(0, std::sqrt(-v)); } static complex_t get_sqrt(const complex_t &v) noexcept { return v.sqrt(); } static real_t get_abs(const real_t &v) noexcept { return std::abs(v); } static real_t get_abs(const complex_t &v) noexcept { return v.abs(); } static real_t get_abs2(const real_t &v) noexcept { return v * v; } static real_t get_abs2(const complex_t &v) noexcept { return v.abs2(); } static bool is_nan_or_infinite(const real_t &v) noexcept { #if defined(_MSC_VER) return _isnan(v) || !_finite(v); #else return std::isnan(v) || !std::isfinite(v); #endif } static bool is_nan_or_infinite(const complex_t &v) noexcept { return is_nan_or_infinite(v.real()) || is_nan_or_infinite(v.imaginary()); } static typename MatrixValue_Special::zero_t zero; // System-wise zero }; template struct MatrixValue_Special { typedef MatrixValue v_t; struct wide_zero_t { typename v_t::real_t width, width_abs2; wide_zero_t(const typename v_t::real_t &width_) : width(v_t::get_abs(width_)), width_abs2(v_t::get_abs2(width_)) {} wide_zero_t &operator=(const typename v_t::real_t &width_) { width = v_t::get_abs(width_); width_abs2 = v_t::get_abs2(width_); return *this; } bool operator==(const typename v_t::real_t &v) const noexcept {return v_t::get_abs(v) <= width;} bool operator==(const typename v_t::complex_t &v) const noexcept {return v_t::get_abs2(v) <= width_abs2;} bool operator!=(const T &v) const noexcept {return !operator==(v);} }; template < bool is_integer = std::numeric_limits::is_integer, class U = void> struct zero_selector_t { typedef wide_zero_t res_t; }; template struct zero_selector_t { // When T is a kind of integer types, exact equalness is easiliy achievable. typedef T res_t; }; typedef typename zero_selector_t<>::res_t zero_t; }; template typename MatrixValue_Special::zero_t MatrixValue::zero = 0; #if 0 // If exact zero is required, then specialization resolves it. template <> struct MatrixValue_Special {typedef double zero_t;}; #endif template struct MatrixViewBase { typedef MatrixViewBase self_t; struct {} prop; static const char *name; template friend std::basic_ostream &operator<<( std::basic_ostream &out, const self_t &view){ return out << name; } inline const unsigned int rows( const unsigned int &_rows, const unsigned int &_columns) const noexcept { return _rows; } inline const unsigned int columns( const unsigned int &_rows, const unsigned int &_columns) const noexcept { return _columns; } template inline T operator()(Array2D_Type &storage, const unsigned int &i, const unsigned int &j) const { return storage.Array2D_Type::operator()(i, j); // direct call instead of via vtable } void update_size(const unsigned int &rows, const unsigned int &columns){} void update_offset(const unsigned int &row, const unsigned int &column){} void update_loop(const unsigned int &rows, const unsigned int &columns){} }; template const char *MatrixViewBase::name = "[Base]"; template struct MatrixViewTranspose; template struct MatrixViewConjugate; template struct MatrixViewSizeVariable; template struct MatrixViewOffset; template struct MatrixViewLoop; template struct MatrixViewProperty { typedef View self_t; static const bool anchor = true; static const bool viewless = false; static const bool transposed = false; static const bool conjugated = false; static const bool offset = false; static const bool variable_size = false; static const char *name; struct inspect_t { template friend std::basic_ostream &operator<<( std::basic_ostream &out, const inspect_t &){ return out << name; } }; static inspect_t inspect(){return inspect_t();} }; template const char *MatrixViewProperty::name = ""; template class V2> struct MatrixViewProperty > { typedef V2 self_t; static const bool anchor = false; template