Search code examples
arraysmatlabmatrixobject-oriented-database

Object-oriented programing in Matlab: Set up new class for 2D arrays, with negative indices


I am new to object-oriented programming. This question seems to resemble previously asked ones, but with one big difference to which I did not find a solution yet:

How do I set up a new class of variables, using object-oriented programming in Matlab, which will behave similarly to a matrix (2D array... ), but for which the rows/columns will be numbered {-2,-1,0,1...} (instead of only {1,2,3...})?

I want to be able to use all the regular vector operations that Matlab offers on this new class. What will be the method to address the values in this variable (e.g., A[-1,2]=... like for a normal array? A.-1.2=...?) ? How would I perform simple A. * B (element-by-element multiplication of vectors in Matlab)? And A(:,5). * B(-1,:)'?


Solution

  • You can subclass the builtin double class which will give you the majority of your functionality. With respect to indexing, you'll just have to write your own subsref and subsasgn methods.

    Since you've subclassed double, all normal matrix operations (such as element-wise multiplication, cumsum, etc.) will continue to work as expected.

    Something like this should work.

    classdef mymatrix < double
    
        methods
            function self = mymatrix(varargin)
                % Constructor that simply calls the double constructor
                self@double(varargin{:});
            end
    
            function res = subsref(self, subs)
                % Call the usual subsref after modifying the subscripts
                res = subsref@double(self, correctsubs(self, subs));
            end
    
            function res = subsasgn(self, subs, val)
                % Call the usual subsasgn after modifying the subscripts
                res = subsasgn@double(self, correctsubs(self, subs), val);
            end
        end
    
        methods (Access = 'private')
            function subs = correctsubs(self, subs)
                % Function for converting subscripts
                for k = 1:numel(subs)
                    % Only process () references and non-linear indices
                    if ~isequal(subs(k).type, '()')
                        continue
                    end
    
                    % Figure out the center of the matrix
                    mid = ceil(size(self) / 2);
    
                    % For each subscript, if it's numeric then add the middle
                    % shift amount to it to turn it into a normal subscript
                    for m = 1:numel(subs(k).subs)
                        if isnumeric(subs(k).subs{m})
                            subs(k).subs{m} = subs(k).subs{m} + mid(m);
                        end
                    end
                end
            end
        end
    end
    

    You could then use this exactly like how you describe.

    m1 = mymatrix(magic(3));
    m2 = mymatrix(magic(3));
    
    % All normal matrix operations will work        
    m3 = m1 .* m2;
    cumsum(m1, 1);
    
    % You can also perform your special indexing
    m4 = m1(:,-1);