In a previous post I explained how to create and return a matrix with LibraryLink. In this post we are going to expand on this a bit and create a function which accepts an integer matrix and returns the transposed result.
Here is the code (call it IntegerMatrixTranspose.c):
#include "WolframLibrary.h"
DLLEXPORT int IntegerMatrixTranspose(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res) {
/* Variable declarations */
int err = LIBRARY_NO_ERROR;
MTensor in;
mint in_type;
mint in_rank;
mint const *in_dims;
mint *in_data;
MTensor out;
mint out_type;
mint out_rank;
mint const *out_dims;
mint *out_data;
int i;
mint *tmp_dims;
mint rows, row, cols, col;
/* Code section */
/* Get the input matrix */
in = MArgument_getMTensor(Args[0]);
in_type = libData->MTensor_getType(in);
in_rank = libData->MTensor_getRank(in);
in_dims = libData->MTensor_getDimensions(in);
in_data = libData->MTensor_getIntegerData(in);
/* The type and rank of the output matrix is the same as the input matrix */
out_type = in_type;
out_rank = in_rank;
/* The output dimensions are the reverse of the input dimensions */
tmp_dims = (mint *) malloc(in_rank * sizeof(mint));
for(i=0; i<in_rank; i++) { tmp_dims[i] = in_dims[in_rank-i-1]; }
out_dims = tmp_dims;
free(tmp_dims);
/* Create the output matrix */
err = libData->MTensor_new(out_type, out_rank, out_dims, &out);
out_data = libData->MTensor_getIntegerData(out);
/* Set the elements of the output matrix */
rows = in_dims[0];
cols = in_dims[1];
for(row=0; row<rows; row++) {
for(col=0; col<cols; col++) {
out_data[row+rows*col] = in_data[col+cols*row];
}
}
MArgument_setMTensor(Res,out);
return err;
}
Let's take a look at each section of this code to understand what is going on. The top section labelled 'Variable declarations' declares the variables we need for transposing a matrix with LibraryLink. We need two tensors ('in' and 'out'). Each tensor has a type (for example the integer type (known as MType_Integer, but that is not needed in the code)). Each tensor also has a rank, and since we're dealing with matrices in this function the rank is going to be '2'. The tensor dimensions 'in_dims' is an array of 'rank' elements. In this case it will indicate the rows and columns. The tensor dimensions for 'out' are the same as 'in' except the rows and columns are reversed.
Now let's take a look at the 'Code section'. First we get the tensor 'in' from the first argument Args[0]. We then use the libData structure to extract the type, rank, dimensions and elements from this tensor. Next we will create the 'out' tensor. For this we need to set the result type (the same as the input type: integer), the result rank (also the same as the input) and the dimensions.
To get the dimensions we create a temporary array to store the dimensions of the input dimensions, but in reversed order.
Now we can create the output tensor 'out' with MTensor_new. This only leaves the task to assign each element in[row,col] to out[col,row], which is achieved by the double for loop in the code.
Finally we return the result using MArgument_setMTensor.
The Wolfram Language build and function loading code will now need to specify that we are going to pass in an integer matrix and that we are going to return an integer matrix (IntegerMatrixTranspose.m):
Needs["CCompilerDriver`"];
lib = CreateLibrary[{"IntegerMatrixTranspose.c"}, "IntegerMatrixTranspose"];
IntegerMatrixTranspose = LibraryFunctionLoad[lib, "IntegerMatrixTranspose", {{Integer, 2}}, {Integer, 2}];
Now we can run this code in the Wolfram Language, and transpose an integer matrix with the c code we just wrote:
Mathematica 10.2.0 for Microsoft Windows (64-bit)
Copyright 1988-2015 Wolfram Research, Inc.
In[1]:= Get["IntegerMatrixTranspose.m"]
In[2]:= IntegerMatrixTranspose[ {{1,2,3}, {4,5,6}, {7,8,9}, {10,11,12}} ] // Grid
Out[2]= 1 4 7 10
2 5 8 11
3 6 9 12
In the next post we will take a look at how to work with complex numbers.