MatX For MATLAB/NumPy/Eigen Users#

MatX has many features directly inspired by MATLAB and Python (Numpy/Scipy) for translating these high-level languages into C++. The table below aims to give users of either of those tools a translation guide to writing efficient MatX code by learning the syntax mapping between the tools. Most of these conversions can also be found inside either the unit tests or the source code as well.

Due to its popularity in linear algebra applications, examples of common Eigen operations have been added to the below table. An example file is provided at examples/eigenExample.cu with examples of common operations in Eigen and their MatX equivalent. If you have Eigen installed on your system, you can build the examples with Eigen by setting the cmake variable eigen_DIR=/path/to/eigen/.

A few key notes to be aware of when using MatX and Eigen in the same environment:

  1. The below example are only valid for 2D data. Eigen and its API is primarily targeting 2D problems (without the unsupported/tensor library), so there is not a single pattern to follow for porting code with rank > 2 tensors from Eigen to MatX; each user’s solution for higher rank data will result in a unique mapping to MatX tensor memory.

  2. When copying data between Eigen and MatX structures (most likely Eigen::MatrixXd to MatX tensors) keep in mind that the underlying data structure may or may not be available on the CPU. use the accessor functions () or a cudaMemcpy when applicable.

  3. Eigen has column-major storage by default, so ensure you transpose any raw data copies between structures.

Overview#

Some general rules to keep in mind about these three tools:

  1. The Python column assumes both Numpy and Scipy are being used for certain library calls.

  2. Both Python and MATLAB use the term “multi-dimensional array”. MatX calls these tensors.

  3. MATLAB uses 1-based indexing while Python and MatX use 0-based indexing.

  4. MATLAB uses inclusive ranges on indexing, while Python and MatX use exclusive ranges.

  5. MATLAB and Python may or may not make a copy of a tensor behind-the-scenes to improve performance. MatX makes this explicit. by never making a copy unless the function call mentions that it copies.

  6. MATLAB uses column-major (FORTRAN) memory order, while Python and MatX use row-major (C). When converting optimized MATLAB scripts, it may be beneficial to transpose the dimension so the fastest changing dimension is the inner-most dimension.

Conversion Table#

Conversion Table#

Operation

MATLAB

Python

Eigen

MatX

Notes

Basic indexing

A(1,5)

A[0,4]

A(0,4)

A(0,4)

Retrieves the element in the first row and fifth column

Tensor addition

A + B

A + B

A + B

A + B

Adds two tensors element-wise

Tensor subtraction

A - B

A - B

A - B

A - B

Subtracts two tensors element-wise

Tensor multiplication

A .* B

A * B

A.cwiseProduct(B)

A * B

Multiplies two tensors element-wise

Tensor division

A ./ B

A / B

A.cwiseQuotient(B)

A / B

Divides two tensors element-wise

Tensor slice (contiguous)

A(1:4,2:5)

A[0:5,1:6]

A.block(0, 1, 5, 6)

slice(A, {0,1}, {5,6});

Slices 4 elements of the outer dimension starting at 0, and 5 elements of the inner dimension, starting at the second element.

Tensor slice (w/stride)

A(1:2:end,2:3:8)

A[::2,1:9:3]

Eigen::Map<Eigen::MatrixXf, 0, Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic>> strided(matrix.data() + 0 * matrix.outerStride() + 0, 5, 3, Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic>(3 * matrix.outerStride(), 2))

slice(A, {0,1}, {matxEnd,9}, {2,3});

Slices N elements of the outer dimension starting at the first element and picking every second element until the end. In the inner dimension, start at the first element and grab every third item, and stop at the 8th item.

Cloning a dimension

reshape(repmat(A, [4,1]), [4 4 4])

np.repeat(np.expand_dims(A, axis=0), 5, 0)

cloneMat = A.replicate(5, 1)

clone<3>(A, {5, matxKeepDim, matxKeepDim})

Takes a 4x4 2D tensor and makes it a 5x4x4 3D tensor where every outer dimension replicates the two inner inner dimensions

Slice off a row or column

A(5,:)

A[4,:]

Eigen::RowVector3d row = a.row(1)

slice<1>(A, {4, 0}, {matxDropDim, matxEnd})

Selects the fifth row and all columns from a 2D tensor, and returns a 1D tensor

Permute dimensions

permute(A, [3 2 1])

np.einsum('kij->ijk', A)

Eigen::PermutationMatrix<3> perm perm.indices() << 2, 1, 0 Eigen::Matrix3d permutedMatrix = perm * a

permute(A, {2,1,0}) or cutensor::einsum("kij->ijk", A);

Permutes the three axes into the opposite order In the inner dimension, start at the first element and grab every third item, and stop at the 8th item. In the inner dimension, start at the first element and grab every third item, and stop at the 8th item.

Get real values

real(A)

np.real(A)

A.real()

A.RealView()

Returns only the real values of the complex series

Matrix multiply (GEMM)

A * B

np.matmul(A, B) or A @ B

A * B

matmul(A, B)

Computes the matrix multiplication of A * B

Compute matrix inverse

inv(A)

np.linalg.inv(A)

A.inverse()

inv(A)

Computes the inverse of matrix A using LU factorization

1D FFT

fft(A)

np.fft.fft(A)

N/A

fft(A)

Computes the 1D fast fourier transfor, (FFT) of rows of A

1D IFFT

ifft(A)

np.fft.ifft(A)

N/A

ifft(A)

Computes the 1D inverse fast fourier transfor, (IFFT) of rows of A

2D FFT

fft2(A)

np.fft.fft2(A)

N/A

fft2(A)

Computes the 2D fast fourier transfor, (FFT) of matrices in outer 2 dimensions of A

2D IFFT

ifft2(A)

np.fft.ifft2(A)

N/A

ifft2(A)

Computes the 2D inverse fast fourier transfor, (IFFT) of matrices in outer 2 dimensions of A

Covariance

cov(A)

np.cov(A)

N/A

cov(A)

Computes the covariance on the rows of matrix A