#ifndef included_ArrayClass_hpp #define included_ArrayClass_hpp #include "common/Array.h" #include "common/FunctionTable.h" #include "common/Utilities.h" #include #include #include #include /******************************************************** * ArraySize * ********************************************************/ inline ArraySize::ArraySize() { d_ndim = 1; d_N[0] = 0; d_N[1] = 1; d_N[2] = 1; d_N[3] = 1; d_N[4] = 1; d_length = 0; } inline ArraySize::ArraySize( size_t N1 ) { d_ndim = 1; d_N[0] = N1; d_N[1] = 1; d_N[2] = 1; d_N[3] = 1; d_N[4] = 1; d_length = N1; } inline ArraySize::ArraySize( size_t N1, size_t N2 ) { d_ndim = 2; d_N[0] = N1; d_N[1] = N2; d_N[2] = 1; d_N[3] = 1; d_N[4] = 1; d_length = N1 * N2; } inline ArraySize::ArraySize( size_t N1, size_t N2, size_t N3 ) { d_ndim = 3; d_N[0] = N1; d_N[1] = N2; d_N[2] = N3; d_N[3] = 1; d_N[4] = 1; d_length = N1 * N2 * N3; } inline ArraySize::ArraySize( size_t N1, size_t N2, size_t N3, size_t N4 ) { d_ndim = 4; d_N[0] = N1; d_N[1] = N2; d_N[2] = N3; d_N[3] = N4; d_N[4] = 1; d_length = N1 * N2 * N3 * N4; } inline ArraySize::ArraySize( size_t N1, size_t N2, size_t N3, size_t N4, size_t N5 ) { d_ndim = 5; d_N[0] = N1; d_N[1] = N2; d_N[2] = N3; d_N[3] = N4; d_N[4] = N5; d_length = N1 * N2 * N3 * N4 * N5; } inline ArraySize::ArraySize( std::initializer_list N ) { d_ndim = N.size(); d_N[0] = 0; d_N[1] = 1; d_N[2] = 1; d_N[3] = 1; d_N[4] = 1; auto it = N.begin(); for ( size_t i = 0; i < d_ndim; i++, ++it ) d_N[i] = *it; d_length = 1; for ( size_t i = 0; i < maxDim(); i++ ) d_length *= d_N[i]; if ( d_ndim == 0 ) d_length = 0; } inline ArraySize::ArraySize( size_t ndim, const size_t *dims ) { d_ndim = ndim; d_N[0] = 0; d_N[1] = 1; d_N[2] = 1; d_N[3] = 1; d_N[4] = 1; for ( size_t i = 0; i < ndim; i++ ) d_N[i] = dims[i]; d_length = 1; for ( size_t i = 0; i < maxDim(); i++ ) d_length *= d_N[i]; if ( d_ndim == 0 ) d_length = 0; } inline ArraySize::ArraySize( const std::vector &N ) { d_ndim = N.size(); d_N[0] = 0; d_N[1] = 1; d_N[2] = 1; d_N[3] = 1; d_N[4] = 1; for ( size_t i = 0; i < d_ndim; i++ ) d_N[i] = N[i]; d_length = 1; for ( size_t i = 0; i < maxDim(); i++ ) d_length *= d_N[i]; if ( d_ndim == 0 ) d_length = 0; } inline ArraySize::ArraySize( const ArraySize &rhs ) { memcpy( this, &rhs, sizeof( *this ) ); } inline ArraySize::ArraySize( ArraySize &&rhs ) { memcpy( this, &rhs, sizeof( *this ) ); } inline ArraySize &ArraySize::operator=( const ArraySize &rhs ) { if ( this != &rhs ) memcpy( this, &rhs, sizeof( *this ) ); return *this; } inline ArraySize &ArraySize::operator=( ArraySize &&rhs ) { if ( this != &rhs ) memcpy( this, &rhs, sizeof( *this ) ); return *this; } inline void ArraySize::resize( uint8_t dim, size_t N ) { if ( dim >= d_ndim ) throw std::out_of_range( "Invalid dimension" ); d_N[dim] = N; d_length = 1; for ( size_t i = 0; i < maxDim(); i++ ) d_length *= d_N[i]; } /******************************************************** * Constructors * ********************************************************/ template Array::Array() { d_data = nullptr; } template Array::Array( const ArraySize &N ) { allocate( N ); } template Array::Array( size_t N ) { allocate( ArraySize( N ) ); } template Array::Array( size_t N_rows, size_t N_cols ) { allocate( ArraySize( N_rows, N_cols ) ); } template Array::Array( size_t N1, size_t N2, size_t N3 ) { allocate( ArraySize( N1, N2, N3 ) ); } template Array::Array( size_t N1, size_t N2, size_t N3, size_t N4 ) { allocate( ArraySize( N1, N2, N3, N4 ) ); } template Array::Array( size_t N1, size_t N2, size_t N3, size_t N4, size_t N5 ) { allocate( ArraySize( N1, N2, N3, N4, N5 ) ); } template Array::Array( const std::vector &N, const TYPE *data ) { allocate( N ); if ( data ) { for ( size_t i = 0; i < d_size.length(); i++ ) d_data[i] = data[i]; } } template Array::Array( const Range &range ) { double tmp = static_cast( ( range.j - range.i ) ) / static_cast( range.k ); size_t N = static_cast( floor( tmp + 1e-12 ) + 1 ); allocate( { N } ); for ( size_t i = 0; i < N; i++ ) d_data[i] = range.k * ( range.i / range.k + i ); } template Array::Array( std::initializer_list x ) { allocate( { x.size() } ); auto it = x.begin(); for ( size_t i = 0; i < x.size(); ++i, ++it ) d_data[i] = *it; } template void Array::allocate( const ArraySize &N ) { d_size = N; auto length = d_size.length(); if ( length == 0 ) d_ptr.reset(); else d_ptr.reset( new ( std::nothrow ) TYPE[length], []( TYPE *p ) { delete[] p; } ); d_data = d_ptr.get(); if ( length > 0 && d_data == nullptr ) throw std::logic_error( "Failed to allocate array" ); } template Array::Array( const Array &rhs ) : d_size( rhs.d_size ), d_data( nullptr ) { allocate( rhs.size() ); for ( size_t i = 0; i < d_size.length(); i++ ) d_data[i] = rhs.d_data[i]; } template Array::Array( Array &&rhs ) : d_size( rhs.d_size ), d_data( rhs.d_data ) { rhs.d_size = ArraySize(); rhs.d_data = nullptr; d_ptr = std::move( rhs.d_ptr ); } template Array &Array::operator=( const Array &rhs ) { if ( this == &rhs ) return *this; this->allocate( rhs.size() ); for ( size_t i = 0; i < d_size.length(); i++ ) this->d_data[i] = rhs.d_data[i]; return *this; } template Array &Array::operator=( Array &&rhs ) { if ( this == &rhs ) return *this; d_size = rhs.d_size; rhs.d_size = ArraySize(); d_data = rhs.d_data; rhs.d_data = nullptr; d_ptr = std::move( rhs.d_ptr ); return *this; } template Array &Array::operator=( const std::vector &rhs ) { this->allocate( ArraySize( rhs.size() ) ); for ( size_t i = 0; i < rhs.size(); i++ ) this->d_data[i] = rhs[i]; return *this; } template Array::~Array() { } template void Array::clear() { d_size = ArraySize(); d_ptr.reset(); d_data = nullptr; } /******************************************************** * Access elements * ********************************************************/ /******************************************************** * Copy/move values from one array to another (resize) * ********************************************************/ template inline void moveValues( const ArraySize &N1, const ArraySize &N2, TYPE *data1, TYPE *data2 ) { for ( size_t i5 = 0; i5 < std::min( N1[4], N2[4] ); i5++ ) { for ( size_t i4 = 0; i4 < std::min( N1[3], N2[3] ); i4++ ) { for ( size_t i3 = 0; i3 < std::min( N1[2], N2[2] ); i3++ ) { for ( size_t i2 = 0; i2 < std::min( N1[1], N2[1] ); i2++ ) { for ( size_t i1 = 0; i1 < std::min( N1[0], N2[0] ); i1++ ) { size_t index1 = N1.index( i1, i2, i3, i4, i5 ); size_t index2 = N2.index( i1, i2, i3, i4, i5 ); data2[index2] = std::move( data1[index1] ); } } } } } } template inline typename std::enable_if::type copyValues( const ArraySize &N1, const ArraySize &N2, const TYPE *data1, TYPE *data2 ) { for ( size_t i5 = 0; i5 < std::min( N1[4], N2[4] ); i5++ ) { for ( size_t i4 = 0; i4 < std::min( N1[3], N2[3] ); i4++ ) { for ( size_t i3 = 0; i3 < std::min( N1[2], N2[2] ); i3++ ) { for ( size_t i2 = 0; i2 < std::min( N1[1], N2[1] ); i2++ ) { for ( size_t i1 = 0; i1 < std::min( N1[0], N2[0] ); i1++ ) { size_t index1 = N1.index( i1, i2, i3, i4, i5 ); size_t index2 = N2.index( i1, i2, i3, i4, i5 ); data2[index2] = data1[index1]; } } } } } } template inline typename std::enable_if::type copyValues( const ArraySize &, const ArraySize &, const TYPE *, TYPE * ) { throw std::logic_error( "No copy constructor" ); } /******************************************************** * Resize the array * ********************************************************/ template void Array::resize( size_t N ) { resize( ArraySize( N ) ); } template void Array::resize( size_t N1, size_t N2 ) { resize( ArraySize( N1, N2 ) ); } template void Array::resize( size_t N1, size_t N2, size_t N3 ) { resize( ArraySize( N1, N2, N3 ) ); } template void Array::resize( const ArraySize &N ) { // Check if the array actually changed size bool equal = true; for ( size_t i = 0; i < ArraySize::maxDim(); i++ ) equal = equal && N[i] == d_size[i]; if ( equal ) { d_size = N; return; } // Store the old data auto N0 = d_size; auto data0 = d_ptr; // Allocate new data allocate( N ); // Copy the old values if ( N.length() > 0 && d_size.length() > 0 ) { if ( data0.use_count() <= 1 ) { // We own the data, use std:move moveValues( N0, N, data0.get(), d_data ); } else { // We do not own the data, copy copyValues::value, TYPE>( N0, N, data0.get(), d_data ); } } } template void Array::resizeDim( int dim, size_t N, const TYPE &value ) { if ( dim < 0 || dim > d_size.ndim() ) throw std::out_of_range( "Invalid dimension" ); size_t N0 = d_size[dim]; auto size = d_size; size.resize( dim, N ); resize( size ); size_t n1 = 1, n2 = 1; for ( int d = 0; d < dim; d++ ) n1 *= size[d]; for ( size_t d = dim + 1; d < size.ndim(); d++ ) n2 *= size[d]; for ( size_t k = 0; k < n2; k++ ) { for ( size_t j = N0; j < N; j++ ) { for ( size_t i = 0; i < n1; i++ ) { d_data[i + j * n1 + k * n1 * N] = value; } } } } /******************************************************** * Reshape the array * ********************************************************/ template void Array::reshape( const ArraySize &N ) { if ( N.length() != d_size.length() ) throw std::logic_error( "reshape is not allowed to change the array size" ); d_size = N; } /******************************************************** * Subset the array * ********************************************************/ // Helper function to check subset indices template inline void Array::checkSubsetIndex( const std::vector> &range ) const { bool test = (int) range.size() == d_size.ndim(); for ( size_t d = 0; d < range.size(); d++ ) test = test && range[d].i >= 0 && range[d].j <= d_size[d]; if ( !test ) throw std::logic_error( "indices for subset are invalid" ); } template inline std::vector> Array::convert( const std::vector &index ) const { std::vector> range( d_size.ndim() ); if ( index.size() % 2 != 0 || static_cast( index.size() / 2 ) < d_size.ndim() ) throw std::logic_error( "indices for subset are invalid" ); for ( int d = 0; d < d_size.ndim(); d++ ) range[d] = Range( index[2 * d + 0], index[2 * d + 1] ); return range; } // Helper function to return dimensions for the subset array template inline void Array::getSubsetArrays( const std::vector> &index, std::array &first, std::array &last, std::array &inc, std::array &N ) { first.fill( 0 ); last.fill( 0 ); inc.fill( 1 ); N.fill( 1 ); size_t ndim = index.size(); for ( size_t d = 0; d < ndim; d++ ) { first[d] = index[d].i; last[d] = index[d].j; inc[d] = index[d].k; N[d] = ( last[d] - first[d] + inc[d] ) / inc[d]; } } template template Array Array::subset( const std::vector> &index ) const { // Get the subset indicies checkSubsetIndex( index ); std::array first, last, inc, N1; getSubsetArrays( index, first, last, inc, N1 ); ArraySize S1( d_size.ndim(), N1.data() ); // Create the new array Array subset_array( S1 ); // Fill the new array static_assert( ArraySize::maxDim() == 5, "Not programmed for more than 5 dimensions" ); TYPE2 *subset_data = subset_array.data(); for ( size_t i4 = first[4], k1 = 0; i4 <= last[4]; i4 += inc[4] ) { for ( size_t i3 = first[3]; i3 <= last[3]; i3 += inc[3] ) { for ( size_t i2 = first[2]; i2 <= last[2]; i2 += inc[2] ) { for ( size_t i1 = first[1]; i1 <= last[1]; i1 += inc[1] ) { for ( size_t i0 = first[0]; i0 <= last[0]; i0 += inc[0], k1++ ) { size_t k2 = d_size.index( i0, i1, i2, i3, i4 ); subset_data[k1] = static_cast( d_data[k2] ); } } } } } return subset_array; } template template Array Array::subset( const std::vector &index ) const { auto range = convert( index ); return subset( range ); } template template void Array::copySubset( const std::vector> &index, const Array &subset ) { // Get the subset indices checkSubsetIndex( index ); std::array first, last, inc, N1; getSubsetArrays( index, first, last, inc, N1 ); // Copy the sub-array static_assert( ArraySize::maxDim() == 5, "Not programmed for more than 5 dimensions" ); const TYPE2 *src_data = subset.data(); for ( size_t i4 = first[4], k1 = 0; i4 <= last[4]; i4 += inc[4] ) { for ( size_t i3 = first[3]; i3 <= last[3]; i3 += inc[3] ) { for ( size_t i2 = first[2]; i2 <= last[2]; i2 += inc[2] ) { for ( size_t i1 = first[1]; i1 <= last[1]; i1 += inc[1] ) { for ( size_t i0 = first[0]; i0 <= last[0]; i0 += inc[0], k1++ ) { size_t k2 = d_size.index( i0, i1, i2, i3, i4 ); d_data[k2] = static_cast( src_data[k1] ); } } } } } } template void Array::addSubset( const std::vector> &index, const Array &subset ) { // Get the subset indices checkSubsetIndex( index ); std::array first, last, inc, N1; getSubsetArrays( index, first, last, inc, N1 ); // add the sub-array static_assert( ArraySize::maxDim() == 5, "Not programmed for more than 5 dimensions" ); for ( size_t i4 = first[4], k1 = 0; i4 <= last[4]; i4 += inc[4] ) { for ( size_t i3 = first[3]; i3 <= last[3]; i3 += inc[3] ) { for ( size_t i2 = first[2]; i2 <= last[2]; i2 += inc[2] ) { for ( size_t i1 = first[1]; i1 <= last[1]; i1 += inc[1] ) { for ( size_t i0 = first[0]; i0 <= last[0]; i0 += inc[0], k1++ ) { size_t k2 = d_size.index( i0, i1, i2, i3, i4 ); d_data[k2] += subset.d_data[k1]; } } } } } } template template void Array::copySubset( const std::vector &index, const Array &subset ) { auto range = convert( index ); copySubset( range, subset ); } template void Array::addSubset( const std::vector &index, const Array &subset ) { auto range = convert( index ); addSubset( range, subset ); } /******************************************************** * Operator overloading * ********************************************************/ template bool Array::operator==( const Array &rhs ) const { if ( this == &rhs ) return true; if ( d_size != rhs.d_size ) return false; bool match = true; for ( size_t i = 0; i < d_size.length(); i++ ) match = match && d_data[i] == rhs.d_data[i]; return match; } /******************************************************** * Get a view of an C array * ********************************************************/ template std::shared_ptr> Array::view( size_t N, std::shared_ptr const &data ) { return view( ArraySize( N ), data ); } template std::shared_ptr> Array::view( size_t N1, size_t N2, std::shared_ptr const &data ) { return view( ArraySize( N1, N2 ), data ); } template std::shared_ptr> Array::view( size_t N1, size_t N2, size_t N3, std::shared_ptr const &data ) { return view( ArraySize( N1, N2, N3 ), data ); } template std::shared_ptr> Array::constView( size_t N, std::shared_ptr const &data ) { return constView( ArraySize( N ), data ); } template std::shared_ptr> Array::constView( size_t N1, size_t N2, std::shared_ptr const &data ) { return constView( ArraySize( N1, N2 ), data ); } template std::shared_ptr> Array::constView( size_t N1, size_t N2, size_t N3, std::shared_ptr const &data ) { return constView( ArraySize( N1, N2, N3 ), data ); } template std::shared_ptr> Array::view( const ArraySize &N, std::shared_ptr const &data ) { std::shared_ptr> array( new Array() ); array->d_size = N; array->d_ptr = data; array->d_data = array->d_ptr.get(); return array; } template std::shared_ptr> Array::constView( const ArraySize &N, std::shared_ptr const &data ) { std::shared_ptr> array( new Array() ); array->d_size = N; array->d_ptr = data; array->d_data = array->d_ptr.get(); return array; } template void Array::view2( Array &src ) { view2( src.size(), src.getPtr() ); d_data = src.d_data; } template void Array::view2( const ArraySize &N, std::shared_ptr const &data ) { d_size = N; d_ptr = data; d_data = d_ptr.get(); } template void Array::viewRaw( int ndim, const size_t *dims, TYPE *data ) { d_size = ArraySize( ndim, dims ); d_ptr.reset(); d_data = data; } template void Array::viewRaw( const ArraySize &N, TYPE *data ) { d_size = N; d_ptr.reset(); d_data = data; } /******************************************************** * Convert array types * ********************************************************/ template template std::shared_ptr> Array::convert( std::shared_ptr> array ) { if ( std::is_same() ) return array; std::shared_ptr> array2( new Array( array->size() ) ); array2.copy( *array ); return array2; } template template std::shared_ptr> Array::convert( std::shared_ptr> array ) { return Array::convert( std::const_pointer_cast>( array ) ); } template template void Array::copy( const Array &array ) { resize( array.size() ); const TYPE2 *src = array.data(); for ( size_t i = 0; i < d_size.length(); i++ ) d_data[i] = static_cast( src[i] ); } template template void Array::copy( const TYPE2 *src ) { for ( size_t i = 0; i < d_size.length(); i++ ) d_data[i] = static_cast( src[i] ); } template template void Array::copyTo( TYPE2 *dst ) const { for ( size_t i = 0; i < d_size.length(); i++ ) dst[i] = static_cast( d_data[i] ); } template template Array Array::cloneTo() const { Array dst( this->size() ); auto dst_data = dst.data(); for ( size_t i = 0; i < d_size.length(); i++ ) dst_data[i] = static_cast( d_data[i] ); return dst; } template void Array::fill( const TYPE &value ) { for ( size_t i = 0; i < d_size.length(); i++ ) d_data[i] = value; } template void Array::scale( const TYPE &value ) { for ( size_t i = 0; i < d_size.length(); i++ ) d_data[i] *= value; } template void Array::pow( const Array &baseArray, const TYPE &exp ) { // not insisting on the shapes being the same // but insisting on the total size being the same if ( d_size.length() != baseArray.length() ) throw std::logic_error( "length of arrays do not match" ); const auto base_data = baseArray.data(); for ( size_t i = 0; i < d_size.length(); i++ ) d_data[i] = std::pow( base_data[i], exp ); } /******************************************************** * Replicate the array * ********************************************************/ template Array Array::repmat( const std::vector &N_rep ) const { std::vector N2( d_size.begin(), d_size.end() ); if ( N2.size() < N_rep.size() ) N2.resize( N_rep.size(), 1 ); std::array N1, Nr; N1.fill( 1 ); Nr.fill( 1 ); for ( size_t d = 0; d < N_rep.size(); d++ ) { N1[d] = d_size[d]; Nr[d] = N_rep[d]; N2[d] *= N_rep[d]; } Array y( N2 ); static_assert( ArraySize::maxDim() <= 5, "Not programmed for dimensions > 5" ); TYPE *y2 = y.data(); for ( size_t i4 = 0, index = 0; i4 < N1[4]; i4++ ) { for ( size_t j4 = 0; j4 < Nr[4]; j4++ ) { for ( size_t i3 = 0; i3 < N1[3]; i3++ ) { for ( size_t j4 = 0; j4 < Nr[3]; j4++ ) { for ( size_t i2 = 0; i2 < N1[2]; i2++ ) { for ( size_t j4 = 0; j4 < Nr[2]; j4++ ) { for ( size_t i1 = 0; i1 < N1[1]; i1++ ) { for ( size_t j4 = 0; j4 < Nr[1]; j4++ ) { for ( size_t i0 = 0; i0 < N1[0]; i0++ ) { size_t k = d_size.index( i0, i1, i2, i3, i4 ); TYPE x = d_data[k]; for ( size_t j4 = 0; j4 < Nr[0]; j4++, index++ ) y2[index] = x; } } } } } } } } } return y; } /******************************************************** * Simple math operations * ********************************************************/ template bool Array::NaNs() const { bool test = false; for ( size_t i = 0; i < d_size.length(); i++ ) test = test || d_data[i] != d_data[i]; return test; } template TYPE Array::mean( void ) const { TYPE x = this->sum() / d_size.length(); return x; } template Array Array::min( int dir ) const { auto size_ans = d_size; size_ans.resize( dir, 1 ); Array ans( size_ans ); size_t N1 = 1, N2 = 1, N3 = 1; for ( int d = 0; d < std::min( dir, d_size.ndim() ); d++ ) N1 *= d_size[d]; N2 = d_size[dir]; for ( size_t d = dir + 1; d < d_size.ndim(); d++ ) N3 *= d_size[d]; TYPE *data2 = ans.d_data; for ( size_t i3 = 0; i3 < N3; i3++ ) { for ( size_t i1 = 0; i1 < N1; i1++ ) { TYPE x = d_data[i1 + i3 * N1 * N2]; for ( size_t i2 = 0; i2 < N2; i2++ ) x = std::min( x, d_data[i1 + i2 * N1 + i3 * N1 * N2] ); data2[i1 + i3 * N1] = x; } } return ans; } template Array Array::max( int dir ) const { auto size_ans = d_size; size_ans.resize( dir, 1 ); Array ans( size_ans ); size_t N1 = 1, N2 = 1, N3 = 1; for ( int d = 0; d < std::min( dir, d_size.ndim() ); d++ ) N1 *= d_size[d]; N2 = d_size[dir]; DISABLE_WARNINGS // Suppress false array subscript is above array bounds for ( size_t d = dir + 1; d < d_size.ndim(); d++ ) N3 *= d_size[d]; ENABLE_WARNINGS // Enable warnings TYPE *data2 = ans.d_data; for ( size_t i3 = 0; i3 < N3; i3++ ) { for ( size_t i1 = 0; i1 < N1; i1++ ) { TYPE x = d_data[i1 + i3 * N1 * N2]; for ( size_t i2 = 0; i2 < N2; i2++ ) x = std::max( x, d_data[i1 + i2 * N1 + i3 * N1 * N2] ); data2[i1 + i3 * N1] = x; } } return ans; } template Array Array::sum( int dir ) const { auto size_ans = d_size; size_ans.resize( dir, 1 ); Array ans( size_ans ); size_t N1 = 1, N2 = 1, N3 = 1; for ( int d = 0; d < std::min( dir, d_size.ndim() ); d++ ) N1 *= d_size[d]; N2 = d_size[dir]; DISABLE_WARNINGS for ( size_t d = dir + 1; d < d_size.ndim(); d++ ) N3 *= d_size[d]; ENABLE_WARNINGS TYPE *data2 = ans.d_data; for ( size_t i3 = 0; i3 < N3; i3++ ) { for ( size_t i1 = 0; i1 < N1; i1++ ) { TYPE x = 0; for ( size_t i2 = 0; i2 < N2; i2++ ) x += d_data[i1 + i2 * N1 + i3 * N1 * N2]; data2[i1 + i3 * N1] = x; } } return ans; } template TYPE Array::min( const std::vector> &range ) const { // Get the subset indicies checkSubsetIndex( range ); std::array first, last, inc, N1; getSubsetArrays( range, first, last, inc, N1 ); static_assert( ArraySize::maxDim() <= 5, "Function programmed for more than 5 dimensions" ); TYPE x = std::numeric_limits::max(); for ( size_t i4 = first[4]; i4 <= last[4]; i4 += inc[4] ) { for ( size_t i3 = first[3]; i3 <= last[3]; i3 += inc[3] ) { for ( size_t i2 = first[2]; i2 <= last[2]; i2 += inc[2] ) { for ( size_t i1 = first[1]; i1 <= last[1]; i1 += inc[1] ) { for ( size_t i0 = first[0]; i0 <= last[0]; i0 += inc[0] ) { size_t k1 = d_size.index( i0, i1, i2, i3, i4 ); x = std::min( x, d_data[k1] ); } } } } } return x; } template TYPE Array::max( const std::vector> &range ) const { // Get the subset indicies checkSubsetIndex( range ); std::array first, last, inc, N1; getSubsetArrays( range, first, last, inc, N1 ); static_assert( ArraySize::maxDim() <= 5, "Function programmed for more than 5 dimensions" ); TYPE x = std::numeric_limits::min(); for ( size_t i4 = first[4]; i4 <= last[4]; i4 += inc[4] ) { for ( size_t i3 = first[3]; i3 <= last[3]; i3 += inc[3] ) { for ( size_t i2 = first[2]; i2 <= last[2]; i2 += inc[2] ) { for ( size_t i1 = first[1]; i1 <= last[1]; i1 += inc[1] ) { for ( size_t i0 = first[0]; i0 <= last[0]; i0 += inc[0] ) { size_t k1 = d_size.index( i0, i1, i2, i3, i4 ); x = std::max( x, d_data[k1] ); } } } } } return x; } template TYPE Array::sum( const std::vector> &range ) const { // Get the subset indicies checkSubsetIndex( range ); std::array first, last, inc, N1; getSubsetArrays( range, first, last, inc, N1 ); static_assert( ArraySize::maxDim() <= 5, "Function programmed for more than 5 dimensions" ); TYPE x = 0; for ( size_t i4 = first[4]; i4 <= last[4]; i4 += inc[4] ) { for ( size_t i3 = first[3]; i3 <= last[3]; i3 += inc[3] ) { for ( size_t i2 = first[2]; i2 <= last[2]; i2 += inc[2] ) { for ( size_t i1 = first[1]; i1 <= last[1]; i1 += inc[1] ) { for ( size_t i0 = first[0]; i0 <= last[0]; i0 += inc[0] ) { size_t k1 = d_size.index( i0, i1, i2, i3, i4 ); x += d_data[k1]; } } } } } return x; } template TYPE Array::mean( const std::vector> &range ) const { // Get the subset indicies checkSubsetIndex( range ); std::array first, last, inc, N1; getSubsetArrays( range, first, last, inc, N1 ); static_assert( ArraySize::maxDim() <= 5, "Function programmed for more than 5 dimensions" ); size_t n = 1; for ( auto &d : N1 ) n *= d; TYPE x = sum( range ) / n; return x; } template TYPE Array::min( const std::vector &index ) const { auto range = convert( index ); return min( range ); } template TYPE Array::max( const std::vector &index ) const { auto range = convert( index ); return max( range ); } template TYPE Array::sum( const std::vector &index ) const { auto range = convert( index ); return sum( range ); } template TYPE Array::mean( const std::vector &index ) const { auto range = convert( index ); return mean( range ); } /******************************************************** * Find all elements that match the given operation * ********************************************************/ template std::vector Array::find( const TYPE &value, std::function compare ) const { std::vector result; result.reserve( d_size.length() ); for ( size_t i = 0; i < d_size.length(); i++ ) { if ( compare( d_data[i], value ) ) result.push_back( i ); } return result; } /******************************************************** * Print an array to an output stream * ********************************************************/ template void Array::print( std::ostream &os, const std::string &name, const std::string &prefix ) const { if ( d_size.ndim() == 1 ) { for ( size_t i = 0; i < d_size[0]; i++ ) os << prefix << name << "[" << i << "] = " << d_data[i] << std::endl; } else if ( d_size.ndim() == 2 ) { os << prefix << name << ":" << std::endl; for ( size_t i = 0; i < d_size[0]; i++ ) { for ( size_t j = 0; j < d_size[1]; j++ ) os << prefix << " " << operator()( i, j ); os << std::endl; } } else { throw std::logic_error( "Not programmed for this dimension" ); } } /******************************************************** * Reverse dimensions (transpose) * ********************************************************/ template Array Array::reverseDim() const { size_t N2[ArraySize::maxDim()]; for ( int d = 0; d < ArraySize::maxDim(); d++ ) N2[d] = d_size[ArraySize::maxDim() - d - 1]; ArraySize S2( ArraySize::maxDim(), N2 ); Array y( S2 ); static_assert( ArraySize::maxDim() == 5, "Not programmed for dimensions other than 5" ); TYPE *y2 = y.data(); for ( size_t i0 = 0; i0 < d_size[0]; i0++ ) { for ( size_t i1 = 0; i1 < d_size[1]; i1++ ) { for ( size_t i2 = 0; i2 < d_size[2]; i2++ ) { for ( size_t i3 = 0; i3 < d_size[3]; i3++ ) { for ( size_t i4 = 0; i4 < d_size[4]; i4++ ) { y2[S2.index( i4, i3, i2, i1, i0 )] = d_data[d_size.index( i0, i1, i2, i3, i4 )]; } } } } } for ( int d = 0; d < d_size.ndim(); d++ ) N2[d] = d_size[d_size.ndim() - d - 1]; y.reshape( ArraySize( d_size.ndim(), N2 ) ); return y; } /******************************************************** * Coarsen the array * ********************************************************/ template Array Array::coarsen( const Array &filter ) const { auto S2 = size(); for ( size_t i = 0; i < S2.size(); i++ ) { S2.resize( i, S2[i] / filter.size(i) ); if ( S2[i] * filter.size( i ) != size( i ) ) throw std::invalid_argument( "Array must be multiple of filter size" ); } Array y( S2 ); if ( d_size.ndim() <= 3 ) throw std::logic_error( "Function programmed for more than 3 dimensions" ); const auto& Nh = filter.d_size; for ( size_t k1 = 0; k1 < y.d_size[2]; k1++ ) { for ( size_t j1 = 0; j1 < y.d_size[1]; j1++ ) { for ( size_t i1 = 0; i1 < y.d_size[0]; i1++ ) { TYPE tmp = 0; for ( size_t k2 = 0; k2 < Nh[2]; k2++ ) { for ( size_t j2 = 0; j2 < Nh[1]; j2++ ) { for ( size_t i2 = 0; i2 < Nh[0]; i2++ ) { tmp += filter( i2, j2, k2 ) * this->operator()( i1 *Nh[0] + i2, j1 * Nh[1] + j2, k1 * Nh[2] + k2 ); } } } y( i1, j1, k1 ) = tmp; } } } return y; } template Array Array::coarsen( const std::vector &ratio, std::function & )> filter ) const { //if ( ratio.size() != d_size.ndim() ) // throw std::logic_error( "ratio size does not match ndim" ); auto S2 = size(); for ( size_t i = 0; i < S2.size(); i++ ) { S2.resize( i, S2[i] / ratio[i] ); if ( S2[i] * ratio[i] != size( i ) ) throw std::invalid_argument( "Array must be multiple of filter size" ); } Array tmp( ratio ); Array y( S2 ); if ( d_size.ndim() <= 3 ) throw std::logic_error( "Function programmed for more than 3 dimensions" ); for ( size_t k1 = 0; k1 < y.d_size[2]; k1++ ) { for ( size_t j1 = 0; j1 < y.d_size[1]; j1++ ) { for ( size_t i1 = 0; i1 < y.d_size[0]; i1++ ) { for ( size_t k2 = 0; k2 < ratio[2]; k2++ ) { for ( size_t j2 = 0; j2 < ratio[1]; j2++ ) { for ( size_t i2 = 0; i2 < ratio[0]; i2++ ) { tmp( i2, j2, k2 ) = this->operator()( i1 *ratio[0] + i2, j1 * ratio[1] + j2, k1 * ratio[2] + k2 ); } } } y( i1, j1, k1 ) = filter( tmp ); } } } return y; } /******************************************************** * Concatenates the arrays * ********************************************************/ template void Array::cat( const Array &x, int dim ) { std::vector> tmp( 2 ); tmp[0].view2( *this ); tmp[1].view2( const_cast &>( x ) ); *this = cat( tmp, dim ); } template Array Array::cat( const std::vector &x, int dim ) { if ( x.empty() ) return Array(); // Check that the dimensions match bool check = true; for ( size_t i = 1; i < x.size(); i++ ) { check = check && x[i].ndim() == x[0].ndim(); for ( int d = 0; d < x[0].ndim(); d++ ) check = check && d == dim; } if ( !check ) throw std::logic_error( "Array dimensions do not match for concatenation" ); // Create the output array auto size = x[0].d_size; for ( size_t i = 1; i < x.size(); i++ ) size.resize( dim, size[dim] + x[i].size( dim ) ); Array out( size ); size_t N1 = 1; size_t N2 = size[dim]; size_t N3 = 1; for ( int d = 0; d < dim; d++ ) N1 *= size[d]; for ( size_t d = dim + 1; d < size.ndim(); d++ ) N3 *= size[d]; TYPE *data = out.data(); for ( size_t i = 0, i0 = 0; i < x.size(); i++ ) { const TYPE *src = x[i].data(); size_t N22 = x[i].size( dim ); for ( size_t j2 = 0; j2 < N3; j2++ ) { for ( size_t i1 = 0; i1 < N22; i1++ ) { for ( size_t j1 = 0; j1 < N1; j1++ ) { data[j1 + ( i1 + i0 ) * N1 + j2 * N1 * N2] = src[j1 + i1 * N1 + j2 * N1 * N22]; } } } i0 += N22; } return out; } /******************************************************** * Math operations (should call the Math class) * ********************************************************/ template void Array::rand() { FUN::rand( *this ); } template Array &Array::operator+=( const Array &rhs ) { const auto &fun = []( const TYPE &a, const TYPE &b ) { return a + b; }; FUN::transform( fun, *this, rhs, *this ); return *this; } template Array &Array::operator-=( const Array &rhs ) { const auto &fun = []( const TYPE &a, const TYPE &b ) { return a - b; }; FUN::transform( fun, *this, rhs, *this ); return *this; } template Array &Array::operator+=( const TYPE &rhs ) { const auto &fun = [rhs]( const TYPE &x ) { return x + rhs; }; FUN::transform( fun, *this, *this ); return *this; } template Array &Array::operator-=( const TYPE &rhs ) { const auto &fun = [rhs]( const TYPE &x ) { return x - rhs; }; FUN::transform( fun, *this, *this ); return *this; } template Array operator+( const Array &a, const Array &b ) { Array c; const auto &fun = []( const TYPE &a, const TYPE &b ) { return a + b; }; FUN::transform( fun, a, b, c ); return c; } template Array operator-( const Array &a, const Array &b ) { Array c; const auto &fun = []( const TYPE &a, const TYPE &b ) { return a - b; }; FUN::transform( fun, a, b, c ); return c; } template Array operator*( const Array &a, const Array &b ) { return Array::multiply( a, b ); } template inline Array operator*( const Array &a, const std::vector &b ) { Array b2; b2.viewRaw( { b.size() }, const_cast( b.data() ) ); return Array::multiply( a, b2 ); } template TYPE Array::min() const { const auto &fun = []( const TYPE &a, const TYPE &b ) { return a < b ? a : b; }; return FUN::reduce( fun, *this ); } template TYPE Array::max() const { const auto &fun = []( const TYPE &a, const TYPE &b ) { return a > b ? a : b; }; return FUN::reduce( fun, *this ); } template TYPE Array::sum() const { const auto &fun = []( const TYPE &a, const TYPE &b ) { return a + b; }; return FUN::reduce( fun, *this ); } template Array Array::multiply( const Array &a, const Array &b ) { Array c; FUN::multiply( a, b, c ); return c; } template void Array::axpby( const TYPE &alpha, const Array &x, const TYPE &beta ) { const auto &fun = [alpha, beta]( const TYPE &x, const TYPE &y ) { return alpha * x + beta * y; }; return FUN::transform( fun, x, *this ); } template Array Array::transform( std::function fun, const Array &x ) { Array y; FUN::transform( fun, x, y ); return y; } template Array Array::transform( std::function fun, const Array &x, const Array &y ) { Array z; FUN::transform( fun, x, y, z ); return z; } #endif