diff m-toolbox/classes/@ao/elementOp.m @ 0:f0afece42f48

Import.
author Daniele Nicolodi <nicolodi@science.unitn.it>
date Wed, 23 Nov 2011 19:22:13 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/m-toolbox/classes/@ao/elementOp.m	Wed Nov 23 19:22:13 2011 +0100
@@ -0,0 +1,587 @@
+% ELEMENTOP applies the given operator to the data.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DESCRIPTION: ELEMENTOP applies the given operator to the data.
+%
+% CALL:        a = elementOp(callerIsMethod, @getInfo, @getDefaultPlist, op, opname, opsym, aosNames, varargin(:));
+%
+% VERSION:     $Id: elementOp.m,v 1.45 2011/04/18 16:53:12 ingo Exp $
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+function res = elementOp(varargin)
+ 
+  
+  import utils.const.*
+  
+  % Settings
+  callerIsMethod = varargin{1};
+  getInfo = varargin{2};
+  getDefaultPlist = varargin{3};
+
+  if callerIsMethod
+    dpl = [];
+    infoObj = [];
+  else    
+    % Check if this is a call for parameters
+    if utils.helper.isinfocall(varargin{end}{:})
+      res = getInfo(varargin{end}{3});
+      return
+    end
+    
+    dpl = getDefaultPlist();
+    infoObj = getInfo('None');
+        
+  end
+  
+  op     = varargin{4};
+  opname = varargin{5};
+  opsym  = varargin{6};
+    
+  % variable names
+  varnames = varargin{7};
+  
+  % Collect AO inputs but preserve the element shapes
+  % ... also collect numeric terms and preserve input names
+  argsin = varargin{8};
+  
+  plin = [];
+  aos         = {};
+  aosVarNames = {};
+  
+  if numel(argsin) == 1
+    for kk=1:numel(argsin{1})
+      aos = [aos {argsin{1}(kk)}];
+      if ~callerIsMethod
+        aosVarNames = [aosVarNames varnames(1)];
+      end
+    end
+  else
+    for kk=1:numel(argsin)
+      if isa(argsin{kk}, 'ao')
+        aos = [aos argsin(kk)];
+        if ~callerIsMethod
+          aosVarNames = [aosVarNames varnames(kk)];
+        end
+      elseif isnumeric(argsin{kk}) || islogical(argsin{kk})
+        % When promoting the number to an AO, we have to be sure to call
+        % the fromVals and allow it to add history.
+        a = fromVals(ao, plist('vals', argsin{kk}), false);
+        aos = [aos {a}];
+        if all(size(argsin{kk}) == [1 1])
+          aosVarNames = [aosVarNames {num2str(argsin{kk})}];
+        elseif any(size(argsin{kk}) == [1 1])
+          aosVarNames = [aosVarNames 'vector'];
+        else
+          aosVarNames = [aosVarNames 'matrix'];
+        end
+      elseif isa(argsin{kk}, 'plist')
+        if isempty(plin)
+          plin = argsin{kk};
+        else
+          plin = combine(plin, argsin{kk});
+        end
+      end
+    end
+  end
+  
+  % Combine input PLIST with default PLIST
+  if callerIsMethod
+    axis = 'y';
+    pl = [];
+  else    
+    pl = applyDefaults(dpl, plin);
+    axis = pl.find('axis');
+    % operate at least the y-values
+    if isempty(axis)
+      axis = 'y';
+    end
+  end  
+  
+  if numel(aos) < 2
+    error('### A %s operator requires at least two AO inputs.', opname)
+  end
+  
+%   utils.helper.msg(msg.PROC3, 'running %s/%s', mfilename('class'), op);
+  
+  if numel(aos) > 2
+    
+    % we recursively pass back to this method
+    res     = copy(aos{1}, 1);
+    resName = aosVarNames{1};
+    for kk=2:numel(aos)
+      res = ao.elementOp(callerIsMethod, getInfo, getDefaultPlist, op, opname, opsym, {resName, aosVarNames{kk}}, {res, aos{kk}});
+      resName = res.name;
+    end
+    
+  else % args == 2
+    
+    a1 = aos{1};
+    a2 = aos{2};
+    
+    %%%%%%%%%%   Rule 3:
+    if numel(a1) > 1 && numel(a2) > 1
+      if isVector(a1) && isVector(a2) && numel(a1) ~= numel(a2)
+        error('### It is not possible to %s two AO vectors of different lengths', opname);
+      end
+    end
+    
+    %%%%%%%%%%   Rule 8
+    if ismatrix(a1) && isVector(a2)
+      if nrows(a2) > 1 && nrows(a2) ~= nrows(a1)
+        error('### The number of rows in AO matrix should match the number of rows in the column vector.');
+      elseif ncols(a2)>1 && ncols(a2) ~= ncols(a1)
+        error('### The number of cols in AO matrix should match the number of cols in the row vector.');
+      end
+    end
+    if ismatrix(a2) && isVector(a1)
+      if nrows(a1) > 1 && nrows(a1) ~= nrows(a2)
+        error('### The number of rows in AO matrix should match the number of rows in the column vector.');
+      elseif ncols(a1)>1 && ncols(a1) ~= ncols(a2)
+        error('### The number of cols in AO matrix should match the number of cols in the row vector.');
+      end
+    end
+    
+    %%%%%%%%%%   Rule 9
+    if ismatrix(a1) && ismatrix(a2)
+      if ~isequal(size(a1), size(a2))
+        error('### Two AO matrices must be the same size to %s them.', opname);
+      end
+    end
+    
+    %------------- Now perform operation
+    
+    if numel(a1) == 1 && numel(a2) == 1
+      
+      %%%%%%%%%%   Rule 1: single AO + single AO
+      res = ao.initObjectWithSize(1,1);
+      operateSingleObject(res, a1, [], a2, []);
+      
+    elseif isVector(a1) && numel(a2) == 1
+      
+      %%%%%%%%%%   Rule 2a: vector + single AO
+      res = ao.initObjectWithSize(size(a1));
+      
+      for ii =1:numel(a1);
+        operateSingleObject(res(ii), a1(ii), ii, a2, []);
+      end
+      
+    elseif numel(a1) == 1 && isVector(a2)
+      
+      %%%%%%%%%%   Rule 2b: single AO + vector
+      res = ao.initObjectWithSize(size(a2));
+      
+      for ii =1:numel(a2);
+        operateSingleObject(res(ii), a1, [], a2(ii), ii);
+      end
+      
+    elseif isVector(a1) && isVector(a2) && numel(a1) == numel(a2)
+      
+      %%%%%%%%%%   Rule 4: vector + vector
+      res = ao.initObjectWithSize(size(a1));
+      
+      for ii =1:numel(a1);
+        operateSingleObject(res(ii), a1(ii), ii, a2(ii), ii);
+      end
+      
+    elseif ismatrix(a1) && numel(a2) == 1
+      
+      %%%%%%%%%%   Rule 5a: matrix + single AO
+      res = ao.initObjectWithSize(size(a1));
+      
+      for ii =1:numel(a1);
+        operateSingleObject(res(ii), a1(ii), ii, a2, []);
+      end
+      
+    elseif numel(a1) == 1 && ismatrix(a2)
+      
+      %%%%%%%%%%   Rule 5b: single AO + matrix
+      res = ao.initObjectWithSize(size(a2));
+      
+      for ii =1:numel(a2);
+        operateSingleObject(res(ii), a1, [], a2(ii), ii);
+      end
+      
+    elseif ismatrix(a1) && isVector(a2) && size(a1,1) == length(a2)
+      
+      %%%%%%%%%%   Rule 6a: matrix NP + vector N
+      res = ao.initObjectWithSize(size(a1));
+      
+      for nn = 1:size(a1,1)
+        for pp = 1:size(a1,2)
+          operateSingleObject(res(nn,pp), a1(nn,pp), [nn pp], a2(nn), nn);
+        end
+      end
+      
+    elseif isVector(a1) && ismatrix(a2) && size(a2,1) == length(a1)
+      
+      %%%%%%%%%%   Rule 6b: vector N + matrix NP
+      res = ao.initObjectWithSize(size(a2));
+      
+      for nn = 1:size(a2,1)
+        for pp = 1:size(a2,2)
+          operateSingleObject(res(nn,pp), a1(nn), nn, a2(nn,pp), [nn pp]);
+        end
+      end
+      
+    elseif ismatrix(a1) && isVector(a2) && size(a1,2) == length(a2)
+      
+      %%%%%%%%%%   Rule 7a: matrix NP + vector P
+      res = ao.initObjectWithSize(size(a1));
+      
+      for nn = 1:size(a1,1)
+        for pp = 1:size(a1,2)
+          operateSingleObject(res(nn,pp), a1(nn,pp), [nn pp], a2(pp), pp);
+        end
+      end
+      
+    elseif isVector(a1) && ismatrix(a2) && size(a2,2) == length(a1)
+      
+      %%%%%%%%%%   Rule 7b: vector P + matrix NP
+      res = ao.initObjectWithSize(size(a2));
+      
+      for nn = 1:size(a2,1)
+        for pp = 1:size(a2,2)
+          operateSingleObject(res(nn,pp), a1(pp), pp, a2(nn,pp), [nn pp]);
+        end
+      end
+      
+    elseif ismatrix(a1) && ismatrix(a2) && size(a1,1) == size(a2,1) && size(a1,2) == size(a2,2)
+      
+      %%%%%%%%%%   Rule 10: matrix NP + matrix NP
+      res = ao.initObjectWithSize(size(a2));
+      
+      for nn = 1:size(a1,1)
+        for pp = 1:size(a1,2)
+          operateSingleObject(res(nn,pp), a1(nn,pp), [nn pp], a2(nn,pp), [nn pp]);
+        end
+      end
+      
+    else
+      error('### Should not happen.')
+    end
+    
+  end
+  
+  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+  %
+  % DESCRIPTION: Applies the given operator to single input objects.
+  %
+  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+  
+  function operateSingleObject(res, a1, a1Idx, a2, a2Idx)
+    
+    % Set data object
+    res.data = operateData(a1, a2, op, axis);
+        
+    if callerIsMethod
+      % do nothing
+    else
+      % Set name
+      n1 = getName(a1.name, aosVarNames{1}, a1Idx);
+      n2 = getName(a2.name, aosVarNames{2}, a2Idx);
+      res.name = sprintf('(%s %s %s)', n1, opsym, n2);
+      % Set description
+      if ~isempty(a1.description) || ~isempty(a2.description)
+        if isempty(a1.description)
+          res.description = a2.description;
+        elseif isempty(a2.description)
+          res.description = a1.description;
+        else
+          res.description = strtrim([a1.description, ', ', a2.description]);
+        end
+      end
+      % Set plotinfo
+      if ~isempty(a1.plotinfo) || ~isempty(a2.plotinfo)
+        res.plotinfo = combine(a2.plotinfo, a1.plotinfo);
+      end
+      res.addHistory(infoObj, pl, {n1, n2}, [a1.hist a2.hist]);
+    end
+    
+  end
+  
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                               Local Functions                               %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Applies the given operator to the data object.
+%
+function data = operateData(a1, a2, op, axis)
+  
+  if isDataCompatible(a1, a2, op)
+    
+    data = getDataObject(a1.data, a2.data, op);
+    
+    operateValues(data, a1.data, a2.data, op, axis);
+    operateError(data, a1.data, a2.data, op, axis);
+    operateUnits(data, a1.data, a2.data, op, axis);
+  end
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Checks if the data objects are compatible
+%
+function res = isDataCompatible(a1, a2, op)
+  
+  d1 = a1.data;
+  d2 = a2.data;
+  %%%%% check: Data types
+  if (isa(d1, 'fsdata') && isa(d2, 'tsdata')) || ...
+      isa(d2, 'fsdata') && isa(d1, 'tsdata')
+    error('### Can not operate time-series data to frequency-series data for the %s operator.', opname);
+  end
+  %%%%% check: Frequency for tsdata
+  if  isa(d1, 'tsdata') && isa(d2, 'tsdata') && ...
+      d1.isprop('fs') && d2.isprop('fs') && ...
+      ~isempty(d1.fs) && ~isempty(d2.fs) && ...
+      ~isnan(d1.fs) && ~isnan(d2.fs) && ...
+      d1.fs ~= d2.fs
+    error('### The objects have different sample rates. Please resample one of the objects.')
+  end
+  if any(strcmpi(op, {'plus', 'minus'}))
+    %%%%% check: Y units
+    if ~isempty(d1.yunits.strs) && ~isempty(d2.yunits.strs)
+      if d1.yunits ~= d2.yunits
+        error('### Y units should be equal for the %s operator %s <-> %s', op, char(a1.yunits), char(a2.yunits));
+      end
+    end
+  end
+  %%%%% check: X units for all data types
+  if ~isa(d1, 'cdata') && ~isa(d2, 'cdata')
+    if ~isempty(d1.xunits.strs) && ~isempty(d2.xunits.strs)
+      if d1.xunits ~= d2.xunits
+        error('### X units should be equal for the %s operator', op);
+      end
+    end
+  end
+  %%%%% check x base of the fsdata objects
+  if isa(d1, 'fsdata') && isa(d2, 'fsdata') && any(abs(a1.x - a2.x) > 2*eps(a1.x))
+    error('### It is not possible to make any operation on frequency-series data if the x values are not the same.');
+  end
+  
+% Remove this condition because I haven't asked the other developers.
+%   %%%%% check: t0 of the tsdata objects
+%   if isa(d1, 'tsdata') && isa(d2, 'tsdata') && ~eq(d1.t0, d2.t0)
+%     error('### It is not possible to make any operation on time-series data if t0 is not the same.');
+%   end
+  res = true;
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Decide which data object should be used as the output object.
+%
+function dout = getDataObject(d1, d2, op)
+  
+  % The output data object is always a copy.
+  if utils.helper.ismember(op, {'or', 'and', 'xor'})
+    dout = cdata.initObjectWithSize(1,1);
+  else
+    if isa(d1, 'data2D') && isa(d2, 'data2D')
+      if numel(d1.getY) > 1
+        dout = copy(d1, 1);
+      elseif numel(d2.getY) > 1
+        dout = copy(d2, 1);
+      else
+        dout = copy(d1, 1);
+      end
+    elseif isa(d1, 'data2D') && isa(d2, 'cdata')
+      dout = copy(d1, 1);
+    elseif isa(d1, 'cdata') && isa(d2, 'data2D')
+      dout = copy(d2, 1);
+    else
+      dout = copy(d1, 1);
+    end
+  end
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Evaluate the output values.
+%
+function operateValues(dout, d1, d2, op, axis)
+  
+  if strcmp(op, 'mtimes') || strcmp(op, 'mrdivide')
+    y = feval(op, d1.y, d2.y);
+  else
+    y = feval(op, d1.getY, d2.getY);
+  end
+  
+  if isa(dout, 'cdata')
+    if any(find(axis == 'y'))
+      dout.setY(y);
+    else
+      error('cdata objects only have y axis');
+    end
+  else
+    if any(find(axis == 'y'))
+      dout.setY(y);
+    end
+    if any(find(axis == 'x'))
+      dout.setX(feval(op, d1.getX, d2.getX));
+    end
+  end
+  
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Evaluate the errors
+%
+function operateError(dout, d1, d2, op, axis)
+  
+  % Define function for the errors
+  switch op
+    case {'plus', 'minus'}
+      err = @(err1, err2, val1, val2) sqrt(err1 .^2 + err2.^2);
+    case {'times', 'mtimes'}
+      err = @(err1, err2, val1, val2) sqrt( (err1./val1).^2 + (err2./val2).^2 ) .* abs(val1.*val2);
+    case {'rdivide', 'mrdivide'}
+      err = @(err1, err2, val1, val2) sqrt( (err1./val1).^2 + (err2./val2).^2 ) .* abs(val1./val2);
+    otherwise
+      err = @(err1, err2, val1, val2) [];
+  end
+  
+  % Compute the error for the y-axis
+  if isa(dout, 'cdata')
+    if any(find(axis == 'y'))
+      if ~isempty(d1.dy) || ~isempty(d2.dy)
+        
+        dy1 = d1.getDy;
+        dy2 = d2.getDy;
+        
+        if isempty(dy1)
+          dy1 = zeros(size(d1.getY));
+        end
+        if isempty(dy2)
+          dy2 = zeros(size(d2.getY));
+        end
+        
+        dy = err(dy1, dy2, d1.getY, d2.getY);
+      else
+        dy = [];
+      end
+      dout.setDy(dy);
+    else
+      warning('!!! The output data object is a ''cdata'' object and you operate only on the x-axis but this axis doesn''t exist on a cdata object.');
+    end
+    
+  else
+    
+    % Compute the error for the y-axis
+    if any(find(axis == 'y'))
+      if ~isempty(d1.dy) || ~isempty(d2.dy)
+        
+        dy1 = d1.getDy;
+        dy2 = d2.getDy;
+        
+        if isempty(dy1)
+          dy1 = zeros(size(d1.getY));
+        end
+        if isempty(dy2)
+          dy2 = zeros(size(d2.getY));
+        end
+        
+        dy = err(dy1, dy2, d1.getY, d2.getY);
+      else
+        dy = [];
+      end
+      dout.setDy(dy);
+    end
+    
+%     % Compute the error for the x-axis
+%     if any(find(axis == 'x'))
+%       if ~isempty(d1.dx) || ~isempty(d2.dx)
+%         
+%         dx1 = d1.getDx;
+%         dx2 = d2.getDx;
+%         
+%         if isempty(dx1)
+%           dx1 = zeros(size(d1.getX));
+%         end
+%         if isempty(dx2)
+%           dx2 = zeros(size(d2.getX));
+%         end
+%         
+%         dx = err(dx1, dx2, d1.getX, d2.getX);
+%       else
+%         dx = [];
+%       end
+%       dout.setDx(dx);
+%     end
+    
+  end
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Evaluate the units
+%
+function operateUnits(data, d1, d2, op, axis)
+  if utils.helper.ismember(op, {'or', 'and', 'xor'})
+    data.setYunits('');
+  elseif any(strcmpi(op, {'plus', 'minus'}))
+    % return the first non-empty
+    if ~isempty(d1.yunits.strs)
+      data.setYunits(d1.yunits);
+    else
+      data.setYunits(d2.yunits);
+    end
+  else
+    % For other operators we need to apply the operator
+    data.setYunits(feval(op, d1.yunits, d2.yunits));
+  end
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+function name = getName(objName, varName, idx)
+  
+  if strcmpi(objName, 'none') || isempty(objName)
+    if ~isempty(varName)
+      useName = varName;
+    else
+      useName = objName;
+    end
+    % Set the name depending to the index
+    if isempty(idx)
+      name = useName;
+    elseif numel(idx) == 1
+      name = sprintf('%s(%d)', useName, idx(1));
+    else
+      name = sprintf('%s(%d,%d)', useName, idx(1), idx(2));
+    end
+  else
+    name = objName;
+  end
+  
+  if isempty(name)
+    name = '?';
+  end
+  
+end
+
+function res = isVector(a)
+  res = any(size(a) > 1) && any(size(a) == 1);
+end
+
+function res = ismatrix(a)
+  res = all(size(a) > 1);
+end
+
+%-------------------------------------
+% Return number of rows in the array
+function r = nrows(a)
+  r = size(a,1);
+end
+
+%-------------------------------------
+% Return number of cols in the array
+function r = ncols(a)
+  r = size(a,2);
+end