Completed
Push — master ( 1f7606...b25cbd )
by Andrei
02:07
created

sync_dynamic.allocate_correlation_matrix()   D

Complexity

Conditions 8

Size

Total Lines 31

Duplication

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