source: cpp/frams/canvas/nn_smart_layout.cpp @ 140

Last change on this file since 140 was 137, checked in by sz, 11 years ago

added (c) headers

  • Property svn:eol-style set to native
File size: 8.5 KB
Line 
1// This file is a part of the Framsticks GDK.
2// Copyright (C) 2002-2014  Maciej Komosinski and Szymon Ulatowski.  See LICENSE.txt for details.
3// Refer to http://www.framsticks.com/ for further information.
4
5#include "nn_layout.h"
6#include <vector>
7#include "common/nonstd_stl.h"
8
9#define DB(x)
10
11#if DB(1)+0
12#include <assert.h>
13#endif
14
15class block;
16
17/** Information about a single element (neuron). There are N einfo objects in an array called "einfo" */
18struct einfo
19{
20        /** Element's owner */
21        class block *block;
22
23        /** Integer coordinates (neurons are simply placed in a grid, (x,y) is a grid cell) */
24        int x, y;
25};
26
27/** Array[0..N-1] - one einfo for each neuron */
28static struct einfo* einfo;
29
30/** Array[0..N-1] - initially each neuron resides in its own block. The algorithm merges blocks until one single block is created */
31static block **blocks;
32
33/** N = number of neurons */
34static int N;
35
36/** Provides neuron connections information and receives the layout result */
37static NNLayoutState *nn;
38
39static char *JEDEN = (char*)"1";
40
41/** Block is a group of neurons.
42        After "blocking" some neurons, their relative location will not change. */
43class block
44{
45public:
46
47        /** Block's id is its index in the "blocks" array */
48        int id;
49
50        /** Block members (neurons), or actually neuron indexes (0..N-1 ints)  */
51        std::vector<int> elementy;
52
53        /** Block's bounding box (a rectangle containing all elemens)
54                w=maxx-minx+1; h=maxy-miny+1;
55                */
56        int w, h;
57        int minx, miny, maxx, maxy;
58
59        /** 2d array, w*h cells, indicating if a given (x,y) location is taken.
60                This speeds up checking if neurons from two blocks overlap.
61                */
62        char *map;
63
64        /** Creating an initial block consisting of a single neuron */
65        block(int nr) : id(nr), w(1), h(1), minx(0), miny(0), maxx(0), maxy(0)
66        {
67                DB(printf("new block(%d)\n", nr));
68                dodajelement(nr, 0, 0);
69                blocks[id] = this;
70                map = JEDEN;
71        }
72
73        ~block()
74        {
75                DB(printf("~ block(%d)\n", id));
76                blocks[id] = 0;
77                zwolnijmape();
78        }
79
80        void zwolnijmape(void)
81        {
82                if (map)
83                {
84                        if (map != JEDEN) free(map);
85                        map = 0;
86                }
87        }
88
89        void potrzebnamapa(void)
90        {
91                if (map) return;
92                odtworzmape();
93        }
94
95        void odtworzmape(void)
96        {
97                zwolnijmape();
98                w = maxx - minx + 1;
99                h = maxy - miny + 1;
100                map = (char*)calloc(1, w*h);
101                int i, e;
102                DB(printf("mapa bloku #%ld\n", id));
103                for (i = 0; i < elementy.size(); i++)
104                {
105                        e = elementy[i];
106                        map[w*(einfo[e].y - miny) + (einfo[e].x - minx)] = 1;
107                }
108                DB(for (i = 0; i < h; i++){ for (e = 0; e < w; e++)printf("%c", map[w*i + e] ? '*' : '.'); printf("\n"); })
109        }
110
111        /** Add a neuron to a block at location(x,y) */
112        void dodajelement(int nr, int x, int y)
113        {
114                elementy.push_back(nr);
115                einfo[nr].x = x;
116                einfo[nr].y = y;
117                einfo[nr].block = this;
118        }
119};
120
121static int moznadolaczyc(block *b, block *b2, int dx, int dy)
122{
123        /* Check if block b2 can be merged with b with b2 shifted by (dx,dy) so no overlap occurs.
124           All coordinates are relative to b->minx/miny
125           */
126        int x1, y1, x2, y2; // union rectangle
127        x1 = max(0, b2->minx - b->minx + dx);
128        x2 = min(b->maxx - b->minx, -b->minx + dx + b2->maxx);
129        if (x1 > x2) return 1;
130        y1 = max(0, b2->miny - b->miny + dy);
131        y2 = min(b->maxy - b->miny, -b->miny + dy + b2->maxy);
132        if (y1 > y2) return 1;
133        int x, y;
134        dx += b2->minx - b->minx; //dx,dy relative to minx,miny
135        dy += b2->miny - b->miny;
136        b->potrzebnamapa();
137        b2->potrzebnamapa();
138        for (y = y1; y <= y2; y++)
139        {
140                for (x = x1; x <= x2; x++)
141                        if (b->map[b->w*y + x] && b2->map[b2->w*(y - dy) + (x - dx)]) return 0;
142        }
143        return 1;
144}
145
146/** Merge b2 with b shifting b2 by (dx,dy) - adds all b2's neurons to b and deletes b2 */
147static int dolaczblock(block *b, block *b2, int dx, int dy)
148{ // return 1 if successful
149        if (!moznadolaczyc(b, b2, dx, dy)) return 0; // merging causes no collision
150        DB(printf("#%ld(%ld,%ld,%ld,%ld) + #%ld(%ld,%ld,%ld,%ld)<%ld,%ld>", b->id, b->minx, b->miny, b->maxx, b->maxy, b2->id, b2->minx, b2->miny, b2->maxx, b2->maxy, dx, dy));
151
152        b->zwolnijmape();
153        int i, e;
154        for (i = 0; i < b2->elementy.size(); i++)
155        {
156                e = b2->elementy[i];
157                b->dodajelement(e, einfo[e].x + dx, einfo[e].y + dy);
158        }
159        b->minx = min(b->minx, dx + b2->minx);
160        b->miny = min(b->miny, dy + b2->miny);
161        b->maxx = max(b->maxx, dx + b2->maxx);
162        b->maxy = max(b->maxy, dy + b2->maxy);
163
164        DB(printf(" -> (%ld,%ld,%ld,%ld)\n", b->minx, b->miny, b->maxx, b->maxy));
165
166        DB(printf(" ...#%ld...(%ld)...", b->id, b->elementy.size()));
167        for (i = 0; i < b->elementy.size(); i++)
168        {
169                e = b->elementy[i];
170                DB(assert(einfo[e].x >= b->minx);)
171                        DB(assert(einfo[e].x <= b->maxx);)
172                        DB(assert(einfo[e].y >= b->miny);)
173                        DB(assert(einfo[e].y <= b->maxy);)
174                        DB(printf("(%ld)%ld,%ld ", e, einfo[e].x, einfo[e].y));
175        }
176
177        DB(printf("\n"));
178
179        delete b2;
180        return 1;
181}
182
183/** e2 neuron will be connected to e neuron's input:
184        - e and e2 belong to different blocks: shift/merge the blocks so e2 is to the left of e
185        - same block: nothing can be done
186        */
187static void polaczjakowejscie(int e, int e2)
188{
189        block *b, *b2;
190        b = einfo[e].block;
191        if (!einfo[e2].block) new block(e2);
192        b2 = einfo[e2].block;
193        if (b == b2)
194        {
195                DB(printf("--- b==b2 -> cancel\n"));
196                return;
197        }
198        int dx, dy;
199        dx = einfo[e].x - einfo[e2].x;
200        dy = einfo[e].y - einfo[e2].y;
201        DB(printf("  elem.%ld (%ld,%ld@%ld) + elem.%ld (%ld,%ld@%ld)...\n", e, einfo[e].x, einfo[e].y, b->id, e2, einfo[e2].x, einfo[e2].y, b2->id));
202        if (dolaczblock(b, b2, dx - 1, dy)) return;
203        int proba; // retry - increasing the y offset (keeps x offset at -1)
204        for (proba = 1;; proba++)
205        {
206                if (dolaczblock(b, b2, dx - 1, dy - proba)) return;
207                if (dolaczblock(b, b2, dx - 1, dy + proba)) return;
208        }
209}
210
211/** Retrieve the information about neuron e inputs and place the input neurons accordingly
212        unless they are already processed
213        */
214static void ustawelement(int e)
215{
216        if (einfo[e].block)
217        {
218                DB(printf("block#%ld exists\n", e));
219                return;
220        }
221        new block(e);
222        int we;
223        int n = nn->GetInputs(e);
224        for (we = 0; we < n; we++)
225        {
226                int e2 = nn->GetLink(e, we);
227                if (e2 < 0) continue;
228                if (e == e2) continue;
229                ustawelement(e2);
230                polaczjakowejscie(e, e2);
231        }
232}
233
234/**
235   The algorithm:
236   1. Phase one
237    - for each neuron, place its input neurons to the left
238      (at relative location (-1,dy), where dy is any number, ideally 0)
239    - the neuron's location in a block is never changed after the initial assignment
240    - which means that any further connections within a given block are ignored (neurons are already fixed in place)
241    - foreign block connections cause block merges, shifting the blocks so the (-1,dy) condition is met
242      (which also affects all neurons in the other block, since their relative positions are fixed)
243    - the final result is a set of blocks corresponding to the "islands" in the neural network
244   2. Phase two
245    - "islands" are merged into one final block. Any relative offsets can be used, as their neurons
246      are not connected anyway. Here a simple method is used: placing the islands in a vertical stack.
247 */
248void smartlayout(NNLayoutState *nnlayout)
249{
250        DB(printf("\n >>>>>>>>> smartlayout <<<<<<<<<<<\n"));
251        nn = nnlayout;
252        N = nn->GetElements();
253        einfo = (struct einfo*)calloc(N, sizeof(struct einfo));
254        blocks = (class block**)calloc(N, sizeof(class block*));
255
256        int el;
257        for (el = 0; el < N; el++) ustawelement(el);
258
259        DB(printf(" - - merging blocks - -\n"));
260
261        block *first;
262        for (el = 0; el < N; el++) if (blocks[el]) { first = blocks[el]; el++; break; }
263        while (el<N)
264        {
265                if ((first->maxx - first->minx)>(first->maxy - first->miny))
266                {
267                        int y = first->maxy + 2;
268                        int x = first->minx;
269                        int ex = first->maxx;
270                        while (el<N)
271                        {
272                                if (blocks[el])
273                                {
274                                        int dx = blocks[el]->maxx - blocks[el]->minx + 2;
275                                        dolaczblock(first, blocks[el], x - blocks[el]->minx, y - blocks[el]->miny);
276                                        x += dx;
277                                        if (x>ex) break;
278                                }
279                                el++;
280                        }
281                }
282                else
283                {
284                        int x = first->maxx + 2;
285                        int y = first->miny;
286                        int ey = first->maxy;
287                        while (el<N)
288                        {
289                                if (blocks[el])
290                                {
291                                        int dy = blocks[el]->maxy - blocks[el]->miny + 2;
292                                        dolaczblock(first, blocks[el], x - blocks[el]->minx, y - blocks[el]->miny);
293                                        y += dy;
294                                        if (y>ey) break;
295                                }
296                                el++;
297                        }
298                }
299        }
300        /*
301        for (;el<N;el++)
302        if (blocks[el])
303        dolaczblock(first,blocks[el],first->minx-blocks[el]->minx,first->maxy-blocks[el]->miny+1);
304        */
305        if (first) // at this stage we have a single block containing all neurons
306        {
307                DB(printf(" - - setting coordinates - -\n"));
308                int i;
309                DB(first->odtworzmape());
310                for (i = 0; i < first->elementy.size(); i++)
311                {
312                        el = first->elementy[i];
313                        nn->SetXYWH(el, einfo[el].x * 70, -einfo[el].y * 70, 50, 50);
314                }
315                delete first;
316        }
317
318        free(blocks);
319        free(einfo);
320        DB(printf("--------------------------------\n\n"));
321}
Note: See TracBrowser for help on using the repository browser.