00001 #ifndef NDARRAY_NDARRAY_H 00002 #define NDARRAY_NDARRAY_H 00003 00004 //-------------------------------------------------------------------------- 00005 // File and Version Information: 00006 // $Id: ndarray.h 6652 2013-08-13 18:19:30Z salnikov@SLAC.STANFORD.EDU $ 00007 // 00008 // Description: 00009 // Class ndarray. 00010 // 00011 //------------------------------------------------------------------------ 00012 00013 //----------------- 00014 // C/C++ Headers -- 00015 //----------------- 00016 #include <algorithm> 00017 #include <functional> 00018 #include <numeric> 00019 #include <iterator> 00020 00021 //---------------------- 00022 // Base Class Headers -- 00023 //---------------------- 00024 #include "nd_elem_access.h" 00025 00026 //------------------------------- 00027 // Collaborating Class Headers -- 00028 //------------------------------- 00029 00030 //------------------------------------ 00031 // Collaborating Class Declarations -- 00032 //------------------------------------ 00033 00034 // --------------------- 00035 // -- Class Interface -- 00036 // --------------------- 00037 00038 /// @addtogroup ndarray 00039 00040 /** 00041 * @ingroup ndarray 00042 */ 00043 namespace ndns { 00044 00045 /** 00046 * Enum defines assumed order of the element in memory. Values are used 00047 * as parameters for constructors and reshape() method. By default methods 00048 * assume C-style memory ordering where last index changes faster. If 00049 * in-memory data has Fortran layout (first index changes faster) than 00050 * one needs to provide Fortran as the last argument to the above methods. 00051 * It is also possible to change strides to any other memory layout 00052 * by using strides() method. 00053 */ 00054 enum Order { C, Fortran }; 00055 00056 } 00057 00058 /// @addtogroup ndarray_details 00059 00060 /** 00061 * @ingroup ndarray_details 00062 */ 00063 namespace ndarray_details { 00064 00065 // special destroyer for non-owned data 00066 template <typename ElemType> 00067 struct _no_delete { 00068 void operator()(ElemType*) const {} 00069 }; 00070 00071 // special destroyer for owned array data 00072 template <typename ElemType> 00073 struct _array_delete { 00074 void operator()(ElemType* ptr) const { delete [] ptr; } 00075 }; 00076 00077 // some template magic to drop const from type decl 00078 template <typename T> 00079 struct unconst { typedef T type; }; 00080 template <typename T> 00081 struct unconst<const T> { typedef T type; }; 00082 00083 } 00084 00085 /// @addtogroup ndarray 00086 00087 /** 00088 * @ingroup ndarray 00089 * 00090 * @brief N-dimensional array class. 00091 * 00092 * Ndarray (short for N-dimensional array) class provides high-level C++ 00093 * interface for multi-dimensional array data. This is a class template with 00094 * two parameters - element type and array rank. Array dimensionality 00095 * (rank) is fixed at compile time and cannot change. Actual dimensions and 00096 * size of array are dynamic and can change (for example in assignment). 00097 * 00098 * The type of the array elements is determined by the first template argument 00099 * and it can be any regular C++ type of fixed size. There is a distinction 00100 * between const and non-const template arguments. If non-const type is 00101 * used for template argument (such as ndarray<int,2>) then the resulting 00102 * ndarray is a modifiable object, one could use its methods to get access to 00103 * array elements and modify them (through non-const references or pointers). 00104 * On the other hand if template argument is a const type (e.g. ndarray<const int,2>) 00105 * then array is non-modifiable, it only returns const pointers and references to 00106 * the data which cannot be used to modify the data. 00107 * 00108 * There are two essential characteristics of every array - array shape and strides. 00109 * Array shape defines the size of every dimension of the array; shape is itself 00110 * a 1-dimensional array of size @c NDim (ndarray rank). Shape of the array is set in 00111 * the constructor and can be queried with @c shape() method. Strides define memory 00112 * layout of the array data. By default (if you do not provide strides in constructor) 00113 * ndarray assumes C-type memory layout (last index changes fastest) and 00114 * calculates appropriate strides itself. One can provide non-standard strides 00115 * for different (even disjoint) memory layouts. ndarray uses strides to calculate 00116 * element offset w.r.t. first element of the array. For example for 3-dimensional 00117 * array it finds an element as: 00118 * 00119 * @code 00120 * ndarr[i][j][k] = *(data + i*stride[0] + j*stride[1] * k*stride[2]) 00121 * @endcode 00122 * 00123 * where @c data is a pointer to first array element. For C-type memory layout 00124 * strides array is calculated from shape array as: 00125 * 00126 * @code 00127 * strides[NDim-1] = 1; 00128 * for (i = NDim-1 .. 1) strides[i-1] = strides[i]*shape[i]; 00129 * @endcode 00130 * 00131 * One can query the strides array with the @c strides() method which returns a pointer 00132 * to the 1-dimensional array of @c NDim size. 00133 * 00134 * Ndarray can be used to either provide access to the already existing data 00135 * (which are not in the form of the ndarray), or to create new objects 00136 * with newly allocated memory for array data. Ndarray transparently supports 00137 * memory management of the data area, in most cases one should not care about 00138 * how memory is allocated or deallocated. There are few different ways to 00139 * construct ndarray instances which determine how ndarray manages its corresponding 00140 * data: 00141 * 00142 * 1. from raw pointers: 00143 * @code 00144 * int* ptr = new int[100*100]; 00145 * unsigned int shape[2] = {100, 100}; 00146 * ndarray<int,2> array(ptr, shape); 00147 * //or 00148 * ndarray<int,2> array = make_ndarray(ptr, 100, 100); 00149 * @endcode 00150 * 00151 * In this case ndarray does not do anything special with the pointer that is 00152 * passed to it, it is responsibility of the client code to make sure that 00153 * memory is correctly deallocated if necessary and deallocation happens only after 00154 * the last copy of ndarray is destroyed. Because of the potential problems it is 00155 * not recommended anymore to make ndarrays from raw pointers. 00156 * 00157 * 2. internal memory allocation: 00158 * @code 00159 * unsigned int shape[2] = {100, 100}; 00160 * ndarray<int,2> array(shape); 00161 * // or 00162 * ndarray<int,2> array = make_ndarray<int>(100, 100); 00163 * @endcode 00164 * 00165 * In this case constructor allocates necessary space to hold array data. This 00166 * space is automatically deallocated when the last copy of the ndarray pointing 00167 * to that array data is destroyed. This is a preferred way to make ndarrays if 00168 * you need to allocate new memory for your data. 00169 * 00170 * 3. from shared pointer (for advanced users): 00171 * @code 00172 * boost::shared_ptr<int> data = ...; 00173 * unsigned int shape[2] = {100, 100} 00174 * ndarray<int,2> array(data, shape); 00175 * // or 00176 * ndarray<int,2> array = make_ndarray(data, 100, 100); 00177 * @endcode 00178 * 00179 * In this case shared pointer defines memory management policy for the data, ndarray 00180 * copies this pointers and shares array data through this pointer with all other 00181 * clients. The memory will be deallocated (if necessary) when last copy of shared 00182 * pointer disappears (including all copies in ndarray instances). Note that 00183 * creating shared pointer for array data needs special care. One cannot just say: 00184 * @code 00185 * // DO NOT DO THIS, BAD THINGS WILL HAPPEN! 00186 * boost::shared_ptr<int> data(new int[100*100]); 00187 * @endcode 00188 * as this will cause incorrect <tt>operator new</tt> be called when data is going to be 00189 * deallocated. Special deleter object (or different way of constructing) is needed 00190 * in this case, check boost.shared_ptr documentation. 00191 * 00192 * The main method of accessing array elements is a traditional C-like square 00193 * bracket syntax: 00194 * 00195 * @code 00196 * ndarray<int, 3> ndarr(...); 00197 * int elem = ndarr[i][j][k]; 00198 * @endcode 00199 * 00200 * Alternatively if the array indices are located in an array one can use @c at() method: 00201 * 00202 * @code 00203 * ndarray<int, 3> ndarr(...); 00204 * unsigned idx[3] = {i, j, k}; 00205 * int elem = ndarr.at(idx); 00206 * @endcode 00207 * 00208 * One can modify elements of the array if array template type is non-const: 00209 * 00210 * @code 00211 * ndarray<int, 3> ndarr(...); 00212 * ndarr[i][j][k] = 1000; 00213 * @endcode 00214 * 00215 * Additionally ndarray class provides STL-compatible iterators and usual methods 00216 * @c begin(), @c end(), @c rbegin(), @c rend(). The iterators can be used with 00217 * many standard algorithm. Note that iterators always scan for elements in the 00218 * memory order from first (data()) to last (data()+size()) array element 00219 * (iterators do not use strides and do not work with disjoint array memory). 00220 * 00221 * Method @c size() returns the total number of elements in array. 00222 * 00223 * Regular ndarray constructor take shape array (and optionally strides array). 00224 * It is not always convenient to pass array dimensions as array elements. Few 00225 * additional functions are provided which construct ndarray from dimensions 00226 * provided via regular arguments, here are few examples of their use: 00227 * 00228 * @code 00229 * // Create 1-dim array of size 1048576 00230 * int* data = ...; 00231 * ndarray<int, 1> arr2d = make_ndarray(data, 1048576); 00232 * 00233 * // Create 2-dim array of dimensions 1024x1024, allocate space for it 00234 * ndarray<int, 2> arr2d = make_ndarray<int>(1024, 1024); 00235 * 00236 * // Create 3-dim array of dimensions 4x512x512 from shared pointer 00237 * boost::shared_ptr<int> shptr = ...; 00238 * ndarray<int, 3> arr2d = make_ndarray(shptr, 4, 512, 512); 00239 * 00240 * // Create non-modifiable array from constant data 00241 * const int* cdata = ...; 00242 * ndarray<const int, 1> arr2d = make_ndarray(cdata, 1048576); 00243 * @endcode 00244 * 00245 * This software was developed for the LCLS project. If you use all or 00246 * part of it, please give an appropriate acknowledgment. 00247 * 00248 * @version $Id: ndarray.h 6652 2013-08-13 18:19:30Z salnikov@SLAC.STANFORD.EDU $ 00249 * 00250 * @author Andy Salnikov 00251 */ 00252 00253 template <typename ElemType, unsigned NDim> 00254 class ndarray : public ndarray_details::nd_elem_access<ElemType, NDim> { 00255 typedef ndarray_details::nd_elem_access<ElemType, NDim> Super; 00256 public: 00257 00258 typedef ElemType element; 00259 typedef element* iterator; 00260 typedef std::reverse_iterator<iterator> reverse_iterator; 00261 typedef size_t size_type; 00262 typedef unsigned shape_t; 00263 typedef int stride_t; 00264 typedef ndarray<typename ndarray_details::unconst<ElemType>::type, NDim> nonconst_ndarray; 00265 00266 /// Default constructor makes an empty array 00267 ndarray () {} 00268 00269 /** 00270 * @brief Constructor that takes pointer to data and shape. 00271 * 00272 * Optional third argument defines memory order of the elements, default is to 00273 * assume C order (last index changes fastest). This argument determines how strides 00274 * are calculated, strides can be changed later with strides() method. 00275 * 00276 * @param[in] data Pointer to data array 00277 * @param[in] shape Pointer to dimensions array, size of array is NDim, array data will be copied. 00278 * @param[in] order Memory order of the elements. 00279 */ 00280 ndarray (ElemType* data, const shape_t* shape, ndns::Order order = ndns::C) 00281 { 00282 std::copy(shape, shape+NDim, Super::m_shape); 00283 _setStrides(order); 00284 Super::m_data = boost::shared_ptr<ElemType>(data, ndarray_details::_no_delete<ElemType>()); 00285 } 00286 00287 /** 00288 * @brief Constructor that takes shared pointer to data and shape array. 00289 * 00290 * Optional third argument defines memory order of the elements, default is to 00291 * assume C order (last index changes fastest). This argument determines how strides 00292 * are calculated, strides can be changed later with strides() method. 00293 * 00294 * @param[in] data Pointer to data array 00295 * @param[in] shape Pointer to dimensions array, size of array is NDim, array data will be copied. 00296 * @param[in] order Memory order of the elements. 00297 */ 00298 ndarray (const boost::shared_ptr<ElemType>& data, const shape_t* shape, ndns::Order order = ndns::C) 00299 { 00300 std::copy(shape, shape+NDim, Super::m_shape); 00301 _setStrides(order); 00302 Super::m_data = data; 00303 } 00304 00305 /** 00306 * @brief Constructor that takes shape array and allocates necessary space for data. 00307 * 00308 * After allocation the data in array is not initialized and will contain garbage. 00309 * Optional second argument defines memory order of the elements, default is to 00310 * assume C order (last index changes fastest). This argument determines how strides 00311 * are calculated, strides can be changed later with strides() method. 00312 * 00313 * @param[in] shape Pointer to dimensions array, size of array is NDim, array data will be copied. 00314 * @param[in] order Memory order of the elements. 00315 */ 00316 ndarray (const shape_t* shape, ndns::Order order = ndns::C) 00317 { 00318 std::copy(shape, shape+NDim, Super::m_shape); 00319 _setStrides(order); 00320 Super::m_data = boost::shared_ptr<ElemType>(new ElemType[size()], ndarray_details::_array_delete<ElemType>()); 00321 } 00322 00323 /** 00324 * @brief Constructor from nd_elem_access_pxy instance. 00325 * 00326 * This constructor is used for slicing of the original ndarray, proxy 00327 * is returned from operator[] and you can make ndarray which is a slice 00328 * of the original array from that proxy object. Both original array and 00329 * this new instance will share the data. 00330 */ 00331 ndarray (const ndarray_details::nd_elem_access_pxy<ElemType, NDim>& pxy) 00332 { 00333 Super::m_data = pxy.m_data; 00334 std::copy(pxy.m_shape, pxy.m_shape+NDim, Super::m_shape); 00335 std::copy(pxy.m_strides, pxy.m_strides+NDim, Super::m_strides); 00336 } 00337 00338 /** 00339 * @brief Copy constructor from other ndarray. 00340 * 00341 * If the template argument (ElemType) is a const type (like <tt>const int</tt> then 00342 * this constructor makes const ndarray from non-const, so for example one can write 00343 * code which converts non-const array to const: 00344 * 00345 * @code 00346 * ndarray<int,3> arr1 = ...; 00347 * ndarray<const int,3> arr2(arr1); 00348 * @endcode 00349 * 00350 * If template argument (ElemType) is non-const type then this is a regular copy 00351 * constructor. It does not actually copy array data, in both cases data is shared 00352 * between original array and new instance. 00353 */ 00354 ndarray(const nonconst_ndarray& other) 00355 : Super(other) 00356 { 00357 } 00358 00359 /// Assignment operator, data is never copied. 00360 ndarray& operator=(const nonconst_ndarray& other) 00361 { 00362 Super::operator=(other); 00363 return *this; 00364 } 00365 00366 /// Assignment from nd_elem_access_pxy. 00367 ndarray& operator=(const ndarray_details::nd_elem_access_pxy<ElemType, NDim>& pxy) 00368 { 00369 Super::m_data = pxy.m_data; 00370 std::copy(pxy.m_shape, pxy.m_shape+NDim, Super::m_shape); 00371 std::copy(pxy.m_strides, pxy.m_strides+NDim, Super::m_strides); 00372 return *this; 00373 } 00374 00375 00376 /** 00377 * @brief Array element access. 00378 * 00379 * This method accepts the array of indices, size of the array is the number of 00380 * dimensions. Alternative way to access elements in the array is to use regular 00381 * operator[] inherited from nd_elem_access base class. 00382 */ 00383 element& at(shape_t index[]) const { 00384 element* data = Super::m_data.get(); 00385 for (unsigned i = 0; i != NDim; ++ i) { 00386 data += index[i]*Super::m_strides[i]; 00387 } 00388 return *data; 00389 } 00390 00391 /** 00392 * Returns pointer to the beginning of the data array. 00393 */ 00394 element* data() const { return Super::m_data.get(); } 00395 00396 /** 00397 * @brief Returns shape of the array. 00398 * 00399 * Returns an array (or pointer to its first element) of the ndarray dimensions. 00400 */ 00401 const shape_t* shape() const { return Super::m_shape; } 00402 00403 /** 00404 * @brief Returns array strides. 00405 * 00406 * Returns an array (or pointer to its first element) of the ndarray strides. 00407 */ 00408 const stride_t* strides() const { return Super::m_strides; } 00409 00410 /** 00411 * @brief Changes strides. 00412 * 00413 * If array has a non-conventional memory layout it is possible to change 00414 * the strides. 00415 * 00416 * @param[in] strides Pointer to new strides array, size of array is NDim, array data will be copied. 00417 */ 00418 void strides(const stride_t* strides) { 00419 std::copy(strides, strides+NDim, Super::m_strides); 00420 } 00421 00422 /** 00423 * @brief Changes the shape of the array. 00424 * 00425 * No checks are done on the size of the new array. 00426 * Optional second argument defines memory order of the elements, default is to 00427 * assume C order (last index changes fastest). This argument determines how strides 00428 * are calculated, strides can be changed later with strides() method. 00429 * 00430 * @param[in] shape Pointer to dimensions array, size of array is NDim, array data will be copied. 00431 * @param[in] order Memory order of the elements. 00432 */ 00433 void reshape(const shape_t* shape, ndns::Order order = ndns::C) { 00434 std::copy(shape, shape+NDim, Super::m_shape); 00435 _setStrides(order); 00436 } 00437 00438 00439 /// Returns total number of elements in array 00440 size_type size() const { 00441 return std::accumulate(Super::m_shape, Super::m_shape+NDim, size_type(1), std::multiplies<size_type>()); 00442 } 00443 00444 /// Returns true if array has no data 00445 bool empty() const { return not Super::m_data; } 00446 00447 /// Returns iterator to the beginning of data, iteration is performed in memory order 00448 iterator begin() const { return Super::m_data.get(); } 00449 00450 /// Returns iterator to the end of data 00451 iterator end() const { return Super::m_data.get() + size(); } 00452 00453 /// Returns reverse iterators 00454 reverse_iterator rbegin() const { return reverse_iterator(end()); } 00455 reverse_iterator rend() const { return reverse_iterator(begin()); } 00456 00457 /// swap contents of two arrays 00458 void swap(ndarray& other) { 00459 std::swap(Super::m_data, other.Super::m_data); 00460 std::swap_ranges(Super::m_shape, Super::m_shape+NDim, other.Super::m_shape); 00461 std::swap_ranges(Super::m_strides, Super::m_strides+NDim, other.Super::m_strides); 00462 } 00463 00464 /** 00465 * Make a deep copy of the array data, returns modifiable array. 00466 * 00467 * Note that this does not work with disjoint arrays, use on your own risk. 00468 */ 00469 nonconst_ndarray copy() const { 00470 nonconst_ndarray res(shape()); 00471 res.strides(strides()); 00472 std::copy(begin(), end(), res.begin()); 00473 return res; 00474 } 00475 00476 protected: 00477 00478 // calculate strides from shape array given the assumed ordering 00479 void _setStrides(ndns::Order order) { 00480 if (order == ndns::C) { 00481 Super::m_strides[NDim-1] = 1; 00482 for (int i = int(NDim)-2; i >= 0; -- i) Super::m_strides[i] = Super::m_strides[i+1] * Super::m_shape[i+1]; 00483 } else { 00484 Super::m_strides[0] = 1; 00485 for (unsigned i = 1; i < NDim; ++ i) Super::m_strides[i] = Super::m_strides[i-1] * Super::m_shape[i-1]; 00486 } 00487 } 00488 00489 private: 00490 00491 }; 00492 00493 /// @ingroup ndarray 00494 /// Helper method to create an instance of 1-dimensional array from raw data pointer. 00495 /// Note there may be potential problems with memory management with raw pointers. 00496 template <typename ElemType> 00497 inline 00498 ndarray<ElemType, 1> 00499 make_ndarray(ElemType* data, unsigned dim0) 00500 { 00501 unsigned shape[] = {dim0}; 00502 return ndarray<ElemType, 1>(data, shape); 00503 } 00504 00505 /// @ingroup ndarray 00506 /// Helper method to create an instance of 2-dimensional array from raw data pointer. 00507 /// Note there may be potential problems with memory management with raw pointers. 00508 template <typename ElemType> 00509 inline 00510 ndarray<ElemType, 2> 00511 make_ndarray(ElemType* data, unsigned dim0, unsigned dim1) 00512 { 00513 unsigned shape[] = {dim0, dim1}; 00514 return ndarray<ElemType, 2>(data, shape); 00515 } 00516 00517 /// @ingroup ndarray 00518 /// Helper method to create an instance of 3-dimensional array from raw data pointer. 00519 /// Note there may be potential problems with memory management with raw pointers. 00520 template <typename ElemType> 00521 inline 00522 ndarray<ElemType, 3> 00523 make_ndarray(ElemType* data, unsigned dim0, unsigned dim1, unsigned dim2) 00524 { 00525 unsigned shape[] = {dim0, dim1, dim2}; 00526 return ndarray<ElemType, 3>(data, shape); 00527 } 00528 00529 /// @ingroup ndarray 00530 /// Helper method to create an instance of 4-dimensional array from raw data pointer. 00531 /// Note there may be potential problems with memory management with raw pointers. 00532 template <typename ElemType> 00533 inline 00534 ndarray<ElemType, 4> 00535 make_ndarray(ElemType* data, unsigned dim0, unsigned dim1, unsigned dim2, unsigned dim3) 00536 { 00537 unsigned shape[] = {dim0, dim1, dim2, dim3}; 00538 return ndarray<ElemType, 4>(data, shape); 00539 } 00540 00541 /// @ingroup ndarray 00542 /// Helper method to create an instance of 1-dimensional array. 00543 /// Memory for data is allocated internally in this case. 00544 template <typename ElemType> 00545 inline 00546 ndarray<ElemType, 1> 00547 make_ndarray(unsigned dim0) 00548 { 00549 unsigned shape[] = {dim0}; 00550 return ndarray<ElemType, 1>(shape); 00551 } 00552 00553 /// @ingroup ndarray 00554 /// Helper method to create an instance of 2-dimensional array. 00555 /// Memory for data is allocated internally in this case. 00556 template <typename ElemType> 00557 inline 00558 ndarray<ElemType, 2> 00559 make_ndarray(unsigned dim0, unsigned dim1) 00560 { 00561 unsigned shape[] = {dim0, dim1}; 00562 return ndarray<ElemType, 2>(shape); 00563 } 00564 00565 /// @ingroup ndarray 00566 /// Helper method to create an instance of 3-dimensional array. 00567 /// Memory for data is allocated internally in this case. 00568 template <typename ElemType> 00569 inline 00570 ndarray<ElemType, 3> 00571 make_ndarray(unsigned dim0, unsigned dim1, unsigned dim2) 00572 { 00573 unsigned shape[] = {dim0, dim1, dim2}; 00574 return ndarray<ElemType, 3>(shape); 00575 } 00576 00577 /// @ingroup ndarray 00578 /// Helper method to create an instance of 4-dimensional array. 00579 /// Memory for data is allocated internally in this case. 00580 template <typename ElemType> 00581 inline 00582 ndarray<ElemType, 4> 00583 make_ndarray(unsigned dim0, unsigned dim1, unsigned dim2, unsigned dim3) 00584 { 00585 unsigned shape[] = {dim0, dim1, dim2, dim3}; 00586 return ndarray<ElemType, 4>(shape); 00587 } 00588 00589 /// @ingroup ndarray 00590 /// Helper method to create an instance of 1-dimensional array from shared pointer. 00591 /// Shaed pointer defines memory management policy in this case. 00592 template <typename ElemType> 00593 inline 00594 ndarray<ElemType, 1> 00595 make_ndarray(const boost::shared_ptr<ElemType>& data, unsigned dim0) 00596 { 00597 unsigned shape[] = {dim0}; 00598 return ndarray<ElemType, 1>(data, shape); 00599 } 00600 00601 /// @ingroup ndarray 00602 /// Helper method to create an instance of 2-dimensional array from shared pointer. 00603 /// Shaed pointer defines memory management policy in this case. 00604 template <typename ElemType> 00605 inline 00606 ndarray<ElemType, 2> 00607 make_ndarray(const boost::shared_ptr<ElemType>& data, unsigned dim0, unsigned dim1) 00608 { 00609 unsigned shape[] = {dim0, dim1}; 00610 return ndarray<ElemType, 2>(data, shape); 00611 } 00612 00613 /// @ingroup ndarray 00614 /// Helper method to create an instance of 3-dimensional array from shared pointer. 00615 /// Shaed pointer defines memory management policy in this case. 00616 template <typename ElemType> 00617 inline 00618 ndarray<ElemType, 3> 00619 make_ndarray(const boost::shared_ptr<ElemType>& data, unsigned dim0, unsigned dim1, unsigned dim2) 00620 { 00621 unsigned shape[] = {dim0, dim1, dim2}; 00622 return ndarray<ElemType, 3>(data, shape); 00623 } 00624 00625 /// @ingroup ndarray 00626 /// Helper method to create an instance of 4-dimensional array from shared pointer. 00627 /// Shaed pointer defines memory management policy in this case. 00628 template <typename ElemType> 00629 inline 00630 ndarray<ElemType, 4> 00631 make_ndarray(const boost::shared_ptr<ElemType>& data, unsigned dim0, unsigned dim1, unsigned dim2, unsigned dim3) 00632 { 00633 unsigned shape[] = {dim0, dim1, dim2, dim3}; 00634 return ndarray<ElemType, 4>(data, shape); 00635 } 00636 00637 #include "nd_format.h" 00638 00639 #endif // NDARRAY_NDARRAY_H