Completed
Push — master ( a63a82...61f52c )
by
unknown
01:32
created

syncpr   B

Complexity

Total Complexity 40

Size/Duplication

Total Lines 325
Duplicated Lines 0 %
Metric Value
dl 0
loc 325
rs 8.2608
wmc 40

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __len__() 0 10 2
A __calculate_memory_order() 0 16 2
F simulate_dynamic() 0 77 9
B train() 0 30 6
A __init__() 0 20 4
A __del__() 0 9 2
B simulate_static() 0 32 4
B __validate_pattern() 0 14 5
A simulate() 0 20 1
B _phase_kuramoto() 0 29 3
A memory_order() 0 18 2

How to fix   Complexity   

Complex Class

Complex classes like syncpr often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
"""!
2
3
@brief Phase oscillatory network for patten recognition based on modified Kuramoto model.
4
@details Based on article description:
5
         - R.Follmann, E.E.N.Macau, E.Rosa, Jr., J.R.C.Piqueira. Phase Oscillatory Network and Visual Pattern Recognition. 2014.
6
         
7
@authors Andrei Novikov ([email protected])
8
@date 2014-2016
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
from pyclustering.nnet          import solve_type, initial_type, conn_type;
29
from pyclustering.nnet.sync     import sync_network, sync_dynamic, sync_visualizer;
30
31
import pyclustering.core.syncpr_wrapper as wrapper;
32
33
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...
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 math;
38
import cmath;
39
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...
40
41
42
class syncpr_dynamic(sync_dynamic):
43
    """!
44
    @brief Represents output dynamic of syncpr (Sync for Pattern Recognition).
45
    
46
    """
47
    
48
    def __init__(self, phase, time, ccore):
49
        """!
50
        @brief Constructor of syncpr dynamic.
51
        
52
        @param[in] phase (list): Dynamic of oscillators on each step of simulation. If ccore pointer is specified than it can be ignored.
53
        @param[in] time (list): Simulation time.
54
        @param[in] ccore (ctypes.pointer): Pointer to CCORE sync_dynamic instance in memory.
55
        
56
        """     
57
        super().__init__(phase, time, ccore);       
58
59
60
class syncpr_visualizer(sync_visualizer):
61
    """!
62
    @brief Visualizer of output dynamic of syncpr network (Sync for Pattern Recognition).
63
    
64
    """
65
    
66
    @staticmethod
67
    def show_pattern(syncpr_output_dynamic, image_height, image_width):
68
        """!
69
        @brief Displays evolution of phase oscillators as set of patterns where the last one means final result of recognition.
70
        
71
        @param[in] syncpr_output_dynamic (syncpr_dynamic): Output dynamic of a syncpr network.
72
        @param[in] image_height (uint): Height of the pattern (image_height * image_width should be equal to number of oscillators).
73
        @param[in] image_width (uint): Width of the pattern.
74
        
75
        """
76
        number_pictures = len(syncpr_output_dynamic);
77
        iteration_math_step = 1.0;
78
        if (number_pictures > 50):
79
            iteration_math_step = number_pictures / 50.0;
80
            number_pictures = 50;
81
            
82
        image_size = image_height * image_width;
83
        
84
        number_cols = int(numpy.ceil(number_pictures ** 0.5));
85
        number_rows = int(numpy.ceil(number_pictures / number_cols));
86
        
87
        real_index = 0, 0;
88
        double_indexer = True;
89
        if ( (number_cols == 1) or (number_rows == 1) ):
90
            real_index = 0;
91
            double_indexer = False;
92
        
93
        (fig, axarr) = plt.subplots(number_rows, number_cols);
0 ignored issues
show
Unused Code introduced by
The variable fig seems to be unused.
Loading history...
94
        
95
        if (number_pictures > 1):
96
            plt.setp([ax for ax in axarr], visible = False);
97
            
98
        iteration_display = 0.0;
99
        for iteration in range(len(syncpr_output_dynamic)):
100
            if (iteration >= iteration_display):
101
                iteration_display += iteration_math_step;
102
                
103
                current_dynamic = syncpr_output_dynamic.output[iteration];
104
                stage_picture = [(255, 255, 255)] * image_size;
105
                for index_phase in range(len(current_dynamic)):
106
                    phase = current_dynamic[index_phase];
107
                    
108
                    pixel_color = math.floor( phase * (255 / (2 * math.pi)) );
109
                    stage_picture[index_phase] = (pixel_color, pixel_color, pixel_color);
110
                  
111
                stage = numpy.array(stage_picture, numpy.uint8);
112
                stage = numpy.reshape(stage, (image_height, image_width) + ((3),)); # ((3),) it's size of RGB - third dimension.
113
                
114
                image_cluster = Image.fromarray(stage, 'RGB');
115
                
116
                ax_handle = axarr;
117
                if (number_pictures > 1):
118
                    ax_handle = axarr[real_index];
119
                    
120
                ax_handle.imshow(image_cluster, interpolation = 'none');
121
                plt.setp(ax_handle, visible = True);
122
                
123
                ax_handle.xaxis.set_ticklabels([]);
124
                ax_handle.yaxis.set_ticklabels([]);
125
                ax_handle.xaxis.set_ticks_position('none');
126
                ax_handle.yaxis.set_ticks_position('none');
127
                
128
                if (double_indexer is True):
129
                    real_index = real_index[0], real_index[1] + 1;
130
                    if (real_index[1] >= number_cols):
131
                        real_index = real_index[0] + 1, 0; 
132
                else:
133
                    real_index += 1;
134
    
135
        plt.show();
136
137
138
class syncpr(sync_network):
139
    """!
140
    @brief Model of phase oscillatory network for pattern recognition that is based on the Kuramoto model.
141
    @details The model uses second-order and third-order modes of the Fourier components.
142
    
143
    Example:
144
    @code
145
    # Network size should be equal to size of pattern for learning.
146
    net = syncpr(size_network, 0.3, 0.3);
147
    
148
    # Train network using list of patterns (input images).
149
    net.train(image_samples);
150
    
151
    # Recognize image using 10 steps during 10 seconds of simulation.
152
    sync_output_dynamic = net.simulate(10, 10, pattern, solve_type.RK4, True);
153
    
154
    # Display output dynamic.
155
    syncpr_visualizer.show_output_dynamic(sync_output_dynamic);
156
    
157
    # Display evolution of recognition of the pattern.
158
    syncpr_visualizer.show_pattern(sync_output_dynamic, image_height, image_width);
159
    
160
    @endcode
161
    
162
    """
163
    
164
    _increase_strength1 = 0.0;
165
    _increase_strength2 = 0.0;
166
    _coupling = None;
167
    
168
    def __init__(self, num_osc, increase_strength1, increase_strength2, ccore = False):
169
        """!
170
        @brief Constructor of oscillatory network for pattern recognition based on Kuramoto model.
171
        
172
        @param[in] num_osc (uint): Number of oscillators in the network.
173
        @param[in] increase_strength1 (double): Parameter for increasing strength of the second term of the Fourier component.
174
        @param[in] increase_strength2 (double): Parameter for increasing strength of the third term of the Fourier component.
175
        @param[in] ccore (bool): If True simulation is performed by CCORE library (C++ implementation of pyclustering).
176
        
177
        """
178
        
179
        if (ccore is True):
180
            self._ccore_network_pointer = wrapper.syncpr_create(num_osc, increase_strength1, increase_strength2);
181
            
182
        else:
183
            self._increase_strength1 = increase_strength1;
184
            self._increase_strength2 = increase_strength2;
185
            self._coupling = [ [0.0 for i in range(num_osc)] for j in range(num_osc) ];
186
        
187
            super().__init__(num_osc, 1, 0, conn_type.ALL_TO_ALL, initial_type.RANDOM_GAUSSIAN);
188
    
189
    
190
    def __del__(self):
191
        """!
192
        @brief Default destructor of syncpr.
193
        
194
        """
195
        
196
        if (self._ccore_network_pointer is not None):
197
            wrapper.syncpr_destroy(self._ccore_network_pointer);
198
            self._ccore_network_pointer = None;
199
200
            
201
    def __len__(self):
202
        """!
203
        @brief Returns size of the network.
204
        
205
        """        
206
        if (self._ccore_network_pointer is not None):
207
            return wrapper.syncpr_get_size(self._ccore_network_pointer);
208
        
209
        else:
210
            return self._num_osc;
211
    
212
    
213
    def train(self, samples):
214
        """!
215
        @brief Trains syncpr network using Hebbian rule for adjusting strength of connections between oscillators during training.
216
        
217
        @param[in] samples (list): list of patterns where each pattern is represented by list of features that are equal to [-1; 1].
218
        
219
        """
220
        
221
        # Verify pattern for learning
222
        for pattern in samples:
223
            self.__validate_pattern(pattern);
224
        
225
        if (self._ccore_network_pointer is not None):
226
            return wrapper.syncpr_train(self._ccore_network_pointer, samples);
227
        
228
        length = len(self);
229
        number_samples = len(samples);
230
        
231
        for i in range(length):
232
            for j in range(i + 1, len(self), 1):
233
                
234
                # go through via all patterns
235
                for p in range(number_samples):
236
                    value1 = samples[p][i];
237
                    value2 = samples[p][j];
238
                    
239
                    self._coupling[i][j] += value1 * value2;
240
                
241
                self._coupling[i][j] /= length;
242
                self._coupling[j][i] = self._coupling[i][j];
243
    
244
    
245
    def simulate(self, steps, time, pattern, solution = solve_type.RK4, collect_dynamic = True):
0 ignored issues
show
Bug introduced by
Arguments number differs from overridden 'simulate' method
Loading history...
246
        """!
247
        @brief Performs static simulation of syncpr oscillatory network.
248
        @details In other words network performs pattern recognition during simulation.
249
        
250
        @param[in] steps (uint): Number steps of simulations during simulation.
251
        @param[in] time (double): Time of simulation.
252
        @param[in] pattern (list): Pattern for recognition represented by list of features that are equal to [-1; 1].
253
        @param[in] solution (solve_type): Type of solver that should be used for simulation.
254
        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
255
        
256
        @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
257
                otherwise returns only last values (last step of simulation) of dynamic.
258
        
259
        @see simulate_dynamic()
260
        @see simulate_static()
261
        
262
        """
263
                    
264
        return self.simulate_static(steps, time, pattern, solution, collect_dynamic);
265
    
266
    
267
    def simulate_dynamic(self, pattern, order = 0.998, solution = solve_type.FAST, collect_dynamic = False, step = 0.1, int_step = 0.01, threshold_changes = 0.0000001):
0 ignored issues
show
Bug introduced by
Arguments number differs from overridden 'simulate_dynamic' method
Loading history...
268
        """!
269
        @brief Performs dynamic simulation of the network until stop condition is not reached.
270
        @details In other words network performs pattern recognition during simulation. 
271
                 Stop condition is defined by input argument 'order' that represents memory order, but
272
                 process of simulation can be stopped if convergance rate is low whose threshold is defined
273
                 by the argument 'threshold_changes'.
274
        
275
        @param[in] pattern (list): Pattern for recognition represented by list of features that are equal to [-1; 1].
276
        @param[in] order (double): Order of process synchronization, distributed 0..1.
277
        @param[in] solution (solve_type): Type of solution.
278
        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
279
        @param[in] step (double): Time step of one iteration of simulation.
280
        @param[in] int_step (double): Integration step, should be less than step.
281
        @param[in] threshold_changes (double): Additional stop condition that helps prevent infinite simulation, defines limit of changes of oscillators between current and previous steps.
282
        
283
        @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
284
                otherwise returns only last values (last step of simulation) of dynamic.
285
        
286
        @see simulate()
287
        @see simulate_static()
288
        
289
        """
290
        
291
        self.__validate_pattern(pattern);
292
        
293
        if (self._ccore_network_pointer is not None):
294
            ccore_instance_dynamic = wrapper.syncpr_simulate_dynamic(self._ccore_network_pointer, pattern, order, solution, collect_dynamic, step);
295
            return syncpr_dynamic(None, None, ccore_instance_dynamic);
296
        
297
        for i in range(0, len(pattern), 1):
298
            if (pattern[i] > 0.0):
299
                self._phases[i] = 0.0;
300
            else:
301
                self._phases[i] = math.pi / 2.0;
302
        
303
        # For statistics and integration
304
        time_counter = 0;
305
        
306
        # Prevent infinite loop. It's possible when required state cannot be reached.
307
        previous_order = 0;
308
        current_order = self.__calculate_memory_order(pattern);
309
        
310
        # If requested input dynamics
311
        dyn_phase = [];
312
        dyn_time = [];
313
        if (collect_dynamic == True):
314
            dyn_phase.append(self._phases);
315
            dyn_time.append(0);
316
        
317
        # Execute until sync state will be reached
318
        while (current_order < order):                
319
            # update states of oscillators
320
            self._phases = self._calculate_phases(solution, time_counter, step, int_step);
321
            
322
            # update time
323
            time_counter += step;
324
            
325
            # if requested input dynamic
326
            if (collect_dynamic == True):
327
                dyn_phase.append(self._phases);
328
                dyn_time.append(time_counter);
329
                
330
            # update orders
331
            previous_order = current_order;
332
            current_order = self.__calculate_memory_order(pattern);
333
            
334
            # hang prevention
335
            if (abs(current_order - previous_order) < threshold_changes):
336
                break;
337
        
338
        if (collect_dynamic != True):
339
            dyn_phase.append(self._phases);
340
            dyn_time.append(time_counter);
341
        
342
        output_sync_dynamic = syncpr_dynamic(dyn_phase, dyn_time, None);
343
        return output_sync_dynamic;
344
345
346
    def simulate_static(self, steps, time, pattern, solution = solve_type.FAST, collect_dynamic = False):
0 ignored issues
show
Bug introduced by
Arguments number differs from overridden 'simulate_static' method
Loading history...
347
        """!
348
        @brief Performs static simulation of syncpr oscillatory network.
349
        @details In other words network performs pattern recognition during simulation.
350
        
351
        @param[in] steps (uint): Number steps of simulations during simulation.
352
        @param[in] time (double): Time of simulation.
353
        @param[in] pattern (list): Pattern for recognition represented by list of features that are equal to [-1; 1].
354
        @param[in] solution (solve_type): Type of solution.
355
        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
356
        
357
        @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
358
                otherwise returns only last values (last step of simulation) of dynamic.
359
        
360
        @see simulate()
361
        @see simulate_dynamic()
362
        
363
        """
364
        
365
        self.__validate_pattern(pattern);
366
        
367
        if (self._ccore_network_pointer is not None):
368
            ccore_instance_dynamic = wrapper.syncpr_simulate_static(self._ccore_network_pointer, steps, time, pattern, solution, collect_dynamic);
369
            return syncpr_dynamic(None, None, ccore_instance_dynamic);
370
        
371
        for i in range(0, len(pattern), 1):
372
            if (pattern[i] > 0.0):
373
                self._phases[i] = 0.0;
374
            else:
375
                self._phases[i] = math.pi / 2.0;
376
                
377
        return super().simulate_static(steps, time, solution, collect_dynamic);
378
    
379
    
380
    def memory_order(self, pattern):
381
        """!
382
        @brief Calculates function of the memorized pattern.
383
        @details Throws exception if length of pattern is not equal to size of the network or if it consists feature with value that are not equal to [-1; 1].
384
        
385
        @param[in] pattern (list): Pattern for recognition represented by list of features that are equal to [-1; 1].
386
        
387
        @return (double) Order of memory for the specified pattern.
388
        
389
        """
390
        
391
        self.__validate_pattern(pattern);
392
        
393
        if (self._ccore_network_pointer is not None):
394
            return wrapper.syncpr_memory_order(self._ccore_network_pointer, pattern);
395
        
396
        else:
397
            return self.__calculate_memory_order(pattern);
398
399
    
400
    def __calculate_memory_order(self, pattern):
401
        """!
402
        @brief Calculates function of the memorized pattern without any pattern validation.
403
        
404
        @param[in] pattern (list): Pattern for recognition represented by list of features that are equal to [-1; 1].
405
        
406
        @return (double) Order of memory for the specified pattern.
407
                
408
        """
409
        
410
        memory_order = 0.0;
411
        for index in range(len(self)):
412
            memory_order += pattern[index] * cmath.exp( 1j * self._phases[index] );
413
        
414
        memory_order /= len(self);
415
        return abs(memory_order);
416
        
417
    
418
    def _phase_kuramoto(self, teta, t, argv):
419
        """!
420
        @brief Returns result of phase calculation for specified oscillator in the network.
421
        
422
        @param[in] teta (double): Phase of the oscillator that is differentiated.
423
        @param[in] t (double): Current time of simulation.
424
        @param[in] argv (tuple): Index of the oscillator in the list.
425
        
426
        @return (double) New phase for specified oscillator (don't assign it here).
427
        
428
        """
429
        
430
        index = argv;
431
        
432
        phase = 0.0;
433
        term = 0.0;
434
        
435
        for k in range(0, self._num_osc):
436
            if (k != index):
437
                phase_delta = self._phases[k] - teta;
438
                
439
                phase += self._coupling[index][k] * math.sin(phase_delta);
440
                
441
                term1 = self._increase_strength1 * math.sin(2.0 * phase_delta);
442
                term2 = self._increase_strength2 * math.sin(3.0 * phase_delta);
443
                
444
                term += (term1 - term2);
445
                
446
        return ( phase + term / len(self) );
447
    
448
    
449
    def __validate_pattern(self, pattern):
450
        """!
451
        @brief Validates pattern.
452
        @details Throws exception if length of pattern is not equal to size of the network or if it consists feature with value that are not equal to [-1; 1].
453
        
454
        @param[in] pattern (list): Pattern for recognition represented by list of features that are equal to [-1; 1].
455
        
456
        """
457
        if (len(pattern) != len(self)):
458
            raise NameError('syncpr: length of the pattern (' + len(pattern) + ') should be equal to size of the network');
459
        
460
        for feature in pattern:
461
            if ( (feature != -1.0) and (feature != 1.0) ):
462
                raise NameError('syncpr: patten feature (' + feature + ') should be distributed in [-1; 1]');