function theResult = qsum1(varargin) % qsum1 -- Generalized summed products. % qsum1(N) exercizes itself with four-dimensional arrays of % length N in each direction (default = 1). % qsum1(a, ia, b, ib, ..., iz, i_sums) multiplies and sums arrays % a, b, ... together, whose compatible dimensions are labeled % (named) by vectors ia, ib, ..., either integers or characters. % The output dimensions are labeled by iz. Results are computed % over the permuted output-indices, always summing along the % directions given by i_sums. Thus, conventional matrix % multiplication can be accomplished with the command: % "qsum1(a, [1 2], b, [2 3], [1 3], [2])", same as % "qsum1(a, 'ij', b, 'jk', 'ik', 'j')". % NOTE: When summing along more than one direction, singleton % dimensions may appear in the output, for which there is no % corresponding input label. To specify a singleton-dimension % in the output, use 0 (zero), or, with character labels, use % char(0). Thus, the double-sum of the scalar product of % two matrices can be accomplished with: % "qsum(a, [1 2], b, [1 2], [0], [1 2])", same as % "qsum(a, 'ij', b, 'ij', char(0), 'ij')". % Copyright (C) 2001 Dr. Charles R. Denham, ZYDECO. % All Rights Reserved. % Disclosure without explicit written consent from the % copyright owner does not constitute publication. % Version of 14-Feb-2001 16:44:29. % Updated 26-Feb-2001 14:20:41. if nargin < 1 help(mfilename) varargin{1} = 1; end if length(varargin) < 2 if isequal(varargin{1}, 'demo') varargin{1} = 1; end if ischar(varargin{1}) varargin{1} = eval(varargin{1}); end end % Demonstration. if nargin < 2 & length(varargin{1}) == 1 n = varargin{1} a = fix(rand(n, n, n, n)*10) + 1; % Results are 1-by-1 if n == 1. b = fix(rand(n, n, n, n)*10) + 1; c = fix(rand(n, n, n, n)*10) + 1; if n < 2, a, b, c, end tic result = feval(mfilename, ... a, [2 1 4 3], b, [4 3 2 1], c, [3 4 1 2], [4 3 2], [1]); disp([' ## Elapsed time: ' num2str(toc) ' s.']) if n < 2 result should_be = a(:) .* b(:) .* c(:) end return end % Catalog the sizes of the input-variables. variables = varargin(1:2:end-2); dimensions = varargin(2:2:end-2); for i = 1:length(dimensions) dimensions{i} = double(dimensions{i}); end lengths = []; for j = 1:length(variables) d = dimensions{j}; % 1x1x1x1 appears as 1-by-1 only. while length(d) > length(lengths) lengths(end+1) = 1; end end for j = 1:length(variables) s = size(variables{j}); d = dimensions{j}; % 1x1x1x1 appears as 1-by-1 only. for i = 1:length(s) lengths(d(i)) = s(i); end end % Allocate the output-variable. dimensions_out = double(varargin{end-1}); for i = 1:length(dimensions_out) if dimensions_out(i) == 0 lengths_out(i) = 1; else lengths_out(i) = lengths(dimensions_out(i)); end end while length(lengths_out) < 2 lengths_out(end+1) = 1; end result = zeros(lengths_out); % Get the summation directions. summation_directions = varargin{end}; % Build the subscripts for "subsref". subscripts = cell(size(variables)); for k = 1:length(variables) d = dimensions{k}; subscripts{k} = cell(size(d)); for j = 1:length(d) subscripts{k}{j} = NaN; % Place-holder only. end end % Attach ":" to the summation directions. for k = 1:length(variables) d = dimensions{k}; for j = 1:length(d) for i = 1:length(summation_directions) if d(j) == summation_directions(i) subscripts{k}{j} = ':'; % Summation direction. end end end subs = subscripts{k}; end % Allocate the inner-product indexing structures. theStruct = []; for k = length(variables):-1:1 theStruct(k).type = '()'; theStruct(k).subs = subscripts{k}; end vars = cell(size(variables)); % Temporary variables. indices= cell(size(lengths_out)); count_out = prod(lengths_out); moduli = [1 cumprod(lengths_out(1:end-1))]; % The big loop. Using one-dimensional indexing is slightly % slower than using the multi-nested for-loops of the QSUM6 % routine, but it has the advantage that it imposes no limit % on the number of dimensions. for count = 1:count_out % remaining = (count_out - count + 1); % if count == 1 | rem(remaining, 5000) == 0 % disp([' ## Remaining: ' int2str(remaining)]) % end % Convert one-dimensional index to subscripts. % We do this brute-force, to save time. % [inds{:}] = ind2sub(lengths_out, count); % Slow. index = count-1; for i = length(moduli):-1:1 indices{i} = floor(index/moduli(i)) + 1; index = rem(index, moduli(i)); end % Fill-in the subscripts. About 50% of the % run-time is spent in this section alone. % It can be made faster by precomputing % the locations of indices that need to % change each time through the loop. for k = 1:length(variables) d = dimensions{k}; % 7 percent. s = theStruct(k).subs; % 7 percent. for j = 1:length(d) % 9 percent. switch ~ischar(s{j}) % 4 percent. case 0 otherwise for i = 1:length(dimensions_out) if d(j) == dimensions_out(i) % 11 percent. s{j} = indices{i}; % 5 percent. end end end end theStruct(k).subs = s; % 9 percent. end % Get the subscripted variables. for k = 1:length(variables) vars{k} = subsref(variables{k}, theStruct(k)); % 13 percent. end % Multiply and sum. result(indices{:}) = prodsum(vars{:}); % 4 percent. end if nargout > 0 theResult = result; else disp(result) end % ---------- prodsum ---------- % function theResult = prodsum(varargin) % prodsum -- Sum of scalar products. % prodsum(a, b, ...) returns sum(a(:) .* b(:) .* ...). % Copyright (C) 2001 Dr. Charles R. Denham, ZYDECO. % All Rights Reserved. % Disclosure without explicit written consent from the % copyright owner does not constitute publication. % Version of 14-Feb-2001 17:32:48. % Updated 21-Feb-2001 15:29:18. if nargin < 1, help(mfilename), return, end p = varargin{1}(:); for i = 2:length(varargin) p = p .* varargin{i}(:); % 4 percent. end p = sum(p); if nargout > 0 theResult = p; else disp(p) end