Completed
Push — master ( 572785...a0f60d )
by Andrei
01:49
created

cnn_network   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 256
Duplicated Lines 0.78 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 2
loc 256
rs 8.6
wmc 37

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __calculate_weight() 0 13 1
B __create_surface() 2 25 3
A __create_weights_all_to_all() 0 17 3
A __init__() 0 22 2
A __len__() 0 6 1
B simulate() 0 27 2
A __calculate_states() 0 15 3
A __neuron_evolution() 0 15 2
F show_network() 0 28 10
B __create_weights() 0 18 6
B __create_weights_delaunay_triangulation() 0 24 4

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
"""!
2
3
@brief Chaotic Neural Network
4
@details Based on article description:
5
         - E.N.Benderskaya, S.V.Zhukova. Large-dimension image clustering by means of fragmentary synchronization in chaotic systems. 2007.
6
         - E.N.Benderskaya, S.V.Zhukova. Clustering by Chaotic Neural Networks with Mean Field Calculated Via Delaunay Triangulation. 2008.
7
8
@authors Andrei Novikov ([email protected])
9
@date 2014-2016
10
@copyright GNU Public License
11
12
@cond GNU_PUBLIC_LICENSE
13
    PyClustering is free software: you can redistribute it and/or modify
14
    it under the terms of the GNU General Public License as published by
15
    the Free Software Foundation, either version 3 of the License, or
16
    (at your option) any later version.
17
    
18
    PyClustering is distributed in the hope that it will be useful,
19
    but WITHOUT ANY WARRANTY; without even the implied warranty of
20
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
    GNU General Public License for more details.
22
    
23
    You should have received a copy of the GNU General Public License
24
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
25
@endcond
26
27
"""
28
29
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...
30
import matplotlib.animation as animation;
0 ignored issues
show
Configuration introduced by
The import matplotlib.animation 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...
Unused Code introduced by
Unused matplotlib.animation imported as animation
Loading history...
31
32
from matplotlib import rcParams;
0 ignored issues
show
Configuration introduced by
The import matplotlib 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...
33
from matplotlib.font_manager import FontProperties;
0 ignored issues
show
Configuration introduced by
The import matplotlib.font_manager 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...
34
35
import math;
36
import numpy;
0 ignored issues
show
Configuration introduced by
The import numpy 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...
37
import random;
38
39
from enum import IntEnum;
40
41
from scipy.spatial import Delaunay;
0 ignored issues
show
Configuration introduced by
The import scipy.spatial 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...
42
43
from pyclustering.utils import euclidean_distance_sqrt, average_neighbor_distance, heaviside, draw_dynamics;
44
45
46
class type_conn(IntEnum):
47
    """!
48
    @brief Enumeration of connection types for Chaotic Neural Network.
49
    
50
    @see cnn_network
51
    
52
    """
53
    
54
    ## All oscillators have connection with each other.
55
    ALL_TO_ALL  = 0,
56
    
57
    ## Connections between oscillators are created in line with Delaunay triangulation.
58
    TRIANGULATION_DELAUNAY = 1,
59
60
61
class cnn_dynamic:
62
    """!
63
    @brief Container of output dynamic of the chaotic neural network where states of each neuron during simulation are stored.
64
    
65
    @see cnn_network
66
    
67
    """
68
    
69
    def __init__(self, output = [], time = []):
0 ignored issues
show
Bug Best Practice introduced by
The default value [] might cause unintended side-effects.

Objects as default values are only created once in Python and not on each invocation of the function. If the default object is modified, this modification is carried over to the next invocation of the method.

# Bad:
# If array_param is modified inside the function, the next invocation will
# receive the modified object.
def some_function(array_param=[]):
    # ...

# Better: Create an array on each invocation
def some_function(array_param=None):
    array_param = array_param or []
    # ...
Loading history...
70
        """!
71
        @brief Costructor of the chaotic neural network output dynamic.
72
73
        @param[in] phase (list): Dynamic of oscillators on each step of simulation.
74
        @param[in] time (list): Simulation time.
75
        
76
        """
77
        
78
        ## Output value of each neuron on each iteration.
79
        self.output = output;
80
        
81
        ## Sequence of simulation steps of the network.
82
        self.time = time;
83
84
85
    def __len__(self):
86
        """!
87
        @brief (uint) Returns amount of simulation steps that are stored.
88
        
89
        """
90
        return len(self.output);
91
92
93
    def allocate_observation_matrix(self):
94
        """!
95
        @brief Allocates observation matrix in line with output dynamic of the network.
96
        @details Matrix where state of each neuron is denoted by zero/one in line with Heaviside function on each iteration.
97
        
98
        @return (list) Observation matrix of the network dynamic.
99
        
100
        """
101
        number_neurons = len(self.output[0]);
102
        observation_matrix = [];
103
        
104
        for iteration in range(len(self.output)):
105
            obervation_column = [];
106
            for index_neuron in range(number_neurons):
107
                obervation_column.append(heaviside(self.output[iteration][index_neuron]));
108
            
109
            observation_matrix.append(obervation_column);
110
        
111
        return observation_matrix;
112
113
114
class cnn_visualizer:
115
    """!
116
    @brief Visualizer of output dynamic of chaotic neural network (CNN).
117
    
118
    """
119
    
120
    @staticmethod
121
    def show_output_dynamic(cnn_output_dynamic):
122
        """!
123
        @brief Shows output dynamic (output of each neuron) during simulation.
124
        
125
        @param[in] cnn_output_dynamic (cnn_dynamic): Output dynamic of the chaotic neural network.
126
        
127
        @see show_dynamic_matrix
128
        @see show_observation_matrix
129
        
130
        """
131
        
132
        draw_dynamics(cnn_output_dynamic.time, cnn_output_dynamic.output, x_title = "t", y_title = "x");
133
    
134
    
135
    @staticmethod
136
    def show_dynamic_matrix(cnn_output_dynamic):
137
        """!
138
        @brief Shows output dynamic as matrix in grey colors.
139
        @details This type of visualization is convenient for observing allocated clusters.
140
        
141
        @param[in] cnn_output_dynamic (cnn_dynamic): Output dynamic of the chaotic neural network.
142
        
143
        @see show_output_dynamic
144
        @see show_observation_matrix
145
        
146
        """
147
        
148
        network_dynamic = numpy.array(cnn_output_dynamic.output);
149
        
150
        plt.imshow(network_dynamic.T, cmap = plt.get_cmap('gray'), interpolation='None', vmin = 0.0, vmax = 1.0); 
151
        plt.show();
152
    
153
    
154
    @staticmethod
155
    def show_observation_matrix(cnn_output_dynamic):
156
        """!
157
        @brief Shows observation matrix as black/white blocks.
158
        @details This type of visualization is convenient for observing allocated clusters.
159
        
160
        @param[in] cnn_output_dynamic (cnn_dynamic): Output dynamic of the chaotic neural network.
161
        
162
        @see show_output_dynamic
163
        @see show_dynamic_matrix
164
        
165
        """
166
        
167
        observation_matrix = numpy.array(cnn_output_dynamic.allocate_observation_matrix());
168
        plt.imshow(observation_matrix.T, cmap = plt.get_cmap('gray'), interpolation='None', vmin = 0.0, vmax = 1.0); 
169
        plt.show();
170
171
172
class cnn_network:
173
    """!
174
    @brief Chaotic neural network based on system of logistic map where clustering phenomenon can be observed.
175
    
176
    Example:
177
    @code
178
        # load stimulus from file
179
        stimulus = read_sample(SIMPLE_SAMPLES.SAMPLE_SIMPLE1);
180
        
181
        # create chaotic neural network, amount of neurons should be equal to amout of stimulus
182
        network_instance = cnn_network(len(stimulus));
183
        
184
        # simulate it during 100 steps
185
        output_dynamic = network_instance.simulate(steps, stimulus);
186
        
187
        # display output dynamic of the network
188
        cnn_visualizer.show_output_dynamic(output_dynamic);
189
        
190
        # dysplay dynamic matrix and observation matrix to show clustering
191
        # phenomenon.
192
        cnn_visualizer.show_dynamic_matrix(output_dynamic);
193
        cnn_visualizer.show_observation_matrix(output_dynamic);
194
    @endcode
195
    
196
    """
197
    
198
    def __init__(self, num_osc, conn_type = type_conn.ALL_TO_ALL, amount_neighbors = 3):
199
        """!
200
        @brief Constructor of chaotic neural network.
201
        
202
        @param[in] num_osc (uint): Amount of neurons in the chaotic neural network.
203
        @param[in] conn_type (type_conn): CNN type connection for the network.
204
        @param[in] amount_neighbors (uint): k-nearest neighbors for calculation scaling constant of weights.
205
        
206
        """
207
        
208
        self.__num_osc = num_osc;
209
        self.__conn_type = conn_type;
210
        self.__amount_neighbors = amount_neighbors;
211
        
212
        self.__average_distance = 0.0;
213
        self.__weights = None;
214
        self.__weights_summary = None;
215
        
216
        self.__location = None;     # just for network visualization
217
        
218
        random.seed();
219
        self.__output = [ random.random() for _ in range(num_osc) ];
220
    
221
    
222
    def __len__(self):
223
        """!
224
        @brief Returns size of the chaotic neural network that is defined by amount of neurons.
225
        
226
        """
227
        return self.__num_osc;
228
    
229
    
230
    def simulate(self, steps, stimulus):
231
        """!
232
        @brief Simulates chaotic neural network with extrnal stimulus during specified steps.
233
        @details Stimulus are considered as a coordinates of neurons and in line with that weights
234
                 are initialized.
235
        
236
        @param[in] steps (uint): Amount of steps for simulation.
237
        @param[in] stimulus (list): Stimulus that are used for simulation.
238
        
239
        @return (cnn_dynamic) Output dynamic of the chaotic neural network.
240
        
241
        """
242
        
243
        self.__create_weights(stimulus);
244
        self.__location = stimulus;
245
        
246
        dynamic = cnn_dynamic([], []);
247
        dynamic.output.append(self.__output);
248
        dynamic.time.append(0);
249
        
250
        for step in range(1, steps, 1):
251
            self.__output = self.__calculate_states();
252
            
253
            dynamic.output.append(self.__output);
254
            dynamic.time.append(step);
255
            
256
        return dynamic;
257
    
258
    
259
    def __calculate_states(self):
260
        """!
261
        @brief Calculates new state of each neuron.
262
        @detail There is no any assignment.
263
        
264
        @return (list) Returns new states (output).
265
        
266
        """
267
        
268
        output = [ 0.0 for _ in range(self.__num_osc) ];
269
        
270
        for i in range(self.__num_osc):
271
            output[i] = self.__neuron_evolution(i);
272
        
273
        return output;
274
    
275
    
276
    def __neuron_evolution(self, index):
277
        """!
278
        @brief Calculates state of the neuron with specified index.
279
        
280
        @param[in] index (uint): Index of neuron in the network.
281
        
282
        @return (double) New output of the specified neuron.
283
        
284
        """
285
        value = 0.0;
286
        
287
        for index_neighbor in range(self.__num_osc):
288
            value += self.__weights[index][index_neighbor] * (1.0 - 2.0 * (self.__output[index_neighbor] ** 2));
289
        
290
        return value / self.__weights_summary[index];
291
    
292
    
293
    def __create_weights(self, stimulus):
294
        """!
295
        @brief Create weights between neurons in line with stimulus.
296
        
297
        @param[in] stimulus (list): External stimulus for the chaotic neural network.
298
        
299
        """
300
        
301
        self.__average_distance = average_neighbor_distance(stimulus, self.__amount_neighbors);
302
        
303
        self.__weights = [ [ 0.0 for _ in range(len(stimulus)) ] for _ in range(len(stimulus)) ];
304
        self.__weights_summary = [ 0.0 for _ in range(self.__num_osc) ];
305
        
306
        if (self.__conn_type == type_conn.ALL_TO_ALL):
307
            self.__create_weights_all_to_all(stimulus);
308
        
309
        elif (self.__conn_type == type_conn.TRIANGULATION_DELAUNAY):
310
            self.__create_weights_delaunay_triangulation(stimulus);
311
    
312
    
313
    def __create_weights_all_to_all(self, stimulus):
314
        """!
315
        @brief Create weight all-to-all structure between neurons in line with stimulus.
316
        
317
        @param[in] stimulus (list): External stimulus for the chaotic neural network.
318
        
319
        """
320
        
321
        for i in range(len(stimulus)):
322
            for j in range(i + 1, len(stimulus)):
323
                weight = self.__calculate_weight(stimulus[i], stimulus[j]);
324
                
325
                self.__weights[i][j] = weight;
326
                self.__weights[j][i] = weight;
327
                
328
                self.__weights_summary[i] += weight;
329
                self.__weights_summary[j] += weight;
330
    
331
    
332
    def __create_weights_delaunay_triangulation(self, stimulus):
333
        """!
334
        @brief Create weight Denlauny triangulation structure between neurons in line with stimulus.
335
        
336
        @param[in] stimulus (list): External stimulus for the chaotic neural network.
337
        
338
        """
339
        
340
        points = numpy.array(stimulus);
341
        triangulation = Delaunay(points);
342
        
343
        for triangle in triangulation.simplices:
344
            for index_tri_point1 in range(len(triangle)):
345
                for index_tri_point2 in range(index_tri_point1 + 1, len(triangle)):
346
                    index_point1 = triangle[index_tri_point1];
347
                    index_point2 = triangle[index_tri_point2];
348
                    
349
                    weight = self.__calculate_weight(stimulus[index_point1], stimulus[index_point2]);
350
                    
351
                    self.__weights[index_point1][index_point2] = weight;
352
                    self.__weights[index_point2][index_point1] = weight;
353
                    
354
                    self.__weights_summary[index_point1] += weight;
355
                    self.__weights_summary[index_point2] += weight;
356
    
357
    
358
    def __calculate_weight(self, stimulus1, stimulus2):
359
        """!
360
        @brief Calculate weight between neurons that have external stimulus1 and stimulus2.
361
        
362
        @param[in] stimulus1 (list): External stimulus of the first neuron.
363
        @param[in] stimulus2 (list): External stimulus of the second neuron.
364
        
365
        @return (double) Weight between neurons that are under specified stimulus.
366
        
367
        """
368
        
369
        distance = euclidean_distance_sqrt(stimulus1, stimulus2);
370
        return math.exp(-distance / (2.0 * self.__average_distance));
371
372
    
373
    def show_network(self):
374
        """!
375
        @brief Shows structure of the network: neurons and connections between them.
376
        
377
        """
378
        
379
        dimension = len(self.__location[0]);
380
        if ( (dimension != 3) and (dimension != 2) ):
381
            raise NameError('Network that is located in different from 2-d and 3-d dimensions can not be represented');
382
383
        (fig, axes) = self.__create_surface(dimension);
0 ignored issues
show
Unused Code introduced by
The variable fig seems to be unused.
Loading history...
384
        
385
        for i in range(0, self.__num_osc, 1):
386
            if (dimension == 2):
387
                axes.plot(self.__location[i][0], self.__location[i][1], 'bo');  
388
                for j in range(i, self.__num_osc, 1):    # draw connection between two points only one time
389
                    if (self.__weights[i][j] > 0.0):
390
                        axes.plot([self.__location[i][0], self.__location[j][0]], [self.__location[i][1], self.__location[j][1]], 'b-', linewidth = 0.5);
391
            
392
            elif (dimension == 3):
393
                axes.scatter(self.__location[i][0], self.__location[i][1], self.__location[i][2], c = 'b', marker = 'o');
394
                
395
                for j in range(i, self._num_osc, 1):    # draw connection between two points only one time
0 ignored issues
show
Bug introduced by
The Instance of cnn_network does not seem to have a member named _num_osc.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
396
                    if (self.__weights[i][j] > 0.0):
397
                        axes.plot([self.__location[i][0], self.__location[j][0]], [self.__location[i][1], self.__location[j][1]], [self.__location[i][2], self.__location[j][2]], 'b-', linewidth = 0.5);
398
                
399
        plt.grid();
400
        plt.show();
401
    
402
    
403
    def __create_surface(self, dimension):
404
        """!
405
        @brief Prepares surface for showing network structure in line with specified dimension.
406 View Code Duplication
        
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
407
        @param[in] dimension (uint): Dimension of processed data (external stimulus).
408
        
409
        @return (tuple) Description of surface for drawing network structure.
410
        
411
        """
412
        
413
        rcParams['font.sans-serif'] = ['Arial'];
414
        rcParams['font.size'] = 12;
415
416
        fig = plt.figure();
417
        axes = None;
418
        if (dimension == 2):
419
            axes = fig.add_subplot(111);
420
        elif (dimension == 3):
421
            axes = fig.gca(projection='3d');
422
        
423
        surface_font = FontProperties();
424
        surface_font.set_name('Arial');
425
        surface_font.set_size('12');
426
        
427
        return (fig, axes);