Mercurial > hg > ishara
comparison general/funutils/zipmap.m @ 4:e44f49929e56
Adding reorganised general toolbox, now in several subdirectories.
author | samer |
---|---|
date | Sat, 12 Jan 2013 19:21:22 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
3:3f77126f7b5f | 4:e44f49929e56 |
---|---|
1 function varargout=zipmap(spec,f,varargin) | |
2 % zipmap - Map and zip function over multidimensional arrays | |
3 % | |
4 % ZIPMAP allows a certain simple functions to be applied to multidimensional | |
5 % array arguments, manipulating the dimensions to combine elementwise and | |
6 % outer-product-like behaviour. (The name comes from functional programming | |
7 % terminology: functions/operators like .* and + which apply elementwise to | |
8 % vector arguments are said to zip their arguments, while outer products are | |
9 % like mapping a function over a list, or a list of functions over another list.) | |
10 % | |
11 % Let f be a vectorised function which knows how to zip array arguments. Eg, | |
12 % f(x,y)=x+y. If x and y are vectors, they must be the same size, and | |
13 % the function is applied to corresponding pairs of elements, resulting in | |
14 % a vector of the same size. With ZIPMAP, you can build functions, that, eg, | |
15 % add EVERY pair of elements (not just corresponding pairs) resulting in a | |
16 % 2D array: zipmap(0,inline('x+y','x','y'),[1:4]',[1:6]') will do this | |
17 % | |
18 % The domain of a multidimensional array can be described a list containing the | |
19 % size of each dimension, as returned by the SIZE function (or SIZE1 which strips | |
20 % trailing 1s). The arguments to f must have a certain domain structure: | |
21 % | |
22 % dom arg1 = [p1 zipdom] | |
23 % dom arg2 = [p2 zipdom] | |
24 % : | |
25 % | |
26 % where p1, p2, etc are the domains of the elementary function of which f | |
27 % is a vectorisation, eg the elementary domain of .* can be scalars and | |
28 % hence p1=p2=[], but the two elementary domains of CROSS are both [3], because it | |
29 % it operates on pairs of 3-D vectors. The zipdom part must be the same for | |
30 % all arguments, otherwise, we cannot extract matching argument tuples. | |
31 % If this is the case, the f must return a result: | |
32 % | |
33 % dom f(...) : [rdom zipdom] | |
34 % | |
35 % where rdom is the domain of the elementary function, eg [3] for the vector | |
36 % valued CROSS product, but [] for the scalar valued DOT product. Note that | |
37 % these domains can be vectors of any length from zero up, eg the determinant | |
38 % of a square matrix has a domain of [n n]. | |
39 % | |
40 % ZIPDIM allows the domains of the arguments and result to be generalised as follows: | |
41 % | |
42 % dom arg1 : [p1 m1 zipdom] | |
43 % dom arg2 : [p2 m2 zipdom] | |
44 % : | |
45 % | |
46 % dom zipdom(...) : [rdom m1 m2 ... zipdom ] | |
47 % | |
48 % All the mapped dimensions m1, m2 etc go into a sort of outer product where | |
49 % every combination of values is evaluated. In addition, the order of the | |
50 % m1, m2 etc can be permuted in the result at essentially no extra computational cost. | |
51 % | |
52 % Usage: r=zipmap(spec,f,<varargs>) | |
53 % | |
54 % spec defines how the domain of each of the arguments is to be interpreted, ie | |
55 % how is is split into elementary, mappable, and zippable domains. | |
56 % | |
57 % If spec is a number z, then the last z dimensions of each argument are marked for | |
58 % zipping, while the rest are for mapping. The elementary domains are assumed to | |
59 % be scalar and hence p1, p2 etc =[]. So, zipmap(0,...) does the complete outer | |
60 % product type of evaluation. | |
61 % | |
62 % If spec is structure, the field 'zipdims' determines how many dimensions are zipped. | |
63 % The field 'pdims' is a the vector [length(p1) length(p2) ..], ie the dimensionality | |
64 % of each elementary domain: 0 for scalars, 1 for vectors, 2 for matrices etc. | |
65 % The optional field 'mapord' determines the reordering of the mapped domains, eg | |
66 % if mapord=[2 1 3], then the result domain is [m2 m1 m3] instead of [m1 m2 m3]. | |
67 % | |
68 % If spec is a cell array, it is interpreted as {zipdims pdims mapord}. | |
69 % | |
70 % Extra options | |
71 % | |
72 % If spec is a structure, it can contain further options: if spec.shift=1, | |
73 % then the domains of all arguments are pre-shifted to remove singleton | |
74 % dimensions before anything else is done. Amongst other effects, this means | |
75 % that row vectors can be used in place of column vectors. | |
76 | |
77 | |
78 % unpack args from spec | |
79 if iscell(spec) | |
80 [zipdims,pdims,mapord]=getargs(spec,{0 [] []}); | |
81 elseif isstruct(spec) | |
82 zipdims=getparam(spec,'zipdims',0); | |
83 pdims=getparam(spec,'pdims',[]); | |
84 mapord=getparam(spec,'mapord',[]); | |
85 if getparam(spec,'shift',0), | |
86 varargin=cellmap(@shiftdim,varargin); | |
87 end | |
88 elseif isscalar(spec) | |
89 zipdims=spec; pdims=[]; mapord=[]; | |
90 end | |
91 | |
92 | |
93 argdoms=cellmap(@size1,varargin); | |
94 if isempty(pdims), pdims=zeros(size(varargin)); end | |
95 for i=1:length(varargin) | |
96 [pdoms{i},mapdoms{i},zipdoms{i}]=split(argdoms{i},pdims(i),zipdims); | |
97 end | |
98 | |
99 | |
100 % check all zipdoms match and use first | |
101 zipdom=zipdoms{1}; | |
102 for i=2:length(zipdoms) | |
103 if ~all(zipdoms{i}==zipdom), error('Zip domains do not match'); end | |
104 end | |
105 | |
106 | |
107 if isempty(mapord), mapord=1:length(varargin); end | |
108 outerdom=cat(2,mapdoms{mapord}); | |
109 outer1=cellmap(@to1s,mapdoms); | |
110 for i=1:length(varargin) | |
111 % need to reshape each arg and then repmat up to correct size | |
112 newsizes=outer1; newsizes{i}=mapdoms{i}; | |
113 newsize=[pdoms{i} cat(2,newsizes{mapord}) zipdom]; | |
114 varargin{i}=reshape(varargin{i},tosize([pdoms{i} cat(2,newsizes{mapord}) zipdom])); | |
115 newsizes=mapdoms; newsizes{i}=outer1{i}; | |
116 newsize=[to1s(pdoms{i}) cat(2,newsizes{mapord}) to1s(zipdom)]; | |
117 varargin{i}=repmat(varargin{i},tosize(newsize)); | |
118 % size(varargin{i}) | |
119 end | |
120 | |
121 [varargout{1:max(1,nargout)}]=feval(f,varargin{:}); | |
122 | |
123 function [a,b,c]=split(x,n,m) | |
124 a=x(1:n); | |
125 b=x(n+1:end-m); | |
126 c=x(end-m+1:end); | |
127 | |
128 % getargs - get values of optional parameters with defaults | |
129 % | |
130 % getargs :: {I:[N]->(A(I) | nil)}, {I:[N]->A(I)} -> A(1), ..., A(N). | |
131 | |
132 function varargout=getargs(args,defs) | |
133 | |
134 nout=max(length(args),length(defs)); | |
135 varargout=defs; | |
136 for i=1:length(args) | |
137 if ~isempty(args{i}), varargout(i)=args(i); end | |
138 end | |
139 |