En un post anterior he hablado sobre el toolbox de redes neuronales que matlab nos ofrece como implementación alternativa de todos los algoritmos y estructuras necesarias para trabajar con este tipo de herramienta. Pues bien, puede ser muy interesante poder interactuar entre un lenguaje convencional (java, c++, c#, etc) y matlab, de manera que podamos embeber dentro de una aplicación de usuario final las funcionalidades que matlab nos puede ofrecer.
Para facilitar esto, matlab ofrece un interfaz externo que nos permite bien directamente con C++ o mediante componentes COM (también podemos hacerlo indirectamente con java, puesto que en el terminal de matlab tenemos acceso directo para ejecutar clases java, pero esto está más pensado para invocar código java desde matlab que a la inversa ) ejecutar comandos en el terminal, intercambiar datos, manipular diferentes propiedades de la sesión etc.
Como lo que yo he utilizado han sido los componentes COM, será lo que pasaré a comentar, si bien las funciones son las mismas, y por supuesto utilizando C++ será mucho más eficiente que utilizando COM.
Haciendo una simple búsqueda en la ayuda de matlab por «automation server», nos encontramos con la documentación que matlab nos ofrece [1] sobre cómo utilizar su interfaz externa con ejemplos en Visual Basic, aquí comentaré los métodos que podemos ejecutar y mostrando ejemplos en C#.
El primer paso que debemos dar en conseguir conectar al «automation server» que nos ofrece matlab, utilizando las facilidades que nos ofrezca el lenguaje de programación seleccionado. En este caso utilizaremos las facilidades que nos ofrece el API de reflesión de .NET, que gracias a las clases Type y Activator podremos enlazar dinámicamente con el componente COM de matlab.
//Obtained COM type from a ProdId. In this
//case matlab’s prodId is matlab.application
Type matlabServerType =
Type.GetTypeFromProgID(«matlab.application»);
//Create a Object with Type information
Object matlabServerObj = Activator.CreateInstance(
matlabServerType);
La clase Type nos ofrece el método GetTypeFromProgID que a partir de un identificador de programa nos devuelve una referencia a la clase Type para después pasárselo al método CreateInstance de la clase Activator para recuperar una referencia de tipo Object que después utilizaremos para invocar todos los métodos disponibles en el componente invocando al método InvokeMember.
Podemos ver la cadena «matlab.application» que se pasa el método GetTypeFromProdID. Esta cadena es el progID (identificador de programa) que todo componente COM registrado en nuestro sistema deberá tener y deberemos conocer para poder utilizarlo con esta técnica. En el caso del servidor COM de matlab el identificador es el que acabamos de utilizar.
Bueno, pues ya está, ahora si ejecutamos el código anterior, crearemos una sesión nueva en matlab, apareciéndosenos una ventana de terminal matlab como la mostrada a continuación.
A partir de este momento podremos comenzar a interactuar con la sesión recién creada, creando matrices, invocando comandos y funciones matlab, cerrar sesión, etc, las cuales repasaremos a continuación.
Cerrar Sessión: Quit
La función «Quit» nos permite cerrar la sesión que acabamos de crear. Para invocarla no necesitamos ningún parámetro y el código C# para utilizarla sería el siguiente:
object result = matlabServerType.InvokeMember(
«Quit»,
BindingFlags.InvokeMethod,
null,
matlabServerObj,
new
Object[]{});
La variable matlabServerType y matlabServerObj corresponden a los dos objetos de tipo Type y Object que hemos creado en el código del apartado anterior cuando hemos iniciado la sesión. Como no necesitamos pasar ningún argumento a la función Quit, como cuarto parámetro al método InvokeMember, le pasamos un array vacio «new Object[]{}».
Ejecución de comandos: Execute
Para poder ejecutar cualquier comando de matlab, disponemos de la función Execute, que recibe como parámetro la cadena que representa el comando a ejecutar.
object[] arrayInput = new
object[] {aStringCommand};
Object result
= matlabServerType.InvokeMember(
«Execute»,
BindingFlags.InvokeMethod,
null,
matlabServerObj,
arrayInput);
En este caso no le pasamos un array vacio de parámetros, puesto que el array contiene un elemento con la cadena que representa el comando a ejecutar. En este caso como resultado en la variable result, obtenemos la salida el comando, que sería lo mismo que matlab nos visualiza cuando ejecutamos el comando sin terminar con el carácter ‘;‘.
Cadenas: PutCharArray & GetCharArray
Con estas dos funciones podemos crear y recuperar variables de tipo cadena.
object[] arrayInput
= new
object[] {«aVarName»,«global»,«aString»};
Object result =
matlabServerType.InvokeMember(
«PutCharArray»,
BindingFlags.InvokeMethod,
null,
matlabServerObj,
arrayInput);
En el vector arrayInput alojamos los parámetros de la función PutCharArray: el nombre de la variable que utilizaremos para almacenar el valor de la variable, el workspace que en este caso será «global» (también pudiera ser «base«), y por último, el valor de la variable creada. Así, en este caso crearemos la variable aVarName con el valor aString.
Para realizar el caso contrario y recuperar el valor de una variable de tipo cadena.
object[] arrayInput
= new
object[] {«aVarName»,«global»};
Object result =
matlabServerType.InvokeMember(
«GetCharArray»,
BindingFlags.InvokeMethod,
null,
matlabServerObj,
arrayInput);
Para recuperar el valor de la variable, tan solo le tenemos que pasar como parámetros, el nombre de la variable y el workspace, obteniendo en la variable result el valor de la variable indicada.
Matrices: PutFullMatrix & GetFullMatrix
Nos permiten almacenar una matriz o recuperarla respectivamente en el workspace indicado.
Array aRealArray = new
Double[3] {2, 3, 5};
Array aImgArray = new
Double[3] {0, 0, 0};
object[] arrayInput = new
object[] {
«aVarName»,
«base»,
aRealArray,
aImgArray
};
object result = matlabServerType.InvokeMember(
«PutFullMatrix»,
BindingFlags.InvokeMethod,
null,
matlabServerObj,
arrayInput);
En matlab todos los arrays tiene una parte real y otra imaginaria, por este motivo, a parte del nombre de la variable y el workspace, le debemos pasar a la función PutFullMatrix dos arrays, uno con los valores para la parte real y otra para la parte imaginaria.
Para hacer la función inversa y recuperar el valor de una matriz haremos lo siguiente.
Array realArray = new
double[n];
Array imgArray = new
double[n];
object[] arrayInput = new
object[] {
«aVarName»,
«base»,
realArray,
imgArray
};
ParameterModifier p = new
ParameterModifier(4);
p[0] = false; p[1] = false;
p[2] = true; p[3] = true;
ParameterModifier[] mods = {p};
Object result = this.matlabServerType.InvokeMember(
«GetFullMatrix»,
BindingFlags.InvokeMethod,
null,
this.matlabServerObj,
arrayInput,
mods,
null, null);
Para este caso, tendremos que utilizar una forma diferente de la función InvokeMember, que recibe tres parámetros más adicionales, de los cuales los dos últimos dejaremos a null, pero el siguiente se trata de un objeto de tipo ParameterModifier, que se trata de un array de booleanos que nos sirve para indicar cuales de los parámetros que indicamos en el array arrayInput son de salida, de manera que pondremos las posiciones 2 y 3 a true para indicar que los parámetros realArray e imgArray son de salida.
Manipulación de la ventana: MaximizeCommandWindow & MinimizeCommandWindow
Como utilidades, se nos ofrecen dos funciones que nos permiten maximizar y minimizar la ventana de terminal asociada a la sesión de Matlab que hemos creado al principio.
object result = matlabServerType.InvokeMember(
«MaximizeCommandWindow»,
BindingFlags.InvokeMethod,
null,
matlabServerObj,
new
Object[]{});object result = matlabServerType.InvokeMember(
«MinimizeCommandWindow»,
BindingFlags.InvokeMethod,
null,
matlabServerObj,
new
Object[]{});
Esto es todo, si a alguien le interesa este tema y tiene alguna duda, podéis compartirla en este blog y si coincide que le puedo ayudar lo haré. Como ejemplo de cosillas que se pueden hacer, aquí va una captura de pantalla de una aplicación que en su día hice (utilizando este api y el toolbox de redes neuronales de matlab) para manipular redes neuronales, de manera que los equipos de investigación que se dedicaran a trabajar con redes neuronales pudieran crear sus propios prototipos de redes y probar los resultados.
Referencias
[1] Página oficial de Matlab, donde podemos encontrar toda la documentación necesaria.
[2] Ayuda Msdn sobre el api de reflexión.
http://msdn.microsoft.com/en-us/library/ms173183(VS.80).aspx
[3] Ejemplo de reflexión e invocación dinámica