Completed
Push — master ( 27be37...a60125 )
by Andrei
01:07
created

pcnn_network.__init__()   C

Complexity

Conditions 9

Size

Total Lines 57

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
dl 0
loc 57
rs 5.2182
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
"""!
2
3
@brief Neural Network: Pulse Coupled Neural Network
4
@details Based on book description:
5
         - T.Lindblad, J.M.Kinser. Image Processing Using Pulse-Coupled Neural Networks (2nd edition). 2005.
6
7
@authors Andrei Novikov ([email protected])
8
@date 2014-2017
9
@copyright GNU Public License
10
11
@cond GNU_PUBLIC_LICENSE
12
    PyClustering is free software: you can redistribute it and/or modify
13
    it under the terms of the GNU General Public License as published by
14
    the Free Software Foundation, either version 3 of the License, or
15
    (at your option) any later version.
16
    
17
    PyClustering is distributed in the hope that it will be useful,
18
    but WITHOUT ANY WARRANTY; without even the implied warranty of
19
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
    GNU General Public License for more details.
21
    
22
    You should have received a copy of the GNU General Public License
23
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24
@endcond
25
26
"""
27
28
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...
29
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...
30
31
import random;
32
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...
33
34
from PIL import Image;
0 ignored issues
show
Configuration introduced by
The import PIL 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...
35
36
from pyclustering.nnet import *;
0 ignored issues
show
Unused Code introduced by
IntEnum was imported with wildcard, but is not used.
Loading history...
Unused Code introduced by
solve_type was imported with wildcard, but is not used.
Loading history...
Unused Code introduced by
initial_type was imported with wildcard, but is not used.
Loading history...
37
import pyclustering.core.pcnn_wrapper as wrapper;
38
39
from pyclustering.utils import draw_dynamics;
40
41
42
class pcnn_parameters:
43
    """!
44
    @brief Parameters for pulse coupled neural network.
45
    
46
    """
47
    
48
    def __init__(self):
49
        """!
50
        @brief    Default constructor of parameters for pulse-coupled neural network.
51
        @details  Constructor initializes parameters by default non-zero values that can be
52
                  used for simple simulation.
53
        """
54
        
55
        ## Multiplier for the feeding compartment at the current step.
56
        self.VF = 1.0;
57
        
58
        ## Multiplier for the linking compartment at the current step.  
59
        self.VL = 1.0;
60
        
61
        ## Multiplier for the threshold at the current step.    
62
        self.VT = 10.0;
63
        
64
        
65
        ## Multiplier for the feeding compartment at the previous step.    
66
        self.AF = 0.1;
67
        
68
        ## Multiplier for the linking compartment at the previous step.
69
        self.AL = 0.1;
70
        
71
        ## Multiplier for the threshold at the previous step.
72
        self.AT = 0.5;
73
        
74
        
75
        ## Synaptic weight - neighbours influence on linking compartment
76
        self.W = 1.0;
77
        
78
        ## Synaptic weight - neighbours influence on feeding compartment.
79
        self.M = 1.0;
80
        
81
        
82
        ## Linking strength in the network.
83
        self.B = 0.1;
84
        
85
        ## Enable/disable Fast-Linking mode. Fast linking helps to overcome some of the effects of time quantisation. This process allows the linking wave to progress a lot faster than the feeding wave.
86
        self.FAST_LINKING = False;
87
    
88
    
89
class pcnn_dynamic:
90
    """!
91
    @brief Represents output dynamic of PCNN (pulse-coupled neural network).
92
    
93
    """
94
95
    @property
96
    def output(self):
97
        """!
98
        @brief (list) Returns oscillato outputs during simulation.
99
        
100
        """
101
        if (self.__ccore_pcnn_dynamic_pointer is not None):
102
            return wrapper.pcnn_dynamic_get_output(self.__ccore_pcnn_dynamic_pointer);
103
            
104
        return self.__dynamic;
105
    
106
    
107
    @property
108
    def time(self):
109
        """!
110
        @brief (list) Returns sampling times when dynamic is measured during simulation.
111
        
112
        """
113
        if (self.__ccore_pcnn_dynamic_pointer is not None):
114
            return wrapper.pcnn_dynamic_get_time(self.__ccore_pcnn_dynamic_pointer);
115
        
116
        return list(range(len(self)));
117
    
118
    
119
    def __init__(self, dynamic, ccore = None):
120
        """!
121
        @brief Constructor of PCNN dynamic.
122
        
123
        @param[in] dynamic (list): Dynamic of oscillators on each step of simulation. If ccore pointer is specified than it can be ignored.
124
        @param[in] ccore (ctypes.pointer): Pointer to CCORE pcnn_dynamic instance in memory.
125
        
126
        """
127
        self.__OUTPUT_TRUE = 1;    # fire value for oscillators.
128
        self.__OUTPUT_FALSE = 0;   # rest value for oscillators.
129
        
130
        self.__dynamic = dynamic;
131
        self.__ccore_pcnn_dynamic_pointer = ccore;
132
    
133
    
134
    def __del__(self):
135
        """!
136
        @brief Default destructor of PCNN dynamic.
137
        
138
        """
139
        if (self.__ccore_pcnn_dynamic_pointer is not None):
140
            wrapper.pcnn_dynamic_destroy(self.__ccore_pcnn_dynamic_pointer);
141
    
142
    
143
    def __len__(self):
144
        """!
145
        @brief (uint) Returns number of simulation steps that are stored in dynamic.
146
        
147
        """
148
        if (self.__ccore_pcnn_dynamic_pointer is not None):
149 View Code Duplication
            return wrapper.pcnn_dynamic_get_size(self.__ccore_pcnn_dynamic_pointer);
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
150
        
151
        return len(self.__dynamic);
152
    
153
    
154
    def allocate_sync_ensembles(self):
155
        """!
156
        @brief Allocate clusters in line with ensembles of synchronous oscillators where each
157
               synchronous ensemble corresponds to only one cluster.
158
        
159
        @return (list) Grours (lists) of indexes of synchronous oscillators. 
160
                For example, [ [index_osc1, index_osc3], [index_osc2], [index_osc4, index_osc5] ].
161
                
162
        """
163
        
164
        if (self.__ccore_pcnn_dynamic_pointer is not None):
165
            return wrapper.pcnn_dynamic_allocate_sync_ensembles(self.__ccore_pcnn_dynamic_pointer);
166
        
167
        sync_ensembles = [];
168
        traverse_oscillators = set();
169
        
170
        number_oscillators = len(self.__dynamic[0]);
171
        
172
        for t in range(len(self.__dynamic) - 1, 0, -1):
173
            sync_ensemble = [];
174
            for i in range(number_oscillators):
175
                if (self.__dynamic[t][i] == self.__OUTPUT_TRUE):
176
                    if (i not in traverse_oscillators):
177
                        sync_ensemble.append(i);
178
                        traverse_oscillators.add(i);
179
            
180
            if (sync_ensemble != []):
181 View Code Duplication
                sync_ensembles.append(sync_ensemble);
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
182
        
183
        return sync_ensembles;
184
185
186
    def allocate_spike_ensembles(self):
187
        """!
188
        @brief Analyses output dynamic of network and allocates spikes on each iteration as a list of indexes of oscillators.
189
        @details Each allocated spike ensemble represents list of indexes of oscillators whose output is active.
190
        
191
        @return (list) Spike ensembles of oscillators.
192
        
193
        """
194
        
195
        if (self.__ccore_pcnn_dynamic_pointer is not None):
196
            return wrapper.pcnn_dynamic_allocate_spike_ensembles(self.__ccore_pcnn_dynamic_pointer);
197
        
198
        spike_ensembles = [];
199
        number_oscillators = len(self.__dynamic[0]);
200
        
201
        for t in range(len(self.__dynamic)):
202
            spike_ensemble = [];
203
            
204
            for index in range(number_oscillators):
205
                if (self.__dynamic[t][index] == self.__OUTPUT_TRUE):
206
                    spike_ensemble.append(index);
207
            
208
            if (len(spike_ensemble) > 0):
209
                spike_ensembles.append(spike_ensemble);
210
        
211
        return spike_ensembles;
212
    
213
    
214
    def allocate_time_signal(self):
215
        """!
216
        @brief Analyses output dynamic and calculates time signal (signal vector information) of network output.
217
           
218
        @return (list) Time signal of network output.
219
        
220
        """
221
        
222
        if (self.__ccore_pcnn_dynamic_pointer is not None):
223
            return wrapper.pcnn_dynamic_allocate_time_signal(self.__ccore_pcnn_dynamic_pointer);
224
        
225
        signal_vector_information = [];
226
        for t in range(0, len(self.__dynamic)):
227
            signal_vector_information.append(sum(self.__dynamic[t]));
228
        
229
        return signal_vector_information;
230
231
232
class pcnn_visualizer:
233
    """!
234
    @brief Visualizer of output dynamic of pulse-coupled neural network (PCNN).
235
    
236
    """
237
    
238
    @staticmethod
239
    def show_time_signal(pcnn_output_dynamic):
240
        """!
241
        @brief Shows time signal (signal vector information) using network dynamic during simulation.
242
        
243
        @param[in] pcnn_output_dynamic (pcnn_dynamic): Output dynamic of the pulse-coupled neural network.
244
        
245
        """
246
        
247
        time_signal = pcnn_output_dynamic.allocate_time_signal();
248
        time_axis = range(len(time_signal));
249
        
250
        plt.subplot(1, 1, 1);
251
        plt.plot(time_axis, time_signal, '-');
252
        plt.ylabel("G (time signal)");
253
        plt.xlabel("t (iteration)");
254
        plt.grid(True);
255
        
256
        plt.show();
257
        
258
    @staticmethod
259
    def show_output_dynamic(pcnn_output_dynamic, separate_representation = False):
260
        """!
261
        @brief Shows output dynamic (output of each oscillator) during simulation.
262
        
263
        @param[in] pcnn_output_dynamic (pcnn_dynamic): Output dynamic of the pulse-coupled neural network.
264
        @param[in] separate_representation (list): Consists of lists of oscillators where each such list consists of oscillator indexes that will be shown on separated stage.
265
        
266
        """
267
        
268
        draw_dynamics(pcnn_output_dynamic.time, pcnn_output_dynamic.output, x_title = "t", y_title = "y(t)", separate = separate_representation);
269
    
270
    @staticmethod
271
    def animate_spike_ensembles(pcnn_output_dynamic, image_size):
272
        """!
273
        @brief Shows animation of output dynamic (output of each oscillator) during simulation.
274
        
275
        @param[in] pcnn_output_dynamic (pcnn_dynamic): Output dynamic of the pulse-coupled neural network.
276
        @param[in] image_size (list): Image size represented as [height, width].
277
        
278
        """
279
        
280
        figure = plt.figure();
281
        
282
        time_signal = pcnn_output_dynamic.allocate_time_signal();
283
        spike_ensembles = pcnn_output_dynamic.allocate_spike_ensembles();
284
        
285
        spike_animation = [];
286
        ensemble_index = 0;
287
        for t in range(len(time_signal)):
288
            image_color_segments = [(255, 255, 255)] * (image_size[0] * image_size[1]);
289
            
290
            if (time_signal[t] > 0):
291
                for index_pixel in spike_ensembles[ensemble_index]:
292
                    image_color_segments[index_pixel] = (0, 0, 0);
293
                
294
                ensemble_index += 1;
295
                
296
            stage = numpy.array(image_color_segments, numpy.uint8);
297
            stage = numpy.reshape(stage, image_size + ((3),)); # ((3),) it's size of RGB - third dimension.
298
            image_cluster = Image.fromarray(stage, 'RGB');
299
            
300
            spike_animation.append( [ plt.imshow(image_cluster, interpolation = 'none') ] );
301
            
302
        
303
        im_ani = animation.ArtistAnimation(figure, spike_animation, interval = 75, repeat_delay = 3000, blit = True)
0 ignored issues
show
Unused Code introduced by
The variable im_ani seems to be unused.
Loading history...
304
        plt.show();
305
306
307
class pcnn_network(network):
308
    """!
309
    @brief Model of oscillatory network that is based on the Eckhorn model.
310
    
311
    Example:
312
    @code
313
        # Create pulse-coupled neural network:
314
        # - 9 oscillators.
315
        # - default parameters.
316
        # - grid type of connections (each oscillator has connection with four neighbors).
317
        net = pcnn_network(9, None, conn_type.GRID_FOUR, ccore = ccore_flag);
318
        
319
        # Create external stimulus. Number of stimulus should be equal to number of neurons.
320
        stimulus = [1, 1, 1, 0, 0, 0, 1, 1, 1];
321
        
322
        # Simulate dynamic of the network during 40 iterations
323
        dynamic = net.simulate(40, stimulus);
324
        
325
        # Allocate synchronous oscillators
326
        ensembles = dynamic.allocate_sync_ensembles();
327
        print(ensembles);
328
        
329
        # Show output dynamic of the network
330
        pcnn_visualizer.show_output_dynamic(dynamic);
331
        
332
        # Show time signal vector information
333
        pcnn_visualizer.show_time_signal(dynamic);
334
    @endcode
335
    
336
    """
337
    
338
    __OUTPUT_TRUE = 1;    # fire value for oscillators.
339
    __OUTPUT_FALSE = 0;   # rest value for oscillators.
340
    
341
    def __init__(self, num_osc, parameters = None, type_conn = conn_type.ALL_TO_ALL, type_conn_represent = conn_represent.MATRIX, height = None, width = None, ccore = False):
342
        """!
343
        @brief Constructor of oscillatory network is based on Kuramoto model.
344
        
345
        @param[in] num_osc (uint): Number of oscillators in the network.
346
        @param[in] parameters (pcnn_parameters): Parameters of the network.
347
        @param[in] type_conn (conn_type): Type of connection between oscillators in the network (all-to-all, grid, bidirectional list, etc.).
348
        @param[in] type_conn_represent (conn_represent): Internal representation of connection in the network: matrix or list.
349
        @param[in] height (uint): Number of oscillators in column of the network, this argument is used 
350
                    only for network with grid structure (GRID_FOUR, GRID_EIGHT), for other types this argument is ignored.
351
        @param[in] width (uint): Number of oscillotors in row of the network, this argument is used only 
352
                    for network with grid structure (GRID_FOUR, GRID_EIGHT), for other types this argument is ignored.
353
        @param[in] ccore (bool): If True then all interaction with object will be performed via CCORE library (C++ implementation of pyclustering).
354
        
355
        """
356
        
357
        self._outputs = None;            # list of outputs of oscillors.
358
    
359
        self._feeding = None;            # feeding compartment of each oscillator.    
360
        self._linking = None;            # linking compartment of each oscillator. 
361
        self._threshold = None;          # threshold of each oscillator.
362
        
363
        self._params = None;
364
        
365
        self.__ccore_pcnn_pointer = None;
366
        
367
        # set parameters of the network
368
        if (parameters is not None):
369
            self._params = parameters;
370
        else:
371
            self._params = pcnn_parameters();
372
        
373
        if (ccore is True):
374
            network_height = height;
375
            network_width = width;
376
            
377
            if ( (type_conn == conn_type.GRID_FOUR) or (type_conn == conn_type.GRID_EIGHT) ):
378
                if ( (network_height is None) or (network_width is None) ):
379
                    side_size = num_osc ** (0.5);
380
                    if (side_size - math.floor(side_size) > 0):
381
                        raise NameError('Invalid number of oscillators in the network in case of grid structure');
382
                
383
                    network_height = int(side_size);
384
                    network_width = int(side_size);
385
            else:
386
                network_height = 0;
387
                network_width = 0;
388
                
389
            self.__ccore_pcnn_pointer = wrapper.pcnn_create(num_osc, type_conn, network_height, network_width, self._params);
390
        else:
391
            super().__init__(num_osc, type_conn, type_conn_represent, height, width);
392
            
393
            self._outputs = [0.0] * self._num_osc;
394
            
395
            self._feeding = [0.0] * self._num_osc;
396
            self._linking = [0.0] * self._num_osc;
397
            self._threshold = [ random.random() for i in range(self._num_osc) ];
398
    
399
    
400
    def __del__(self):
401
        """!
402
        @brief Default destructor of PCNN.
403
        
404
        """
405
        if (self.__ccore_pcnn_pointer is not None):
406
            wrapper.pcnn_destroy(self.__ccore_pcnn_pointer);
407
            self.__ccore_pcnn_pointer = None;
408
    
409
    
410
    def __len__(self):
411
        """!
412
        @brief (uint) Returns size of oscillatory network.
413
        
414
        """
415
        
416
        if (self.__ccore_pcnn_pointer is not None):
417
            return wrapper.pcnn_get_size(self.__ccore_pcnn_pointer);
418
        
419
        return self._num_osc;
420
    
421
        
422
    def simulate(self, steps, stimulus):
423
        """!
424
        @brief Performs static simulation of pulse coupled neural network using.
425
        
426
        @param[in] steps (uint): Number steps of simulations during simulation.
427
        @param[in] stimulus (list): Stimulus for oscillators, number of stimulus should be equal to number of oscillators.
428
        
429
        @return (pcnn_dynamic) Dynamic of oscillatory network - output of each oscillator on each step of simulation.
430
        
431
        """
432
        
433
        if (len(stimulus) != len(self)):
434
            raise NameError('Number of stimulus should be equal to number of oscillators. Each stimulus corresponds to only one oscillators.');
435
        
436
        if (self.__ccore_pcnn_pointer is not None):
437
            ccore_instance_dynamic = wrapper.pcnn_simulate(self.__ccore_pcnn_pointer, steps, stimulus);
438
            return pcnn_dynamic(None, ccore_instance_dynamic);
439
        
440
        dynamic = [];
441
        dynamic.append(self._outputs);
442
        
443
        for step in range(1, steps, 1):
0 ignored issues
show
Unused Code introduced by
The variable step seems to be unused.
Loading history...
444
            self._outputs = self._calculate_states(stimulus);
445
            
446
            dynamic.append(self._outputs);
447
        
448
        return pcnn_dynamic(dynamic);
449
    
450
    
451
    def _calculate_states(self, stimulus):
452
        """!
453
        @brief Calculates states of oscillators in the network for current step and stored them except outputs of oscillators.
454
        
455
        @param[in] stimulus (list): Stimulus for oscillators, number of stimulus should be equal to number of oscillators.
456
        
457
        @return (list) New outputs for oscillators (do not stored it).
458
        
459
        """
460
        
461
        feeding = [0.0] * self._num_osc;
462
        linking = [0.0] * self._num_osc;
463
        outputs = [0.0] * self._num_osc;
464
        threshold = [0.0] * self._num_osc;
465
        
466
        # Used by Fast-Linking
467
        output_change = False;
468
        
469
        for index in range(0, self._num_osc, 1):
470
            neighbors = self.get_neighbors(index);
471
            
472
            feeding_influence = 0.0;
473
            linking_influence = 0.0;
474
            
475
            for index_neighbour in neighbors:
476
                feeding_influence += self._outputs[index_neighbour] * self._params.M;
477
                linking_influence += self._outputs[index_neighbour] * self._params.W;
478
            
479
            feeding_influence *= self._params.VF;
480
            linking_influence *= self._params.VL;
481
            
482
            feeding[index] = self._params.AF * self._feeding[index] + stimulus[index] + feeding_influence;
483
            linking[index] = self._params.AL * self._linking[index] + linking_influence;
484
            
485
            # calculate internal activity
486
            internal_activity = feeding[index] * (1.0 + self._params.B * linking[index]);
487
            
488
            # calculate output of the oscillator
489
            if (internal_activity > self._threshold[index]):
490
                outputs[index] = self.__OUTPUT_TRUE;
491
            else:
492
                outputs[index] = self.__OUTPUT_FALSE;
493
            
494
            # In case of Fast Linking we should calculate threshould until output is changed.
495
            if (self._params.FAST_LINKING is not True):
496
                threshold[index] = self._params.AT * self._threshold[index] + self._params.VT * outputs[index];
497
        
498
        
499
        # In case of Fast Linking we need to wait until output is changed.
500
        if (self._params.FAST_LINKING is True):
501
            current_output_change = False;
502
            previous_outputs = outputs[:];
503
            
504
            while (output_change is True):               
505
                for index in range(0, self._num_osc, 1):
506
                    linking_influence = 0.0;
507
            
508
                    for index_neighbour in neighbors:
509
                        linking_influence += previous_outputs[index_neighbour] * self._params.W;
510
                    
511
                    linking_influence *= self._params.VL;
512
                    linking[index] = linking_influence;
513
                    
514
                    internal_activity = feeding[index] * (1.0 + self._params.B * linking[index]);
515
                    
516
                    # calculate output of the oscillator
517
                    if (internal_activity > self._threshold[index]):
518
                        outputs[index] = self.__OUTPUT_TRUE;
519
                    else:
520
                        outputs[index] = self.__OUTPUT_FALSE;
521
                        
522
                    if (outputs[index] != previous_outputs[index]):
523
                        current_output_change = True;
524
                
525
                output_change = current_output_change;
526
                current_output_change = False;
527
                
528
                if (output_change is True):
529
                    previous_outputs = outputs[:];
530
        
531
        # In case of Fast Linking threshould should be calculated after fast linking.
532
        if (self._params.FAST_LINKING is True):
533
            for index in range(0, self._num_osc, 1):
534
                threshold[index] = self._params.AT * self._threshold[index] + self._params.VT * outputs[index];
535
        
536
        self._feeding = feeding[:];
537
        self._linking = linking[:];
538
        self._threshold = threshold[:];
539
        
540
        return outputs
541
542
        
543