I would like to welcome guest writer Joshua Kaplan, an expert in one of the darkest undocumented corners of Matlab – that of the Java-to-Matlab interface (JMI). Today, Joshua will introduce JMI and its core functionality. Later articles will explore additional aspects of this complex technology.
As you’ve seen many times on this blog before, Matlab can easily call Java. This article explores the dark undocumented territory known as JMI (Java Matlab Interface) that enables calling Matlab from Java. This interface takes the form of a file called jmi.jar that comes with every copy of Matlab released in the past decade. JAR files are essentially a zip file containing Java class files. In this case, several Java classes in jmi.jar enable interaction with Matlab. This post will discuss the most important class: com.mathworks.jmi.Matlab.
JMI easily allows calling two built-in Matlab functions: eval and feval. Essentially, eval evaluates any string typed into Matlab’s Command Window, and feval allows calling any function by name and passing in arguments. For example:
>> sqrt(5) ans = 2.2361 >> eval('sqrt(5)') ans = 2.2361 >> feval('sqrt',5) ans = 2.2361
>> sqrt(5) ans = 2.2361 >> eval('sqrt(5)') ans = 2.2361 >> feval('sqrt',5) ans = 2.2361
The first approach takes the square root of 5 normally by directly calling Matlab’s square root function sqrt. JMI does not enable this direct-invocation approach. Instead, JMI uses the second approach, where eval mimics the first call by evaluating the entire expression inside single quotes. The third option, also used by JMI, is to use feval where the function and arguments are specified separately. While in the above example calling eval and feval is very similar, there are differences. For instance, assignment can be done as x=5 or eval(‘x=5’) but there is no feval equivalent. There are also other eval relatives, such as hgfeval, evalc and evalin, which will not be explained today.
Before we explore com.mathworks.jmi.Matlab, it is important to note a few things: Everything that follows is based on many hours of experimentation and online research and so while I am more or less certain of what I am about to explain, it could be deficient or incorrect in many respects. In fact, this area is so undocumented that an article written in 2002 by one of The MathWorks employees and published in the official newsletter, has been removed from their website a year or two ago.
Secondly, this post is written to satisfy people’s curiosities regarding JMI. If you actually wish to call Matlab from Java, I strongly suggest you use MatlabControl, a user-friendly Java library I have written that wraps JMI. There are many complications that arise with threading, method completion blocking, virtual machine restrictions, and other domains that prevent using JMI directly to be practical and reliable.
With all of that said, let’s dive in! In the Matlab Command Window type:
methodsview('com.mathworks.jmi.Matlab')
methodsview('com.mathworks.jmi.Matlab')
You will see all of the Matlab class’s numerous methods. Many of the methods have extremely similar names; many others have the same names and just different parameters. In order to call eval and feval we are going to use two of Matlab‘s methods:
public static Object mtEval(String command, int returnCount) public static Object mtFeval(String functionName, Object[] args, int returnCount)
public static Object mtEval(String command, int returnCount) public static Object mtFeval(String functionName, Object[] args, int returnCount)
Since Matlab can call Java, we can experiment with these methods from Matlab. That’s right, we’re about to use Matlab to call Java to call Matlab!
First, let’s import the Java package that contains the Matlab class, to reduce typing:
import com.mathworks.jmi.*
import com.mathworks.jmi.*
Now let’s take the square root of 5 like we did above, but this time from Java. Using JMI’s eval-equivalent:
Matlab.mtEval('sqrt(5)',1)
Matlab.mtEval('sqrt(5)',1)
Here, ‘sqrt(5)’ is what will be passed to eval, and 1 signifies that Matlab should expect one value to be returned. It is important that the second argument be accurate. If instead the call had been:
Matlab.mtEval('sqrt(5)',0)
Matlab.mtEval('sqrt(5)',0)
Then an empty string (”) will be returned. If instead, 2 or more were passed in:
Matlab.mtEval('sqrt(5)',2)
Matlab.mtEval('sqrt(5)',2)
Then a Java exception like the one below will occur:
??? Java exception occurred: com.mathworks.jmi.MatlabException: Error using ==> sqrt Too many output arguments. 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.mtFeval(Matlab.java:1478) at com.mathworks.jmi.Matlab.mtEval(Matlab.java:1439)
Looking at this exception’s stack trace we notice that mtEval() is actually internally calling mtFeval().
Now to perform the square root using JMI’s feval-equivalent:
Matlab.mtFeval('sqrt',5,1)
Matlab.mtFeval('sqrt',5,1)
Here, ‘sqrt’ is the name of the Matlab function to be called, 5 is the argument to the function, and 1 is the expected number of return values. Again, the number of return values. If this number is specified as 0 instead of 1, the function call will still succeed, although not all of the results will necessarily be returned. The second mtFeval() argument, which specifies the arguments to the invoked Matlab function, can take any number of arguments as an array. Therefore the following is acceptable:
Matlab.mtFeval('sqrt',[5 3],1)
Matlab.mtFeval('sqrt',[5 3],1)
This will return an array containing both of their square roots. Note that although two vales are returned, they are considered as only 1 since it is a single array that is returned.
Multiple Matlab arguments can be specified in mtFeval() using a cell array. For example, consider the following equivalent formats (note the different returned array orientation):
>> min(1:4,2) ans = 1 2 2 2 >> Matlab.mtFeval('min',{1:4,2},1) ans = 1 2 2 2
>> min(1:4,2) ans = 1 2 2 2 >> Matlab.mtFeval('min',{1:4,2},1) ans = 1 2 2 2
As we observed above, mtEval() is really just calling mtFeval(). This works because eval is a function, so feval can call it. An illustration:
Matlab.mtFeval('eval','sqrt(5)',1)
Matlab.mtFeval('eval','sqrt(5)',1)
com.mathworks.jmi.Matlab class’s mtFevalConsoleOutput() methodBoth mtFeval() and mtEval() have allowed us to interact with Matlab, but the effects are not shown in the Command Window. There is a method that will allow us to do this:
public static Object mtFevalConsoleOutput(String functionName, Object[] args, int returnCount)
public static Object mtFevalConsoleOutput(String functionName, Object[] args, int returnCount)
mtFevalConsoleOutput() is just liked the mtFeval() command except that its effects will be shown. For instance:
>> Matlab.mtFeval('disp','hi',0); % no visible output >> Matlab.mtFevalConsoleOutput('disp','hi',0); hi
>> Matlab.mtFeval('disp','hi',0); % no visible output >> Matlab.mtFevalConsoleOutput('disp','hi',0); hi
There is no equivalent mtEvalConsoleOutput() method, but that’s not a problem because we have seen that eval can be accomplished using feval:
>> Matlab.mtFevalConsoleOutput('eval','x=5',0); x = 5
>> Matlab.mtFevalConsoleOutput('eval','x=5',0); x = 5
Other methods in com.mathworks.jmi.MatlabThere are many more eval and feval methods in the Matlab class. Most of these methods’ names begin with eval or feval instead of mtEval and mtFeval. Many of these methods are asynchronous, which means their effect on Matlab can occur after the method call returns. This is often problematic because if one method call creates a variable which is then used by the next call, there is no guarantee that the first call has completed (or even begun) by the time the second call tries to use the new variable. Unlike mtEval() and mtFeval(), these methods are not static, meaning we must have an instance of the Java class Matlab:
>> proxy = Matlab proxy = com.mathworks.jmi.Matlab@1faf67f0
>> proxy = Matlab proxy = com.mathworks.jmi.Matlab@1faf67f0
Using this instance we will attempt to assign a variable and then retrieve it into a different variable. The result will be a Java exception indicating that ‘a’ does not currently exist:
>> proxy.evalConsoleOutput('a=5'); b = proxy.mtEval('a',1) ??? Java exception occurred: com.mathworks.jmi.MatlabException: Error using ==> eval Undefined function or variable 'a'. 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.mtFeval(Matlab.java:1478) at com.mathworks.jmi.Matlab.mtEval(Matlab.java:1439) a = 5
>> proxy.evalConsoleOutput('a=5'); b = proxy.mtEval('a',1) ??? Java exception occurred: com.mathworks.jmi.MatlabException: Error using ==> eval Undefined function or variable 'a'. 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.mtFeval(Matlab.java:1478) at com.mathworks.jmi.Matlab.mtEval(Matlab.java:1439) a = 5
If you run the above code you are not guaranteed to get that exception because of the nature of asynchronous method calls. However, this inherent unpredictability makes it difficult to perform almost any sequential action. Therefore, it is best to stick to mtEval, mtFeval, and mtFevalConsoleOutput, where this type of exception will be very remote. (They can still occur, about 1 in 100 times, as to why – I’d love to know as well.)
Two particular methods that may come in handy are mtSet() and mtGet(), which are the Java proxies for the Matlab set and get functions – they accept a Matlab handle (a double value) and a property name (a string) and either set the value or return it. Like Matlab, they also accept an array of property names. This can be used to update Matlab HG handles from within Java code, without needing to pass through an intermediary Matlab eval function:
>> Matlab.mtSet(gcf,'Color','b') >> Matlab.mtGet(gcf,'Color') ans = 0 0 1 >> Matlab.mtGet(gcf,{'Color','Name'}) ans = java.lang.Object[]: [3x1 double] 'My figure'
>> Matlab.mtSet(gcf,'Color','b') >> Matlab.mtGet(gcf,'Color') ans = 0 0 1 >> Matlab.mtGet(gcf,{'Color','Name'}) ans = java.lang.Object[]: [3x1 double] 'My figure'
SummaryWith just eval and feval, an enormous amount of Matlab’s functionality can be accessed from Java. For instance, this allows for creating sophisticated Java GUIs with Swing and then being able to call Matlab code when the user clicks a button or moves a slider.
My next post will be on using MatlabControl to control Matlab from Java from within Matlab. The following post will discuss doing the same, but from a Java program launched outside of Matlab.
Addendum 2016-10-21: In R2016b, MathWorks has finally released a documented connector from Java to the Matlab engine, which should be used in place of JMI where possible.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4