Mercurial > hg > ltpda
diff m-toolbox/classes/@ao/melementOp.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/melementOp.m Wed Nov 23 19:22:13 2011 +0100 @@ -0,0 +1,404 @@ +% MELEMENTOP applies the given matrix operator to the data. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% DESCRIPTION: MELEMENTOP applies the given matrix operator to the data. +% +% CALL: +% a = melementOp(callerIsMethod, op, opname, opsym, minfo, pl, a1, a2,...) +% +% +% VERSION: $Id: melementOp.m,v 1.12 2011/04/18 16:55:43 ingo Exp $ +% +% HISTORY: 01-02-07 M Hewitson +% Creation +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +function varargout = melementOp(varargin) + + import utils.const.* + + % Settings + callerIsMethod = varargin{1}; + op = varargin{2}; + opname = varargin{3}; + opsym = varargin{4}; + % Info to pass to history + iobj = varargin{5}; + pl = varargin{6}; + + % variable names + varnames = varargin{8}; + + % Collect AO inputs but preserve the element shapes + % ... also collect numeric terms and preserve input names + argsin = varargin{7}; + args = {}; + in_names = {}; + for kk=1:numel(argsin) + if isa(argsin{kk}, 'ao') + args = [args argsin(kk)]; + in_names = [in_names varnames(kk)]; + elseif isnumeric(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}), 0); + args = [args {a}]; + if all(size(argsin{kk}) == [1 1]) + in_names = [in_names num2str(argsin{kk})]; + elseif any(size(argsin{kk}) == [1 1]) + in_names = [in_names 'vector']; + else + in_names = [in_names 'matrix']; + end + end + end + + if numel(args) < 2 + error('### %s operator requires at least two AO inputs.', opname) + end + + if numel(args) == 2 + + % get the two arrays + a1 = args{1}; + a2 = args{2}; + + % check the data + for kk=1:numel(a1) + if ~isa(a1(kk).data, 'ltpda_data') + error('### one of the input AOs has an empty data field'); + end + end + for kk=1:numel(a2) + if ~isa(a2(kk).data, 'ltpda_data') + error('### one of the input AOs has an empty data field'); + end + end + + % Here we operate on two AO arrays according to the rules + + %---------- Deal with error cases first + r1 = size(a1,1); + c1 = size(a1,2); + r2 = size(a2,1); + c2 = size(a2,2); + + %== Rule 4: [1xN] */ [Nx1] + if r1 == 1 && r2 == 1 && c1==c2 && c1>1 + error('### It is not possible to %s two AO vectors of size [1xN]', opname); + end + + %== Rule 6: [Nx1] */ [Nx1] + if r1 == r2 && c1==1 && c2==1 && r1>1 + error('### It is not possible to %s two AO vectors of the size [Nx1]', opname); + end + + %== Rule 7: [NxP] */ [Nx1] + if r1 == r2 && c1>1 && c2==1 && c1~=r1 && r1>1 + error('### It is not possible to %s [NxP] and [Nx1]', opname); + end + + %== Rule 8: [NxP] */ [Px1] + if c1 == c2 && r1>1 && r2==1 && c1>1 + error('### It is not possible to %s [NxP] and [1xP]', opname); + end + + %== Rule 9: [NxP] */ [NxP] + if isequal(size(a1), size(a2)) && r1>1 && c1>1 + if size(a1,1) ~= size(a1,2) + error('### It is not possible to %s [NxP] and [NxP]', opname); + end + end + + + %------------- Now perform operation + if numel(a1)==1 || numel(a2)==1 + + % Rules 1,2,5 + if isvector(a1) || isvector(a2) || ismatrix(a1) || ismatrix(a2) + % Rule 2,5: vector or matrix + single AO + if isvector(a1) || ismatrix(a1) + res = copy(a1,1); + for ee=1:numel(res) + res(ee).data = compatibleData(res(ee),a2); + res(ee).data.setY(operate(a1(ee), a2)); + res(ee).data.setDy(operateError(a1(ee), a2)); + % set history and name + if ~callerIsMethod + names = getNames(in_names, res(ee), ee, a2, []); + res(ee).addHistory(iobj, pl, names(1:2), [res(ee).hist a2.hist]); + res(ee).name = names{3}; + end + res(ee).data.setYunits(getYunits(a1(ee), a2)); + end + else + res = copy(a2,1); + for ee=1:numel(res) + res(ee).data = compatibleData(res(ee),a1); + res(ee).data.setY(operate(a2(ee), a1)); + res(ee).data.setDy(operateError(a2(ee), a1)); + % set history and name + if ~callerIsMethod + names = getNames(in_names, a1, [], res(ee), ee); + res(ee).addHistory(iobj, pl, names(1:2), [a1.hist res(ee).hist]); + res(ee).name = names{3}; + end + res(ee).data.setYunits(getYunits(a1, a2(ee))); + end + end + else + % Rule 1: [1x1] */ [1x1] + res = copy(a1,1); + res.data = compatibleData(res,a2); + res.data.setY(operate(a1, a2)); + res.data.setDy(operateError(a1, a2)); + % set history and name + if ~callerIsMethod + names = getNames(in_names, res, [], a2, []); + res.addHistory(iobj, pl, names(1:2), [res.hist a2.hist]); + res.name = names{3}; + end + res.data.setYunits(getYunits(a1, a2)); + end + elseif isvector(a1) && isvector(a2) && r1==1 && c2==1 && r2==c1 + % Rule 3: [1xN] */ [Nx1] + if strcmp(op, 'mrdivide') + error('### It is not possible to divide two matrices with different sizes'); + end + res = []; + if strcmp(op, 'mtimes') + inner = 'times'; + else + inner = 'rdivide'; + end + + for ee=1:numel(a1) + if isempty(res) + res = feval(inner,a1(ee),a2(ee)); + else + res = res + feval(inner,a1(ee),a2(ee)); + end + end + elseif isvector(a1) && isvector(a2) && r1>1 && c1==1 && r2==1 && c2>1 + % Rule 5: [Nx1] */ [1xM] + res(r1,c2) = ao(); + for kk=1:r1 + for ll=1:c2 + res(kk,ll) = feval(op,a1(kk),a2(ll)); + end + end + elseif ismatrix(a1) && (ismatrix(a2) || isvector(a2)) + if strcmp(op, 'mrdivide') && ~isequal(size(a1),size(a2)) + error('### Can only divide matrices of the same size'); + end + % Rule 10: matrix */ matrix + res(r1,c2) = ao; + for kk=1:r1 + for ll=1:c2 + res(kk,ll) = feval(op,a1(kk,:),a2(:,ll)); + end + end + else + error('### The inputs were not properly handled. This shouldn''t happen.'); + end + + % Did something go wrong? + if isempty(res) + error('### The inputs were not properly handled. This shouldn''t happen.'); + end + + else + % we recursively pass back to this method + res = copy(args{1}, 1); + for kk=2:numel(args) + res = feval(op, res, args{kk}); + end + end + + % Set output + varargout{1} = res; + + %---------- nested functions + + %------------------------------------------------- + % Check the two inputs have compatible data types + function dout = compatibleData(a1,a2) + %== Data types + if (isa(a1.data, 'fsdata') && isa(a2.data, 'tsdata')) || ... + isa(a2.data, 'fsdata') && isa(a1.data, 'tsdata') + error('### Can not %s time-series data to frequency-series data.', opname); + end + % check X units for all data types + if ~isa(a1.data, 'cdata') && ~isa(a2.data, 'cdata') + if ~isempty(a1.data.xunits.strs) && ~isempty(a2.data.xunits.strs) + if a1.data.xunits ~= a2.data.xunits + error('### X units should be equal for the %s operator', op); + end + end + end + + % determine output data type + d1 = copy(a1.data,1); + d2 = copy(a2.data,1); + + if isa(d1, 'data2D') && isa(d2, 'data2D') + if numel(d1.y) > 1 + dout = d1; + elseif numel(d2.y) > 1 + dout = d2; + else + dout = d1; + end + elseif isa(d1, 'data2D') && isa(d2, 'cdata') + dout = d1; + elseif isa(d1, 'cdata') && isa(d2, 'data2D') + dout = d2; + else + dout = d1; + end + + end + + function uo = getYunits(a1, a2) + % For other operators we need to apply the operator + uo = feval(op, a1.data.yunits, a2.data.yunits); + end + + % Perform the desired operation on the data + function y = operate(a1, a2) + y = feval(op, a1.data.y, a2.data.y); + end + + % Perform the desired operation on the data uncertainty + function dy = operateError(a1, a2) + + if ~isempty(a1.dy) || ~isempty(a2.dy) + + da1 = a1.dy; + da2 = a2.dy; + + if isempty(da1) + da1 = zeros(size(a1.y)); + end + if isempty(da2) + da2 = zeros(size(a2.y)); + end + + switch op + case {'plus', 'minus'} + dy = sqrt(da1.^2 + da2.^2); + case {'times', 'mtimes'} + dy = sqrt( (da1./a1.y).^2 + (da2./a2.y).^2 ) .* abs(a1.y.*a2.y); + case {'rdivide', 'mrdivide'} + dy = sqrt( (da1./a1.y).^2 + (da2./a2.y).^2 ) .* abs(a1.y./a2.y); + otherwise + dy = []; + end + + else + dy = []; + end + + end + + %----------------------------------------------- + % Get two new AO names from the input var names, + % the input AO names, and the indices. + function names = getNames(in_names, a1, jj, a2, kk) + + % First variable name + if isempty(a1.name) && ~isempty(in_names{1}) + if ~isempty(jj) + if numel(jj) == 1 + names{1} = sprintf('%s(%d)', in_names{1}, jj); + else + names{1} = sprintf('%s(%d,%d)', in_names{1}, jj(1), jj(2)); + end + else + names{1} = in_names{1}; + end + else + if ~isempty(jj) + if numel(jj) == 1 + % names{1} = sprintf('%s(%d)', a1.name, jj); + names{1} = sprintf('%s', a1.name); + else + % names{1} = sprintf('%s(%d,%d)', a1.name, jj(1), jj(2)); + names{1} = sprintf('%s', a1.name); + end + else + names{1} = a1.name; + end + end + % Second variable name + if isempty(a2.name) && ~isempty(in_names{2}) + if isempty(in_names{2}) + in_names{2} = a2.name; + end + if ~isempty(kk) + if numel(kk) == 1 + names{2} = sprintf('%s(%d)', in_names{2}, kk); + else + names{1} = sprintf('%s(%d,%d)', in_names{2}, kk(1), kk(2)); + end + else + names{2} = in_names{2}; + end + else + names{2} = a2.name; + if ~isempty(kk) + if numel(kk) == 1 + % names{2} = sprintf('%s(%d)', a2.name, kk); + names{2} = sprintf('%s', a2.name); + else + % names{2} = sprintf('%s(%d,%d)', a2.name, kk(1), kk(2)); + names{2} = sprintf('%s', a2.name); + end + else + names{2} = a2.name; + end + end + + % The output AO name + names{3} = sprintf('(%s%s%s)', names{1}, opsym, names{2}); + end + + %------------------------------------- + % Return true if the input is a matrix + function r = ismatrix(a) + if nrows(a) > 1 && ncols(a) > 1 + r = true; + else + r = false; + end + end + + %------------------------------------- + % Return true if the input is a vector + function r = isvector(a) + if (nrows(a)==1 && ncols(a)>1) || (ncols(a)==1 && nrows(a)>1) + r = true; + else + r = false; + end + end + + %------------------------------------- + % Return numnber of rows in the array + function r = nrows(a) + r = size(a,1); + end + + %------------------------------------- + % Return numnber of cols in the array + function r = ncols(a) + r = size(a,2); + end + + +end % End of add + + + +% END