Diferencia entre revisiones de «MATLAB»

De Cerlab Wiki
Saltar a: navegación, buscar
(MEX functions)
(Recibiendo los datos en la función mexFunction y preparando la salida)
Línea 142: Línea 142:
  
 
Nuestra función mexFunction está lista ahora para recibir las variables desde Matlab y devolver la salida.
 
Nuestra función mexFunction está lista ahora para recibir las variables desde Matlab y devolver la salida.
 +
===Agregando nuestra función C===
 +
Finalmente, agregamos nuestra función C al archivo que vamos a compilar con mex.
 +
<nowiki>
 +
#include "mex.h"
 +
 +
void arrayProduct(double x, double *y, double *z, mwSize n){
 +
  mwSize i;
 +
 
 +
  for (i=0; i<n; i++) {
 +
    z[i] = x * y[i];
 +
  }
 +
}
 +
 +
void mexFunction( int nlhs, mxArray *plhs[],
 +
(…)
 +
</nowiki>
 +
Note que la variable i se redifinió a mwSize, como se había anticipado.
 +
===Compilando nuestro archivo C con mex===
 +
 +
Con nuestro archivo cvector.c  listo procedemos a compilar con  mex (La opción -R2018a es requerida porque en este ejemplo se han usado funciones del más reciente API de Matlab.)
 +
<nowiki>
 +
>> mex cvector.c -R2018a
 +
Building with 'gcc'.
 +
MEX completed successfully.
 +
</nowiki>
 +
En seguida, procedemos a crear un vector de ejemplo y una constante arbitraria para usar nuestra función MEX.
 +
<nowiki>
 +
>> c = 3
 +
c =
 +
    3
 +
>> A = [2,5,6,3.5]
 +
A =
 +
    2.0000    5.0000    6.0000    3.5000
 +
>> cvector(c,A)
 +
ans =
 +
    6.0000  15.0000  18.0000  10.5000
 +
</nowiki>
 +
===Arreglos de tamaño variable===
 +
 +
Nótese que en el ejemplo anterior el tamaño del arreglo de entrada nunca se definió a algún valor fijo.
 +
Retomemos el código de nuestra mexFunction:
 +
<nowiki>
 +
  (...)
 +
  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]);
 +
  (...)
 +
</nowiki>
 +
Note que para recibir el vector de entrada se utiliza el puntero inMatrix. El vector de entrada recordemos que corresponde a la posición 1 del arreglo de punteros de entrada prhs, el cual es de tamaño arbitrario. Posteriormente el tamaño de este arreglo de entrada se obtiene con mxGetN  el cual servirá luego como parámetro en nuestra función de C que multiplica el vector por el escalar.
 +
 +
En el ejemplo se utilizó un vector de entrada de 3 valores pero perfectamente se puede cambiar. Por ejemplo,
 +
<nowiki>
 +
>> c = 2;
 +
>> B = [1,2,3,4,5,6,7,8,9];
 +
>> cvector(c,B)
 +
 +
ans =
 +
 +
    2    4    6    8    10    12    14    16    18
 +
</nowiki>

Revisión del 22:40 17 jun 2018

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.

Tabla 1. Argumentos de la MEX function.
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.

Agregando nuestra función C

Finalmente, agregamos nuestra función C al archivo que vamos a compilar con mex.

#include "mex.h"

void arrayProduct(double x, double *y, double *z, mwSize n){
  mwSize i;
  
  for (i=0; i<n; i++) {
    z[i] = x * y[i];
  }
}

void mexFunction( int nlhs, mxArray *plhs[],
(…)

Note que la variable i se redifinió a mwSize, como se había anticipado.

Compilando nuestro archivo C con mex

Con nuestro archivo cvector.c listo procedemos a compilar con mex (La opción -R2018a es requerida porque en este ejemplo se han usado funciones del más reciente API de Matlab.)

>> mex cvector.c -R2018a
Building with 'gcc'.
MEX completed successfully.

En seguida, procedemos a crear un vector de ejemplo y una constante arbitraria para usar nuestra función MEX.

>> c = 3
c =
     3
>> A = [2,5,6,3.5]
A =
    2.0000    5.0000    6.0000    3.5000
>> cvector(c,A)
ans =
    6.0000   15.0000   18.0000   10.5000

Arreglos de tamaño variable

Nótese que en el ejemplo anterior el tamaño del arreglo de entrada nunca se definió a algún valor fijo. Retomemos el código de nuestra mexFunction:

   (...)
   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]);
   (...)

Note que para recibir el vector de entrada se utiliza el puntero inMatrix. El vector de entrada recordemos que corresponde a la posición 1 del arreglo de punteros de entrada prhs, el cual es de tamaño arbitrario. Posteriormente el tamaño de este arreglo de entrada se obtiene con mxGetN el cual servirá luego como parámetro en nuestra función de C que multiplica el vector por el escalar.

En el ejemplo se utilizó un vector de entrada de 3 valores pero perfectamente se puede cambiar. Por ejemplo,

>> c = 2;
>> B = [1,2,3,4,5,6,7,8,9];
>> cvector(c,B)

ans =

     2     4     6     8    10    12    14    16    18