Once again I would like to welcome guest blogger Joshua Kaplan, who continues his series of JMI-related articles
Local and remote MatlabControl
Several weeks ago, I discussed the undocumented world of JMI (Java-to-Matlab Interface), that enables calling Matlab from Java. Today I will discuss matlabcontrol, an open-source wrapper I have written for JMI, that is both documented and user-friendly.
matlabcontrol supports calling Matlab in two different ways: local control where the Java code is launched from Matlab, and remote control where the Java code launches Matlab. Today we shall explore matlabcontrol‘s local control methods; in my next post we’ll create a simple Java program that uses matlabcontrol for local control; a later post will discuss the remote control path. These posts assume a medium to high level of Java experience.
matlabcontrol is a collection of Java classes, bundled together in a downloadable jar file, which is essentially just a zip file with a different file extension. As of this post the most recent version is matlabcontrol-3.01.jar. Note down where you’ve downloaded the jar file – you’ll need to use this information shortly.
For local control we’ll interact with the classes LocalMatlabProxy and MatlabInvocationException. LocalMatlabProxy contains all the methods required for calling Matlab; instances of MatlabInvocationException will be thrown when a problem occurs while attempting to control Matlab.
To tell Matlab where matlabcontrol-3.01.jar is, add the jar file path to Matlab’s dynamic (via the built-in javaaddpath function) or static (via edit(‘classpath.txt’)) Java classpath. You will need to restart Matlab if you have modified the static classpath, but it has the benefit of working better than the dynamic classpath in some situations.
Matlab now knows where to find the Java class files in matlabcontrol. To save some typing later on, type the following in the Matlab Command Window (or in your JMI-empowered Matlab application):
LocalMatlabProxy is easy to use. All of its methods are static meaning they can be called without needing to assign LocalMatlabProxy to a variable. The methods are:
void exit() java.lang.Object getVariable(java.lang.String variableName) void setVariable(java.lang.String variableName, java.lang.Object value) void eval(java.lang.String command) java.lang.Object returningEval(java.lang.String command, int returnCount) void feval(java.lang.String functionName, java.lang.Object args) java.lang.Object returningFeval(java.lang.String functionName, java.lang.Object args) java.lang.Object returningFeval(java.lang.String functionName, java.lang.Object args, int returnCount) void setEchoEval(boolean echoFlag)
Detailed javadocs exist for all these methods. Here is an overview:
- exit() is as straightforward as it sounds: it will exit Matlab. While it is possible to programmatically exit Matlab by other means, they may be unreliable. So, to exit Matlab from Java:
- Setting and getting variables can be done using the getVariable(…) and setVariable(…) methods. These methods will auto-convert between Java and Matlab types where applicable.
- Java types in the Matlab environment retrieved will be returned as Java types.
- Matlab types will be converted into Java types.
- Java types will be converted into Matlab types if they can. The rules are outlined here. Java Strings are converted to Matlab char arrays. Additionally, arrays of one of those Java types are converted to arrays of the corresponding Matlab type.
Using these methods is fairly intuitive:
>> LocalMatlabProxy.setVariable('x',5) >> LocalMatlabProxy.getVariable('x') ans = 5
Getting and setting basic types (numbers, strings and Java objects) is quite reliable and consistent. It gets complicated when passing in an array (particularly multidimensional) from Java using setVariable(…), or getting a Matlab struct or cell array using getVariable(…). The type conversion in such cases is unpredictable, and may be inconsistent across Matlab versions. In such cases you are best off building a Java object with Matlab code and then getting the Java object you created.
- The eval() and feval() methods were described in detail in my previous post. The functions will return the result, if any, as a Java object. Due to the way the underlying JMI operates, it is necessary to know in advance the number of expected return arguments. Matlab built-in nargout function reveals this number. Some functions (e.g., feval) return a variable number of arguments, in which case nargout returns -1. For instance:
>> nargout sqrt ans = 1 >> nargout feval ans = -1
- LocalMatlabProxy‘s returningFeval(functionName, args) method uses the nargout information to determine the number of returned arguments and provide it to JMI. It will likely not function as expected if the function specified by functionName returns a variable number of arguments. In such a case, call returningFeval(…) with a third input argument that specifies the expected number of returned arguments. Since an eval() function can evaluate anything, just as if it were typed in the Command Window, there is no reliable way to determine what will be returned. All of this said, in most situations returningEval(…) can be used with a return count of 1, and the returningFeval(…) that automatically determines the return count will operate as expected.
Some simple usage examples
Let’s perform some of the same simple square root operations we did in the pure-JMI article, this time using matlabcontrol. First we’ll take the square root of 5, assigning the result to the Matlab variable y (note that we are calling Matlab from Java, that is called from within Matlab):
>> LocalMatlabProxy.eval('sqrt(5)') ans = 2.2361 >> y = LocalMatlabProxy.returningEval('sqrt(5)',1) y = 2.2361 >> LocalMatlabProxy.feval('sqrt',5) >> y = LocalMatlabProxy.returningFeval('sqrt',5) y = 2.2361 >> y = LocalMatlabProxy.returningFeval('sqrt',5,1) y = 2.2361
In this situation there is no major difference between using eval() or feval() in the above situation. However, if instead of taking the square root of 5 we want to take the square root of a variable, then eval() is our only option.
>> a = 5 a = 5 >> LocalMatlabProxy.eval('sqrt(a)') ans = 2.2361 >> y = LocalMatlabProxy.returningEval('sqrt(a)',1) y = 2.2361 >> LocalMatlabProxy.feval('sqrt','a') ??? Undefined function or method 'sqrt' for input arguments of type 'char'. ??? Java exception occurred: matlabcontrol.MatlabInvocationException: Method could not return a value because of an internal Matlab exception at matlabcontrol.JMIWrapper.returningFeval(JMIWrapper.java:256) at matlabcontrol.JMIWrapper.feval(JMIWrapper.java:210) at matlabcontrol.LocalMatlabProxy.feval(LocalMatlabProxy.java:132) Caused by: com.mathworks.jmi.MatlabException: Undefined function or method 'sqrt' for input arguments of type 'char'. at com.mathworks.jmi.NativeMatlab.SendMatlabMessage(Native Method) at com.mathworks.jmi.NativeMatlab.sendMatlabMessage(NativeMatlab.java:212) at com.mathworks.jmi.MatlabLooper.sendMatlabMessage(MatlabLooper.java:121) at com.mathworks.jmi.Matlab.mtFevalConsoleOutput(Matlab.java:1511) at matlabcontrol.JMIWrapper.returningFeval(JMIWrapper.java:252) ... 2 more
The automatic Matlab/Java type conversions discussed above are equally applicable to eval() and feval(). feval() automatically converted the argument ‘a’ into a Matlab char, instead of considering it as a Matlab variable. As seen above, the feval() invocation fails with a Java MatlabInvocationException. So, the only way to interact with Matlab variables is via eval() methods; feval() will not work.
Lastly there is the setEchoEval(echoFlag) method: If this method is called with a true argument, then all Java to Matlab calls will be logged in a dedicated window. This can be very helpful for debugging.
In my next post we shall put together this knowledge to create a small Java program that uses matlabcontrol to interact with Matlab.