Code covered by the BSD License  

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

On-The-Fly Definition of Custom Matrix Objects

by

 

07 Feb 2010 (Updated )

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

mobjtest(TOL)
function mobjtest(TOL)
%Performs numerous tests of RobustSparse math operations, 
%
%  mobjtest(TOL)
%
%TOL is a tolerance value on the percent error. Execution will pause in debug
%mode for inspection if any one of the tests exhibits an error greater than
%TOL.

if nargin<1
 TOL=inf; %default tolerance value on discrepancies
end

CHECKTYPES=false;

%%function for measuring error

 err=@(x,y) DiscrepancyMeasure(x,y,TOL,CHECKTYPES);


 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%TEST DATA

A=rand(3); B=rand(3)>.5;

Aobj=MatrixObj;
  Aobj.Params=A;



Ops.size = [];                
Ops.subsref = [];          
Ops.subsasgn = [];        
Ops.end = [];                  
Ops.subsindex = [];        
Ops.colon = [];              
Ops.vertcat = @(obj,Q) [obj.Params; Q];          
Ops.horzcat = @(obj,Q) [obj.Params, Q];        
Ops.ctranspose = @(obj) obj.Params';    
Ops.transpose = @(obj) obj.Params.';      
Ops.uminus = @(obj) -obj.Params;            
Ops.uplus = @(obj) +obj.Params;    
Ops.plus = @(obj,Q) obj.Params+Q;        
Ops.minus = @(obj,Q) obj.Params-Q;    
Ops.mldivide = @(obj,Q) obj.Params\Q;        
Ops.mpower = @(obj,Q) obj.Params^Q;            
Ops.mrdivide = @(obj,Q) obj.Params/Q;        
Ops.mtimes = @(obj,Q) obj.Params*Q;           
Ops.power = @(obj,Q) obj.Params.^Q;            
Ops.rdivide = @(obj,Q) obj.Params./Q;         
Ops.ldivide = @(obj,Q) obj.Params.\Q;          
Ops.times = @(obj,Q) obj.Params.*Q;               
Ops.inv = @(obj) inv(obj.Params);                  
Ops.sum = @mysum; 
    function sumval=mysum(obj,varargin)
        sumval=sum(obj.Params,varargin{:});
    end

 
Ops.gt = @(obj,Q) obj.Params>Q;
Ops.not = @(obj,Q) ~obj.Params; 
Ops.eq = @(obj,Q) obj.Params==Q;  
Ops.or = @(obj,Q) obj.Params|Q;    


Ops.ge = @(obj,Q) obj.Params>=Q;                   
Ops.lt = @(obj,Q) obj.Params<Q;                    
Ops.le = @(obj,Q) obj.Params<=Q;                    
Ops.ne = @(obj,Q) obj.Params~=Q;         
Ops.and = @(obj,Q) obj.Params&Q;    
 





        


%%%%%%%%%%%%%%%%%%%%%%%%%%%TESTS%%%%%%%%%%%%%%%%%%%%%%%%%%


Aobj.Ops=Ops;


%%Test of uplus, uminus
Error(1)= err( +Aobj ,  +A   );
Error(end+1)= err( -Aobj  ,  -A   );

    Aobj.Ops.uplus=[]; %test default

Error(end+1)= err( +Aobj ,  +A   );    


%%Test of plus, minus

Error(end+1)= err( Aobj+B  , A+B );
Error(end+1)= err( Aobj-B ,  A-B );


%%Test of inv

Error(end+1)= err( inv(Aobj)  , inv(A) );


%%Test of sum

Error(end+1)= err( sum(Aobj)    , sum(A) );
Error(end+1)= err( sum(Aobj,2)  , sum(A,2) );


%%Test of mtimes

Error(end+1)= err( Aobj*3 , A*3  );  


%%Test of transpose, ctranspose

Error(end+1)= err( Aobj.' ,  A.'   );
Error(end+1)= err( Aobj'  ,  A'   );

Error(end+1)= err( (Aobj*i).' ,  (A*i).'   );
Error(end+1)= err( (Aobj*i)'  ,  (A*i)'   );

  %Test of Trans feature
  Zobj=Aobj; 
  Zobj.Ops.transpose=@(obj) setfield(obj,'Params',obj.Params.');
  Zobj.Ops.ctranspose=@(obj) setfield(obj,'Params',obj.Params');
  Zobj.Trans.transpose=@(obj) -obj.Params; 
  Zobj.Trans.ctranspose=@(obj) -obj.Params*i;
  
  
Error(end+1)= err( (Zobj.').' ,  -A.'   );
Error(end+1)= err( (Zobj')' ,  -A'*i   );  
  
%%Test of mldivide

Error(end+1)= err( Aobj\B , A\B  ); 


%%Test of mrdivide

    C=rand(size(A));

Error(end+1)= err( Aobj/C , A/C  ); 


%%Test of times

Error(end+1)= err( Aobj.*B , A.*B  ); 


%%Test of rdivide

Error(end+1)= err( Aobj./B , A./B  ); 


%%Test of ldivide


Error(end+1)= err( Aobj.\B , A.\B  ); 

%%Test of power, mpower
Error(end+1)= err( Aobj.^2 ,  A.^2   );
Error(end+1)= err( Aobj^2  ,  A^2   );


%%Test of vertcat,horzcat

Error(end+1)= err( [Aobj,B]  , [A,B]   );
Error(end+1)= err( [Aobj;B]  , [A;B]   );

%%Test of size

    [aa,bb,cc]=size(A);

    Aobj.Ops.size = size(A); %implement using numeric data

Error(end+1)= err( size(Aobj)  , size(A)   );   
Error(end+1)= err( size(Aobj,1)  , size(A,1)   );       
Error(end+1)= err( size(Aobj,2)  , size(A,2)   );  
Error(end+1)= err( size(Aobj,3)  , size(A,3)   );  

    [aaa,bbb,ccc]=size(Aobj);   

Error(end+1)= err( [aaa,bbb,ccc] , [aa,bb,cc] );  


    Aobj.Ops.size = @(obj) size(obj.Params); %implement using function handle

Error(end+1)= err( size(Aobj)  , size(A)   );   
Error(end+1)= err( size(Aobj,1)  , size(A,1)   );       
Error(end+1)= err( size(Aobj,2)  , size(A,2)   );  
Error(end+1)= err( size(Aobj,3)  , size(A,3)   );  

    [aaa,bbb,ccc]=size(Aobj);   

Error(end+1)= err( [aaa,bbb,ccc] , [aa,bb,cc] );  


%%Test of subsindex


    Aobj.Ops.subsindex = @(obj) find(obj.Params>.5)-1; 

Error(end+1)= err( B(Aobj)  , B(A>0.5)   );   

%%Test of colon

  Aobj.Ops.colon=@(J,D,K) 1:10;

Error(end+1)= err( Aobj:1:10  , 1:10 ); 

    Aobj.Ops.colon=@(J,K) 1:10;

Error(end+1)= err( Aobj:10  , 1:10 ); 


%%Test of set,get

      A(1)=rand;
      Aobj=set(Aobj,'Params',A);

Error(end+1)= err( get(Aobj,'Params')  , A ); 



%%Test of relational and logical operators

Error(end+1)= err( Aobj>B  , A>B   );
Error(end+1)= err( Aobj>=B  , A>=B   );
Error(end+1)= err( Aobj<B  , A<B   );
Error(end+1)= err( Aobj<=B  , A<=B   );
Error(end+1)= err( Aobj==B  , A==B   );
Error(end+1)= err( Aobj~=B  , A~=B   );
Error(end+1)= err( Aobj|B  , A|B   );
Error(end+1)= err( ~Aobj ,   ~A   );


%%Test of subsref, subsasgn, end

   Tobj=Aobj;

   Tobj.Ops.subsref=@mysubsref;  %defined down at the bottom
   Tobj.Ops.subsasgn=@mysubsasgn;%defined down at the bottom

Error(end+1)= err( Tobj(1)  , A(1) ); 

   Tobj(1)=pi; A(1)=pi;

Error(end+1)= err( Tobj.Params  , A );


Error(end+1)= err( Tobj(end,1)  , A(end,1) ); %test of "end"
Error(end+1)= err( Tobj(:,:,end)  , A(:,:,end) ); 


   Tobj=Aobj; Tobj.Params=A;  %some resettings

   Tobj.Ops.end=@(obj,kk,nn) size(A,kk); %override default "end"   
   Tobj.Ops.subsref=@mysubsref;  %defined down at the bottom
   Tobj.Ops.subsasgn=@mysubsasgn;%defined down at the bottom



Error(end+1)= err( Tobj(end,1)  , A(end,1) ); 
Error(end+1)= err( Tobj(:,end)  , A(:,end) ); 

    Tobj(1,end)=exp(1); A(1,end)=exp(1);

Error(end+1)= err( Tobj.Params  , A );




%%%%%%%%%%%%%%%%%%%%%%%TESTS - DATAOBJ SUBCLASSS%%%%%%%%%%%%%%%%%%%%%%%%%%

A=rand(3); B=rand(3)>.5;

Aobj=DataObj(A);


%%Test of uplus, uminus
Error(end+1)= err( +Aobj ,  +A   );
Error(end+1)= err( -Aobj  ,  -A   );

    Aobj.Ops.uplus=[]; %test default

Error(end+1)= err( +Aobj ,  +A   );    


%%Test of plus, minus

Error(end+1)= err( Aobj+B  , A+B );
Error(end+1)= err( Aobj-B ,  A-B );


%%Test of inv

Error(end+1)= err( inv(Aobj)  , inv(A) );


%%Test of sum

Error(end+1)= err( sum(Aobj)    , sum(A) );
Error(end+1)= err( sum(Aobj,2)  , sum(A,2) );


%%Test of mtimes

Error(end+1)= err( Aobj*3 , A*3  );  


%%Test of transpose, ctranspose

Error(end+1)= err( Aobj.' ,  A.'   );
Error(end+1)= err( Aobj'  ,  A'   );

Error(end+1)= err( (Aobj*i).' ,  (A*i).'   );
Error(end+1)= err( (Aobj*i)'  ,  (A*i)'   );


  %Test of Trans feature
  Zobj=Aobj; 
  Zobj.Trans.transpose=@(Data) -Data; 
  Zobj.Trans.ctranspose=@(Data) -Data*i;

  
Error(end+1)= err( (Zobj.').' ,  -A.'   );
Error(end+1)= err( (Zobj')' ,  -A'*i   );    
  
%%Test of mldivide

Error(end+1)= err( Aobj\B , A\B  ); 


%%Test of mrdivide

    C=rand(size(A));

Error(end+1)= err( Aobj/C , A/C  ); 


%%Test of times

Error(end+1)= err( Aobj.*B , A.*B  ); 


%%Test of rdivide

Error(end+1)= err( Aobj./B , A./B  ); 


%%Test of ldivide


Error(end+1)= err( Aobj.\B , A.\B  ); 

%%Test of power, mpower
Error(end+1)= err( Aobj.^2 ,  A.^2   );
Error(end+1)= err( Aobj^2  ,  A^2   );


%%Test of vertcat,horzcat

Error(end+1)= err( [Aobj,B]  , [A,B]   );
Error(end+1)= err( [Aobj;B]  , [A;B]   );

%%Test of size

    [aa,bb,cc]=size(A);

    Aobj.Ops.size = size(A); %implement using numeric data

Error(end+1)= err( size(Aobj)  , size(A)   );   
Error(end+1)= err( size(Aobj,1)  , size(A,1)   );       
Error(end+1)= err( size(Aobj,2)  , size(A,2)   );  
Error(end+1)= err( size(Aobj,3)  , size(A,3)   );  

    [aaa,bbb,ccc]=size(Aobj);   

Error(end+1)= err( [aaa,bbb,ccc] , [aa,bb,cc] );  


    Aobj.Ops.size = @(obj) size(obj); %implement using function handle

Error(end+1)= err( size(Aobj)  , size(A)   );   
Error(end+1)= err( size(Aobj,1)  , size(A,1)   );       
Error(end+1)= err( size(Aobj,2)  , size(A,2)   );  
Error(end+1)= err( size(Aobj,3)  , size(A,3)   );  

    [aaa,bbb,ccc]=size(Aobj);   

Error(end+1)= err( [aaa,bbb,ccc] , [aa,bb,cc] );  


%%Test of subsindex


    Aobj.Ops.subsindex = @(obj) find(obj>.5)-1; 

 
Error(end+1)= err( B(Aobj)  , B(A>0.5)   );   

%%Test of colon

  Aobj.Ops.colon=@(J,D,K) 1:10;

Error(end+1)= err( Aobj:1:10  , 1:10 ); 

    Aobj.Ops.colon=@(J,K) 1:10;

Error(end+1)= err( Aobj:10  , 1:10 ); 


%%Test of set,get

      A(1)=rand;
      Aobj=set(Aobj,'Params',A);

Error(end+1)= err( get(Aobj,'Params')  , A ); 



%%Test of relational and logical operators

Error(end+1)= err( Aobj>B  , A>B   );
Error(end+1)= err( Aobj>=B  , A>=B   );
Error(end+1)= err( Aobj<B  , A<B   );
Error(end+1)= err( Aobj<=B  , A<=B   );
Error(end+1)= err( Aobj==B  , A==B   );
Error(end+1)= err( Aobj~=B  , A~=B   );
Error(end+1)= err( Aobj|B  , A|B   );
Error(end+1)= err( ~Aobj ,   ~A   );


%%Test of subsref, subsasgn, end

   Tobj=Aobj; A=Aobj.Data; %some resettings

   Tobj.Ops.subsref=@Psubsref;  %defined down at the bottom
   Tobj.Ops.subsasgn=@Psubsasgn;%defined down at the bottom

   
Error(end+1)= err( Tobj(1)  , A(1) ); 

   Tobj(1)=pi; A(1)=pi;

   
Error(end+1)= err( Tobj  , A );


Error(end+1)= err( Tobj(end,1)  , A(end,1) ); %test of "end"
Error(end+1)= err( Tobj(:,:,end)  , A(:,:,end) ); 


   Tobj=Aobj; A=Aobj.Data; %some resettings

   Tobj.Ops.end=@(obj,kk,nn) size(A,kk); %override default "end"   
   Tobj.Ops.subsref=@Psubsref;  %defined down at the bottom
   Tobj.Ops.subsasgn=@Psubsasgn;%defined down at the bottom



Error(end+1)= err( Tobj(end,1)  , A(end,1) ); 
Error(end+1)= err( Tobj(:,end)  , A(:,end) ); 

    Tobj(1,end)=exp(1); A(1,end)=exp(1);

Error(end+1)= err( Tobj  , A );


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%END TESTS%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


MAX_ERROR=max(Error);

disp(['Maximum observed error was   ' num2str(MAX_ERROR) ' percent.'])

end



function errval=DiscrepancyMeasure(X,Y,TOL,CHECKTYPES)

  if isa(X,'DataObj'), X=X.Data; end
  if isa(Y,'DataObj'), Y=Y.Data; end

  errval=Discrepancy(X,Y)/Discrepancy(0,Y)*100; %normalize
  

  if errval>TOL, 
      disp ' '; disp 'Discrepancy detected'
      errval, 
      x=myfull(X); y=myfull(Y);
      keyboard;
  end 
end
  
function errval=Discrepancy(X,Y) 
%Primary error measurement function

  fin=@(a) a(isfinite(a));
  nonfin=@(a) a(~isfinite(a)); 

  x=myfull(X); y=myfull(Y);

  errval= norm( fin(x-y) , inf)+...   
       ~isequalwithequalnans(nonfin(x),nonfin(y))*...
       ~isempty([nonfin(x);nonfin(y)]); 


end


function A=myfull(A)

 if isa(A,'MatrixObj')
   A=A.Params;
 else
     A=full(A);
 end
     
end


function out=mysubsref(obj,S)

    switch S.type
        case '.'
            
         out=get(obj,S.subs);

        otherwise
         Params=get(obj,'Params');
         out=Params(S.subs{:});


    end
end


function out=mysubsasgn(obj,S,rhs)

    switch S.type
        case '.'
            
         out=set(obj,S.subs,rhs);

        otherwise
         Params=get(obj,'Params');
         Params(S.subs{:})=rhs;
         out=set(obj,'Params',Params);

    end
end





function out=Psubsref(X,S)


            
         out=X(S.subs{:});


end


function X=Psubsasgn(X,S,rhs)


         X(S.subs{:})=rhs;

end

Contact us