Completed
Push — master ( a74470...c8bc69 )
by Andrei
58s
created

som.show_winner_matrix()   B

Complexity

Conditions 5

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
dl 0
loc 27
rs 8.0894
c 0
b 0
f 0
1
"""!
2
3
@brief Neural Network: Self-Organized Feature Map
4
@details Based on article description:
5
         - T.Kohonen. The Self-Organizing Map. 1990.
6
         - T.Kohonen, E.Oja, O.Simula, A.Visa, J.Kangas. Engineering Applications of the Self-Organizing Map. 1996.
7
         - A.Novikov, E.Benderskaya. SYNC-SOM Double-layer Oscillatory Network for Cluster Analysis. 2014.
8
9
@authors Andrei Novikov ([email protected])
10
@date 2014-2018
11
@copyright GNU Public License
12
13
@cond GNU_PUBLIC_LICENSE
14
    PyClustering is free software: you can redistribute it and/or modify
15
    it under the terms of the GNU General Public License as published by
16
    the Free Software Foundation, either version 3 of the License, or
17
    (at your option) any later version.
18
    
19
    PyClustering is distributed in the hope that it will be useful,
20
    but WITHOUT ANY WARRANTY; without even the implied warranty of
21
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
    GNU General Public License for more details.
23
    
24
    You should have received a copy of the GNU General Public License
25
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
26
@endcond
27
28
"""
29
30
31
import math;
32
33
import random;
34
35
import matplotlib.pyplot as plt;
0 ignored issues
show
Configuration introduced by
The import matplotlib.pyplot could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
36
37
import pyclustering.core.som_wrapper as wrapper;
38
39
from pyclustering.core.wrapper import ccore_library;
40
41
from pyclustering.utils import euclidean_distance_sqrt;
42
from pyclustering.utils.dimension import dimension_info;
43
44
from enum import IntEnum;
45
46
47
class type_conn(IntEnum):
48
    """!
49
    @brief Enumeration of connection types for SOM.
50
    
51
    @see som
52
    
53
    """
54
    
55
    ## Grid type of connections when each oscillator has connections with left, upper, right, lower neighbors.
56
    grid_four = 0;
57
    
58
    ## Grid type of connections when each oscillator has connections with left, upper-left, upper, upper-right, right, right-lower, lower, lower-left neighbors.
59
    grid_eight = 1;
60
    
61
    ## Grid type of connections when each oscillator has connections with left, upper-left, upper-right, right, right-lower, lower-left neighbors.
62
    honeycomb = 2;
63
    
64
    ## Grid type of connections when existance of each connection is defined by the SOM rule on each step of simulation.
65
    func_neighbor = 3;
66
    
67
    
68
class type_init(IntEnum):
69
    """!
70
    @brief Enumeration of initialization types for SOM.
71
    
72
    @see som
73
    
74
    """
75
    
76
    ## Weights are randomly distributed using Gaussian distribution (0, 1).
77
    random = 0;
78
    
79
    ## Weights are randomly distributed using Gaussian distribution (input data centroid, 1).
80
    random_centroid = 1;
81
    
82
    ## Weights are randomly distrbiuted using Gaussian distribution (input data centroid, surface of input data).
83
    random_surface = 2;
84
    
85
    ## Weights are distributed as a uniform grid that covers whole surface of the input data.
86
    uniform_grid = 3;
87
88
89
class som_parameters:
90
    """!
91
    @brief Represents SOM parameters.
92
    
93
    """
94
    
95
    def __init__(self):
96
        """!
97
        @brief Constructor container of SOM parameters.
98
        
99
        """
100
        
101
        ## Type of initialization of initial neuron weights (random, random in center of the input data, random distributed in data, ditributed in line with uniform grid).
102
        self.init_type = type_init.uniform_grid; 
103
        
104
        ## Initial radius (if not specified then will be calculated by SOM). 
105
        self.init_radius = None;
106
        
107
        ## Rate of learning.   
108
        self.init_learn_rate = 0.1;
109
        
110
        ## Condition when learining process should be stoped. It's used when autostop mode is used. 
111
        self.adaptation_threshold = 0.001; 
112
113
114
class som:
115
    """!
116
    @brief Represents self-organized feature map (SOM).
117
    @details The self-organizing feature map (SOM) method is a powerful tool for the visualization of
118
             of high-dimensional data. It converts complex, nonlinear statistical relationships between
119
             high-dimensional data into simple geometric relationships on a low-dimensional display.
120
    
121
    @details CCORE option can be used to use the pyclustering core - C/C++ shared library for processing that significantly increases performance.
122
    
123
    Example:
124
    @code
125
        # sample for training
126
        sample_train = read_sample(file_train_sample);
127
        
128
        # create self-organized feature map with size 5x5
129
        network = som(5, 5, sample_train, 100);
130
        
131
        # train network
132
        network.train();
133
        
134
        # simulate using another sample
135
        sample = read_sample(file_sample);
136
        index_winner = network.simulate(sample);
137
        
138
        # check what it is.
139
        index_similar_objects = network.capture_objects[index_winner];
140
        
141
        # result visualization:
142
        # show distance matrix (U-matrix).
143
        network.show_distance_matrix();
144
        
145
        # show density matrix (P-matrix).
146
        network.show_density_matrix();
147
        
148
        # show winner matrix.
149
        network.show_winner_matrix();
150
        
151
        # show self-organized map.
152
        network.show_network();
153
    @endcode
154
    
155
    There is a visualization of 'Target' sample that was done by the self-organized feature map:
156
    @image html target_som_processing.png
157
    
158
    """
159
160
161
    @property
162
    def size(self):
163
        """!
164
        @return (uint) Size of self-organized map (number of neurons).
165
        
166
        """
167
        
168
        if (self.__ccore_som_pointer is not None):
169
            self._size = wrapper.som_get_size(self.__ccore_som_pointer);
170
            
171
        return self._size;
172
    
173
    @property
174
    def weights(self):
175
        """!
176
        @return (list) Weights of each neuron.
177
        
178
        """
179
        
180
        if (self.__ccore_som_pointer is not None):
181
            self._weights = wrapper.som_get_weights(self.__ccore_som_pointer);
182
        
183
        return self._weights;
184
    
185
    @property
186
    def awards(self):
187
        """!
188
        @return (list) Numbers of captured objects by each neuron.
189
        
190
        """
191
        
192
        if (self.__ccore_som_pointer is not None):
193
            self._award = wrapper.som_get_awards(self.__ccore_som_pointer);
194
        
195
        return self._award;
196
    
197
    @property
198
    def capture_objects(self):
199
        """!
200
        @return (list) Indexes of captured objects by each neuron.
201
        
202
        """
203
        
204
        if (self.__ccore_som_pointer is not None):
205
            self._capture_objects = wrapper.som_get_capture_objects(self.__ccore_som_pointer);
206
        
207
        return self._capture_objects;
208
    
209
    
210
    def __init__(self, rows, cols, conn_type = type_conn.grid_eight, parameters = None, ccore = True):
211
        """!
212
        @brief Constructor of self-organized map.
213
        
214
        @param[in] rows (uint): Number of neurons in the column (number of rows).
215
        @param[in] cols (uint): Number of neurons in the row (number of columns).
216
        @param[in] conn_type (type_conn): Type of connection between oscillators in the network (grid four, grid eight, honeycomb, function neighbour).
217
        @param[in] parameters (som_parameters): Other specific parameters.
218
        @param[in] ccore (bool): If True simulation is performed by CCORE library (C++ implementation of pyclustering).
219
        
220
        """
221
        
222
        # some of these parameters are required despite core implementation, for example, for network demonstration.
223
        self._cols = cols;
224
        
225
        self._rows = rows;
226
        
227
        self._size = cols * rows;
228
        
229
        self._conn_type = conn_type;
230
        
231
        self._data = None;
232
        
233
        self._neighbors = None;
234
        
235
        self._local_radius = 0.0;
236
        
237
        self._learn_rate = 0.0;
238
        
239
        self.__ccore_som_pointer = None;
240
        
241
        if (parameters is not None):
242
            self._params = parameters;
243
        else:
244
            self._params = som_parameters();
245
            
246
        if (self._params.init_radius is None):
247
            self._params.init_radius = self.__initialize_initial_radius(rows, cols);
248
        
249
        if ( (ccore is True) and ccore_library.workable() ):
250
            self.__ccore_som_pointer = wrapper.som_create(rows, cols, conn_type, self._params);
251
            
252
        else:
253
            # location
254
            self._location = self.__initialize_locations(rows, cols);
255
            
256
            # default weights
257
            self._weights = [ [0.0] ] * self._size;
258
            
259
            # awards
260
            self._award = [0] * self._size;
261
            
262
            # captured objects
263
            self._capture_objects = [ [] for i in range(self._size) ];
264
            
265
            # distances
266
            self._sqrt_distances = self.__initialize_distances(self._size, self._location);
267
        
268
            # connections
269
            if (conn_type != type_conn.func_neighbor):
270
                self._create_connections(conn_type);
271
272
273
    def __del__(self):
274
        """!
275
        @brief Destructor of the self-organized feature map.
276
        
277
        """
278
        
279
        if (self.__ccore_som_pointer is not None):
280
            wrapper.som_destroy(self.__ccore_som_pointer);
281
    
282
    
283
    def __len__(self):
284
        """!
285
        @return (uint) Size of self-organized map (number of neurons).
286
        
287
        """
288
        
289
        return self._size;
290
    
291
    
292
    def __initialize_initial_radius(self, rows, cols):
293
        """!
294
        @brief Initialize initial radius using map sizes.
295
        
296
        @param[in] rows (uint): Number of neurons in the column (number of rows).
297
        @param[in] cols (uint): Number of neurons in the row (number of columns).
298
        
299
        @return (list) Value of initial radius.
300
        
301
        """
302
        
303
        if ((cols + rows) / 4.0 > 1.0): 
304
            return 2.0;
305
        
306
        elif ( (cols > 1) and (rows > 1) ): 
307
            return 1.5;
308
        
309
        else: 
310
            return 1.0;
311
    
312
    
313
    def __initialize_locations(self, rows, cols):
314
        """!
315
        @brief Initialize locations (coordinates in SOM grid) of each neurons in the map.
316
        
317
        @param[in] rows (uint): Number of neurons in the column (number of rows).
318
        @param[in] cols (uint): Number of neurons in the row (number of columns).
319
        
320
        @return (list) List of coordinates of each neuron in map.
321
        
322
        """
323
        
324
        location = list();
325
        for i in range(rows):
326
            for j in range(cols):
327
                location.append([float(i), float(j)]);
328
        
329
        return location;
330
    
331
    
332
    def __initialize_distances(self, size, location):
333
        """!
334
        @brief Initialize distance matrix in SOM grid.
335
        
336
        @param[in] size (uint): Amount of neurons in the network.
337
        @param[in] location (list): List of coordinates of each neuron in the network.
338
        
339
        @return (list) Distance matrix between neurons in the network.
340
        
341
        """
342
        sqrt_distances = [ [ [] for i in range(size) ] for j in range(size) ];
343
        for i in range(size):
344
            for j in range(i, size, 1):
345
                dist = euclidean_distance_sqrt(location[i], location[j]);
346
                sqrt_distances[i][j] = dist;
347
                sqrt_distances[j][i] = dist;
348
        
349
        return sqrt_distances;
350
    
351
    
352
    def _create_initial_weights(self, init_type):
353
        """!
354
        @brief Creates initial weights for neurons in line with the specified initialization.
355
        
356
        @param[in] init_type (type_init): Type of initialization of initial neuron weights (random, random in center of the input data, random distributed in data, ditributed in line with uniform grid).
357
        
358
        """
359
        
360
        dim_info = dimension_info(self._data);
361
        
362
        step_x = dim_info.get_center()[0];
363
        if (self._rows > 1): step_x = dim_info.get_width()[0] / (self._rows - 1);
364
        
365
        step_y = 0.0;
366
        if (dim_info.get_dimensions() > 1):
367
            step_y = dim_info.get_center()[1];
368
            if (self._cols > 1): step_y = dim_info.get_width()[1] / (self._cols - 1); 
369
                      
370
        # generate weights (topological coordinates)
371
        random.seed();
372
        
373
        # Feature SOM 0002: Uniform grid.
374
        if (init_type == type_init.uniform_grid):
375
            # Predefined weights in line with input data.
376
            self._weights = [ [ [] for i in range(dim_info.get_dimensions()) ] for j in range(self._size)];
377
            for i in range(self._size):
378
                location = self._location[i];
379
                for dim in range(dim_info.get_dimensions()):
380
                    if (dim == 0):
381
                        if (self._rows > 1):
382
                            self._weights[i][dim] = dim_info.get_minimum_coordinate()[dim] + step_x * location[dim];
383
                        else:
384
                            self._weights[i][dim] = dim_info.get_center()[dim];
385
                            
386
                    elif (dim == 1):
387
                        if (self._cols > 1):
388
                            self._weights[i][dim] = dim_info.get_minimum_coordinate()[dim] + step_y * location[dim];
389
                        else:
390
                            self._weights[i][dim] = dim_info.get_center()[dim];
391
                    else:
392
                        self._weights[i][dim] = dim_info.get_center()[dim];
393
        
394
        elif (init_type == type_init.random_surface):
395
            # Random weights at the full surface.
396
            self._weights = [ [random.uniform(dim_info.get_minimum_coordinate()[i], dim_info.get_maximum_coordinate()[i]) for i in range(dim_info.get_dimensions())] for _ in range(self._size) ];
397
        
398
        elif (init_type == type_init.random_centroid):
399
            # Random weights at the center of input data.
400
            self._weights = [ [(random.random() + dim_info.get_center()[i])  for i in range(dim_info.get_dimensions())] for _ in range(self._size) ];
401
        
402
        else:
403
            # Random weights of input data.
404
            self._weights = [ [random.random()  for i in range(dim_info.get_dimensions())] for _ in range(self._size) ]; 
405
406
407
    def _create_connections(self, conn_type):
408
        """!
409
        @brief Create connections in line with input rule (grid four, grid eight, honeycomb, function neighbour).
410
        
411
        @param[in] conn_type (type_conn): Type of connection between oscillators in the network.
412
        
413
        """
414
        
415
        self._neighbors = [[] for index in range(self._size)];
416
            
417
        for index in range(0, self._size, 1):
418
            upper_index = index - self._cols;
419
            upper_left_index = index - self._cols - 1;
420
            upper_right_index = index - self._cols + 1;
421
            
422
            lower_index = index + self._cols;
423
            lower_left_index = index + self._cols - 1;
424
            lower_right_index = index + self._cols + 1;
425
            
426
            left_index = index - 1;
427
            right_index = index + 1;
428
            
429
            node_row_index = math.floor(index / self._cols);
430
            upper_row_index = node_row_index - 1;
431
            lower_row_index = node_row_index + 1;
432
            
433
            if ( (conn_type == type_conn.grid_eight) or (conn_type == type_conn.grid_four) ):
434
                if (upper_index >= 0):
435
                    self._neighbors[index].append(upper_index);
436
                    
437
                if (lower_index < self._size):
438
                    self._neighbors[index].append(lower_index);
439
            
440
            if ( (conn_type == type_conn.grid_eight) or (conn_type == type_conn.grid_four) or (conn_type == type_conn.honeycomb) ):
441
                if ( (left_index >= 0) and (math.floor(left_index / self._cols) == node_row_index) ):
442
                    self._neighbors[index].append(left_index);
443
                
444
                if ( (right_index < self._size) and (math.floor(right_index / self._cols) == node_row_index) ):
445
                    self._neighbors[index].append(right_index);  
446
                
447
                
448
            if (conn_type == type_conn.grid_eight):
449
                if ( (upper_left_index >= 0) and (math.floor(upper_left_index / self._cols) == upper_row_index) ):
450
                    self._neighbors[index].append(upper_left_index);
451
                
452
                if ( (upper_right_index >= 0) and (math.floor(upper_right_index / self._cols) == upper_row_index) ):
453
                    self._neighbors[index].append(upper_right_index);
454
                    
455
                if ( (lower_left_index < self._size) and (math.floor(lower_left_index / self._cols) == lower_row_index) ):
456
                    self._neighbors[index].append(lower_left_index);
457
                    
458
                if ( (lower_right_index < self._size) and (math.floor(lower_right_index / self._cols) == lower_row_index) ):
459
                    self._neighbors[index].append(lower_right_index);          
460
                
461
            
462
            if (conn_type == type_conn.honeycomb):
463
                if ( (node_row_index % 2) == 0):
464
                    upper_left_index = index - self._cols;
465
                    upper_right_index = index - self._cols + 1;
466
                
467
                    lower_left_index = index + self._cols;
468
                    lower_right_index = index + self._cols + 1;
469
                else:
470
                    upper_left_index = index - self._cols - 1;
471
                    upper_right_index = index - self._cols;
472
                
473
                    lower_left_index = index + self._cols - 1;
474
                    lower_right_index = index + self._cols;
475
                
476
                if ( (upper_left_index >= 0) and (math.floor(upper_left_index / self._cols) == upper_row_index) ):
477
                    self._neighbors[index].append(upper_left_index);
478
                
479
                if ( (upper_right_index >= 0) and (math.floor(upper_right_index / self._cols) == upper_row_index) ):
480
                    self._neighbors[index].append(upper_right_index);
481
                    
482
                if ( (lower_left_index < self._size) and (math.floor(lower_left_index / self._cols) == lower_row_index) ):
483
                    self._neighbors[index].append(lower_left_index);
484
                    
485
                if ( (lower_right_index < self._size) and (math.floor(lower_right_index / self._cols) == lower_row_index) ):
486
                    self._neighbors[index].append(lower_right_index);
487
    
488
    
489
    def _competition(self, x):
490
        """!
491
        @brief Calculates neuron winner (distance, neuron index).
492
        
493
        @param[in] x (list): Input pattern from the input data set, for example it can be coordinates of point.
494
        
495
        @return (uint) Returns index of neuron that is winner.
496
        
497
        """
498
        
499
        index = 0;
500
        minimum = euclidean_distance_sqrt(self._weights[0], x);
501
        
502
        for i in range(1, self._size, 1):
503
            candidate = euclidean_distance_sqrt(self._weights[i], x);
504
            if (candidate < minimum):
505
                index = i;
506
                minimum = candidate;
507
        
508
        return index;
509
    
510
    
511
    def _adaptation(self, index, x):
512
        """!
513
        @brief Change weight of neurons in line with won neuron.
514
        
515
        @param[in] index (uint): Index of neuron-winner.
516
        @param[in] x (list): Input pattern from the input data set.
517
        
518
        """
519
        
520
        dimension = len(self._weights[0]);
521
        
522
        if (self._conn_type == type_conn.func_neighbor):
523
            for neuron_index in range(self._size):
524
                distance = self._sqrt_distances[index][neuron_index];
525
                
526
                if (distance < self._local_radius):
527
                    influence = math.exp( -( distance / (2.0 * self._local_radius) ) );
528
                    
529
                    for i in range(dimension):
530
                        self._weights[neuron_index][i] = self._weights[neuron_index][i] + self._learn_rate * influence * (x[i] - self._weights[neuron_index][i]); 
531
                    
532
        else:
533
            for i in range(dimension):
534
                self._weights[index][i] = self._weights[index][i] + self._learn_rate * (x[i] - self._weights[index][i]); 
535
                
536
            for neighbor_index in self._neighbors[index]: 
537
                distance = self._sqrt_distances[index][neighbor_index]
538
                if (distance < self._local_radius):
539
                    influence = math.exp( -( distance / (2.0 * self._local_radius) ) );
540
                    
541
                    for i in range(dimension):       
542
                        self._weights[neighbor_index][i] = self._weights[neighbor_index][i] + self._learn_rate * influence * (x[i] - self._weights[neighbor_index][i]);  
543
544
545
    def train(self, data, epochs, autostop = False):
546
        """!
547
        @brief Trains self-organized feature map (SOM).
548
549
        @param[in] data (list): Input data - list of points where each point is represented by list of features, for example coordinates.
550
        @param[in] epochs (uint): Number of epochs for training.        
551
        @param[in] autostop (bool): Automatic termination of learining process when adaptation is not occurred.
552
        
553
        @return (uint) Number of learining iterations.
554
        
555
        """
556
        
557
        self._data = data;
558
        
559
        if (self.__ccore_som_pointer is not None):
560
            return wrapper.som_train(self.__ccore_som_pointer, data, epochs, autostop);
561
        
562
        for i in range(self._size):
563
            self._award[i] = 0;
564
            self._capture_objects[i].clear();
565
        
566
        # weights
567
        self._create_initial_weights(self._params.init_type);
568
        
569
        previous_weights = None;
570
        
571
        for epoch in range(1, epochs + 1):
572
            # Depression term of coupling
573
            self._local_radius = ( self._params.init_radius * math.exp(-(epoch / epochs)) ) ** 2;
574
            self._learn_rate = self._params.init_learn_rate * math.exp(-(epoch / epochs));
575
576
            #random.shuffle(self._data);    # Random order
577
            
578
            # Feature SOM 0003: Clear statistics
579
            if (autostop == True):
580
                for i in range(self._size):
581
                    self._award[i] = 0;
582
                    self._capture_objects[i].clear();
583
            
584
            for i in range(len(self._data)):
585
                # Step 1: Competition:
586
                index = self._competition(self._data[i]);
587
                    
588
                # Step 2: Adaptation:   
589
                self._adaptation(index, self._data[i]);
590
                
591
                # Update statistics
592
                if ( (autostop == True) or (epoch == epochs) ):
593
                    self._award[index] += 1;
594
                    self._capture_objects[index].append(i);
595
            
596
            # Feature SOM 0003: Check requirement of stopping
597
            if (autostop == True):
598
                if (previous_weights is not None):
599
                    maximal_adaptation = self._get_maximal_adaptation(previous_weights);
600
                    if (maximal_adaptation < self._params.adaptation_threshold):
601
                        return epoch;
602
            
603
                previous_weights = [item[:] for item in self._weights];
604
        
605
        return epochs;
606
607
608
    def simulate(self, input_pattern):
609
        """!
610
        @brief Processes input pattern (no learining) and returns index of neuron-winner.
611
               Using index of neuron winner catched object can be obtained using property capture_objects.
612
               
613
        @param[in] input_pattern (list): Input pattern.
614
        
615
        @return (uint) Returns index of neuron-winner.
616
               
617
        @see capture_objects
618
        
619
        """
620
                
621
        if (self.__ccore_som_pointer is not None):
622
            return wrapper.som_simulate(self.__ccore_som_pointer, input_pattern);
623
            
624
        return self._competition(input_pattern);
625
    
626
    
627
    def _get_maximal_adaptation(self, previous_weights):
628
        """!
629
        @brief Calculates maximum changes of weight in line with comparison between previous weights and current weights.
630
        
631
        @param[in] previous_weights (list): Weights from the previous step of learning process.
632
        
633
        @return (double) Value that represents maximum changes of weight after adaptation process.
634
        
635
        """
636
        
637
        dimension = len(self._data[0]);
638
        maximal_adaptation = 0.0;
639
        
640
        for neuron_index in range(self._size):
641
            for dim in range(dimension):
642
                current_adaptation = previous_weights[neuron_index][dim] - self._weights[neuron_index][dim];
643
                        
644
                if (current_adaptation < 0): current_adaptation = -current_adaptation;
645
                        
646
                if (maximal_adaptation < current_adaptation):
647
                    maximal_adaptation = current_adaptation;
648
                    
649
        return maximal_adaptation;
650
    
651
    
652
    def get_winner_number(self):
653
        """!
654
        @brief Calculates number of winner at the last step of learning process.
655
        
656
        @return (uint) Number of winner.
657
        
658
        """
659
        
660
        if (self.__ccore_som_pointer is not None):
661
            self._award = wrapper.som_get_awards(self.__ccore_som_pointer);
662
        
663
        winner_number = 0;
664
        for i in range(self._size):
665
            if (self._award[i] > 0):
666
                winner_number += 1;
667
                
668
        return winner_number;
669
    
670
    
671
    def show_distance_matrix(self):
672
        """!
673
        @brief Shows gray visualization of U-matrix (distance matrix).
674
        
675
        @see get_distance_matrix()
676
        
677
        """
678
        distance_matrix = self.get_distance_matrix();
679
        
680
        plt.imshow(distance_matrix, cmap = plt.get_cmap('hot'), interpolation='kaiser');
681
        plt.title("U-Matrix");
682
        plt.colorbar();
683
        plt.show();
684
685
    
686
    def get_distance_matrix(self):
687
        """!
688
        @brief Calculates distance matrix (U-matrix).
689
        @details The U-Matrix visualizes based on the distance in input space between a weight vector and its neighbors on map.
690
        
691
        @return (list) Distance matrix (U-matrix).
692
        
693
        @see show_distance_matrix()
694
        @see get_density_matrix()
695
        
696
        """
697
        if (self.__ccore_som_pointer is not None):
698
            self._weights = wrapper.som_get_weights(self.__ccore_som_pointer);
699
            
700
            if (self._conn_type != type_conn.func_neighbor):
701
                self._neighbors = wrapper.som_get_neighbors(self.__ccore_som_pointer);
702
            
703
        distance_matrix = [ [0.0] * self._cols for i in range(self._rows) ];
704
        
705
        for i in range(self._rows):
706
            for j in range(self._cols):
707
                neuron_index = i * self._cols + j;
708
                
709
                if (self._conn_type == type_conn.func_neighbor):
710
                    self._create_connections(type_conn.grid_eight);
711
                
712
                for neighbor_index in self._neighbors[neuron_index]:
713
                    distance_matrix[i][j] += euclidean_distance_sqrt(self._weights[neuron_index], self._weights[neighbor_index]);
714
                    
715
                distance_matrix[i][j] /= len(self._neighbors[neuron_index]);
716
    
717
        return distance_matrix;
718
    
719
    
720
    def show_density_matrix(self, surface_divider = 20.0):
0 ignored issues
show
Unused Code introduced by
The argument surface_divider seems to be unused.
Loading history...
721
        """!
722
        @brief Show density matrix (P-matrix) using kernel density estimation.
723
        
724
        @param[in] surface_divider (double): Divider in each dimension that affect radius for density measurement.
725
        
726
        @see show_distance_matrix()
727
        
728
        """        
729
        density_matrix = self.get_density_matrix();
730
        
731
        plt.imshow(density_matrix, cmap = plt.get_cmap('hot'), interpolation='kaiser');
732
        plt.title("P-Matrix");
733
        plt.colorbar();
734
        plt.show();
735
    
736
    
737
    def get_density_matrix(self, surface_divider = 20.0):
738
        """!
739
        @brief Calculates density matrix (P-Matrix).
740
        
741
        @param[in] surface_divider (double): Divider in each dimension that affect radius for density measurement.
742
        
743
        @return (list) Density matrix (P-Matrix).
744
        
745
        @see get_distance_matrix()
746
        
747
        """
748
        
749
        if (self.__ccore_som_pointer is not None):
750
            self._weights = wrapper.som_get_weights(self.__ccore_som_pointer);
751
        
752
        density_matrix = [ [0] * self._cols for i in range(self._rows) ];
753
        dimension = len(self._weights[0]);
754
        
755
        dim_max = [ float('-Inf') ] * dimension;
756
        dim_min = [ float('Inf') ] * dimension;
757
        
758
        for weight in self._weights:
759
            for index_dim in range(dimension):
760
                if (weight[index_dim] > dim_max[index_dim]):
761
                    dim_max[index_dim] = weight[index_dim];
762
                
763
                if (weight[index_dim] < dim_min[index_dim]):
764
                    dim_min[index_dim] = weight[index_dim];
765
        
766
        radius = [0.0] * len(self._weights[0]);
767
        for index_dim in range(dimension):
768
            radius[index_dim] = ( dim_max[index_dim] - dim_min[index_dim] ) / surface_divider;
769
        
770
        for point in self._data:
771
            for index_neuron in range(len(self)):
772
                point_covered = True;
773
                
774
                for index_dim in range(dimension):
775
                    if (abs(point[index_dim] - self._weights[index_neuron][index_dim]) > radius[index_dim]):
776
                        point_covered = False;
777
                        break;
778
                
779
                row = math.floor(index_neuron / self._cols);
780
                col = index_neuron - row * self._cols;
781
                
782
                if (point_covered is True):
783
                    density_matrix[row][col] += 1;
784
        
785
        return density_matrix;
786
    
787
    
788
    def show_winner_matrix(self):
789
        """!
790
        @brief Show winner matrix where each element corresponds to neuron and value represents
791
               amount of won objects from input dataspace at the last training iteration.
792
        
793
        @see show_distance_matrix()
794
        
795
        """
796
        
797
        if (self.__ccore_som_pointer is not None):
798
            self._award = wrapper.som_get_awards(self.__ccore_som_pointer);
799
        
800
        (fig, ax) = plt.subplots();
0 ignored issues
show
Unused Code introduced by
The variable fig seems to be unused.
Loading history...
801
        winner_matrix = [ [0] * self._cols for i in range(self._rows) ];
802
        
803
        for i in range(self._rows):
804
            for j in range(self._cols):
805
                neuron_index = i * self._cols + j;
806
                
807
                winner_matrix[i][j] = self._award[neuron_index];
808
                ax.text(i, j, str(winner_matrix[i][j]), va='center', ha='center')
809
        
810
        ax.imshow(winner_matrix, cmap = plt.get_cmap('cool'), interpolation='none');
811
        ax.grid(True);
812
        
813
        plt.title("Winner Matrix");
814
        plt.show();
815
            
816
    
817
    def show_network(self, awards = False, belongs = False, coupling = True, dataset = True, marker_type = 'o'):
818
        """!
819
        @brief Shows neurons in the dimension of data.
820
        
821
        @param[in] awards (bool): If True - displays how many objects won each neuron.
822
        @param[in] belongs (bool): If True - marks each won object by according index of neuron-winner (only when dataset is displayed too).
823
        @param[in] coupling (bool): If True - displays connections between neurons (except case when function neighbor is used).
824
        @param[in] dataset (bool): If True - displays inputs data set.
825
        @param[in] marker_type (string): Defines marker that is used for dispaying neurons in the network.
826
        
827
        """
828
        
829
        if (self.__ccore_som_pointer is not None):
830
            self._size = wrapper.som_get_size(self.__ccore_som_pointer);
831
            self._weights = wrapper.som_get_weights(self.__ccore_som_pointer);
832
            self._neighbors = wrapper.som_get_neighbors(self.__ccore_som_pointer);
833
            self._award = wrapper.som_get_awards(self.__ccore_som_pointer);
834
        
835
        
836
        dimension = len(self._weights[0]);
837
        
838
        fig = plt.figure();
839
        axes = None;
840
        
841
        # Check for dimensions
842
        if ( (dimension == 1) or (dimension == 2) ):
843
            axes = fig.add_subplot(111);
844
        elif (dimension == 3):
845
            axes = fig.gca(projection='3d');
846
        else:
847
            raise NameError('Dwawer supports only 1D, 2D and 3D data representation');
848
        
849
        
850
        # Show data
851
        if ((self._data is not None) and (dataset is True) ):
852
            for x in self._data:
853
                if (dimension == 1):
854
                    axes.plot(x[0], 0.0, 'b|', ms = 30);
855
                    
856
                elif (dimension == 2):
857
                    axes.plot(x[0], x[1], 'b.');
858
                    
859
                elif (dimension == 3):
860
                    axes.scatter(x[0], x[1], x[2], c = 'b', marker = '.');
861
        
862
        # Show neurons
863
        for index in range(self._size):
864
            color = 'g';
865
            if (self._award[index] == 0): color = 'y';
866
            
867
            if (dimension == 1):
868
                axes.plot(self._weights[index][0], 0.0, color + marker_type);
869
                
870
                if (awards == True):
871
                    location = '{0}'.format(self._award[index]);
872
                    axes.text(self._weights[index][0], 0.0, location, color='black', fontsize = 10);
873
            
874
                if (belongs == True):
875
                    location = '{0}'.format(index);
876
                    axes.text(self._weights[index][0], 0.0, location, color='black', fontsize = 12);
877
                    for k in range(len(self._capture_objects[index])):
878
                        point = self._data[self._capture_objects[index][k]];
879
                        axes.text(point[0], 0.0, location, color='blue', fontsize = 10);
880
            
881
            if (dimension == 2):
882
                axes.plot(self._weights[index][0], self._weights[index][1], color + marker_type);
883
                
884
                if (awards == True):
885
                    location = '{0}'.format(self._award[index]);
886
                    axes.text(self._weights[index][0], self._weights[index][1], location, color='black', fontsize = 10);
887
                    
888
                if (belongs == True):
889
                    location = '{0}'.format(index);
890
                    axes.text(self._weights[index][0], self._weights[index][1], location, color='black', fontsize = 12);
891
                    for k in range(len(self._capture_objects[index])):
892
                        point = self._data[self._capture_objects[index][k]];
893
                        axes.text(point[0], point[1], location, color='blue', fontsize = 10);
894
                
895
                if ( (self._conn_type != type_conn.func_neighbor) and (coupling != False) ):
896
                    for neighbor in self._neighbors[index]:
897
                        if (neighbor > index):
898
                            axes.plot([self._weights[index][0], self._weights[neighbor][0]], [self._weights[index][1], self._weights[neighbor][1]], 'g', linewidth = 0.5);
899
            
900
            elif (dimension == 3):
901
                axes.scatter(self._weights[index][0], self._weights[index][1], self._weights[index][2], c = color, marker = marker_type);
902
                
903
                if ( (self._conn_type != type_conn.func_neighbor) and (coupling != False) ):
904
                    for neighbor in self._neighbors[index]:
905
                        if (neighbor > index):
906
                            axes.plot([self._weights[index][0], self._weights[neighbor][0]], [self._weights[index][1], self._weights[neighbor][1]], [self._weights[index][2], self._weights[neighbor][2]], 'g-', linewidth = 0.5);
907
                        
908 View Code Duplication
                
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
909
        plt.title("Network Structure");
910
        plt.grid();
911
        plt.show();