Skip to content
tqchen edited this page Feb 15, 2014 · 87 revisions

Take a look at data structure

The basic data structure of mshadow is Tensor. The following is a simplified equivalent version of the declaration in mashadow/tensor.h

typedef real_t float;
typedef unsigned index_t;
template<int dimension>
struct Shape{
    index_t shape_[ dimension ];
    index_t stride_;
};
template<typename Device, int dimension>
struct Tensor{
    real_t *dptr;
    Shape<dimension> shape;
};
// this is how shape object declaration
Shape<2> shape2;
// this is how tensor object declaration
Tensor<cpu,2> ts2;
Tensor<gpu,3> ts3;

Tensor<cpu,2> means a two dimensional tensor in CPU, while Tensor<gpu,3> means three dimensional tensor in CPU. Shape<k> gives the shape information of k-dimensional tensor. The declaration use template, and can be specialized into tensor of specific device and dimension. This is what two dimensional tensor will look like:

struct Shape<2>{
    index_t shape_[ 2 ];
    index_t stride_;
};
struct Tensor<cpu,2>{
    real_t *dptr;
    Shape<2> shape;
};

Tensor<cpu,2> contains dptr, which points to the space that backup the tensor. Shape<2> is a structure that stores shape information:

  • shape_[0] gives the lowest dimension, shape_[1] gives the second dimension, etc. This is different from numpy.
  • stride_ gives the number of cell space allocated in the lowest dimension. This is introduced when we introduce some padding cells in lowest dimension to make sure memory is aligned. stride_ is automatically set during memory allocation of tensor in mshadow.

To understand the data structure, consider the following code:

real_t data[9] = {0,1,2,3,4,5,6,7,8};
Tensor<cpu,2> ts; ts.dptr = data;
ts.shape[0] = 2, ts.shape[1] = 3; ts.shape.stride_ = 3;  
// now: ts[0][0] == 0, ts[0][1] == 1 , ts[1][0] == 3, ts[1][1] == 4 
for( index_t i = 0; i < ts.shape[1]; i ++ ){
    for( index_t j = 0; j < ts.shape[0], j ++ ){
        printf("ts[%u][%u]=%f\n", i,j, ts[i][j] );
    }
}

The result ts should be a 3 * 2 matrix, where data[2], data[5], data[8] are padding cells that are ignored. If you want a continuous memory, set stride_=shape_[0].

Basic operations of Tensor

Element-wise operations.

The following code is from example/basic.cpp, that illustrate basic usage of mshadow.

// header file to use mshadow
#include "mshadow/tensor.h"
// this namespace contains all data structures, functions
using namespace mshadow;
// this namespace contains all operator overloads 
using namespace mshadow::expr;

int main( void ){
    // assume we have a float space
    float data[ 20 ];
    // create a 2 x 5 x 2 tensor, from existing space
    Tensor<cpu,3> ts( data, Shape3(2,5,2) );
    // take first subscript of the tensor 
    Tensor<cpu,2> mat = ts[0];
    // Tensor object is only a handle, assignment means they have same data content
    Tensor<cpu,2> mat2 = mat;

    // shape of matrix, note shape order is different from numpy
    // shape[i] indicate the shape of i-th dimension
    printf("%u X %u matrix\n", mat.shape[1], mat.shape[0] );

    // initialize all element to zero
    mat = 0.0f;
    // assign some values
    mat[0][1] = 1.0f; mat[1][0] = 2.0f;
    // elementwise operations
    mat += ( mat + 10.0f ) / 10.0f + 2.0f;

    // print out matrix, note: mat2 and mat1 are handles(pointers)
    for( index_t i = 0; i < mat.shape[1]; i ++ ){
        for( index_t j = 0; j < mat.shape[0]; j ++ ){
            printf("%.2f ", mat2[i][j]);
        }
        printf("\n");
    }
    return 0;
}
Clone this wiki locally