Completed
Push — master ( 1c08ec...4b45a1 )
by Andrei
01:32
created

sync_visualizer.animate()   B

Complexity

Conditions 3

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 3
dl 0
loc 33
rs 8.8571
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):
60
            return 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):
72
            return 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):
128
        """!
129
        @brief Allocate clusters in line with ensembles of synchronous oscillators where each
130
               synchronous ensemble corresponds to only one cluster.
131
               
132
        @param[in] tolerance (double): Maximum error for allocation of synchronous ensemble oscillators.
133
        @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]).
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);
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 = self._dynamic[len(self._dynamic) - 1];
155
        
156
        clusters = [];
157
        if (number_oscillators > 0):
158
            clusters.append([0]);
159
        
160
        for i in range(1, number_oscillators, 1):
161
            cluster_allocated = False;
162
            for cluster in clusters:
163
                for neuron_index in cluster:
164
                    last_state_shifted = abs(last_state[i] - 2 * pi);
165
                    
166
                    if ( ( (last_state[i] < (last_state[neuron_index] + tolerance)) and (last_state[i] > (last_state[neuron_index] - tolerance)) ) or
167
                         ( (last_state_shifted < (last_state[neuron_index] + tolerance)) and (last_state_shifted > (last_state[neuron_index] - tolerance)) ) ):
168
                        cluster_allocated = True;
169
                        
170
                        real_index = i;
171
                        if (indexes is not None):
172
                            real_index = indexes[i];
173
                        
174
                        cluster.append(real_index);
175
                        break;
176
                
177
                if (cluster_allocated == True):
178
                    break;
179
            
180
            if (cluster_allocated == False):
181
                clusters.append([i]);
182
        
183
        return clusters;
184
    
185
    
186
    def allocate_correlation_matrix(self, iteration = None):
187
        """!
188
        @brief Allocate correlation matrix between oscillators at the specified step of simulation.
189
               
190
        @param[in] iteration (uint): Number of iteration of simulation for which correlation matrix should be allocated.
191
                                      If iternation number is not specified, the last step of simulation is used for the matrix allocation.
192
        
193
        @return (list) Correlation matrix between oscillators with size [number_oscillators x number_oscillators].
194
        
195
        """
196
        
197
        if (self._ccore_sync_dynamic_pointer is not None):
198
            return wrapper.sync_dynamic_allocate_correlation_matrix(self._ccore_sync_dynamic_pointer, iteration);
199
        
200
        if ( (self._dynamic is None) or (len(self._dynamic) == 0) ):
201
            return [];
202
        
203
        dynamic = self._dynamic;
204
        current_dynamic = dynamic[len(dynamic) - 1];
205
        
206
        if (iteration is not None):
207
            current_dynamic = dynamic[iteration];
208
        
209
        number_oscillators = len(dynamic[0]);
210
        affinity_matrix = [ [ 0.0 for i in range(number_oscillators) ] for j in range(number_oscillators) ];  
211
        
212
        for i in range(number_oscillators):
213
            for j in range(number_oscillators):
214
                phase1 = current_dynamic[i];
215
                phase2 = current_dynamic[j];
216
                
217
                affinity_matrix[i][j] = math.sin(abs(phase1 - phase2));
218
                
219
        return affinity_matrix;
220
221
222
class sync_visualizer:
223
    """!
224
    @brief Visualizer of output dynamic of sync network (Sync).
225
    
226
    """
227
        
228
    @staticmethod
229
    def show_output_dynamic(sync_output_dynamic):
230
        """!
231
        @brief Shows output dynamic (output of each oscillator) during simulation.
232
        
233
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
234
        
235
        @see show_output_dynamics
236
        
237
        """
238
        
239
        draw_dynamics(sync_output_dynamic.time, sync_output_dynamic.output, x_title = "t", y_title = "phase", y_lim = [0, 2 * 3.14]);
240
    
241
    
242
    @staticmethod
243
    def show_output_dynamics(sync_output_dynamics):
244
        """!
245
        @brief Shows several output dynamics (output of each oscillator) during simulation.
246
        @details Each dynamic is presented on separate plot.
247
        
248
        @param[in] sync_output_dynamics (list): list of output dynamics 'sync_dynamic' of the Sync network.
249
        
250
        @see show_output_dynamic
251
        
252
        """
253
        
254
        draw_dynamics_set(sync_output_dynamics, "t", "phase", None, [0, 2 * 3.14], False, False);
255
    
256
    
257
    @staticmethod
258
    def show_correlation_matrix(sync_output_dynamic, iteration = None):
259
        """!
260
        @brief Shows correlation matrix between oscillators at the specified iteration.
261
        
262
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
263
        @param[in] iteration (uint): Number of interation of simulation for which correlation matrix should be allocated.
264
                                      If iternation number is not specified, the last step of simulation is used for the matrix allocation.
265
        
266
        """
267
        
268
        _ = plt.figure();
269
        correlation_matrix = sync_output_dynamic.allocate_correlation_matrix(iteration);
270
        
271
        plt.imshow(correlation_matrix, cmap = plt.get_cmap('cool'), interpolation='kaiser'); 
272
        plt.show();
273
        
274
    
275
    
276
    @staticmethod
277
    def animate_output_dynamic(sync_output_dynamic, animation_velocity = 75):
278
        """!
279
        @brief Shows animation of output dynamic (output of each oscillator) during simulation on a circle from [0; 2pi].
280
        
281
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
282
        @param[in] animation_velocity (uint): Interval between frames in milliseconds. 
283
        
284
        """
285
        
286
        figure = plt.figure();
287
        
288
        dynamic = sync_output_dynamic.output[0];
289
        artist, = plt.polar(dynamic, [1.0] * len(dynamic), 'o', color = 'blue');
290
        
291
        def init_frame():
292
            return [ artist ];
293
        
294
        def frame_generation(index_dynamic):
295
            dynamic = sync_output_dynamic.output[index_dynamic];
296
            artist.set_data(dynamic, [1.0] * len(dynamic));
297
            
298
            return [ artist ];
299
        
300
        _ = animation.FuncAnimation(figure, frame_generation, len(sync_output_dynamic), interval = animation_velocity, init_func = init_frame, repeat_delay = 5000);
301
        plt.show();
302
    
303
    
304
    @staticmethod
305
    def animate_correlation_matrix(sync_output_dynamic, animation_velocity = 75, colormap = 'cool', save_movie = None):
306
        """!
307
        @brief Shows animation of correlation matrix between oscillators during simulation.
308
        
309
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
310
        @param[in] animation_velocity (uint): Interval between frames in milliseconds.
311
        @param[in] colormap (string): Name of colormap that is used by matplotlib ('gray', 'pink', 'cool', spring', etc.).
312
        @param[in] save_movie (string): If it is specified then animation will be stored to file that is specified in this parameter.
313
        
314
        """
315
        
316
        figure = plt.figure();
317
        
318
        correlation_matrix = sync_output_dynamic.allocate_correlation_matrix(0);
319
        artist = plt.imshow(correlation_matrix, cmap = plt.get_cmap(colormap), interpolation='kaiser', hold = True);
320
        
321
        def init_frame(): 
322
            return [ artist ];
323
        
324
        def frame_generation(index_dynamic):
325
            correlation_matrix = sync_output_dynamic.allocate_correlation_matrix(index_dynamic);
326
            artist.set_data(correlation_matrix);
327
            
328
            return [ artist ];
329
330
        correlation_animation = animation.FuncAnimation(figure, frame_generation, len(sync_output_dynamic), init_func = init_frame, interval = animation_velocity , repeat_delay = 1000, blit = True);
331
        
332
        if (save_movie is not None):
333
            correlation_animation.save(save_movie, writer = 'ffmpeg', fps = 15);
334
        else:
335
            plt.show();
336
    
337
    
338
    @staticmethod
339
    def animate(sync_output_dynamic):
340
        """!
341
        @brief Shows animation of phase coordinates and animation of correlation matrix together for the Sync dynamic output on the same figure.
342
        
343
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
344
        
345
        """
346
        
347
        dynamic = sync_output_dynamic.output[0];
348
        correlation_matrix = sync_output_dynamic.allocate_correlation_matrix(0);
349
        
350
        figure = plt.figure(1);
351
        ax1 = figure.add_subplot(121, projection='polar');
352
        ax2 = figure.add_subplot(122);
353
        
354
        artist1, = ax1.plot(dynamic, [1.0] * len(dynamic), marker = 'o', color = 'blue', ls = '');
355
        artist2 = ax2.imshow(correlation_matrix, cmap = plt.get_cmap('cool'), interpolation='kaiser');
356
        
357
        def init_frame():
358
            return [ artist1, artist2 ];
359
360
        def frame_generation(index_dynamic):
361
            dynamic = sync_output_dynamic.output[index_dynamic];
362
            artist1.set_data(dynamic, [1.0] * len(dynamic));
363
            
364
            correlation_matrix = sync_output_dynamic.allocate_correlation_matrix(index_dynamic);
365
            artist2.set_data(correlation_matrix);
366
            
367
            return [ artist1, artist2 ];
368
        
369
        _ = animation.FuncAnimation(figure, frame_generation, len(sync_output_dynamic), interval = 75, init_func = init_frame, repeat_delay = 5000);
370
        plt.show();
371
372
373
class sync_network(network):
374
    """!
375
    @brief Model of oscillatory network that is based on the Kuramoto model of synchronization.
376
    
377
    """
378
    
379
    # Protected members:
380
    _name = 'Phase Sync Network'
381
    _phases = None;                     # Current phases of oscillators.
382
    _freq = None;                       # Own frequencies of oscillators.
383
    _weight = 0;                        # Strength of connections between oscillators.
384
    
385
    _ccore_network_pointer = None;      # Pointer to CCORE Sync implementation of the network.
386
    
387
388
    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):
389
        """!
390
        @brief Constructor of oscillatory network is based on Kuramoto model.
391
        
392
        @param[in] num_osc (uint): Number of oscillators in the network.
393
        @param[in] weight (double): Coupling strength of the links between oscillators.
394
        @param[in] frequency (double): Multiplier of internal frequency of the oscillators.
395
        @param[in] type_conn (conn_type): Type of connection between oscillators in the network (all-to-all, grid, bidirectional list, etc.).
396
        @param[in] representation (conn_represent): Internal representation of connection in the network: matrix or list.
397
        @param[in] initial_phases (initial_type): Type of initialization of initial phases of oscillators (random, uniformly distributed, etc.).
398
        @param[in] ccore (bool): If True simulation is performed by CCORE library (C++ implementation of pyclustering).
399
        
400
        """
401
        
402
        if (ccore is True):
403
            self._ccore_network_pointer = wrapper.sync_create_network(num_osc, weight, frequency, type_conn, initial_phases);
404
        else:   
405
            super().__init__(num_osc, type_conn, representation);
406
            
407
            self._weight = weight;
408
            
409
            self._phases = list();
410
            self._freq = list();
411
            
412
            for index in range(0, num_osc, 1):    
413
                if (initial_phases == initial_type.RANDOM_GAUSSIAN):
414
                    self._phases.append(random.random() * 2.0 * pi);
415
                elif (initial_phases == initial_type.EQUIPARTITION):
416
                    self._phases.append( pi / num_osc * index);
417
                
418
                self._freq.append(random.random() * frequency);
419
    
420
    
421
    def __del__(self):
422
        """!
423
        @brief Destructor of oscillatory network is based on Kuramoto model.
424
        
425
        """
426
        
427
        if (self._ccore_network_pointer is not None):
428
            wrapper.sync_destroy_network(self._ccore_network_pointer);
429
            self._ccore_network_pointer = None;
430
    
431
    
432
    def sync_order(self):
433
        """!
434
        @brief Calculates level of global synchorization in the network.
435
        
436
        @return (double) Level of global synchronization.
437
        
438
        @see sync_local_order()
439
        
440
        """
441
        
442
        if (self._ccore_network_pointer is not None):
443
            return wrapper.sync_order(self._ccore_network_pointer);
444
        
445
        exp_amount = 0;
446
        average_phase = 0;
447
        
448
        for index in range(0, self._num_osc, 1):
449
            exp_amount += math.expm1( abs(1j * self._phases[index]) );
450
            average_phase += self._phases[index];
451
        
452
        exp_amount /= self._num_osc;
453
        average_phase = math.expm1( abs(1j * (average_phase / self._num_osc)) );
454
        
455
        return abs(average_phase) / abs(exp_amount);    
456
    
457
    
458
    def sync_local_order(self):
459
        """!
460
        @brief Calculates level of local (partial) synchronization in the network.
461
        
462
        @return (double) Level of local (partial) synchronization.
463
        
464
        @see sync_order()
465
        
466
        """
467
        
468
        if (self._ccore_network_pointer is not None):
469
            return wrapper.sync_local_order(self._ccore_network_pointer);
470
        
471
        exp_amount = 0.0;
472
        num_neigh = 0;
473
        
474
        for i in range(0, self._num_osc, 1):
475
            for j in range(0, self._num_osc, 1):
476
                if (self.has_connection(i, j) == True):
477
                    exp_amount += math.exp(-abs(self._phases[j] - self._phases[i]));
478
                    num_neigh += 1;
479
        
480
        if (num_neigh == 0):
481
            num_neigh = 1;
482
        
483
        return exp_amount / num_neigh;
484
    
485
    
486
    def _phase_kuramoto(self, teta, t, argv):
0 ignored issues
show
Unused Code introduced by
The argument t seems to be unused.
Loading history...
487
        """!
488
        @brief Returns result of phase calculation for specified oscillator in the network.
489
        
490
        @param[in] teta (double): Phase of the oscillator that is differentiated.
491
        @param[in] t (double): Current time of simulation.
492
        @param[in] argv (tuple): Index of the oscillator in the list.
493
        
494
        @return (double) New phase for specified oscillator (don't assign here).
495
        
496
        """
497
        
498
        index = argv;
499
        phase = 0;
500
        for k in range(0, self._num_osc):
501
            if (self.has_connection(index, k) == True):
502
                phase += math.sin(self._phases[k] - teta);
503
            
504
        return ( self._freq[index] + (phase * self._weight / self._num_osc) );
505
        
506
    
507
    def simulate(self, steps, time, solution = solve_type.FAST, collect_dynamic = True):
508
        """!
509
        @brief Performs static simulation of Sync oscillatory network.
510
        
511
        @param[in] steps (uint): Number steps of simulations during simulation.
512
        @param[in] time (double): Time of simulation.
513
        @param[in] solution (solve_type): Type of solution (solving).
514
        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
515
        
516
        @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
517
                otherwise returns only last values (last step of simulation) of dynamic.
518
        
519
        @see simulate_dynamic()
520
        @see simulate_static()
521
        
522
        """
523
        
524
        return self.simulate_static(steps, time, solution, collect_dynamic);
525
526
527
    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):
528
        """!
529
        @brief Performs dynamic simulation of the network until stop condition is not reached. Stop condition is defined by input argument 'order'.
530
        
531
        @param[in] order (double): Order of process synchronization, distributed 0..1.
532
        @param[in] solution (solve_type): Type of solution.
533
        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
534
        @param[in] step (double): Time step of one iteration of simulation.
535
        @param[in] int_step (double): Integration step, should be less than step.
536
        @param[in] threshold_changes (double): Additional stop condition that helps prevent infinite simulation, defines limit of changes of oscillators between current and previous steps.
537
        
538
        @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
539
                otherwise returns only last values (last step of simulation) of dynamic.
540
        
541
        @see simulate()
542
        @see simulate_static()
543
        
544
        """
545
        
546
        if (self._ccore_network_pointer is not None):
547
            ccore_instance_dynamic = wrapper.sync_simulate_dynamic(self._ccore_network_pointer, order, solution, collect_dynamic, step, int_step, threshold_changes);
548
            return sync_dynamic(None, None, ccore_instance_dynamic);
549
        
550
        # For statistics and integration
551
        time_counter = 0;
552
        
553
        # Prevent infinite loop. It's possible when required state cannot be reached.
554
        previous_order = 0;
555
        current_order = self.sync_local_order();
556
        
557
        # If requested input dynamics
558
        dyn_phase = [];
559
        dyn_time = [];
560
        if (collect_dynamic == True):
561
            dyn_phase.append(self._phases);
562
            dyn_time.append(0);
563
        
564
        # Execute until sync state will be reached
565
        while (current_order < order):                
566
            # update states of oscillators
567
            self._phases = self._calculate_phases(solution, time_counter, step, int_step);
568
            
569
            # update time
570
            time_counter += step;
571
            
572
            # if requested input dynamic
573
            if (collect_dynamic == True):
574
                dyn_phase.append(self._phases);
575
                dyn_time.append(time_counter);
576
                
577
            # update orders
578
            previous_order = current_order;
579
            current_order = self.sync_local_order();
580
            
581
            # hang prevention
582
            if (abs(current_order - previous_order) < threshold_changes):
583
                # print("Warning: sync_network::simulate_dynamic - simulation is aborted due to low level of convergence rate (order = " + str(current_order) + ").");
584
                break;
585
            
586
        if (collect_dynamic != True):
587
            dyn_phase.append(self._phases);
588
            dyn_time.append(time_counter);
589
590
        output_sync_dynamic = sync_dynamic(dyn_phase, dyn_time, None);
591
        return output_sync_dynamic;
592
593
594
    def simulate_static(self, steps, time, solution = solve_type.FAST, collect_dynamic = False):
595
        """!
596
        @brief Performs static simulation of oscillatory network.
597
        
598
        @param[in] steps (uint): Number steps of simulations during simulation.
599
        @param[in] time (double): Time of simulation.
600
        @param[in] solution (solve_type): Type of solution.
601
        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
602
        
603
        @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
604
                otherwise returns only last values (last step of simulation) of dynamic.
605
        
606
        @see simulate()
607
        @see simulate_dynamic()
608
        
609
        """
610
        
611
        if (self._ccore_network_pointer is not None):
612
            ccore_instance_dynamic = wrapper.sync_simulate_static(self._ccore_network_pointer, steps, time, solution, collect_dynamic);
613
            return sync_dynamic(None, None, ccore_instance_dynamic);
614
        
615
        dyn_phase = [];
616
        dyn_time = [];
617
        
618
        if (collect_dynamic == True):
619
            dyn_phase.append(self._phases);
620
            dyn_time.append(0);
621
        
622
        step = time / steps;
623
        int_step = step / 10.0;
624
        
625
        for t in numpy.arange(step, time + step, step):
626
            # update states of oscillators
627
            self._phases = self._calculate_phases(solution, t, step, int_step);
628
            
629
            # update states of oscillators
630
            if (collect_dynamic == True):
631
                dyn_phase.append(self._phases);
632
                dyn_time.append(t);
633
        
634
        if (collect_dynamic != True):
635
            dyn_phase.append(self._phases);
636
            dyn_time.append(time);
637
                        
638
        output_sync_dynamic = sync_dynamic(dyn_phase, dyn_time);
639
        return output_sync_dynamic;     
640
641
642
    def _calculate_phases(self, solution, t, step, int_step):
643
        """!
644
        @brief Calculates new phases for oscillators in the network in line with current step.
645
        
646
        @param[in] solution (solve_type): Type solver of the differential equation.
647
        @param[in] t (double): Time of simulation.
648
        @param[in] step (double): Step of solution at the end of which states of oscillators should be calculated.
649
        @param[in] int_step (double): Step differentiation that is used for solving differential equation.
650
        
651
        @return (list) New states (phases) for oscillators.
652
        
653
        """
654
        
655
        next_phases = [0] * self._num_osc;    # new oscillator _phases
656
        
657
        for index in range (0, self._num_osc, 1):
658
            if (solution == solve_type.FAST):
659
                result = self._phases[index] + self._phase_kuramoto(self._phases[index], 0, index);
660
                next_phases[index] = self._phase_normalization(result);
661
                
662
            elif (solution == solve_type.RK4):
663
                result = odeint(self._phase_kuramoto, self._phases[index], numpy.arange(t - step, t, int_step), (index , ));
664
                next_phases[index] = self._phase_normalization(result[len(result) - 1][0]);
665
            
666
            else:
667
                raise NameError("Solver '" + solution + "' is not supported");
668
        
669
        return next_phases;
670
        
671
672
    def _phase_normalization(self, teta):
673
        """!
674
        @brief Normalization of phase of oscillator that should be placed between [0; 2 * pi].
675
        
676
        @param[in] teta (double): phase of oscillator.
677
        
678
        @return (double) Normalized phase.
679
        
680
        """
681
        
682
        norm_teta = teta;
683
        while (norm_teta > (2.0 * pi)) or (norm_teta < 0):
684
            if (norm_teta > (2.0 * pi)):
685
                norm_teta -= 2.0 * pi;
686
            else:
687
                norm_teta += 2.0 * pi;
688
        
689
        return norm_teta;
690