MATLAB
Contenido
MEX functions
Un archivo MEX permite llamar una función escrita en C desde Matlab. Para ello se hace uso del comando de Matlab ‘mex’ y de un compilador compatible instalado en el sistema operativo.
Eligiendo compilador
Para el sistema operativo Linux la version por defecto del compilador es GCC versión ‘6.3.X’. La lista de compiladores compatibles de acuerdo al sistema instalado se puede consultar aquí. Para corroborar que su sistema posee un compilador compatible (o elegir entre más de un compilador, si su sistema los posee) ejecute lo siguiente:
>> mex -setup
MEX configured to use 'gcc' for C language compilation.
Un primer programa ‘Hola mundo’
El objetivo de esta seccion es compilar y ejecutar el contenido de un archivo C sencillo desde Matlab.
Pasos:
- 1. Utilizando su editor de texto favorito, cree un archivo llamado ‘hola.c’ en su área de trabajo de Matlab. Su área de trabajo actual la puede encontrar ejecutando
>> pwd
- 2. Agregue el siguiente contenido a su archivo ‘hola.c’
#include "mex.h" void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[]){ printf("Hola mundo!\n"); return; }
- Note que el header ‘mex.h’ es requerido en el archivo. Éste contiene las funciones del API de Matlab. Las demás secciones seran explicadas a lo largo de esta guía.
- 3.Una vez guardado el archivo, desde la consola de Matlab ejecute
>> mex hola.c Building with 'gcc'. MEX completed successfully.
- 4. Una vez que el archivo ha sido satisfactoriamente compilado la función MEX ‘hola’ estará disponible para usarse desde la consola de Matlab:
>> hola Hola mundo!
Utilizando una función C existente para crear un archivo MEX
Suponga que se tiene el siguiente código funcional en C:
#include<stdio.h> void arrayProduct(double x, double *y, double *z, int n); void main(){ double vect0[] = {3,2,5,4}; double cval = 4; double vect1[4]; arrayProduct(cval, vect0, vect1, 4); printf("Third scaled value is %3.2f\n", vect1[2]); } void arrayProduct(double x, double *y, double *z, int n){ int i; for (i=0; i<n; i++) { z[i] = x * y[i]; } }
el cual simplemente escala un vector dado por una constante, usando para ello la función arrayProduct. Se requiere crear un archivo MEX de manera que la funcionalidad de arrayProduct se pueda usar directamente desde Matlab, por ejemplo:
>>s = 5; >>A = [1.5, 2, 9]; >>B = arrayProduct(s,A) B = 7.5000 10.0000 45.0000
Creando el archivo fuente: La función mexFunction
En esta sección se creará el archivo fuente para ser usado por Matlab. Cree un archivo *.c y agregue el siguiente header para poder usar el API de Matlab (en este ejemplo, es nombrado como cvector.c)
#include "mex.h"
Lo siguiente es agregar la función mexFunction. Esta función es requerida en cualquier archivo *.c que se desee compilar con ‘mex’, y es el puente entre nuestro código C y Matlab. Cada vez que una función MEX es llamada Matlab busca en el código correspondiente a la función mexFunction. Si mexFunction no es encontrada entonces un error es disparado. La función mexFunction tiene la siguiente firma:
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
Los argumentos se detallan en la siguiente tabla.
nlhs | Número de argumentos de salida, o bien el tamaño del arreglo plhs |
plhs | Arreglo de punteros a los argumentos de salida. |
nrhs | Número de argumentos de entrada, o bien el tamaño del arreglo prhs |
prhs | Arreglo de punteros a los argumentos de entrada. |
Note que los argumentos de entrada o salida son del tipo mxArray, el cual es el tipo del 'arreglo de Matlab' (En Matlab todas las variables (escalares, vectores, matrices, objetos …) son guardadas como arreglos. Al declararse una variable de Matlab en C este tipo de variable debe de ser explícito).
En nuestro caso, queremos definir un arreglo de tamaño arbitrario en Matlab y definir una constante para multiplicar cada entrada de este arreglo. Buscamos algo como
>> s = 5; >> A = [3,2,8]; >> B = cvector(s,A);
Entonces estaremos enviando por medio del argumento prhs la constante en la primera posición (s en el código anterior), y el vector en la segunda posición (A en el código anterior).
Recibiendo los datos en la función mexFunction y preparando la salida
Necesitamos entonces saber interpretar nuestro arreglo de entrada y preparar la salida de forma consistente con lo que se quiere hacer (funcion cvector en nuestro caso, figura 2). El código que debemos agregar nuestra función mexFunction se presenta a continuación.
void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ double multiplier; /* input scalar */ double *inMatrix; /* 1xN input matrix */ mwSize ncols; /* size of matrix */ double *outMatrix; /* output matrix */ multiplier = mxGetScalar(prhs[0]); inMatrix = mxGetDoubles(prhs[1]); ncols = mxGetN(prhs[1]); /* create the output matrix */ plhs[0] = mxCreateDoubleMatrix(1,ncols,mxREAL); /* get a pointer to the real data in the output matrix */ outMatrix = mxGetDoubles(plhs[0]); /* call the computational routine */ arrayProduct(multiplier,inMatrix,outMatrix,ncols); }
En la variable multiplier guardaremos el escalar de entrada, que como sabemos de las especificaciones está dada en la primera posición del arreglo de entrada. Usamos la función de Matlab mxGetScalar que devuelve la parte real del primer elemento de un arreglo del tipo mxArray. Seguidamente recibimos el vector usando la función mxGetDoubles que devuelve los elementos reales de un arreglo, y la guardamos en inMatrix. Mas detalles sobre estas funciones pueden ser consultados acá.
El número de columnas es guardado en ncols. Note que esta variable es del tipo mwSize. El tipo mwSize es una macro que permite redefinir el tamaño del entero basado en la plataforma que se esté trabajando, lo cual brinda flexibilidad. Para ser consistente, la definción de i en la función arrayProduct será redefinido posteriormente al tipo mwSize también.
La cantidad de columnas del arreglo es guardada en ncols por medio de la función mxGetN que devuelve la cantidad de columnas de un arreglo.
Seguidamente se prepara la salida de nuestra función. Creamos primero el arreglo de salida plhs de una fila, n columnas y de tipo real. Las entradas reales de este arreglo las guardamos a continuación en outMatrix.
Nuestra función mexFunction está lista ahora para recibir las variables desde Matlab y devolver la salida.