diff --git a/cwMtx.cpp b/cwMtx.cpp index 90cb06c..30eb58e 100644 --- a/cwMtx.cpp +++ b/cwMtx.cpp @@ -3,6 +3,7 @@ #include "cwCommonImpl.h" #include "cwMem.h" #include "cwObject.h" +#include "cwVectOps.h" #include "cwMtx.h" @@ -95,8 +96,13 @@ cw::rc_t cw::mtx::test( const object_t* cfg ) d_t* mtx1 = nullptr; d_t* mtx2 = nullptr; d_t* mtx3 = nullptr; + d_t* mtx4 = nullptr; d_t* mtx_y0 = nullptr; d_t* mtx_y1 = nullptr; + d_t* mtx_y2 = nullptr; + d_t* mtx_y3 = nullptr; + d_t* mtx_y4 = nullptr; + d_t* mtx_y5 = nullptr; d_t y; const object_t* m0 = cfg->find("m0"); @@ -115,6 +121,10 @@ cw::rc_t cw::mtx::test( const object_t* cfg ) if( m3 != nullptr ) mtx3 = allocCfg(m3); + const object_t* m4 = cfg->find("m4"); + if( m4 != nullptr ) + mtx4 = allocCfg(m4); + const object_t* y0 = cfg->find("y0"); if( y0 != nullptr ) mtx_y0 = allocCfg(y0); @@ -123,7 +133,7 @@ cw::rc_t cw::mtx::test( const object_t* cfg ) if( y1 != nullptr ) mtx_y1 = allocCfg(y1); - unsigned n = offset(mtx1,1,1); + unsigned n = offset(*mtx1,1,1); printf("offset: %i\n",n); @@ -131,6 +141,7 @@ cw::rc_t cw::mtx::test( const object_t* cfg ) report(*mtx1,"m1"); report(*mtx2,"m2"); report(*mtx3,"m3"); + report(*mtx4,"m4"); report(*mtx_y0,"y0"); report(*mtx_y1,"y1"); @@ -152,13 +163,48 @@ cw::rc_t cw::mtx::test( const object_t* cfg ) if( !is_equal(*mtx_y1,y) ) rc = cwLogError(kTestFailRC,"Test 1 fail."); } + + + + transpose(*mtx0); + report(*mtx0,"m0"); + mtx_y2 = join(0,*mtx0,*mtx4); + if( mtx_y2 != nullptr ) + report(*mtx_y2,"y2"); + + report(*mtx0,"m0"); + report(*mtx4,"m4"); + mtx_y3 = join(1,*mtx0,*mtx4); + if( mtx_y3 != nullptr ) + report(*mtx_y3,"y3"); + + + mtx_y4 = slice_alias(*mtx_y3,0,0,1); + if( mtx_y4 != nullptr ) + { + report(*mtx_y4,"y4 - slice"); + + ele(*mtx_y4,2) = 1; + ele(*mtx_y4,3) = 2; + report(*mtx_y4,"y4 - mod"); + + + mtx_y5 = alloc_one_hot(*mtx_y4); + if( mtx_y5 != nullptr ) + report(*mtx_y5,"y5 -(one_hot(y4))"); + } release(mtx0); release(mtx1); release(mtx2); release(mtx3); + release(mtx4); release(mtx_y0); release(mtx_y1); + release(mtx_y2); + release(mtx_y3); + release(mtx_y4); + release(mtx_y5); release(y); return rc; } diff --git a/cwMtx.h b/cwMtx.h index d44f679..54c890a 100644 --- a/cwMtx.h +++ b/cwMtx.h @@ -31,8 +31,9 @@ namespace cw { kAliasReleaseFl = 0x01, // do not allocate memory, use the passed data pointer, and eventually release it kAliasNoReleaseFl = 0x02, // do not allocate memory, use the passed data pointer, and do not ever release it - kDuplDataFl = 0x04, // allocate data space and copy the data in - kZeroFl = 0x08, // zero the newly allocated data + kDimV_NoReleaseFl = 0x04, // do not release the dimV array when the matrix is released. + kDuplDataFl = 0x08, // allocate data space and copy the data in + kZeroFl = 0x10, // zero the newly allocated data }; template< typename T > @@ -50,9 +51,11 @@ namespace cw template< typename T > void release( struct mtx_str& m ) { + if( cwIsNotFlag(m.flags,kDimV_NoReleaseFl) ) mem::release(m.dimV); - if( cwIsNotFlag(m.flags,kAliasNoReleaseFl) ) - mem::release(m.base); + + if( cwIsNotFlag(m.flags,kAliasNoReleaseFl) ) + mem::release(m.base); } template< typename T > @@ -65,6 +68,24 @@ namespace cw } } + // Release data memory when this matrix is released + template< typename T > + void set_memory_release_flag( struct mtx_str& m, bool linkDimVFl=true ) + { + m.flags = cwClearFlag(m.flags,kAliasNoReleaseFl); + if( linkDimVFl ) + m.flags = cwClearFlag(m.flags,kDimV_NoReleaseFl); + } + + // Do NOT release data memory when this matrix is released. + template< typename T > + void clear_memory_release_flag( struct mtx_str& m, bool linkDimVFl=true ) + { + m.flags = cwSetFlag(m.flags,kAliasNoReleaseFl); + if( linkDimVFl ) + m.flags = cwSetFlag(m.flags,kDimV_NoReleaseFl); + } + // Note that dimV[] is always copied and therefore is the reponsibility of the caller to free. template< typename T > struct mtx_str* _init( struct mtx_str* m, unsigned dimN, const unsigned* dimV, T* base=nullptr, unsigned flags=0 ) @@ -133,14 +154,39 @@ namespace cw } + template< typename T > + struct mtx_str* alloc( const unsigned* dimV, unsigned dimN, T* base, unsigned flags=0 ) + { return _init( nullptr, dimN, dimV, base, flags); } + + + template< typename T> + struct mtx_str* _alloc_( unsigned flags, unsigned* dimV, unsigned dimN) + { + return alloc(dimV, dimN, nullptr, flags ); + } + + template< typename T, typename... ARGS> + struct mtx_str* _alloc_( unsigned flags, unsigned* dimV, unsigned dimN, unsigned n, ARGS&&... args) + { + unsigned _dimV[ dimN + 1 ]; + vop::copy(_dimV,dimV,dimN); + _dimV[dimN] = n; + return _alloc_(flags,_dimV, dimN+1, std::forward(args)...); + } + + template< typename T, typename... ARGS> + struct mtx_str* alloc( unsigned flags, ARGS&&... args) + { return _alloc_(flags,nullptr,0,args...); } + + // Allocate the matrix w/o zeroing the initial contents template< typename T > - struct mtx_str* alloc( unsigned dimN, const unsigned* dimV ) + struct mtx_str* alloc( const unsigned* dimV, unsigned dimN ) { return _init( nullptr, dimN, dimV, nullptr, 0); } // Allocate the matrix and zero the contents template< typename T > - struct mtx_str* allocZ( unsigned dimN, const unsigned* dimV ) + struct mtx_str* allocZ( const unsigned* dimV, unsigned dimN ) { return _init( nullptr, dimN, dimV, nullptr, kZeroFl); } // Allocate the matrix and copy the data from base[] @@ -161,7 +207,7 @@ namespace cw unsigned _offsetDimV( const unsigned* dimV, unsigned dimN, unsigned* idxV ); unsigned _offsetMulV( const unsigned* dimV, unsigned dimN, unsigned* idxV ); - unsigned _mtx_object_get_degree( const struct object_str* cfg ); + unsigned _mtx_object_get_degree( const struct object_str* cfg ); rc_t _mtx_object_get_shape( const struct object_str* cfg, unsigned i, unsigned* dimV, unsigned dimN, unsigned& eleN ); // 'i' is the index into 'idxV[]' of the matrix dimension which 'cfg' refers to @@ -199,6 +245,7 @@ namespace cw return rc; } + // Allocate a new matrix by parsing an object_t description. template< typename T > struct mtx_str* allocCfg( const struct object_str* cfg ) { @@ -225,7 +272,7 @@ namespace cw return nullptr; // allocate the matrix - if((m = alloc(dimN,dimV)) == nullptr ) + if((m = alloc(dimV,dimN)) == nullptr ) cwLogError(kObjAllocFailRC,"A matrix allocation failed."); else // @@ -233,15 +280,151 @@ namespace cw return m; } - return nullptr; - } - template< typename T > - struct mtx_str* alloc( unsigned dimN, const unsigned* dimV, T* base=nullptr, unsigned flags=0 ) - { return _init( nullptr, dimN, dimV, base, flags); } + void _slice_setup( const struct mtx_str& m, const unsigned* sIdxV, const unsigned* sCntV, unsigned* siV, unsigned *snV ) + { + // if sIdx is not given then assume it is the origin + if( sIdxV != nullptr ) + vop::copy(siV,sIdxV,m.dimN); + else + vop::zero(siV,m.dimN); + + // calculate the length in each dimension + for(unsigned i=0; i + struct mtx_str* alloc( const struct mtx_str& src, const unsigned* sIdxV, const unsigned* sCntV ) + { + struct mtx_str* m; + unsigned siV[ src.dimN ]; + unsigned snV[ src.dimN ]; + unsigned dIdxV[ src.dimN ]; + vop::zero(dIdxV,src.dimN); + + _slice_setup(src,sIdxV,sCntV,siV,snV); + + if((m = alloc(snV,src.dimN) ) == nullptr ) + return nullptr; + + copy(*m,dIdxV,src,siV,snV); + return m; + } + + + // Allocate a new matrix by slicing an existing matrix and aliasing the contents into a new matrix. + // Set the elements of sCntV to kInvalidCnt to indicate that the entire dimension following the offset index should be copied. + // Set sIdxV to nullptr to begin at 0,0,... + // Set sCntV to nullptr to take all elements after sIdxV. + template< typename T > + struct mtx_str* sliceAlias( const struct mtx_str& src, const unsigned* sIdxV, const unsigned* sCntV ) + { + struct mtx_str* m; + unsigned siV[ src.dimN ]; + unsigned snV[ src.dimN ]; + + _slice_setup(src,sIdxV,sCntV,siV,snV); + + m = allocAliasNoRelease( src.dimN, snV, addr(src,siV) ); + + // the memory layout in the slice mtx is the same as the matrix + // that it aliases and therefore the 'mulV' vector is the same + // in both matrices. + vop::copy(m->mulV,src.mulV,src.dimN); + + return m; + } + + + template + struct mtx_str* _slice( const struct mtx_str& m, unsigned* iV, unsigned iN, unsigned index ) + { + // the offset index vector must be fully specified + if( index < m.dimN ) + { + cwLogError(kInvalidArgRC,"An invalid number index + count values was given to slice(). %i < %i.",index,m.dimN); + return nullptr; + } + + // fill in the end of iV[] with kInvalidCnt to indicate that all values after the offset should be copied + for(; index(m,iV,iV+m.dimN); + } + + template< typename T0, typename T1, typename... ARGS> + struct mtx_str* _slice( const struct mtx_str& m, unsigned* iV, unsigned iN, unsigned index, unsigned n, ARGS&&... args) + { + if( index >= iN ) + { + cwLogError(kInvalidArgRC,"Too many index/count arguments were passed to mtx::slice()."); + return nullptr; + } + + iV[index] = n; + return _slice(m,iV,iN,index+1,std::forward(args)...); + } + + // This function is a wrapper around alloc(const struct mtx_str& src, const unsigned* sIdxV, const unsigned* sCntV ). + // The argument list should specify the values for sIdxV[0:dimN] and sCntV[0:dimN]. + // The count of arguments should therefore not exceed src.dimN*2. + // The sCntV[] argument list may be truncated, or set to kInvalidCnt, if all values after the offset for a given dimension are to be copied. + template< typename T0, typename T1, typename... ARGS> + struct mtx_str* slice( const struct mtx_str& m, ARGS&&... args) + { + unsigned iV[ m.dimN*2 ]; + return _slice(m, iV, m.dimN*2, 0, std::forward(args)...); + } + + template + struct mtx_str* _sliceAlias( const struct mtx_str& m, unsigned* iV, unsigned iN, unsigned index ) + { + // the offset index vector must be fully specified + if( index < m.dimN ) + { + cwLogError(kInvalidArgRC,"An invalid number index + count values was given to sliceAlias(). %i < %i.",index,m.dimN); + return nullptr; + } + + // fill in the end of iV[] with kInvalidCnt to indicate that all values after the offset should be copied + for(; index(m,iV,iV+m.dimN); + } + + template< typename T, typename... ARGS> + struct mtx_str* _sliceAlias( const struct mtx_str& m, unsigned* iV, unsigned iN, unsigned index, unsigned n, ARGS&&... args) + { + if( index >= iN ) + { + cwLogError(kInvalidArgRC,"Too many index/count arguments were passed to mtx::sliceAlias()."); + return nullptr; + } + + iV[index] = n; + return _sliceAlias(m,iV,iN,index+1,std::forward(args)...); + } + + template< typename T, typename... ARGS> + struct mtx_str* slice_alias( const struct mtx_str& m, ARGS&&... args) + { + unsigned iV[ m.dimN*2 ]; + return _sliceAlias(m,iV, m.dimN*2, 0, args...); + } // resize m[] @@ -255,57 +438,242 @@ namespace cw { return resize(y,x->dimV,x->dimN); } - template< typename T > - unsigned offset( const struct mtx_str* m, const unsigned* idxV ) + // Copy a slice of src[] into dst[] at a particular location. + // dst[] is assumed to be allocated with sufficient size to receive src[]. + // Set dIdxV to nullptr to copy to the 0,0, ... of the dst matrix + // Set sIdxV to nullptr to copy from the 0,0, ... of the src matrix. + // Set sCntV to nullptr to cop all of the src matrix. + template< typename T0, typename T1 > + rc_t copy( struct mtx_str& dst, const unsigned* dIdxV, const struct mtx_str& src, const unsigned* sIdxV, const unsigned* sCntV ) { - unsigned offset = 0; - for(unsigned i=0; idimN; ++i) - offset += idxV[i] * m->mulV[i]; + rc_t rc = kOkRC; + unsigned nV[ src.dimN ]; + unsigned siV[ src.dimN ]; + unsigned diV[ dst.dimN ]; + vop::zero(nV,src.dimN); + + if( sCntV == nullptr ) + sCntV = src.dimV; + + if( sIdxV == nullptr ) + vop::zero(siV,src.dimN); + else + vop::copy(siV,sIdxV,src.dimN); - return offset; + if( dIdxV == nullptr ) + vop::zero(diV,dst.dimN); + else + vop::copy(diV,dIdxV,dst.dimN); + +#ifndef NDEBUG + + // verify the starting address + assert( is_legal_address(dst, diV) ); + assert( is_legal_address(src, siV) ); + + vop::add(siV,sCntV,src.dimN); + vop::add(diV,sCntV,dst.dimN); + + vop::sub(siV,1,src.dimN); + vop::sub(diV,1,dst.dimN); + + //verify the ending address + assert( is_legal_address(dst, diV) ); + assert( is_legal_address(src, siV) ); + + vop::copy(siV,sIdxV,src.dimN); + vop::copy(diV,dIdxV,dst.dimN); + +#endif + + // copy one element + ele(dst, diV ) = ele(src, siV ); + + + for(int j=0; j >= 0; ) + { + // increment the src and dst addr + + // from highest to lowest degree + for(j=src.dimN-1; j>=0; --j) + { + // if incrementing the jth dim does not overflow ... + if( ++nV[j] < sCntV[j] ) + { + siV[j] += 1; + diV[j] += 1; + + // copy one element + ele(dst, diV ) = ele(src, siV ); + + break; // .. then incr siV[] and diV[] with the next src/dst address + } + + // otherwise reset the counter and address for this dim and backup by one dim. + nV[j] = 0; + diV[j] = dIdxV[j]; + siV[j] = sIdxV[j]; + } + + } + + return rc; + } + + + template< typename T > + rc_t _join_update_dims( unsigned index, unsigned* dimV, unsigned dimN, const struct mtx_str& m ) + { + rc_t rc = kOkRC; + // verify that the degree of all matrices are the same + if( m.dimN != dimN ) + return cwLogError(kInvalidArgRC,"Join matrix size mismatch. dimN:%i != %i", m.dimN, dimN); + + // only the dimension specified by 'index' may be different + for(unsigned i=0; i + void _join_copy( struct mtx_str& dst, const struct mtx_str& src, unsigned index, unsigned ii ) + { + unsigned dIdxV[ dst.dimN ]; + unsigned sIdxV[ src.dimN ]; + vop::zero(dIdxV,dst.dimN); + vop::zero(sIdxV,src.dimN); + dIdxV[ index ] = ii; + copy(dst,dIdxV,src,sIdxV,src.dimV); } + + template< typename T > + struct mtx_str* _join( unsigned index, unsigned* dimV, unsigned dimN, unsigned ii ) + { + // Allocate an empty matrix to copy the joined matrices into. + return alloc(dimV,dimN); + } + + + template< typename T, typename... ARGS> + struct mtx_str* _join( unsigned index, unsigned* dimV, unsigned dimN, unsigned ii, const struct mtx_str& m, ARGS&&... args) + { + struct mtx_str* y = nullptr; + + if( _join_update_dims(index,dimV,dimN,m) != kOkRC ) + return nullptr; + + if((y = _join( index, dimV, dimN, ii + m.dimV[index], std::forward(args)...)) != nullptr ) + { + _join_copy(*y,m,index,ii); + } + + return y; + } + + template< typename T, typename... ARGS> + struct mtx_str* join( unsigned index, const struct mtx_str& m, ARGS&&... args) + { + + struct mtx_str* y = nullptr; + unsigned dimV[ m.dimN ]; + + for(unsigned i=0; i(args)...)) != nullptr ) + { + _join_copy(*y,m,index,0); + } + + return y; + } + template< typename T > - unsigned _offset( const struct mtx_str* m, int i, unsigned offs ) + bool is_legal_address( const struct mtx_str& m, const unsigned* idxV ) + { + for(unsigned i=0; i= m.dimV[i] ) + return false; + + return true; + } + + template< typename T > + unsigned offset( const struct mtx_str& m, const unsigned* idxV ) + { return vop::mac(idxV,m.mulV,m.dimN); } + + + template< typename T > + unsigned _offset( const struct mtx_str& m, int i, unsigned offs ) { return offs; } template< typename T, typename... ARGS> - unsigned _offset( const struct mtx_str* m, int i, unsigned offs, unsigned idx, ARGS&&... args) - { return _offset(m,i+1, offs + idx*m->mulV[i], std::forward(args)...); } + unsigned _offset( const struct mtx_str& m, int i, unsigned offs, unsigned idx, ARGS&&... args) + { return _offset(m,i+1, offs + idx*m.mulV[i], std::forward(args)...); } - template< typename T, typename... ARGS> - unsigned offset( const struct mtx_str* m, unsigned idx, ARGS&&... args) - { return _offset(m,0,0,idx,std::forward(args)...); } - template< typename T, typename... ARGS> unsigned offset( const struct mtx_str& m, unsigned idx, ARGS&&... args) - { return _offset(&m,0,0,idx,std::forward(args)...); } + { return _offset(m,0,0,idx,std::forward(args)...); } + template< typename T > - T* addr( const struct mtx_str* m, const unsigned* idxV ) - { return m->base + offset(m,idxV); } - - template< typename T, typename... ARGS> - T* addr( struct mtx_str* m, unsigned i, ARGS&&... args) - { return m->base + offset(m,i,std::forward(args)...); } + T* addr( struct mtx_str& m, const unsigned* idxV ) + { return m.base + offset(m,idxV); } template< typename T > - T& ele( const struct mtx_str* m, const unsigned* idxV ) - { return *addr(m,idxV); } + const T* addr( const struct mtx_str& m, const unsigned* idxV ) + { return m.base + offset(m,idxV); } + template< typename T, typename... ARGS> - T& ele( struct mtx_str* m, unsigned i, ARGS&&... args) + T* addr( struct mtx_str& m, unsigned i, ARGS&&... args) + { return m.base + offset(m,i,std::forward(args)...); } + + + template< typename T, typename... ARGS> + const T* addr( const struct mtx_str& m, unsigned i, ARGS&&... args) + { return m.base + offset(m,i,std::forward(args)...); } + + + + + + template< typename T > + T& ele( struct mtx_str& m, const unsigned* idxV ) + { return *addr(m,idxV); } + + template< typename T > + const T& ele( const struct mtx_str& m, const unsigned* idxV ) + { return *addr(m,idxV); } + + template< typename T, typename... ARGS> + T& ele( struct mtx_str& m, unsigned i, ARGS&&... args) + { return *addr(m,i,std::forward(args)...); } + + + template< typename T, typename... ARGS> + const T& ele( const struct mtx_str& m, unsigned i, ARGS&&... args) { return *addr(m,i,std::forward(args)...); } template< typename T > bool is_col_vector( const struct mtx_str& m ) - { return m->dimN==1 || (m->dimN==2 && m->dimV[1]==1); }; + { return m.dimN==1 || (m.dimN==2 && m.dimV[1]==1); }; template< typename T > bool is_row_vector( const struct mtx_str& m ) - { return m->dimN==2 && m->dimV[0]==1; } + { return m.dimN==2 && m.dimV[0]==1; } template< typename T > bool is_vector( const struct mtx_str& m ) @@ -317,12 +685,8 @@ namespace cw { if( x0.dimN != x1.dimN ) return false; - - for(unsigned i=0; i @@ -331,24 +695,14 @@ namespace cw if( !is_size_equal(x0,x1) ) return false; - unsigned N = ele_count(x0); - for(unsigned i=0; i unsigned ele_count( const struct mtx_str& x ) - { - unsigned eleN = 1; - for(unsigned i=0; i @@ -372,7 +726,7 @@ namespace cw { if( i == m.dimN ) { - double v = ele( &m, idxV ); + double v = ele( m, idxV ); // print the value printf("%*.*f ",colWidth,decPl,v); @@ -408,7 +762,7 @@ namespace cw unsigned idxV[ m.dimN ]; memset(idxV,0,sizeof(idxV)); - if( is_int(*m.base) ) + if( std::numeric_limits::is_integer ) decPl = 0; _print( m, idxV, 0, decPl, colWidth ); @@ -441,9 +795,7 @@ namespace cw void mult( struct mtx_str& y, const struct mtx_str& x ) { assert( is_size_equal(y,x) ); - unsigned n = ele_count(x); - for(unsigned i=0; i(x)); } // y = x * scalar (elementwise) @@ -451,19 +803,13 @@ namespace cw void mult( struct mtx_str& y, const struct mtx_str& x, const T& scalar ) { resize(&y,x); // resize y to the same dim's as m - unsigned n = ele_count(x); - for(unsigned i=0; i(x),scalar); } // y *= scalar (elementwise) template< typename T > void mult( struct mtx_str& y, const T& scalar ) - { - unsigned n = ele_count(y); - for(unsigned i=0; i(y)); } // y = m + x (elementwise) template< typename T > @@ -471,9 +817,7 @@ namespace cw { assert( is_size_equal(x0,x1) ); resize(&y,x0); // resize y to the same dim's as m - unsigned n = ele_count(x0); - for(unsigned i=0; i& y, const struct mtx_str& x ) { assert( is_size_equal(y,x) ); - unsigned n = ele_count(x); - for(unsigned i=0; i& y, const struct mtx_str& x, const T& scalar ) { resize(&y,x); - unsigned n = ele_count(y); - for(unsigned i=0; i(y)); } // y += scalar (elementwise) template< typename T > void add( struct mtx_str& y, const T& scalar ) { - unsigned n = ele_count(y); - for(unsigned i=0; i(y)); + } + + + template + const T max( const struct mtx_str& x ) + { + return vop::max(x.base,ele_count(x)); + } + + template + const T min( const struct mtx_str& x ) + { + return vop::min(x.base,ele_count(x)); + } + + template< typename T> + struct mtx_str* alloc_one_hot( const struct mtx_str& mV ) + { + if( !is_vector(mV) ) + { + cwLogError(kInvalidArgRC,"Only vectors can be converted to one-hot matrices."); + return nullptr; + } + + int min_val = (int)mtx::min(mV); + int max_val = (int)mtx::max(mV); + unsigned rN = (max_val - min_val) + 1; + unsigned cN = ele_count(mV); + struct mtx::mtx_str* zM = mtx::alloc(kZeroFl,rN,cN); + + for(unsigned i=0; i(mV,i) - min_val; + ele(*zM, j, i) = 1; + } + + return zM; } - template< typename T0, typename T1 > rc_t mtx_mul( struct mtx_str& y, const struct mtx_str& m, const struct mtx_str& x )