Difference between revisions of "Matlab Bindings"

From LIBISIS
Jump to navigation Jump to search
 
(13 intermediate revisions by the same user not shown)
Line 63: Line 63:
  
 
when data is sent back to the matlab front-end the <tt>IXBsendToBinding</tt> subroutine is called. There are two possible syntaxes:
 
when data is sent back to the matlab front-end the <tt>IXBsendToBinding</tt> subroutine is called. There are two possible syntaxes:
 +
 +
1) if we are returning a simple type variable
 
<pre>
 
<pre>
 
     call IXFsendToBinding(plhs(number_left),' ',1,0,variable,status)
 
     call IXFsendToBinding(plhs(number_left),' ',1,0,variable,status)
</pre>
+
</pre>
 
 
if we are returning a simple type variable then the first syntax is used
 
 
* the 1st argument defines which matlab pointer to write the data in to according to <tt>number_left</tt>, which refers to the position of the left hand side argument when the [[Matlab Bindings#write an appropriate matlab function|matlab function]] is called
 
* the 1st argument defines which matlab pointer to write the data in to according to <tt>number_left</tt>, which refers to the position of the left hand side argument when the [[Matlab Bindings#write an appropriate matlab function|matlab function]] is called
 
* the 2nd, 3rd & 4th arguments are '''always''' <tt>  ' ',1,0  </tt>  
 
* the 2nd, 3rd & 4th arguments are '''always''' <tt>  ' ',1,0  </tt>  
 
* the 5th argument is the variable to be sent back to the matlab front-end
 
* the 5th argument is the variable to be sent back to the matlab front-end
 +
 +
2) if we are returning a structure
 
<pre>
 
<pre>
 
     call IXFsendToBinding(plhs(number_left),prhs(number_right),' ',1,0,variable,status)
 
     call IXFsendToBinding(plhs(number_left),prhs(number_right),' ',1,0,variable,status)
 
</pre>
 
</pre>
if we are returning a structure the second syntax is used
 
 
* the 1st argument defines which matlab pointer to write the data in to according to <tt>number_left</tt>, which refers to the position of the left hand side argument when the [[Matlab Bindings#write an appropriate matlab function|matlab function]] is called
 
* the 1st argument defines which matlab pointer to write the data in to according to <tt>number_left</tt>, which refers to the position of the left hand side argument when the [[Matlab Bindings#write an appropriate matlab function|matlab function]] is called
 
* the 2nd argument is the return type which has been sent from matlab
 
* the 2nd argument is the return type which has been sent from matlab
Line 80: Line 81:
 
* the 6th argument is the variable to be populated, it '''must''' agree with the type which has been sent in <tt>prhs(number_right)</tt>
 
* the 6th argument is the variable to be populated, it '''must''' agree with the type which has been sent in <tt>prhs(number_right)</tt>
  
 
+
an example <tt>IXBmethod_classname</tt> is defined below
 
<pre>
 
<pre>
   subroutine IXBplus_Testclass(nlhs, plhs, nrhs, prhs, status)
+
   subroutine IXBplus_testclass(nlhs, plhs, nrhs, prhs, status)
 
</pre>
 
</pre>
 
this provides access to the <tt>IXBgetFromBinding</tt> and <tt>IXBsendtoBinding</tt> interfaces to read/write variables from/to matlab
 
this provides access to the <tt>IXBgetFromBinding</tt> and <tt>IXBsendtoBinding</tt> interfaces to read/write variables from/to matlab
Line 120: Line 121:
 
         call IXBsendToBinding(plhs(1), prhs(1), ' ', 1, 0, wres, status)
 
         call IXBsendToBinding(plhs(1), prhs(1), ' ', 1, 0, wres, status)
 
     endif
 
     endif
   end subroutine
+
   end subroutine IXBplus_testclass
 +
</pre>
 +
 
 +
===preprocessing operations===
 +
 
 +
preprocesing can also be used to generate common code in the bindings, for example to create general code for unary operations (log, exp,sin etc...) called from the matlab front-end, there is explanation on preprocessing files [[Libclasses#Preprocessing files|here]]
 +
 
 +
in the file <tt>bindings/matlab/IXMdataset_2d_m.f90</tt> the KEYWORDS are defined
 +
<pre>
 +
#define IXD_TYPE dataset_2d
 +
#define IXD_NAME Sin_Dataset_2d
 +
#include "unary_ops.f90"
 +
</pre>
 +
 
 +
and in the include file <tt>bindings/matlab/unary_ops.f90</tt> they are substituted
 +
<pre>
 +
#if defined(IXD_TYPE) && defined(IXD_NAME)
 +
 
 +
subroutine IXB&/**/
 +
              &IXD_NAME (nlhs, plhs, nrhs, prhs, s)
 +
  use IXMm_&/**/
 +
          &IXD_TYPE
 +
  implicit none
 +
  integer :: nlhs,nrhs
 +
  integer(cpointer_t) :: plhs(nlhs),prhs(nrhs)
 +
  type(IXT&/**/
 +
          &IXD_TYPE ), allocatable :: wres(:), w1(:)
 +
  type(IXTstatus) :: s
 +
  integer n, i
 +
  n = ixGetNumberOfElements(prhs(2))
 +
  allocate(w1(n),wres(n))
 +
  call IXBgetFromBinding(prhs(2), ' ', 1, 0, w1, s)
 +
  if (s == IXCseverity_error) return
 +
  do i = 1,n
 +
      call IXF&/**/
 +
              &IXD_NAME (wres(i),w1(i),s)
 +
  enddo
 +
  if (s == IXCseverity_error) then
 +
    plhs(1)=ixDuplicateArray(prhs(1))
 +
  else
 +
    call IXBsendToBinding(plhs(1), prhs(1), ' ', 1, 0, wres, s)
 +
  endif
 +
  deallocate(w1,wres)
 +
end subroutine
 +
 
 +
#undef IXD_NAME
 +
#undef IXD_TYPE
 +
 
 +
#endif /* defined(IXD_TYPE) && defined(IXD_NAME) */
 +
</pre>
 +
 
 +
==define object method pair in <tt>bindings/matlab/libisisexc.txt</tt> ==
 +
we need to define all the subroutines which need to be accessed from the matlab front-end in another file, together with the number of input and output arguments. they are defined as a classname/method pair in the <tt>bindings/matlab/libisisexc.txt</tt> file with a general constuction:
 +
<pre>
 +
IXTclassname    method    no_of_output_args  no_of_input_args
 
</pre>
 
</pre>
  
==define object subroutine pair in <tt>libisisexc.txt</tt> ==
+
if the number of arguments is variable then -1 is defined. the number of arguments is essentially <tt>nrhs</tt>
 +
 
 +
where <tt>#</tt> is a comment character, the definition for IXTtestclass is as follows:
 +
<pre>
 +
#
 +
# IXTtestclass
 +
#
 +
IXTtestclass plus 1 4
 +
IXTtestclass display 0 1
 +
IXTtestclass create 1 2
 +
IXTtestclass check 1 1
 +
IXTtestclass read 1 3
 +
IXTtestclass write 1 3
 +
</pre>
  
 
==write an appropriate matlab function==
 
==write an appropriate matlab function==
in <tt>bindings/matlab/classes/@IXTclassname</tt> directory
+
The last step to creating a new object in the framework is to create a <tt>bindings/matlab/classes/@IXTclassname</tt> directory. this identifies <tt>IXTclassname</tt> as a class to matlab. Within this directory you create <tt>.m</tt> files corresponding to the functions that operation on the class. these can have any name you wish, but there are two that you must create.
this function must make a call to the libisisexc DLL
+
<tt>
 +
* IXTclassname.m
 +
* display.m
 +
</tt>
 +
 
 +
The first of these is called the constructor and is named after the class i.e. the file <tt>@IXTtestclass/IXTtestclass.m</tt> - the constructor tells matlab the data members that are contained within the class and how they should/can be initialised. The second file tells matlab how to display (print) the class to the screen.
 +
 
 +
every any non-standard object method which is defined in <tt>libisisexc.txt</tt> will also need to have a <tt>.m</tt> file to make a call into the DLL. this file does not necessarily have to have the same name as the method.
 +
 
 +
 
 +
<tt>display.m</tt> has a standard construction.
 +
<pre>
 +
function display(object)
 +
libisisexc('IXTclassname','display',object);
 +
</pre>
 +
 
 +
The constructor defines the data within a class and the order that these members are in if the class in indexed. It is thus important that the order defined in this class is followed '''EXACTLY''' in fortran when calling <tt>IXFoperationRun_classname</tt>. In our case the constructor should populate a default empty object and then pass this empty object and any arguments out to LIBISIS with a call to <tt>libisisexc()</tt>, which will check the arguments and then complete creation of the object. The first two string arguments to <tt>libisisexc()</tt> are the <tt>classname</tt> and <tt>method</tt>, inside the DLL they are mangled together and the DLL then calls the appropriate <tt>IXBmethod_classname</tt>, provided it has been defined in <tt>libisisexc.txt</tt>.
 +
 
 +
<pre>
 +
function object = IXTtestclass(varargin)
 +
object.base = IXTbase;
 +
object.val = 0;
 +
object.nx = int32(1);
 +
object.val_static = ones(3);
 +
object.int_static =  ones(4);
 +
object.val_array = zeros(2);
 +
object.int_arr = int32(zeros(2,2));
 +
object.spectra = IXTspectra;
 +
object.xhist = true;
 +
object.label='test';
 +
object.cell_string = [ 'one' ];
 +
object.d2d = [ IXTdataset_2d ];
 +
object = class(object ,'IXTtestclass');
 +
 
 +
if (nargin > 0)
 +
    object = libisisexc('IXTtestclass','create',object ,varargin);
 +
end
 +
</pre>
 +
 
 +
 
 +
We will often need to pass out an empty/default object as the first argument, this is because FORTRAN can modify, but not create, Matlab class objects (it sees them as structures), for example in <tt>bindings/matlab/classes/@IXTtestclass/plus.m</tt>
 +
<pre>
 +
function object = plus(a,b,array)
 +
object = libisisexc('IXTtestclass','plus',IXTtestclass,a,b,array);
 +
</pre>
 +
 
 +
the arguments to <tt>libisisexc()</tt> are directly related to the variables used in <tt>IXBplus_testclass</tt>
 +
<tt>
 +
* prhs(1) => IXTtestclass ,</tt>(empty objectused to populate return result)<tt>
 +
* prhs(2) => a </tt> (a is a populated IXTtestclass object input argument)<tt>
 +
* prhs(3) => b </tt> (a is a populated IXTtestclass object input argument)<tt>
 +
* prhs(4) => array
 +
</tt>

Latest revision as of 16:27, 16 May 2008

Introduction

The main binding to the Libisis framework which has been implemented is matlab. Each type defined in the framework has a module which contains type-specific methods for getting objects and variables from the matlab front-end and sending objects and variables back to the matlab front-end. These methods are called using two interfaces which are defined in a module IXMm_classname.

  • IXBgetFromBinding
  • IXBsendToBinding

This module is always defined in the file bindings/matlab/IXMclassname_m.f90. As a rule this module always contains the line use IXMclassname (it is however included automatically and does not need to be explicitly specified in the code)

The interfaces and methods of the IXMm_classname module are generated automatically using the preprocessor, in the following example for the IXTtestclass object in the IXMtestclass_m.f90 file. These are the only statements required to define the IXMm_testclass module.

module IXMm_testclass

#define IXD_TYPE testclass
#include "bindings_header.f90"

contains

#define IXD_TYPE testclass
#include "bindings_base.f90"

end module IXMm_testclass

If a class method implemented in the fortran framework is to be called from the matlab front-end various steps need to be followed.

define an appropriate binding subroutine in IXMclassname_m.f90

functions which are called from matlab are defined in the same file but outside of the IXMm_classname module. They are all prefixed with IXB. all the standard functions which are called such as IXBdisplay_classname, IXBcheck_classname are auto-generated using the preprocessor, so do not need any extra implementation.

#define IXD_TYPE testclass
#include "bindings_extra.f90"

only functions which are extra to the standard module subroutines which you need to call from matlab have to have a binding subroutine defined. We can create an IXBplus_testclass subroutine to allow matlab calling of the IXFplus_testclass method of IXTtestclass.

The arguments of every IXBmethod_classname subroutine are the same

  • nlhs is the number of left hand side arguments from matlab (ie what is the output of the matlab call)
  • plhs(nlhs) is an array of pointers to the left hand side arguments
  • nrhs is the number of right hand side arguments from matlab (ie what are the arguments of the matlab call)
  • prhs(nrhs) is an array of pointers to the right hand side arguments

for the most part memory is shared between the matlab binding and fortran framework, so we work with pointers.

there are four types of subroutine which can be called, each with different variables which are returned to the matlab front-end

  1. functions which have no variable output eg. display,check (defined in bindings_base.f90)
  2. functions which return a simple type (real, integer, character) eg. getting a value from an object
  3. functions which return an object(s) (IXTtestclass, IXTdataset_2d) eg. the result of a plus operation (defined below)
  4. functions which return an object(s) and a simple type back to matlab

the IXBgetFromBinding command reads data into a variable which has been passed down to the bindings layer from the matlab front-end. the call syntax is defined below.

    call IXFgetFromBinding(prhs(number),' ',1,0,variable,status)
    call IXFgetFromBindingPtr(prhs(number),' ',1,0,variable,status)
  • the 1st argument defines which matlab pointer to read the data from according to number, which refers to the position of the right hand side argument when the matlab function is called
  • the 2nd, 3rd & 4th arguments are always ' ',1,0
  • the 5th argument is the variable to be populated
  • if a variable length array is to be populated, the variable is defined as a pointer and the IXBgetFromBindingPtr subroutine is called, with the same syntax
  • if a structure is to be returned to the matlab front-end prhs(1) is generally reserved for the object to be returned, it does not need to be read in, more details are specified below


when data is sent back to the matlab front-end the IXBsendToBinding subroutine is called. There are two possible syntaxes:

1) if we are returning a simple type variable

    call IXFsendToBinding(plhs(number_left),' ',1,0,variable,status)
  • the 1st argument defines which matlab pointer to write the data in to according to number_left, which refers to the position of the left hand side argument when the matlab function is called
  • the 2nd, 3rd & 4th arguments are always ' ',1,0
  • the 5th argument is the variable to be sent back to the matlab front-end

2) if we are returning a structure

    call IXFsendToBinding(plhs(number_left),prhs(number_right),' ',1,0,variable,status)
  • the 1st argument defines which matlab pointer to write the data in to according to number_left, which refers to the position of the left hand side argument when the matlab function is called
  • the 2nd argument is the return type which has been sent from matlab
  • the 3rd, 4th & 5th arguments are always ' ',1,0
  • the 6th argument is the variable to be populated, it must agree with the type which has been sent in prhs(number_right)

an example IXBmethod_classname is defined below

  subroutine IXBplus_testclass(nlhs, plhs, nrhs, prhs, status)

this provides access to the IXBgetFromBinding and IXBsendtoBinding interfaces to read/write variables from/to matlab

    use IXMm_testclass
    implicit none

standard declarations

    integer :: nlhs, nrhs
    integer(cpointer_t) :: plhs(nlhs), prhs(nrhs)
  • declarations of input and output objects used by the method called
  • array input is defined as a pointer
    type(IXTtestclass) :: wres, w1, w2
    real(dp),pointer::array(:)
    type(IXTstatus) :: status
  • read in two structures which will be added together
  • check read went OK - report errors and return if not
    call IXBgetFromBinding(prhs(2),' ', 1, 0, w1, status)
    call IXBgetFromBinding(prhs(3),' ', 1, 0, w2, status)
    call IXBgetFromBindingPtr(prhs(4),' ', 1, 0, array, status)
    if (status == IXCseverity_error) return

do adding operation

    call IXFplus_testclass(wres, w1, w2,array, status)

wres has now been filled and needs to be entered into the matlab memory if the process was successfull

    if (status == IXCseverity_error) then
        plhs(1)=ixDuplicateArray(prhs(1))
    else
        call IXBsendToBinding(plhs(1), prhs(1), ' ', 1, 0, wres, status)
    endif
  end subroutine IXBplus_testclass

preprocessing operations

preprocesing can also be used to generate common code in the bindings, for example to create general code for unary operations (log, exp,sin etc...) called from the matlab front-end, there is explanation on preprocessing files here

in the file bindings/matlab/IXMdataset_2d_m.f90 the KEYWORDS are defined

#define IXD_TYPE	dataset_2d
#define IXD_NAME	Sin_Dataset_2d
#include "unary_ops.f90"

and in the include file bindings/matlab/unary_ops.f90 they are substituted

#if defined(IXD_TYPE) && defined(IXD_NAME)

subroutine IXB&/**/
              &IXD_NAME (nlhs, plhs, nrhs, prhs, s)
  use IXMm_&/**/
           &IXD_TYPE
  implicit none
  integer :: nlhs,nrhs
  integer(cpointer_t) :: plhs(nlhs),prhs(nrhs)
  type(IXT&/**/
          &IXD_TYPE ), allocatable :: wres(:), w1(:)
  type(IXTstatus) :: s
  integer n, i
  n = ixGetNumberOfElements(prhs(2))
  allocate(w1(n),wres(n))
  call IXBgetFromBinding(prhs(2), ' ', 1, 0, w1, s)
  if (s == IXCseverity_error) return
  do i = 1,n
      call IXF&/**/
              &IXD_NAME (wres(i),w1(i),s)
  enddo
  if (s == IXCseverity_error) then
    plhs(1)=ixDuplicateArray(prhs(1))
  else
    call IXBsendToBinding(plhs(1), prhs(1), ' ', 1, 0, wres, s)
  endif
  deallocate(w1,wres)
end subroutine

#undef IXD_NAME
#undef IXD_TYPE

#endif /* defined(IXD_TYPE) && defined(IXD_NAME) */

define object method pair in bindings/matlab/libisisexc.txt

we need to define all the subroutines which need to be accessed from the matlab front-end in another file, together with the number of input and output arguments. they are defined as a classname/method pair in the bindings/matlab/libisisexc.txt file with a general constuction:

IXTclassname     method    no_of_output_args   no_of_input_args

if the number of arguments is variable then -1 is defined. the number of arguments is essentially nrhs

where # is a comment character, the definition for IXTtestclass is as follows:

#
# IXTtestclass
#
IXTtestclass		plus			1		4
IXTtestclass		display			0		1
IXTtestclass		create			1		2
IXTtestclass		check			1		1
IXTtestclass		read			1		3
IXTtestclass		write			1		3

write an appropriate matlab function

The last step to creating a new object in the framework is to create a bindings/matlab/classes/@IXTclassname directory. this identifies IXTclassname as a class to matlab. Within this directory you create .m files corresponding to the functions that operation on the class. these can have any name you wish, but there are two that you must create.

  • IXTclassname.m
  • display.m

The first of these is called the constructor and is named after the class i.e. the file @IXTtestclass/IXTtestclass.m - the constructor tells matlab the data members that are contained within the class and how they should/can be initialised. The second file tells matlab how to display (print) the class to the screen.

every any non-standard object method which is defined in libisisexc.txt will also need to have a .m file to make a call into the DLL. this file does not necessarily have to have the same name as the method.


display.m has a standard construction.

function display(object)
libisisexc('IXTclassname','display',object);

The constructor defines the data within a class and the order that these members are in if the class in indexed. It is thus important that the order defined in this class is followed EXACTLY in fortran when calling IXFoperationRun_classname. In our case the constructor should populate a default empty object and then pass this empty object and any arguments out to LIBISIS with a call to libisisexc(), which will check the arguments and then complete creation of the object. The first two string arguments to libisisexc() are the classname and method, inside the DLL they are mangled together and the DLL then calls the appropriate IXBmethod_classname, provided it has been defined in libisisexc.txt.

function object = IXTtestclass(varargin)
object.base = IXTbase;
object.val = 0;
object.nx = int32(1);
object.val_static = ones(3);
object.int_static =  ones(4);
object.val_array = zeros(2);
object.int_arr = int32(zeros(2,2));
object.spectra = IXTspectra;
object.xhist = true;
object.label='test';
object.cell_string = [ 'one' ];
object.d2d = [ IXTdataset_2d ];
object = class(object ,'IXTtestclass');

if (nargin > 0) 
    object = libisisexc('IXTtestclass','create',object ,varargin);
end


We will often need to pass out an empty/default object as the first argument, this is because FORTRAN can modify, but not create, Matlab class objects (it sees them as structures), for example in bindings/matlab/classes/@IXTtestclass/plus.m

function object = plus(a,b,array)
object = libisisexc('IXTtestclass','plus',IXTtestclass,a,b,array);

the arguments to libisisexc() are directly related to the variables used in IXBplus_testclass

  • prhs(1) => IXTtestclass ,(empty objectused to populate return result)
  • prhs(2) => a (a is a populated IXTtestclass object input argument)
  • prhs(3) => b (a is a populated IXTtestclass object input argument)
  • prhs(4) => array