Completed
Push — master ( db8e52...7d3475 )
by Andrei
01:43
created

sync_dynamic.__getitem__()   A

Complexity

Conditions 3

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 3
dl 0
loc 13
rs 9.4285
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
    @staticmethod
276
    def animate_output_dynamic(sync_output_dynamic, animation_velocity = 75):
277
        """!
278
        @brief Shows animation of output dynamic (output of each oscillator) during simulation on a circle from [0; 2pi].
279
        
280
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
281
        @param[in] animation_velocity (uint): Interval between frames in milliseconds. 
282
        
283
        """
284
        
285
        figure = plt.figure();
286
        
287
        xcircle = numpy.linspace(-1.0, 1.0, 500);
288
        ycircle_positive = [ (1.0 - x ** 2) ** 0.5 for x in xcircle ];
289
        ycircle_negative = [ -y for y in ycircle_positive ];
290
        
291
        def init_frame():
292
            artist1, = plt.plot(xcircle, ycircle_positive, 'b-');
293
            artist2, = plt.plot(xcircle, ycircle_negative, 'b-');
294
            artist3, = plt.plot([-1.1, 1.1], [0.0, 0.0], 'b-');
295
            artist4, = plt.plot([0.0, 0.0], [-1.1, 1.1], 'b-');
296
            
297
            text1 = plt.text(-1.1, 0.0, r'$\pi$');
298
            text2 = plt.text(1.1, 0.0, r'0');
299
            text3 = plt.text(0.0, 1.1, r'$\pi$/2');
300
            text4 = plt.text(0.0, -1.1, r'3$\pi$/2');
301
            
302
            return [ artist1, artist2, artist3, artist4, text1, text2, text3, text4 ];
303
        
304
        def frame_generation(index_dynamic):
305
            dynamic = sync_output_dynamic.output[index_dynamic];
306
            
307
            xdata = [];
308
            ydata = [];
309
            
310
            for phase in dynamic:
311
                xcoord = math.cos(phase);
312
                ycoord = math.sin(phase);
313
                
314
                xdata.append(xcoord);
315
                ydata.append(ycoord);
316
            
317
            artist5, = plt.plot(xdata, ydata, 'ro');
318
            return [ artist5 ];
319
        
320
        _ = animation.FuncAnimation(figure, frame_generation, len(sync_output_dynamic), interval = animation_velocity, repeat_delay = 5000, init_func = init_frame, blit = True);
321
        plt.show();
322
    
323
    
324
    @staticmethod
325
    def animate_correlation_matrix(sync_output_dynamic, animation_velocity = 75):
326
        """!
327
        @brief Shows animation of correlation matrix between oscillators during simulation.
328
        
329
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
330
        @param[in] animation_velocity (uint): Interval between frames in milliseconds. 
331
        
332
        """
333
        
334
        figure = plt.figure();
335
        
336
        correlation_matrix = sync_output_dynamic.allocate_correlation_matrix(0);
337
        
338
        def init_frame(): 
339
            artist = plt.imshow(correlation_matrix, cmap = plt.get_cmap('cool'), interpolation='kaiser', hold = True);
340
            return [ artist ];
341
        
342
        def frame_generation(index_dynamic):
343
            correlation_matrix = sync_output_dynamic.allocate_correlation_matrix(index_dynamic);
344
            artist = plt.imshow(correlation_matrix, cmap = plt.get_cmap('cool'), interpolation='kaiser');
345
            
346
            return [ artist ];
347
348
        _ = animation.FuncAnimation(figure, frame_generation, len(sync_output_dynamic), init_func = init_frame, interval = animation_velocity , repeat_delay = 1000, blit = True);
349
        plt.show();
350
351
352
class sync_network(network):
353
    """!
354
    @brief Model of oscillatory network that is based on the Kuramoto model of synchronization.
355
    
356
    """
357
    
358
    # Protected members:
359
    _name = 'Phase Sync Network'
360
    _phases = None;                     # Current phases of oscillators.
361
    _freq = None;                       # Own frequencies of oscillators.
362
    _weight = 0;                        # Strength of connections between oscillators.
363
    
364
    _ccore_network_pointer = None;      # Pointer to CCORE Sync implementation of the network.
365
    
366
367
    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):
368
        """!
369
        @brief Constructor of oscillatory network is based on Kuramoto model.
370
        
371
        @param[in] num_osc (uint): Number of oscillators in the network.
372
        @param[in] weight (double): Coupling strength of the links between oscillators.
373
        @param[in] frequency (double): Multiplier of internal frequency of the oscillators.
374
        @param[in] type_conn (conn_type): Type of connection between oscillators in the network (all-to-all, grid, bidirectional list, etc.).
375
        @param[in] representation (conn_represent): Internal representation of connection in the network: matrix or list.
376
        @param[in] initial_phases (initial_type): Type of initialization of initial phases of oscillators (random, uniformly distributed, etc.).
377
        @param[in] ccore (bool): If True simulation is performed by CCORE library (C++ implementation of pyclustering).
378
        
379
        """
380
        
381
        if (ccore is True):
382
            self._ccore_network_pointer = wrapper.sync_create_network(num_osc, weight, frequency, type_conn, initial_phases);
383
        else:   
384
            super().__init__(num_osc, type_conn, representation);
385
            
386
            self._weight = weight;
387
            
388
            self._phases = list();
389
            self._freq = list();
390
            
391
            for index in range(0, num_osc, 1):    
392
                if (initial_phases == initial_type.RANDOM_GAUSSIAN):
393
                    self._phases.append(random.random() * 2.0 * pi);
394
                elif (initial_phases == initial_type.EQUIPARTITION):
395
                    self._phases.append( pi / num_osc * index);
396
                
397
                self._freq.append(random.random() * frequency);
398
    
399
    
400
    def __del__(self):
401
        """!
402
        @brief Destructor of oscillatory network is based on Kuramoto model.
403
        
404
        """
405
        
406
        if (self._ccore_network_pointer is not None):
407
            wrapper.sync_destroy_network(self._ccore_network_pointer);
408
            self._ccore_network_pointer = None;
409
    
410
    
411
    def sync_order(self):
412
        """!
413
        @brief Calculates level of global synchorization in the network.
414
        
415
        @return (double) Level of global synchronization.
416
        
417
        @see sync_local_order()
418
        
419
        """
420
        
421
        if (self._ccore_network_pointer is not None):
422
            return wrapper.sync_order(self._ccore_network_pointer);
423
        
424
        exp_amount = 0;
425
        average_phase = 0;
426
        
427
        for index in range(0, self._num_osc, 1):
428
            exp_amount += math.expm1( abs(1j * self._phases[index]) );
429
            average_phase += self._phases[index];
430
        
431
        exp_amount /= self._num_osc;
432
        average_phase = math.expm1( abs(1j * (average_phase / self._num_osc)) );
433
        
434
        return abs(average_phase) / abs(exp_amount);    
435
    
436
    
437
    def sync_local_order(self):
438
        """!
439
        @brief Calculates level of local (partial) synchronization in the network.
440
        
441
        @return (double) Level of local (partial) synchronization.
442
        
443
        @see sync_order()
444
        
445
        """
446
        
447
        if (self._ccore_network_pointer is not None):
448
            return wrapper.sync_local_order(self._ccore_network_pointer);
449
        
450
        exp_amount = 0.0;
451
        num_neigh = 0;
452
        
453
        for i in range(0, self._num_osc, 1):
454
            for j in range(0, self._num_osc, 1):
455
                if (self.has_connection(i, j) == True):
456
                    exp_amount += math.exp(-abs(self._phases[j] - self._phases[i]));
457
                    num_neigh += 1;
458
        
459
        if (num_neigh == 0):
460
            num_neigh = 1;
461
        
462
        return exp_amount / num_neigh;
463
    
464
    
465
    def _phase_kuramoto(self, teta, t, argv):
0 ignored issues
show
Unused Code introduced by
The argument t seems to be unused.
Loading history...
466
        """!
467
        @brief Returns result of phase calculation for specified oscillator in the network.
468
        
469
        @param[in] teta (double): Phase of the oscillator that is differentiated.
470
        @param[in] t (double): Current time of simulation.
471
        @param[in] argv (tuple): Index of the oscillator in the list.
472
        
473
        @return (double) New phase for specified oscillator (don't assign here).
474
        
475
        """
476
        
477
        index = argv;
478
        phase = 0;
479
        for k in range(0, self._num_osc):
480
            if (self.has_connection(index, k) == True):
481
                phase += math.sin(self._phases[k] - teta);
482
            
483
        return ( self._freq[index] + (phase * self._weight / self._num_osc) );
484
        
485
    
486
    def simulate(self, steps, time, solution = solve_type.FAST, collect_dynamic = True):
487
        """!
488
        @brief Performs static simulation of Sync oscillatory network.
489
        
490
        @param[in] steps (uint): Number steps of simulations during simulation.
491
        @param[in] time (double): Time of simulation.
492
        @param[in] solution (solve_type): Type of solution (solving).
493
        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
494
        
495
        @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
496
                otherwise returns only last values (last step of simulation) of dynamic.
497
        
498
        @see simulate_dynamic()
499
        @see simulate_static()
500
        
501
        """
502
        
503
        return self.simulate_static(steps, time, solution, collect_dynamic);
504
505
506
    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):
507
        """!
508
        @brief Performs dynamic simulation of the network until stop condition is not reached. Stop condition is defined by input argument 'order'.
509
        
510
        @param[in] order (double): Order of process synchronization, distributed 0..1.
511
        @param[in] solution (solve_type): Type of solution.
512
        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
513
        @param[in] step (double): Time step of one iteration of simulation.
514
        @param[in] int_step (double): Integration step, should be less than step.
515
        @param[in] threshold_changes (double): Additional stop condition that helps prevent infinite simulation, defines limit of changes of oscillators between current and previous steps.
516
        
517
        @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
518
                otherwise returns only last values (last step of simulation) of dynamic.
519
        
520
        @see simulate()
521
        @see simulate_static()
522
        
523
        """
524
        
525
        if (self._ccore_network_pointer is not None):
526
            ccore_instance_dynamic = wrapper.sync_simulate_dynamic(self._ccore_network_pointer, order, solution, collect_dynamic, step, int_step, threshold_changes);
527
            return sync_dynamic(None, None, ccore_instance_dynamic);
528
        
529
        # For statistics and integration
530
        time_counter = 0;
531
        
532
        # Prevent infinite loop. It's possible when required state cannot be reached.
533
        previous_order = 0;
534
        current_order = self.sync_local_order();
535
        
536
        # If requested input dynamics
537
        dyn_phase = [];
538
        dyn_time = [];
539
        if (collect_dynamic == True):
540
            dyn_phase.append(self._phases);
541
            dyn_time.append(0);
542
        
543
        # Execute until sync state will be reached
544
        while (current_order < order):                
545
            # update states of oscillators
546
            self._phases = self._calculate_phases(solution, time_counter, step, int_step);
547
            
548
            # update time
549
            time_counter += step;
550
            
551
            # if requested input dynamic
552
            if (collect_dynamic == True):
553
                dyn_phase.append(self._phases);
554
                dyn_time.append(time_counter);
555
                
556
            # update orders
557
            previous_order = current_order;
558
            current_order = self.sync_local_order();
559
            
560
            # hang prevention
561
            if (abs(current_order - previous_order) < threshold_changes):
562
                # print("Warning: sync_network::simulate_dynamic - simulation is aborted due to low level of convergence rate (order = " + str(current_order) + ").");
563
                break;
564
            
565
        if (collect_dynamic != True):
566
            dyn_phase.append(self._phases);
567
            dyn_time.append(time_counter);
568
569
        output_sync_dynamic = sync_dynamic(dyn_phase, dyn_time, None);
570
        return output_sync_dynamic;
571
572
573
    def simulate_static(self, steps, time, solution = solve_type.FAST, collect_dynamic = False):
574
        """!
575
        @brief Performs static simulation of oscillatory network.
576
        
577
        @param[in] steps (uint): Number steps of simulations during simulation.
578
        @param[in] time (double): Time of simulation.
579
        @param[in] solution (solve_type): Type of solution.
580
        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
581
        
582
        @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
583
                otherwise returns only last values (last step of simulation) of dynamic.
584
        
585
        @see simulate()
586
        @see simulate_dynamic()
587
        
588
        """
589
        
590
        if (self._ccore_network_pointer is not None):
591
            ccore_instance_dynamic = wrapper.sync_simulate_static(self._ccore_network_pointer, steps, time, solution, collect_dynamic);
592
            return sync_dynamic(None, None, ccore_instance_dynamic);
593
        
594
        dyn_phase = [];
595
        dyn_time = [];
596
        
597
        if (collect_dynamic == True):
598
            dyn_phase.append(self._phases);
599
            dyn_time.append(0);
600
        
601
        step = time / steps;
602
        int_step = step / 10.0;
603
        
604
        for t in numpy.arange(step, time + step, step):
605
            # update states of oscillators
606
            self._phases = self._calculate_phases(solution, t, step, int_step);
607
            
608
            # update states of oscillators
609
            if (collect_dynamic == True):
610
                dyn_phase.append(self._phases);
611
                dyn_time.append(t);
612
        
613
        if (collect_dynamic != True):
614
            dyn_phase.append(self._phases);
615
            dyn_time.append(time);
616
                        
617
        output_sync_dynamic = sync_dynamic(dyn_phase, dyn_time);
618
        return output_sync_dynamic;     
619
620
621
    def _calculate_phases(self, solution, t, step, int_step):
622
        """!
623
        @brief Calculates new phases for oscillators in the network in line with current step.
624
        
625
        @param[in] solution (solve_type): Type solver of the differential equation.
626
        @param[in] t (double): Time of simulation.
627
        @param[in] step (double): Step of solution at the end of which states of oscillators should be calculated.
628
        @param[in] int_step (double): Step differentiation that is used for solving differential equation.
629
        
630
        @return (list) New states (phases) for oscillators.
631
        
632
        """
633
        
634
        next_phases = [0] * self._num_osc;    # new oscillator _phases
635
        
636
        for index in range (0, self._num_osc, 1):
637
            if (solution == solve_type.FAST):
638
                result = self._phases[index] + self._phase_kuramoto(self._phases[index], 0, index);
639
                next_phases[index] = self._phase_normalization(result);
640
                
641
            elif (solution == solve_type.RK4):
642
                result = odeint(self._phase_kuramoto, self._phases[index], numpy.arange(t - step, t, int_step), (index , ));
643
                next_phases[index] = self._phase_normalization(result[len(result) - 1][0]);
644
            
645
            else:
646
                raise NameError("Solver '" + solution + "' is not supported");
647
        
648
        return next_phases;
649
        
650
651
    def _phase_normalization(self, teta):
652
        """!
653
        @brief Normalization of phase of oscillator that should be placed between [0; 2 * pi].
654
        
655
        @param[in] teta (double): phase of oscillator.
656
        
657
        @return (double) Normalized phase.
658
        
659
        """
660
        
661
        norm_teta = teta;
662
        while (norm_teta > (2.0 * pi)) or (norm_teta < 0):
663
            if (norm_teta > (2.0 * pi)):
664
                norm_teta -= 2.0 * pi;
665
            else:
666
                norm_teta += 2.0 * pi;
667
        
668
        return norm_teta;
669