wolffd@0
|
1 function Coords = som_unit_coords(topol,lattice,shape)
|
wolffd@0
|
2
|
wolffd@0
|
3 %SOM_UNIT_COORDS Locations of units on the SOM grid.
|
wolffd@0
|
4 %
|
wolffd@0
|
5 % Co = som_unit_coords(topol, [lattice], [shape])
|
wolffd@0
|
6 %
|
wolffd@0
|
7 % Co = som_unit_coords(sMap);
|
wolffd@0
|
8 % Co = som_unit_coords(sMap.topol);
|
wolffd@0
|
9 % Co = som_unit_coords(msize, 'hexa', 'cyl');
|
wolffd@0
|
10 % Co = som_unit_coords([10 4 4], 'rect', 'toroid');
|
wolffd@0
|
11 %
|
wolffd@0
|
12 % Input and output arguments ([]'s are optional):
|
wolffd@0
|
13 % topol topology of the SOM grid
|
wolffd@0
|
14 % (struct) topology or map struct
|
wolffd@0
|
15 % (vector) the 'msize' field of topology struct
|
wolffd@0
|
16 % [lattice] (string) map lattice, 'rect' by default
|
wolffd@0
|
17 % [shape] (string) map shape, 'sheet' by default
|
wolffd@0
|
18 %
|
wolffd@0
|
19 % Co (matrix, size [munits k]) coordinates for each map unit
|
wolffd@0
|
20 %
|
wolffd@0
|
21 % For more help, try 'type som_unit_coords' or check out online documentation.
|
wolffd@0
|
22 % See also SOM_UNIT_DISTS, SOM_UNIT_NEIGHS.
|
wolffd@0
|
23
|
wolffd@0
|
24 %%%%%%%%%%%%% DETAILED DESCRIPTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
wolffd@0
|
25 %
|
wolffd@0
|
26 % som_unit_coords
|
wolffd@0
|
27 %
|
wolffd@0
|
28 % PURPOSE
|
wolffd@0
|
29 %
|
wolffd@0
|
30 % Returns map grid coordinates for the units of a Self-Organizing Map.
|
wolffd@0
|
31 %
|
wolffd@0
|
32 % SYNTAX
|
wolffd@0
|
33 %
|
wolffd@0
|
34 % Co = som_unit_coords(sTopol);
|
wolffd@0
|
35 % Co = som_unit_coords(sM.topol);
|
wolffd@0
|
36 % Co = som_unit_coords(msize);
|
wolffd@0
|
37 % Co = som_unit_coords(msize,'hexa');
|
wolffd@0
|
38 % Co = som_unit_coords(msize,'rect','toroid');
|
wolffd@0
|
39 %
|
wolffd@0
|
40 % DESCRIPTION
|
wolffd@0
|
41 %
|
wolffd@0
|
42 % Calculates the map grid coordinates of the units of a SOM based on
|
wolffd@0
|
43 % the given topology. The coordinates are such that they can be used to
|
wolffd@0
|
44 % position map units in space. In case of 'sheet' shape they can be
|
wolffd@0
|
45 % (and are) used to measure interunit distances.
|
wolffd@0
|
46 %
|
wolffd@0
|
47 % NOTE: for 'hexa' lattice, the x-coordinates of every other row are shifted
|
wolffd@0
|
48 % by +0.5, and the y-coordinates are multiplied by sqrt(0.75). This is done
|
wolffd@0
|
49 % to make distances of a unit to all its six neighbors equal. It is not
|
wolffd@0
|
50 % possible to use 'hexa' lattice with higher than 2-dimensional map grids.
|
wolffd@0
|
51 %
|
wolffd@0
|
52 % 'cyl' and 'toroid' shapes: the coordinates are initially determined as
|
wolffd@0
|
53 % in case of 'sheet' shape, but are then bended around the x- or the
|
wolffd@0
|
54 % x- and then y-axes to get the desired shape.
|
wolffd@0
|
55 %
|
wolffd@0
|
56 % POSSIBLE BUGS
|
wolffd@0
|
57 %
|
wolffd@0
|
58 % I don't know if the bending operation works ok for high-dimensional
|
wolffd@0
|
59 % map grids. Anyway, if anyone wants to make a 4-dimensional
|
wolffd@0
|
60 % toroid map, (s)he deserves it.
|
wolffd@0
|
61 %
|
wolffd@0
|
62 % REQUIRED INPUT ARGUMENTS
|
wolffd@0
|
63 %
|
wolffd@0
|
64 % topol Map grid dimensions.
|
wolffd@0
|
65 % (struct) topology struct or map struct, the topology
|
wolffd@0
|
66 % (msize, lattice, shape) of the map is taken from
|
wolffd@0
|
67 % the appropriate fields (see e.g. SOM_SET)
|
wolffd@0
|
68 % (vector) the vector which gives the size of the map grid
|
wolffd@0
|
69 % (msize-field of the topology struct).
|
wolffd@0
|
70 %
|
wolffd@0
|
71 % OPTIONAL INPUT ARGUMENTS
|
wolffd@0
|
72 %
|
wolffd@0
|
73 % lattice (string) The map lattice, either 'rect' or 'hexa'. Default
|
wolffd@0
|
74 % is 'rect'. 'hexa' can only be used with 1- or
|
wolffd@0
|
75 % 2-dimensional map grids.
|
wolffd@0
|
76 % shape (string) The map shape, either 'sheet', 'cyl' or 'toroid'.
|
wolffd@0
|
77 % Default is 'sheet'.
|
wolffd@0
|
78 %
|
wolffd@0
|
79 % OUTPUT ARGUMENTS
|
wolffd@0
|
80 %
|
wolffd@0
|
81 % Co (matrix) coordinates for each map units, size is [munits k]
|
wolffd@0
|
82 % where k is 2, or more if the map grid is higher
|
wolffd@0
|
83 % dimensional or the shape is 'cyl' or 'toroid'
|
wolffd@0
|
84 %
|
wolffd@0
|
85 % EXAMPLES
|
wolffd@0
|
86 %
|
wolffd@0
|
87 % Simplest case:
|
wolffd@0
|
88 % Co = som_unit_coords(sTopol);
|
wolffd@0
|
89 % Co = som_unit_coords(sMap.topol);
|
wolffd@0
|
90 % Co = som_unit_coords(msize);
|
wolffd@0
|
91 % Co = som_unit_coords([10 10]);
|
wolffd@0
|
92 %
|
wolffd@0
|
93 % If topology is given as vector, lattice is 'rect' and shape is 'sheet'
|
wolffd@0
|
94 % by default. To change these, you can use the optional arguments:
|
wolffd@0
|
95 % Co = som_unit_coords(msize, 'hexa', 'toroid');
|
wolffd@0
|
96 %
|
wolffd@0
|
97 % The coordinates can also be calculated for high-dimensional grids:
|
wolffd@0
|
98 % Co = som_unit_coords([4 4 4 4 4 4]);
|
wolffd@0
|
99 %
|
wolffd@0
|
100 % SEE ALSO
|
wolffd@0
|
101 %
|
wolffd@0
|
102 % som_unit_dists Calculate interunit distance along the map grid.
|
wolffd@0
|
103 % som_unit_neighs Calculate neighborhoods of map units.
|
wolffd@0
|
104
|
wolffd@0
|
105 % Copyright (c) 1997-2000 by the SOM toolbox programming team.
|
wolffd@0
|
106 % http://www.cis.hut.fi/projects/somtoolbox/
|
wolffd@0
|
107
|
wolffd@0
|
108 % Version 1.0beta juuso 110997
|
wolffd@0
|
109 % Version 2.0beta juuso 101199 070600
|
wolffd@0
|
110
|
wolffd@0
|
111 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
wolffd@0
|
112 %% Check arguments
|
wolffd@0
|
113
|
wolffd@0
|
114 error(nargchk(1, 3, nargin));
|
wolffd@0
|
115
|
wolffd@0
|
116 % default values
|
wolffd@0
|
117 sTopol = som_set('som_topol','lattice','rect');
|
wolffd@0
|
118
|
wolffd@0
|
119 % topol
|
wolffd@0
|
120 if isstruct(topol),
|
wolffd@0
|
121 switch topol.type,
|
wolffd@0
|
122 case 'som_map', sTopol = topol.topol;
|
wolffd@0
|
123 case 'som_topol', sTopol = topol;
|
wolffd@0
|
124 end
|
wolffd@0
|
125 elseif iscell(topol),
|
wolffd@0
|
126 for i=1:length(topol),
|
wolffd@0
|
127 if isnumeric(topol{i}), sTopol.msize = topol{i};
|
wolffd@0
|
128 elseif ischar(topol{i}),
|
wolffd@0
|
129 switch topol{i},
|
wolffd@0
|
130 case {'rect','hexa'}, sTopol.lattice = topol{i};
|
wolffd@0
|
131 case {'sheet','cyl','toroid'}, sTopol.shape = topol{i};
|
wolffd@0
|
132 end
|
wolffd@0
|
133 end
|
wolffd@0
|
134 end
|
wolffd@0
|
135 else
|
wolffd@0
|
136 sTopol.msize = topol;
|
wolffd@0
|
137 end
|
wolffd@0
|
138 if prod(sTopol.msize)==0, error('Map size is 0.'); end
|
wolffd@0
|
139
|
wolffd@0
|
140 % lattice
|
wolffd@0
|
141 if nargin>1 & ~isempty(lattice) & ~isnan(lattice), sTopol.lattice = lattice; end
|
wolffd@0
|
142
|
wolffd@0
|
143 % shape
|
wolffd@0
|
144 if nargin>2 & ~isempty(shape) & ~isnan(shape), sTopol.shape = shape; end
|
wolffd@0
|
145
|
wolffd@0
|
146 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
wolffd@0
|
147 %% Action
|
wolffd@0
|
148
|
wolffd@0
|
149 msize = sTopol.msize;
|
wolffd@0
|
150 lattice = sTopol.lattice;
|
wolffd@0
|
151 shape = sTopol.shape;
|
wolffd@0
|
152
|
wolffd@0
|
153 % init variables
|
wolffd@0
|
154
|
wolffd@0
|
155 if length(msize)==1, msize = [msize 1]; end
|
wolffd@0
|
156 munits = prod(msize);
|
wolffd@0
|
157 mdim = length(msize);
|
wolffd@0
|
158 Coords = zeros(munits,mdim);
|
wolffd@0
|
159
|
wolffd@0
|
160 % initial coordinates for each map unit ('rect' lattice, 'sheet' shape)
|
wolffd@0
|
161 k = [1 cumprod(msize(1:end-1))];
|
wolffd@0
|
162 inds = [0:(munits-1)]';
|
wolffd@0
|
163 for i = mdim:-1:1,
|
wolffd@0
|
164 Coords(:,i) = floor(inds/k(i)); % these are subscripts in matrix-notation
|
wolffd@0
|
165 inds = rem(inds,k(i));
|
wolffd@0
|
166 end
|
wolffd@0
|
167 % change subscripts to coordinates (move from (ij)-notation to (xy)-notation)
|
wolffd@0
|
168 Coords(:,[1 2]) = fliplr(Coords(:,[1 2]));
|
wolffd@0
|
169
|
wolffd@0
|
170 % 'hexa' lattice
|
wolffd@0
|
171 if strcmp(lattice,'hexa'),
|
wolffd@0
|
172 % check
|
wolffd@0
|
173 if mdim > 2,
|
wolffd@0
|
174 error('You can only use hexa lattice with 1- or 2-dimensional maps.');
|
wolffd@0
|
175 end
|
wolffd@0
|
176 % offset x-coordinates of every other row
|
wolffd@0
|
177 inds_for_row = (cumsum(ones(msize(2),1))-1)*msize(1);
|
wolffd@0
|
178 for i=2:2:msize(1),
|
wolffd@0
|
179 Coords(i+inds_for_row,1) = Coords(i+inds_for_row,1) + 0.5;
|
wolffd@0
|
180 end
|
wolffd@0
|
181 end
|
wolffd@0
|
182
|
wolffd@0
|
183 % shapes
|
wolffd@0
|
184 switch shape,
|
wolffd@0
|
185 case 'sheet',
|
wolffd@0
|
186 if strcmp(lattice,'hexa'),
|
wolffd@0
|
187 % this correction is made to make distances to all
|
wolffd@0
|
188 % neighboring units equal
|
wolffd@0
|
189 Coords(:,2) = Coords(:,2)*sqrt(0.75);
|
wolffd@0
|
190 end
|
wolffd@0
|
191
|
wolffd@0
|
192 case 'cyl',
|
wolffd@0
|
193 % to make cylinder the coordinates must lie in 3D space, at least
|
wolffd@0
|
194 if mdim<3, Coords = [Coords ones(munits,1)]; mdim = 3; end
|
wolffd@0
|
195
|
wolffd@0
|
196 % Bend the coordinates to a circle in the plane formed by x- and
|
wolffd@0
|
197 % and z-axis. Notice that the angle to which the last coordinates
|
wolffd@0
|
198 % are bended is _not_ 360 degrees, because that would be equal to
|
wolffd@0
|
199 % the angle of the first coordinates (0 degrees).
|
wolffd@0
|
200
|
wolffd@0
|
201 Coords(:,1) = Coords(:,1)/max(Coords(:,1));
|
wolffd@0
|
202 Coords(:,1) = 2*pi * Coords(:,1) * msize(2)/(msize(2)+1);
|
wolffd@0
|
203 Coords(:,[1 3]) = [cos(Coords(:,1)) sin(Coords(:,1))];
|
wolffd@0
|
204
|
wolffd@0
|
205 case 'toroid',
|
wolffd@0
|
206
|
wolffd@0
|
207 % NOTE: if lattice is 'hexa', the msize(1) should be even, otherwise
|
wolffd@0
|
208 % the bending the upper and lower edges of the map do not match
|
wolffd@0
|
209 % to each other
|
wolffd@0
|
210 if strcmp(lattice,'hexa') & rem(msize(1),2)==1,
|
wolffd@0
|
211 warning('Map size along y-coordinate is not even.');
|
wolffd@0
|
212 end
|
wolffd@0
|
213
|
wolffd@0
|
214 % to make toroid the coordinates must lie in 3D space, at least
|
wolffd@0
|
215 if mdim<3, Coords = [Coords ones(munits,1)]; mdim = 3; end
|
wolffd@0
|
216
|
wolffd@0
|
217 % First bend the coordinates to a circle in the plane formed
|
wolffd@0
|
218 % by x- and z-axis. Then bend in the plane formed by y- and
|
wolffd@0
|
219 % z-axis. (See also the notes in 'cyl').
|
wolffd@0
|
220
|
wolffd@0
|
221 Coords(:,1) = Coords(:,1)/max(Coords(:,1));
|
wolffd@0
|
222 Coords(:,1) = 2*pi * Coords(:,1) * msize(2)/(msize(2)+1);
|
wolffd@0
|
223 Coords(:,[1 3]) = [cos(Coords(:,1)) sin(Coords(:,1))];
|
wolffd@0
|
224
|
wolffd@0
|
225 Coords(:,2) = Coords(:,2)/max(Coords(:,2));
|
wolffd@0
|
226 Coords(:,2) = 2*pi * Coords(:,2) * msize(1)/(msize(1)+1);
|
wolffd@0
|
227 Coords(:,3) = Coords(:,3) - min(Coords(:,3)) + 1;
|
wolffd@0
|
228 Coords(:,[2 3]) = Coords(:,[3 3]) .* [cos(Coords(:,2)) sin(Coords(:,2))];
|
wolffd@0
|
229
|
wolffd@0
|
230 end
|
wolffd@0
|
231
|
wolffd@0
|
232 return;
|
wolffd@0
|
233
|
wolffd@0
|
234 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
wolffd@0
|
235 %% subfunctions
|
wolffd@0
|
236
|
wolffd@0
|
237 function C = bend(cx,cy,angle,xishexa)
|
wolffd@0
|
238
|
wolffd@0
|
239 dx = max(cx) - min(cx);
|
wolffd@0
|
240 if dx ~= 0,
|
wolffd@0
|
241 % in case of hexagonal lattice it must be taken into account that
|
wolffd@0
|
242 % coordinates of every second row are +0.5 off to the right
|
wolffd@0
|
243 if xishexa, dx = dx-0.5; end
|
wolffd@0
|
244 cx = angle*(cx - min(cx))/dx;
|
wolffd@0
|
245 end
|
wolffd@0
|
246 C(:,1) = (cy - min(cy)+1) .* cos(cx);
|
wolffd@0
|
247 C(:,2) = (cy - min(cy)+1) .* sin(cx);
|
wolffd@0
|
248
|
wolffd@0
|
249 % end of bend
|
wolffd@0
|
250
|
wolffd@0
|
251 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
wolffd@0
|
252
|