Completed
Push — master ( eb13df...83fb71 )
by Andrei
01:39
created

sync_visualizer.show_output_dynamic()   A

Complexity

Conditions 1

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
1
"""!
2
3
@brief Neural Network: Oscillatory Neural Network based on Kuramoto model
4
@details Based on article description:
5
         - A.Arenas, Y.Moreno, C.Zhou. Synchronization in complex networks. 2008.
6
         - X.B.Lu. Adaptive Cluster Synchronization in Coupled Phase Oscillators. 2009.
7
         - X.Lou. Adaptive Synchronizability of Coupled Oscillators With Switching. 2012.
8
         - A.Novikov, E.Benderskaya. Oscillatory Neural Networks Based on the Kuramoto Model. 2014.
9
10
@authors Andrei Novikov ([email protected])
11
@date 2014-2016
12
@copyright GNU Public License
13
14
@cond GNU_PUBLIC_LICENSE
15
    PyClustering is free software: you can redistribute it and/or modify
16
    it under the terms of the GNU General Public License as published by
17
    the Free Software Foundation, either version 3 of the License, or
18
    (at your option) any later version.
19
    
20
    PyClustering is distributed in the hope that it will be useful,
21
    but WITHOUT ANY WARRANTY; without even the implied warranty of
22
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
    GNU General Public License for more details.
24
    
25
    You should have received a copy of the GNU General Public License
26
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
27
@endcond
28
29
"""
30
31
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...
32
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...
33
34
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...
35
import random;
36
37
import pyclustering.core.sync_wrapper as wrapper;
38
39
from scipy import pi;
0 ignored issues
show
Configuration introduced by
The import scipy 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
from scipy.integrate import odeint;
0 ignored issues
show
Configuration introduced by
The import scipy.integrate 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...
41
42
from pyclustering.nnet import *;
0 ignored issues
show
Unused Code introduced by
IntEnum was imported with wildcard, but is not used.
Loading history...
43
44
from pyclustering.utils import draw_dynamics, draw_dynamics_set;
45
46
47
class sync_dynamic:
48
    """!
49
    @brief Represents output dynamic of Sync.
50
    
51
    """
52
    
53
    @property
54
    def output(self):
55
        """!
56
        @brief (list) Returns outputs of oscillator during simulation.
57
        
58
        """
59
        if ( (self._ccore_sync_dynamic_pointer is not None) and ( (self._dynamic is None) or (len(self._dynamic) == 0) ) ):
60
            self._dynamic = wrapper.sync_dynamic_get_output(self._ccore_sync_dynamic_pointer);
61
        
62
        return self._dynamic;
63
    
64
    
65
    @property
66
    def time(self):
67
        """!
68
        @brief (list) Returns sampling times when dynamic is measured during simulation.
69
        
70
        """
71
        if ( (self._ccore_sync_dynamic_pointer is not None) and ( (self._dynamic is None) or (len(self._dynamic) == 0) ) ):
72
            self._time = wrapper.sync_dynamic_get_time(self._ccore_sync_dynamic_pointer);
73
        
74
        return self._time;
75
    
76
    
77
    def __init__(self, phase, time, ccore = None):
78
        """!
79
        @brief Constructor of Sync dynamic.
80
        
81
        @param[in] phase (list): Dynamic of oscillators on each step of simulation. If ccore pointer is specified than it can be ignored.
82
        @param[in] time (list): Simulation time.
83
        @param[in] ccore (ctypes.pointer): Pointer to CCORE sync_dynamic instance in memory.
84
        
85
        """
86
        
87
        self._dynamic = phase;
88
        self._time = time;
89
        self._ccore_sync_dynamic_pointer = ccore;
90
    
91
    
92
    def __del__(self):
93
        """!
94
        @brief Default destructor of Sync dynamic.
95
        
96
        """
97
        if (self._ccore_sync_dynamic_pointer is not None):
98
            wrapper.sync_dynamic_destroy(self._ccore_sync_dynamic_pointer);
99
    
100
    
101
    def __len__(self):
102
        """!
103
        @brief (uint) Returns number of simulation steps that are stored in dynamic.
104
        
105
        """
106
        if (self._ccore_sync_dynamic_pointer is not None):
107
            return wrapper.sync_dynamic_get_size(self._ccore_sync_dynamic_pointer);
108
        
109
        return len(self._dynamic);
110
    
111
    
112
    def __getitem__(self, index):
113
        """!
114
        @brief Indexing of the dynamic.
115
        
116
        """
117
        if (index is 0):
118
            return self.time;
119
        
120
        elif (index is 1):
121
            return self.output;
122
        
123
        else:
124
            raise NameError('Out of range ' + index + ': only indexes 0 and 1 are supported.');
125
126
127
    def allocate_sync_ensembles(self, tolerance = 0.01, indexes = None, iteration = None):
128
        """!
129
        @brief Allocate clusters in line with ensembles of synchronous oscillators where each synchronous ensemble corresponds to only one cluster.
130
               
131
        @param[in] tolerance (double): Maximum error for allocation of synchronous ensemble oscillators.
132
        @param[in] indexes (list): List of real object indexes and it should be equal to amount of oscillators (in case of 'None' - indexes are in range [0; amount_oscillators]).
133
        @param[in] iteration (uint): Iteration of simulation that should be used for allocation.
134
        
135
        @return (list) Grours (lists) of indexes of synchronous oscillators.
136
                For example [ [index_osc1, index_osc3], [index_osc2], [index_osc4, index_osc5] ].
137
        
138
        """
139
        
140
        if (self._ccore_sync_dynamic_pointer is not None):
141
            ensembles = wrapper.sync_dynamic_allocate_sync_ensembles(self._ccore_sync_dynamic_pointer, tolerance, iteration);
142
            
143
            if (indexes is not None):
144
                for ensemble in ensembles:
145
                    for index in range(len(ensemble)):
146
                        ensemble[index] = indexes[ ensemble[index] ];
147
                
148
            return ensembles;
149
        
150
        if ( (self._dynamic is None) or (len(self._dynamic) == 0) ):
151
            return [];
152
        
153
        number_oscillators = len(self._dynamic[0]);
154
        last_state = None;
155
        
156
        if (iteration is None):
157
            last_state = self._dynamic[len(self._dynamic) - 1];
158
        else:
159
            last_state = self._dynamic[iteration];
160
        
161
        clusters = [];
162
        if (number_oscillators > 0):
163
            clusters.append([0]);
164
        
165
        for i in range(1, number_oscillators, 1):
166
            cluster_allocated = False;
167
            for cluster in clusters:
168
                for neuron_index in cluster:
169
                    last_state_shifted = abs(last_state[i] - 2 * pi);
170
                    
171
                    if ( ( (last_state[i] < (last_state[neuron_index] + tolerance)) and (last_state[i] > (last_state[neuron_index] - tolerance)) ) or
172
                         ( (last_state_shifted < (last_state[neuron_index] + tolerance)) and (last_state_shifted > (last_state[neuron_index] - tolerance)) ) ):
173
                        cluster_allocated = True;
174
                        
175
                        real_index = i;
176
                        if (indexes is not None):
177
                            real_index = indexes[i];
178
                        
179
                        cluster.append(real_index);
180
                        break;
181
                
182
                if (cluster_allocated == True):
183
                    break;
184
            
185
            if (cluster_allocated == False):
186
                clusters.append([i]);
187
        
188
        return clusters;
189
    
190
    
191
    def allocate_phase_matrix(self, grid_width = None, grid_height = None, iteration = None):
192
        """!
193
        @brief Returns 2D matrix of phase values of oscillators at the specified iteration of simulation.
194
        @details User should ensure correct matrix sizes in line with following expression grid_width x grid_height that should be equal to 
195
                  amount of oscillators otherwise exception is thrown. If grid_width or grid_height are not specified than phase matrix size 
196
                  will by calculated automatically by square root.
197
        
198
        @param[in] grid_width (uint): Width of the allocated matrix.
199
        @param[in] grid_height (uint): Height of the allocated matrix.
200
        @param[in] iteration (uint): Number of iteration of simulation for which correlation matrix should be allocated.
201
                    If iternation number is not specified, the last step of simulation is used for the matrix allocation.
202
        
203
        @return (list) Phase value matrix of oscillators with size [number_oscillators x number_oscillators].
204
        
205
        """
206
        
207
        output_dynamic = self.output;
208
        
209
        if ( (output_dynamic is None) or (len(output_dynamic) == 0) ):
210
            return [];
211
        
212
        current_dynamic = output_dynamic[len(output_dynamic) - 1];
213
        if (iteration is not None):
214
            current_dynamic = output_dynamic[iteration];
215
        
216
        width_matrix = grid_width;
217
        height_matrix = grid_height;
218
        number_oscillators = len(current_dynamic);
219
        if ( (width_matrix is None) or (height_matrix is None) ):
220
            width_matrix = int(math.ceil(math.sqrt(number_oscillators)));
221
            height_matrix = width_matrix;
222
223
        if (number_oscillators != width_matrix * height_matrix):
224
            raise NameError("Impossible to allocate phase matrix with specified sizes, amout of neurons should be equal to grid_width * grid_height.");
225
        
226
        
227
        phase_matrix = [ [ 0.0 for i in range(width_matrix) ] for j in range(height_matrix) ];
228
        for i in range(height_matrix):
229
            for j in range(width_matrix):
230
                phase_matrix[i][j] = current_dynamic[j + i * width_matrix];
231
        
232
        return phase_matrix;
233
    
234
    
235
    def allocate_correlation_matrix(self, iteration = None):
236
        """!
237
        @brief Allocate correlation matrix between oscillators at the specified step of simulation.
238
               
239
        @param[in] iteration (uint): Number of iteration of simulation for which correlation matrix should be allocated.
240
                    If iternation number is not specified, the last step of simulation is used for the matrix allocation.
241
        
242
        @return (list) Correlation matrix between oscillators with size [number_oscillators x number_oscillators].
243
        
244
        """
245
        
246
        if (self._ccore_sync_dynamic_pointer is not None):
247
            return wrapper.sync_dynamic_allocate_correlation_matrix(self._ccore_sync_dynamic_pointer, iteration);
248
        
249
        if ( (self._dynamic is None) or (len(self._dynamic) == 0) ):
250
            return [];
251
        
252
        dynamic = self._dynamic;
253
        current_dynamic = dynamic[len(dynamic) - 1];
254
        
255
        if (iteration is not None):
256
            current_dynamic = dynamic[iteration];
257
        
258
        number_oscillators = len(dynamic[0]);
259
        affinity_matrix = [ [ 0.0 for i in range(number_oscillators) ] for j in range(number_oscillators) ];
260
        
261
        for i in range(number_oscillators):
262
            for j in range(number_oscillators):
263
                phase1 = current_dynamic[i];
264
                phase2 = current_dynamic[j];
265
                
266
                affinity_matrix[i][j] = abs(math.sin(phase1 - phase2));
267
                
268
        return affinity_matrix;
269
270
271
class sync_visualizer:
272
    """!
273
    @brief Visualizer of output dynamic of sync network (Sync).
274
    
275
    """
276
        
277
    @staticmethod
278
    def show_output_dynamic(sync_output_dynamic):
279
        """!
280
        @brief Shows output dynamic (output of each oscillator) during simulation.
281
        
282
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
283
        
284
        @see show_output_dynamics
285
        
286
        """
287
        
288
        draw_dynamics(sync_output_dynamic.time, sync_output_dynamic.output, x_title = "t", y_title = "phase", y_lim = [0, 2 * 3.14]);
289
    
290
    
291
    @staticmethod
292
    def show_output_dynamics(sync_output_dynamics):
293
        """!
294
        @brief Shows several output dynamics (output of each oscillator) during simulation.
295
        @details Each dynamic is presented on separate plot.
296
        
297
        @param[in] sync_output_dynamics (list): list of output dynamics 'sync_dynamic' of the Sync network.
298
        
299
        @see show_output_dynamic
300
        
301
        """
302
        
303
        draw_dynamics_set(sync_output_dynamics, "t", "phase", None, [0, 2 * 3.14], False, False);
304
    
305
    
306
    @staticmethod
307
    def show_correlation_matrix(sync_output_dynamic, iteration = None):
308
        """!
309
        @brief Shows correlation matrix between oscillators at the specified iteration.
310
        
311
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
312
        @param[in] iteration (uint): Number of interation of simulation for which correlation matrix should be allocated.
313
                                      If iternation number is not specified, the last step of simulation is used for the matrix allocation.
314
        
315
        """
316
        
317
        _ = plt.figure();
318
        correlation_matrix = sync_output_dynamic.allocate_correlation_matrix(iteration);
319
        
320
        plt.imshow(correlation_matrix, cmap = plt.get_cmap('cool'), interpolation='kaiser', vmin = 0.0, vmax = 1.0); 
321
        plt.show();
322
        
323
    
324
    
325
    @staticmethod
326
    def show_phase_matrix(sync_output_dynamic, grid_width = None, grid_height = None, iteration = None):
327
        """!
328
        @brief Shows 2D matrix of phase values of oscillators at the specified iteration.
329
        @details User should ensure correct matrix sizes in line with following expression grid_width x grid_height that should be equal to 
330
                  amount of oscillators otherwise exception is thrown. If grid_width or grid_height are not specified than phase matrix size 
331
                  will by calculated automatically by square root.
332
        
333
        @param[in] grid_width (uint): Width of the phase matrix.
334
        @param[in] grid_height (uint): Height of the phase matrix.
335
        @param[in] iteration (uint): Number of iteration of simulation for which correlation matrix should be allocated.
336
                    If iternation number is not specified, the last step of simulation is used for the matrix allocation.
337
        
338
        """
339
        
340
        _ = plt.figure();
341
        phase_matrix = sync_output_dynamic.allocate_phase_matrix(iteration, grid_width, grid_height);
342
        
343
        plt.imshow(phase_matrix, cmap = plt.get_cmap('jet'), interpolation='kaiser', vmin = 0.0, vmax = 2.0 * math.pi); 
344
        plt.show();
345
    
346
    
347
    @staticmethod
348
    def animate_output_dynamic(sync_output_dynamic, animation_velocity = 75, save_movie = None):
349
        """!
350
        @brief Shows animation of output dynamic (output of each oscillator) during simulation on a circle from [0; 2pi].
351
        
352
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
353
        @param[in] animation_velocity (uint): Interval between frames in milliseconds.
354
        @param[in] save_movie (string): If it is specified then animation will be stored to file that is specified in this parameter.
355
        
356
        """
357
        
358
        figure = plt.figure();
359
        
360
        dynamic = sync_output_dynamic.output[0];
361
        artist, = plt.polar(dynamic, [1.0] * len(dynamic), 'o', color = 'blue');
362
        
363
        def init_frame():
364
            return [ artist ];
365
        
366
        def frame_generation(index_dynamic):
367
            dynamic = sync_output_dynamic.output[index_dynamic];
368
            artist.set_data(dynamic, [1.0] * len(dynamic));
369
            
370
            return [ artist ];
371
        
372
        phase_animation = animation.FuncAnimation(figure, frame_generation, len(sync_output_dynamic), interval = animation_velocity, init_func = init_frame, repeat_delay = 5000);
373
374
        if (save_movie is not None):
375
            phase_animation.save(save_movie, writer = 'ffmpeg', fps = 15, bitrate = 1500);
376
        else:
377
            plt.show();
378
379
380 View Code Duplication
    @staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
381
    def animate_correlation_matrix(sync_output_dynamic, animation_velocity = 75, colormap = 'cool', save_movie = None):
382
        """!
383
        @brief Shows animation of correlation matrix between oscillators during simulation.
384
        
385
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
386
        @param[in] animation_velocity (uint): Interval between frames in milliseconds.
387
        @param[in] colormap (string): Name of colormap that is used by matplotlib ('gray', 'pink', 'cool', spring', etc.).
388
        @param[in] save_movie (string): If it is specified then animation will be stored to file that is specified in this parameter.
389
        
390
        """
391
        
392
        figure = plt.figure();
393
        
394
        correlation_matrix = sync_output_dynamic.allocate_correlation_matrix(0);
395
        artist = plt.imshow(correlation_matrix, cmap = plt.get_cmap(colormap), interpolation='kaiser', hold = True, vmin = 0.0, vmax = 1.0);
396
        
397
        def init_frame(): 
398
            return [ artist ];
399
        
400
        def frame_generation(index_dynamic):
401
            correlation_matrix = sync_output_dynamic.allocate_correlation_matrix(index_dynamic);
402
            artist.set_data(correlation_matrix);
403
            
404
            return [ artist ];
405
406
        correlation_animation = animation.FuncAnimation(figure, frame_generation, len(sync_output_dynamic), init_func = init_frame, interval = animation_velocity , repeat_delay = 1000, blit = True);
407
        
408
        if (save_movie is not None):
409
            correlation_animation.save(save_movie, writer = 'ffmpeg', fps = 15, bitrate = 1500);
410
        else:
411
            plt.show();
412
413
414 View Code Duplication
    @staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
415
    def animate_phase_matrix(sync_output_dynamic, grid_width = None, grid_height = None, animation_velocity = 75, colormap = 'jet', save_movie = None):
416
        """!
417
        @brief Shows animation of phase matrix between oscillators during simulation on 2D stage.
418
        @details If grid_width or grid_height are not specified than phase matrix size will by calculated automatically by square root.
419
        
420
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
421
        @param[in] grid_width (uint): Width of the phase matrix.
422
        @param[in] grid_height (uint): Height of the phase matrix.
423
        @param[in] animation_velocity (uint): Interval between frames in milliseconds.
424
        @param[in] colormap (string): Name of colormap that is used by matplotlib ('gray', 'pink', 'cool', spring', etc.).
425
        @param[in] save_movie (string): If it is specified then animation will be stored to file that is specified in this parameter.
426
        
427
        """
428
        
429
        figure = plt.figure();
430
        
431
        def init_frame(): 
432
            return frame_generation(0);
433
        
434
        def frame_generation(index_dynamic):
435
            figure.clf();
436
            axis = figure.add_subplot(111);
437
            
438
            phase_matrix = sync_output_dynamic.allocate_phase_matrix(grid_width, grid_height, index_dynamic);
439
            axis.imshow(phase_matrix, cmap = plt.get_cmap(colormap), interpolation='kaiser', vmin = 0.0, vmax = 2.0 * math.pi);
440
            artist = figure.gca();
441
            
442
            return [ artist ];
443
444
        phase_animation = animation.FuncAnimation(figure, frame_generation, len(sync_output_dynamic), init_func = init_frame, interval = animation_velocity , repeat_delay = 1000);
445
        
446
        if (save_movie is not None):
447
            phase_animation.save(save_movie, writer = 'ffmpeg', fps = 15, bitrate = 1500);
448
        else:
449
            plt.show();
450
451
452
453
    @staticmethod
454
    def animate(sync_output_dynamic, title = None, save_movie = None):
455
        """!
456
        @brief Shows animation of phase coordinates and animation of correlation matrix together for the Sync dynamic output on the same figure.
457
        
458
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
459
        @param[in] title (string): Title of the animation that is displayed on a figure if it is specified.
460
        @param[in] save_movie (string): If it is specified then animation will be stored to file that is specified in this parameter.
461
        
462
        """
463
        
464
        dynamic = sync_output_dynamic.output[0];
465
        correlation_matrix = sync_output_dynamic.allocate_correlation_matrix(0);
466
        
467
        figure = plt.figure(1);
468
        if (title is not None):
469
            figure.suptitle(title, fontsize = 26, fontweight = 'bold')
470
        
471
        ax1 = figure.add_subplot(121, projection='polar');
472
        ax2 = figure.add_subplot(122);
473
        
474
        artist1, = ax1.plot(dynamic, [1.0] * len(dynamic), marker = 'o', color = 'blue', ls = '');
475
        artist2 = ax2.imshow(correlation_matrix, cmap = plt.get_cmap('Accent'), interpolation='kaiser');
476
        
477
        def init_frame():
478
            return [ artist1, artist2 ];
479
480
        def frame_generation(index_dynamic):
481
            dynamic = sync_output_dynamic.output[index_dynamic];
482
            artist1.set_data(dynamic, [1.0] * len(dynamic));
483
            
484
            correlation_matrix = sync_output_dynamic.allocate_correlation_matrix(index_dynamic);
485
            artist2.set_data(correlation_matrix);
486
            
487
            return [ artist1, artist2 ];
488
        
489
        dynamic_animation = animation.FuncAnimation(figure, frame_generation, len(sync_output_dynamic), interval = 75, init_func = init_frame, repeat_delay = 5000);
490
        
491
        if (save_movie is not None):
492
            dynamic_animation.save(save_movie, writer = 'ffmpeg', fps = 15, bitrate = 1500);
493
        else:
494
            plt.show();
495
496
497
class sync_network(network):
498
    """!
499
    @brief Model of oscillatory network that is based on the Kuramoto model of synchronization.
500
    
501
    """
502
503
    def __init__(self, num_osc, weight = 1, frequency = 0, type_conn = conn_type.ALL_TO_ALL, representation = conn_represent.MATRIX, initial_phases = initial_type.RANDOM_GAUSSIAN, ccore = False):
504
        """!
505
        @brief Constructor of oscillatory network is based on Kuramoto model.
506
        
507
        @param[in] num_osc (uint): Number of oscillators in the network.
508
        @param[in] weight (double): Coupling strength of the links between oscillators.
509
        @param[in] frequency (double): Multiplier of internal frequency of the oscillators.
510
        @param[in] type_conn (conn_type): Type of connection between oscillators in the network (all-to-all, grid, bidirectional list, etc.).
511
        @param[in] representation (conn_represent): Internal representation of connection in the network: matrix or list.
512
        @param[in] initial_phases (initial_type): Type of initialization of initial phases of oscillators (random, uniformly distributed, etc.).
513
        @param[in] ccore (bool): If True simulation is performed by CCORE library (C++ implementation of pyclustering).
514
        
515
        """
516
        
517
        self._ccore_network_pointer = None;      # Pointer to CCORE Sync implementation of the network.
518
        
519
        if (ccore is True):
520
            self._ccore_network_pointer = wrapper.sync_create_network(num_osc, weight, frequency, type_conn, initial_phases);
521
        else:   
522
            super().__init__(num_osc, type_conn, representation);
523
            
524
            self._weight = weight;
525
            
526
            self._phases = list();
527
            self._freq = list();
528
            
529
            random.seed();
530
            for index in range(0, num_osc, 1):
531
                if (initial_phases == initial_type.RANDOM_GAUSSIAN):
532
                    self._phases.append(random.random() * 2 * pi);
533
                
534
                elif (initial_phases == initial_type.EQUIPARTITION):
535
                    self._phases.append( pi / num_osc * index);
536
                
537
                self._freq.append(random.random() * frequency);
538
539
540
    def __del__(self):
541
        """!
542
        @brief Destructor of oscillatory network is based on Kuramoto model.
543
        
544
        """
545
        
546
        if (self._ccore_network_pointer is not None):
547
            wrapper.sync_destroy_network(self._ccore_network_pointer);
548
            self._ccore_network_pointer = None;
549
    
550
    
551
    def sync_order(self):
552
        """!
553
        @brief Calculates level of global synchorization in the network.
554
        
555
        @return (double) Level of global synchronization.
556
        
557
        @see sync_local_order()
558
        
559
        """
560
        
561
        if (self._ccore_network_pointer is not None):
562
            return wrapper.sync_order(self._ccore_network_pointer);
563
        
564
        exp_amount = 0;
565
        average_phase = 0;
566
        
567
        for index in range(0, self._num_osc, 1):
568
            exp_amount += math.expm1( abs(1j * self._phases[index]) );
569
            average_phase += self._phases[index];
570
        
571
        exp_amount /= self._num_osc;
572
        average_phase = math.expm1( abs(1j * (average_phase / self._num_osc)) );
573
        
574
        return abs(average_phase) / abs(exp_amount);    
575
    
576
    
577
    def sync_local_order(self):
578
        """!
579
        @brief Calculates level of local (partial) synchronization in the network.
580
        
581
        @return (double) Level of local (partial) synchronization.
582
        
583
        @see sync_order()
584
        
585
        """
586
        
587
        if (self._ccore_network_pointer is not None):
588
            return wrapper.sync_local_order(self._ccore_network_pointer);
589
        
590
        exp_amount = 0.0;
591
        num_neigh = 0;
592
        
593
        for i in range(0, self._num_osc, 1):
594
            for j in range(0, self._num_osc, 1):
595
                if (self.has_connection(i, j) == True):
596
                    exp_amount += math.exp(-abs(self._phases[j] - self._phases[i]));
597
                    num_neigh += 1;
598
        
599
        if (num_neigh == 0):
600
            num_neigh = 1;
601
        
602
        return exp_amount / num_neigh;
603
    
604
    
605
    def _phase_kuramoto(self, teta, t, argv):
0 ignored issues
show
Unused Code introduced by
The argument t seems to be unused.
Loading history...
606
        """!
607
        @brief Returns result of phase calculation for specified oscillator in the network.
608
        
609
        @param[in] teta (double): Phase of the oscillator that is differentiated.
610
        @param[in] t (double): Current time of simulation.
611
        @param[in] argv (tuple): Index of the oscillator in the list.
612
        
613
        @return (double) New phase for specified oscillator (don't assign here).
614
        
615
        """
616
        
617
        index = argv;
618
        phase = 0;
619
        for k in range(0, self._num_osc):
620
            if (self.has_connection(index, k) == True):
621
                phase += math.sin(self._phases[k] - teta);
622
            
623
        return ( self._freq[index] + (phase * self._weight / self._num_osc) );
624
        
625
    
626
    def simulate(self, steps, time, solution = solve_type.FAST, collect_dynamic = True):
627
        """!
628
        @brief Performs static simulation of Sync oscillatory network.
629
        
630
        @param[in] steps (uint): Number steps of simulations during simulation.
631
        @param[in] time (double): Time of simulation.
632
        @param[in] solution (solve_type): Type of solution (solving).
633
        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
634
        
635
        @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
636
                otherwise returns only last values (last step of simulation) of dynamic.
637
        
638
        @see simulate_dynamic()
639
        @see simulate_static()
640
        
641
        """
642
        
643
        return self.simulate_static(steps, time, solution, collect_dynamic);
644
645
646
    def simulate_dynamic(self, order = 0.998, solution = solve_type.FAST, collect_dynamic = False, step = 0.1, int_step = 0.01, threshold_changes = 0.0000001):
647
        """!
648
        @brief Performs dynamic simulation of the network until stop condition is not reached. Stop condition is defined by input argument 'order'.
649
        
650
        @param[in] order (double): Order of process synchronization, distributed 0..1.
651
        @param[in] solution (solve_type): Type of solution.
652
        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
653
        @param[in] step (double): Time step of one iteration of simulation.
654
        @param[in] int_step (double): Integration step, should be less than step.
655
        @param[in] threshold_changes (double): Additional stop condition that helps prevent infinite simulation, defines limit of changes of oscillators between current and previous steps.
656
        
657
        @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
658
                otherwise returns only last values (last step of simulation) of dynamic.
659
        
660
        @see simulate()
661
        @see simulate_static()
662
        
663
        """
664
        
665
        if (self._ccore_network_pointer is not None):
666
            ccore_instance_dynamic = wrapper.sync_simulate_dynamic(self._ccore_network_pointer, order, solution, collect_dynamic, step, int_step, threshold_changes);
667
            return sync_dynamic(None, None, ccore_instance_dynamic);
668
        
669
        # For statistics and integration
670
        time_counter = 0;
671
        
672
        # Prevent infinite loop. It's possible when required state cannot be reached.
673
        previous_order = 0;
674
        current_order = self.sync_local_order();
675
        
676
        # If requested input dynamics
677
        dyn_phase = [];
678
        dyn_time = [];
679
        if (collect_dynamic == True):
680
            dyn_phase.append(self._phases);
681
            dyn_time.append(0);
682
        
683
        # Execute until sync state will be reached
684
        while (current_order < order):                
685
            # update states of oscillators
686
            self._phases = self._calculate_phases(solution, time_counter, step, int_step);
687
            
688
            # update time
689
            time_counter += step;
690
            
691
            # if requested input dynamic
692
            if (collect_dynamic == True):
693
                dyn_phase.append(self._phases);
694
                dyn_time.append(time_counter);
695
                
696
            # update orders
697
            previous_order = current_order;
698
            current_order = self.sync_local_order();
699
            
700
            # hang prevention
701
            if (abs(current_order - previous_order) < threshold_changes):
702
                # print("Warning: sync_network::simulate_dynamic - simulation is aborted due to low level of convergence rate (order = " + str(current_order) + ").");
703
                break;
704
            
705
        if (collect_dynamic != True):
706
            dyn_phase.append(self._phases);
707
            dyn_time.append(time_counter);
708
709
        output_sync_dynamic = sync_dynamic(dyn_phase, dyn_time, None);
710
        return output_sync_dynamic;
711
712
713
    def simulate_static(self, steps, time, solution = solve_type.FAST, collect_dynamic = False):
714
        """!
715
        @brief Performs static simulation of oscillatory network.
716
        
717
        @param[in] steps (uint): Number steps of simulations during simulation.
718
        @param[in] time (double): Time of simulation.
719
        @param[in] solution (solve_type): Type of solution.
720
        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
721
        
722
        @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
723
                otherwise returns only last values (last step of simulation) of dynamic.
724
        
725
        @see simulate()
726
        @see simulate_dynamic()
727
        
728
        """
729
        
730
        if (self._ccore_network_pointer is not None):
731
            ccore_instance_dynamic = wrapper.sync_simulate_static(self._ccore_network_pointer, steps, time, solution, collect_dynamic);
732
            return sync_dynamic(None, None, ccore_instance_dynamic);
733
        
734
        dyn_phase = [];
735
        dyn_time = [];
736
        
737
        if (collect_dynamic == True):
738
            dyn_phase.append(self._phases);
739
            dyn_time.append(0);
740
        
741
        step = time / steps;
742
        int_step = step / 10.0;
743
        
744
        for t in numpy.arange(step, time + step, step):
745
            # update states of oscillators
746
            self._phases = self._calculate_phases(solution, t, step, int_step);
747
            
748
            # update states of oscillators
749
            if (collect_dynamic == True):
750
                dyn_phase.append(self._phases);
751
                dyn_time.append(t);
752
        
753
        if (collect_dynamic != True):
754
            dyn_phase.append(self._phases);
755
            dyn_time.append(time);
756
                        
757
        output_sync_dynamic = sync_dynamic(dyn_phase, dyn_time);
758
        return output_sync_dynamic;     
759
760
761
    def _calculate_phases(self, solution, t, step, int_step):
762
        """!
763
        @brief Calculates new phases for oscillators in the network in line with current step.
764
        
765
        @param[in] solution (solve_type): Type solver of the differential equation.
766
        @param[in] t (double): Time of simulation.
767
        @param[in] step (double): Step of solution at the end of which states of oscillators should be calculated.
768
        @param[in] int_step (double): Step differentiation that is used for solving differential equation.
769
        
770
        @return (list) New states (phases) for oscillators.
771
        
772
        """
773
        
774
        next_phases = [0] * self._num_osc;    # new oscillator _phases
775
        
776
        for index in range (0, self._num_osc, 1):
777
            if (solution == solve_type.FAST):
778
                result = self._phases[index] + self._phase_kuramoto(self._phases[index], 0, index);
779
                next_phases[index] = self._phase_normalization(result);
780
                
781
            elif (solution == solve_type.RK4):
782
                result = odeint(self._phase_kuramoto, self._phases[index], numpy.arange(t - step, t, int_step), (index , ));
783
                next_phases[index] = self._phase_normalization(result[len(result) - 1][0]);
784
            
785
            else:
786
                raise NameError("Solver '" + solution + "' is not supported");
787
        
788
        return next_phases;
789
        
790
791
    def _phase_normalization(self, teta):
792
        """!
793
        @brief Normalization of phase of oscillator that should be placed between [0; 2 * pi].
794
        
795
        @param[in] teta (double): phase of oscillator.
796
        
797
        @return (double) Normalized phase.
798
        
799
        """
800
        
801
        norm_teta = teta;
802
        while (norm_teta > (2.0 * pi)) or (norm_teta < 0):
803
            if (norm_teta > (2.0 * pi)):
804
                norm_teta -= 2.0 * pi;
805
            else:
806
                norm_teta += 2.0 * pi;
807
        
808
        return norm_teta;
809