Using C#, you can program virtually everything in EventIDE, but for many experimental methods it will be a reinvention of the wheel. There are many packages, e.g. the Psychophysics Toolbox (PTB), that contain already a great number of implemented methods. The PTB is coded in Matlab and this article shows how to call any Matlab function (not only from PTB) in the new version of EventIDE.
Enabling the Matlab interoperability
In order to get access to Matlab functions in all code snippets , you just need to declare an interface to the Matlab command server. You have to do it only once, for example, declaring the server object in the Header snippet and initializing it in the Start snippet:
‘Header’ snippet:
1 MLApp.MLApp matlab;// declaration
‘Start’ snippet:
1 matlab = new MLApp.MLApp(); /// start-up initialization
In order to shut down the Matlab server after an experiment is completed, you can use this:
‘End’ snippet:
1 matlab.Execute("quit");
Once this above code is added, the Matlab server will be automatically started, as you run the experiment (extra start-up delay can be a price). After that, you can call Matlab functions and commands in any snippet, using ‘matlab’ object as a reference to the server.
Calling a parameterized Matlab function
The best example of Matlab-EventIDE interoperability is the ability to adopt various stimulus generators, implemented in Matlab. The function by Niru Maheswaranathan (nirum_at_stanford.edu) produces a 2D Gabor patch with a small set of input parameters. The function returns 2D array of pixels, which can be rendered by the Pixel Aperture element in EventIDE. Here is the complete snippet code:
1 // set a patch to the script's m- file 2 matlab.Execute(@"cd('"+MatLabScriptPath+"');"); 3 4 object result=null; 5 6 // call the gabor function with 1 output parameter 7 matlab.Feval("GaborPatch", 1, out result,"theta",3.14/2,"width",width,"height",height, 8 "sigma",20.0, "lambda",40.0,"px",0.5,"py",0.5); 9 // cast the first function output value to the .NET 2D array 10 double[,] GaborPixels = (double[,]) ((object[]) result)[0]; 11 12 // fill the pixel aperture 13 for (int i = 0; i <height; i++){ 14 for (int l = 0; l < width; l++){ 15 Pixels[i,l]=GaborPixels[i,l]; 16 } 17 }
Note the Matlab ‘cd(path)’ command, which is invoked in the line 2. Using the ‘Execute’ method of the Matlab server object, you can invoke standard Matlab commands. The parameterized call of a custom function is made in the line 7, with the ‘Feval’ method.
To see the above code in action, you can load ‘Calling the Matlab function that generates a Gabar patch’ experiment under the Matlab category in the demo gallery of EventIDE.
Adopting the PTB implementation of the Quest staircase
The Psychophysics Toolbox includes the approved implementation of Quest (Watson,Pelli 1983) – a Bayesian staircase method for measuring perception thresholds. The following example show how to adopt the Quest method in an EventIDE experiment with a help of interoperable code. The example is taken from the ‘The Quest staircase method for measuring the orientation detection threshold’ experiment, which is available in the EventIDE demo gallery.
‘Start’ snippet:
1 matlab = new MLApp.MLApp(); 2 double beta=3.5; 3 double delta=0.01; 4 double gamma=0.5; 5 if (QuestPath[QuestPath.Length-1]!='\\') 6 QuestPath=QuestPath+@"\"; 7 // set a Matlab working path to the Quest implementation 8 matlab.Execute(@"cd('"+QuestPath+"');"); 9 // create the Quest procedure with custom initial parameters 10 matlab.Execute("q=QuestCreate("+InitialGuessThreshold+","+InitialGuessThresholdSD 11 +","+(DesiredCorrectRate/100.0)+","+beta+","+delta+","+gamma+");");
‘After Onset’ snippet at the trial start:
1 // request the recommended stimulus intensity from Quest 2 matlab.Execute("tTest=QuestQuantile(q);");// % Recommended by Pelli (1987), and still our favorite. 3 //matlab.Execute("tTest=QuestMean(q); ");// % Recommended by King-Smith et al. (1994) 4 //matlab.Execute("tTest=QuestMode(q)");// % Recommended by Watson & Pelli (1983) 5 6 // obtain the intensity value from the Matlab workspace 7 QuestSuggestedIntensity = (double) matlab.GetVariable("tTest", "base"); 8 9 // Trial preparations 10 // ********************************* 11 // set the stimulus intensity in EventIDE 12 Opacity=Convert.ToInt32((1-QuestSuggestedIntensity)*100.0); 13 // choose the random stimuli orientation in a trial 14 if (StimulusOrientation=="Left") 15 TestAngle=12; 16 else 17 TestAngle=-12;
‘Before Onset’ snippet at the trial end:
1 /// update Quest with the response code (1 or 0) 2 matlab.Execute("q=QuestUpdate(q,"+QuestSuggestedIntensity+","+response+");"); 3 /// request the current threshold estimate and its SD 4 matlab.Execute("t=QuestMean(q);"); 5 matlab.Execute("sd=QuestSd(q);"); 6
Note that all PTB functions are invoked as Matlab commands, without actual data exchange. All Quest-related data structures are handled by the Matlab server, whereas EventIDE simply requests numerical results from the Matlab workspace by the ‘GetVariable’ method.
Using a similar interoperable approach, you can adopt any experimental method from the PTB and Matlab.
Additional notes
- You need to download the most recent EventIDE release, in order to use the Matlab interoperability and see the related demos.
- When you send numbers to Matlab functions, always use the double type, because Matlab can not handle the integer type. For string parameters in the ‘Feval’ method, use “40.0” instead of “40”, otherwise you will get an error.
- You can not pass or receive Matlab structures in EventIDE with the ‘Feval’ method. It’s possible only for simple data types, such as double, string or double array (matrix). If you work with the data structure, create it in the Matlab workspace and access structure’s fields with the GetVariable and SetVariable methods.
- Invoking a Matlab function is a slow operation that can take up to 100 ms. Therefore, avoid Matlab calls in time-critical moments in your experiment.
- If you need really fast Matlab code, consider compiling it to .NET assembly with the Matlab Compiler SDK, (you can find a tutorial here). The compiled assembly can be then directly used in EventIDE.
Useful links
- Matlab documentation: MATLAB COM Automation Server and its methods
- Matlab documentation: Call MATLAB Function from C# Client
- Matlab documentation: Pass Complex Data to MATLAB from C# Client