Code covered by the BSD License  

Highlights from
On-The-Fly Definition of Custom Matrix Objects

5.0

5.0 | 1 rating Rate this file 9 Downloads (last 30 days) File Size: 31 KB File ID: #26611

On-The-Fly Definition of Custom Matrix Objects

by

 

07 Feb 2010 (Updated )

Class of matrix-like objects with on-the-fly definable methods.

| Watch this File

File Information
Description

This submission defines a generic class of matrix-like objects called MatrixObj and a subclass called DataObj. Objects of the class are capable of behaving as matrices, but whose math operators (+,-,*,\,.*,<,>,etc...) and other methods can be defined/redefined from within any Mfile or even from the command line. This removes the restriction of writing a dedicated classdef file or class directory for every new matrix-type object that a user might wish to create.
 
The class works by storing function handles to the various matrix operator functions (plus, minus, mtimes, mldivide, etc...) in a property of MatrixObj called Ops, which is a structure variable. Hence, one can set the matrix operators as desired simply by setting the fields of Ops to an appropriate function handle.
 
MatrixObj objects are particularly useful when an object needs to be endowed with just a few matrix-like capabilities that are very quickly expressed using anonymous functions or a few short nested functions. This is illustrated in the examples below that deal with creating an efficient version of a DFT matrix. Another advantage of MatrixObj objects is that it is not necessary to issue a "clear classes" command when their Ops methods need to be edited or redefined.
 
The DataObj subclass is a specialized version of MatrixObj well-suited for mimicking/modifying the behavior of existing MATLAB numeric data types. Its Ops property contains default methods appropriate to existing data types, but which can be selectively overwritten. Example 4 below illustrates its use.

EXAMPLE 1: Implementing fft() in operator form. As is well-known, the operation fft(x) can be represented as a matrix-vector multiplication. If you have the Signal Processing Toolbox, the relevant matrix can be generated using the DFTMTX function. Otherwise, it can be generated as follows,
  
   d=2500;
   Q=fft(eye(d)); %DFT matrix - 2500x2500
   
The operation fft(x) is equivalent to Q*x, but this is a slow way to perform the operation,
  
   x=rand(d);
   
   tic; y0=Q*x; toc %Elapsed time is 3.595847 seconds.
 
However, using the MatrixObj class, we can quickly create an object Qobj which can transform x using the same matrix multiplication syntax, Qobj*x, but which uses fft() under the hood, with all of its advantages in speed,
  
 
   Qobj=MatrixObj;
   Qobj.Ops.mtimes=@(obj,z) fft(z); %set the mtimes method in 1 line!!
   

   tic; y1=Qobj*x; toc %Elapsed time is 0.212282 seconds.
   tic; y2=fft(x); toc %Elapsed time is 0.212496 seconds.
   
   isequal(y1,y2); % =1
   
 
And of of course, the memory footprint of Qobj is far less than for the full matrix Q
  
  >>whos Q Qobj

  Name Size Bytes Class Attributes

  Q 2500x2500 100000000 double complex
  Qobj 1x1 4412 MatrixObj
    
        

EXAMPLE 2: Continuing with Example 1, suppose I now decide that I still want Qobj to represent an fft() operation, but that it be normalized to satisfy Parseval's theorem. A simple on-the-fly redefinition of mtimes() can accomplish this.
  
   Qobj.Ops.mtimes=@(obj,z) (1/sqrt(numel(z)))*fft(z);
   
   x=rand(d,1);
   
   TestParseval=[norm(x), norm(Qobj*x)], % =[28.2807, 28.2807]
   
  
EXAMPLE 3: Continuing with Example 2, let us now look at how to give Qobj a ctranspose method so that Qobj' is defined. Because Qobj satisfies Parseval's theorem, Qobj' is its inverse. A one-line definition can be made using the Trans property,

  Qobj.Trans.mtimes=@(obj,z) sqrt(numel(z))*ifft(z) ;
      
The code below verifies that the ctranpose operation has various anticipated properties,
  
    TestParseval=[norm(x), norm(Qobj'*x)], % =[28.2807, 28.2807]
    
    AdjointOfAdjoint=isequal(Qobj*x, (Qobj')'*x), % =1
    
    InversionErrorLeft=norm(x- Qobj'*(Qobj*x)), % =8.4315e-015
    
    InversionErrorRight=norm(x- Qobj*(Qobj'*x)), % =7.9086e-015

  
EXAMPLE 4:The following is an example of the DataObj subclass. Here, we use it to create a specialized array type which invokes bsxfun() for certain operations. This can be a useful way of circumventing bsxfun's lengthy functional syntax. Other operations like mtimes have default implementations.

 P=DataObj;
  P.Data=[1,2;3,4].',

        P.Ops.minus=@(A,B) bsxfun(@minus,A,B);
        P.Ops.plus= @(A,B) bsxfun(@plus,A,B);

  Q=P-[1,2],
  R=P+[3;7],
  S=P*Q, %This uses a natural default

P =

     1 3
     2 4
 

Q =

     0 1
     1 2
 

R =

     4 6
     9 11
 

S =

     3 7
     4 10

MATLAB release MATLAB 7.9 (R2009b)
Tags for This File   Please login to tag files.
Please login to add a comment or rating.
Comments and Ratings (3)
18 Nov 2010 Joao Henriques

Yes, the main application IMO is simplifying syntax, creating a sort of mini-language. Another good use would be creating a matrix that self-validates on any operations, for instance a "positive-definite matrix" that raises an error if this condition is not verified. This kind of use is what motivated my question earlier :)

16 Nov 2010 Matt J

Thanks Joao. For operating on small data sizes, native code could definitely reduce overhead. In situations like those, the class would be best used just for simplifying syntax. For large data sizes, of course, the processing of the data become the primary bottleneck. In those cases, overhead from the class interface can be negligible, as for instance the FFT operator (Example 1 above) showed.

15 Nov 2010 Joao Henriques

Nice and useful! One question, do you think a native code implementation would minimize the overhead significantly? Or the bottleneck here is the call to an anonymous function?

Updates
05 Aug 2010

Corrected small bug in the subsref method that prevented the 'Params' object property from being indexed.

01 Sep 2010

*Added an HTML user guide
*Created DataObj subclass (explained in User Guide).
*Added Trans property to MatrixObj (explained in User Guide). Implementing transposition is sometimes easier through Trans.
*Added conj() and display() class methods

03 Oct 2010

Added logical() converter method to the DataObj subclass. This allows DataObj objects to be used in relational expressions and if/while statements. Similarly, added double/single converter methods.

Contact us