Completed
Push — master ( 8edca8...4108fb )
by Andrei
01:34
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
from scipy.integrate import ode;
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...
Unused Code introduced by
Unused ode imported from scipy.integrate
Loading history...
42
43
from pyclustering.nnet import *;
0 ignored issues
show
Unused Code introduced by
IntEnum was imported with wildcard, but is not used.
Loading history...
44
45
from pyclustering.utils import draw_dynamics;
46
47
48
class sync_dynamic:
49
    """!
50
    @brief Represents output dynamic of Sync.
51
    
52
    """
53
    
54
    @property
55
    def output(self):
56
        """!
57
        @brief (list) Returns outputs of oscillator during simulation.
58
        
59
        """
60
        if (self._ccore_sync_dynamic_pointer is not None):
61
            return wrapper.sync_dynamic_get_output(self._ccore_sync_dynamic_pointer);
62
        
63
        return self._dynamic;
64
    
65
    
66
    @property
67
    def time(self):
68
        """!
69
        @brief (list) Returns sampling times when dynamic is measured during simulation.
70
        
71
        """
72
        if (self._ccore_sync_dynamic_pointer is not None):
73
            return wrapper.sync_dynamic_get_time(self._ccore_sync_dynamic_pointer);
74
        
75
        return self._time;
76
    
77
    
78
    def __init__(self, phase, time, ccore = None):
79
        """!
80
        @brief Constructor of Sync dynamic.
81
        
82
        @param[in] phase (list): Dynamic of oscillators on each step of simulation. If ccore pointer is specified than it can be ignored.
83
        @param[in] time (list): Simulation time.
84
        @param[in] ccore (ctypes.pointer): Pointer to CCORE sync_dynamic instance in memory.
85
        
86
        """
87
        
88
        self._dynamic = phase;
89
        self._time = time;
90
        self._ccore_sync_dynamic_pointer = ccore;
91
    
92
    
93
    def __del__(self):
94
        """!
95
        @brief Default destructor of Sync dynamic.
96
        
97
        """
98
        if (self._ccore_sync_dynamic_pointer is not None):
99
            wrapper.sync_dynamic_destroy(self._ccore_sync_dynamic_pointer);
100
    
101
    
102
    def __len__(self):
103
        """!
104
        @brief (uint) Returns number of simulation steps that are stored in dynamic.
105
        
106
        """
107
        if (self._ccore_sync_dynamic_pointer is not None):
108
            return wrapper.sync_dynamic_get_size(self._ccore_sync_dynamic_pointer);
109
        
110
        return len(self._dynamic);
111
    
112
    
113
    def allocate_sync_ensembles(self, tolerance = 0.01):
114
        """!
115
        @brief Allocate clusters in line with ensembles of synchronous oscillators where each
116
               synchronous ensemble corresponds to only one cluster.
117
               
118
        @param[in] tolerance (double): Maximum error for allocation of synchronous ensemble oscillators.
119
        
120
        @return (list) Grours (lists) of indexes of synchronous oscillators.
121
                For example [ [index_osc1, index_osc3], [index_osc2], [index_osc4, index_osc5] ].
122
        
123
        """
124
        
125
        if (self._ccore_sync_dynamic_pointer is not None):
126
            return wrapper.sync_dynamic_allocate_sync_ensembles(self._ccore_sync_dynamic_pointer, tolerance);
127
        
128
        if ( (self._dynamic is None) or (len(self._dynamic) == 0) ):
129
            return [];
130
        
131
        number_oscillators = len(self._dynamic[0]);
132
        last_state = self._dynamic[len(self._dynamic) - 1];
133
        
134
        clusters = [];
135
        if (number_oscillators > 0):
136
            clusters.append([0]);
137
        
138
        for i in range(1, number_oscillators, 1):
139
            cluster_allocated = False;
140
            for cluster in clusters:
141
                for neuron_index in cluster:
142
                    last_state_shifted = abs(last_state[i] - 2 * pi);
143
                    
144
                    if ( ( (last_state[i] < (last_state[neuron_index] + tolerance)) and (last_state[i] > (last_state[neuron_index] - tolerance)) ) or
145
                         ( (last_state_shifted < (last_state[neuron_index] + tolerance)) and (last_state_shifted > (last_state[neuron_index] - tolerance)) ) ):
146
                        cluster_allocated = True;
147
                        cluster.append(i);
148
                        break;
149
                
150
                if (cluster_allocated == True):
151
                    break;
152
            
153
            if (cluster_allocated == False):
154
                clusters.append([i]);
155
        
156
        return clusters;
157
    
158
    
159
    def allocate_correlation_matrix(self, iteration = None):
160
        """!
161
        @brief Allocate correlation matrix between oscillators at the specified step of simulation.
162
               
163
        @param[in] iteration (uint): Number of iteration of simulation for which correlation matrix should be allocated.
164
                                      If iternation number is not specified, the last step of simulation is used for the matrix allocation.
165
        
166
        @return (list) Correlation matrix between oscillators with size [number_oscillators x number_oscillators].
167
        
168
        """
169
        
170
        if ( (self._dynamic is None) or (len(self._dynamic) == 0) ):
171
            return [];
172
        
173
        dynamic = self._dynamic;
174
        current_dynamic = dynamic[len(dynamic) - 1];
175
        
176
        if (iteration is not None):
177
            current_dynamic = dynamic[iteration];
178
        
179
        number_oscillators = len(dynamic[0]);
180
        affinity_matrix = [ [ 0.0 for i in range(number_oscillators) ] for j in range(number_oscillators) ];  
181
        
182
        for i in range(number_oscillators):
183
            for j in range(number_oscillators):
184
                phase1 = current_dynamic[i];
185
                phase2 = current_dynamic[j];
186
                
187
                affinity_matrix[i][j] = math.sin(phase1 - phase2);
188
                
189
        return affinity_matrix;
190
191
192
class sync_visualizer:
193
    """!
194
    @brief Visualizer of output dynamic of sync network (Sync).
195
    
196
    """
197
        
198
    @staticmethod
199
    def show_output_dynamic(sync_output_dynamic):
200
        """!
201
        @brief Shows output dynamic (output of each oscillator) during simulation.
202
        
203
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
204
        
205
        """
206
        
207
        draw_dynamics(sync_output_dynamic.time, sync_output_dynamic.output, x_title = "t", y_title = "phase", y_lim = [0, 2 * 3.14]);
208
    
209
    
210
    @staticmethod
211
    def show_correlation_matrix(sync_output_dynamic, iteration = None):
212
        """!
213
        @brief Shows correlation matrix between oscillators at the specified iteration.
214
        
215
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
216
        @param[in] iteration (uint): Number of interation of simulation for which correlation matrix should be allocated.
217
                                      If iternation number is not specified, the last step of simulation is used for the matrix allocation.
218
        
219
        """
220
        
221
        figure = plt.figure();
0 ignored issues
show
Unused Code introduced by
The variable figure seems to be unused.
Loading history...
222
        correlation_matrix = sync_output_dynamic.allocate_correlation_matrix(iteration);
223
        
224
        plt.imshow(correlation_matrix, cmap = plt.get_cmap('cool'), interpolation='kaiser'); 
225
        plt.show();
226
        
227
    
228
    @staticmethod
229
    def animate_output_dynamic(sync_output_dynamic, animation_velocity = 75):
0 ignored issues
show
Unused Code introduced by
The argument animation_velocity seems to be unused.
Loading history...
230
        """!
231
        @brief Shows animation of output dynamic (output of each oscillator) during simulation on a circle from [0; 2pi].
232
        
233
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
234
        @param[in] animation_velocity (uint): Interval between frames in milliseconds. 
235
        
236
        """
237
        
238
        figure = plt.figure();
239
        
240
        xcircle = numpy.linspace(-1.0, 1.0, 500);
241
        ycircle_positive = [ (1.0 - x ** 2) ** 0.5 for x in xcircle ];
242
        ycircle_negative = [ -y for y in ycircle_positive ];
243
        
244
        def init_frame():
245
            artist1, = plt.plot(xcircle, ycircle_positive, 'b-');
246
            artist2, = plt.plot(xcircle, ycircle_negative, 'b-');
247
            artist3, = plt.plot([-1.1, 1.1], [0.0, 0.0], 'b-');
248
            artist4, = plt.plot([0.0, 0.0], [-1.1, 1.1], 'b-');
249
            
250
            text1 = plt.text(-1.1, 0.0, r'$\pi$');
251
            text2 = plt.text(1.1, 0.0, r'0');
252
            text3 = plt.text(0.0, 1.1, r'$\pi$/2');
253
            text4 = plt.text(0.0, -1.1, r'3$\pi$/2');
254
            
255
            return [ artist1, artist2, artist3, artist4, text1, text2, text3, text4 ];          
256
        
257
        def frame_generation(index_dynamic):
258
            dynamic = sync_output_dynamic.output[index_dynamic];
259
            
260
            xdata = [];
261
            ydata = [];
262
            
263
            for phase in dynamic:
264
                xcoord = math.cos(phase);
265
                ycoord = math.sin(phase);
266
                
267
                xdata.append(xcoord);
268
                ydata.append(ycoord);
269
            
270
            artist5, = plt.plot(xdata, ydata, 'ro');
271
            return [ artist5 ];
272
        
273
        im_ani = animation.FuncAnimation(figure, frame_generation, len(sync_output_dynamic), interval = 75, repeat_delay = 5000, init_func = init_frame, blit = True);
0 ignored issues
show
Unused Code introduced by
The variable im_ani seems to be unused.
Loading history...
274
        plt.show();
275
    
276
    
277
    @staticmethod
278
    def animate_correlation_matrix(sync_output_dynamic, animation_velocity = 75):
0 ignored issues
show
Unused Code introduced by
The argument animation_velocity seems to be unused.
Loading history...
279
        """!
280
        @brief Shows animation of correlation matrix between oscillators during simulation.
281
        
282
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
283
        @param[in] animation_velocity (uint): Interval between frames in milliseconds. 
284
        
285
        """
286
        
287
        figure = plt.figure();
288
        
289
        correlation_matrix = sync_output_dynamic.allocate_correlation_matrix(0);
290
        
291
        def init_frame(): 
292
            artist = plt.imshow(correlation_matrix, cmap = plt.get_cmap('cool'), interpolation='kaiser', hold = True);           
293
            return [ artist ];   
294
        
295
        def frame_generation(index_dynamic):
296
            correlation_matrix = sync_output_dynamic.allocate_correlation_matrix(index_dynamic);
297
            artist = plt.imshow(correlation_matrix, cmap = plt.get_cmap('cool'), interpolation='kaiser');
298
            
299
            return [ artist ];
300
301
        im_ani = animation.FuncAnimation(figure, frame_generation, len(sync_output_dynamic), init_func = init_frame, interval = 75, repeat_delay = 1000, blit = True);
0 ignored issues
show
Unused Code introduced by
The variable im_ani seems to be unused.
Loading history...
302
        plt.show();        
303
304
305
class sync_network(network):    
306
    """!
307
    @brief Model of oscillatory network that is based on the Kuramoto model of synchronization.
308
    
309
    """
310
    
311
    # Protected members:
312
    _name = 'Phase Sync Network'
313
    _phases = None;                     # Current phases of oscillators.
314
    _freq = None;                       # Own frequencies of oscillators.
315
    _weight = 0;                        # Strength of connections between oscillators.
316
    
317
    _ccore_network_pointer = None;      # Pointer to CCORE Sync implementation of the network.
318
    
319
320
    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):
321
        """!
322
        @brief Constructor of oscillatory network is based on Kuramoto model.
323
        
324
        @param[in] num_osc (uint): Number of oscillators in the network.
325
        @param[in] weight (double): Coupling strength of the links between oscillators.
326
        @param[in] frequency (double): Multiplier of internal frequency of the oscillators.
327
        @param[in] type_conn (conn_type): Type of connection between oscillators in the network (all-to-all, grid, bidirectional list, etc.).
328
        @param[in] representation (conn_represent): Internal representation of connection in the network: matrix or list.
329
        @param[in] initial_phases (initial_type): Type of initialization of initial phases of oscillators (random, uniformly distributed, etc.).
330
        @param[in] ccore (bool): If True simulation is performed by CCORE library (C++ implementation of pyclustering).
331
        
332
        """
333
        
334
        if (ccore is True):
335
            self._ccore_network_pointer = wrapper.sync_create_network(num_osc, weight, frequency, type_conn, initial_phases);
336
        else:   
337
            super().__init__(num_osc, type_conn, representation);
338
            
339
            self._weight = weight;
340
            
341
            self._phases = list();
342
            self._freq = list();
343
            
344
            for index in range(0, num_osc, 1):    
345
                if (initial_phases == initial_type.RANDOM_GAUSSIAN):
346
                    self._phases.append(random.random() * 2.0 * pi);
347
                elif (initial_phases == initial_type.EQUIPARTITION):
348
                    self._phases.append( pi / num_osc * index);
349
                
350
                self._freq.append(random.random() * frequency);
351
    
352
    
353
    def __del__(self):
354
        """!
355
        @brief Destructor of oscillatory network is based on Kuramoto model.
356
        
357
        """
358
        
359
        if (self._ccore_network_pointer is not None):
360
            wrapper.sync_destroy_network(self._ccore_network_pointer);
361
            self._ccore_network_pointer = None;
362
    
363
    
364
    def sync_order(self):
365
        """!
366
        @brief Calculates level of global synchorization in the network.
367
        
368
        @return (double) Level of global synchronization.
369
        
370
        @see sync_local_order()
371
        
372
        """
373
        
374
        if (self._ccore_network_pointer is not None):
375
            return wrapper.sync_order(self._ccore_network_pointer);
376
        
377
        exp_amount = 0;
378
        average_phase = 0;
379
        
380
        for index in range(0, self._num_osc, 1):
381
            exp_amount += math.expm1( abs(1j * self._phases[index]) );
382
            average_phase += self._phases[index];
383
        
384
        exp_amount /= self._num_osc;
385
        average_phase = math.expm1( abs(1j * (average_phase / self._num_osc)) );
386
        
387
        return abs(average_phase) / abs(exp_amount);    
388
    
389
    
390
    def sync_local_order(self):
391
        """!
392
        @brief Calculates level of local (partial) synchronization in the network.
393
        
394
        @return (double) Level of local (partial) synchronization.
395
        
396
        @see sync_order()
397
        
398
        """
399
        
400
        if (self._ccore_network_pointer is not None):
401
            return wrapper.sync_local_order(self._ccore_network_pointer);
402
        
403
        exp_amount = 0.0;
404
        num_neigh = 0;
405
        
406
        for i in range(0, self._num_osc, 1):
407
            for j in range(0, self._num_osc, 1):
408
                if (self.has_connection(i, j) == True):
409
                    exp_amount += math.exp(-abs(self._phases[j] - self._phases[i]));
410
                    num_neigh += 1;
411
        
412
        if (num_neigh == 0):
413
            num_neigh = 1;
414
        
415
        return exp_amount / num_neigh;
416
    
417
    
418
    def _phase_kuramoto(self, teta, t, argv):
0 ignored issues
show
Unused Code introduced by
The argument t seems to be unused.
Loading history...
419
        """!
420
        @brief Returns result of phase calculation for specified oscillator in the network.
421
        
422
        @param[in] teta (double): Phase of the oscillator that is differentiated.
423
        @param[in] t (double): Current time of simulation.
424
        @param[in] argv (tuple): Index of the oscillator in the list.
425
        
426
        @return (double) New phase for specified oscillator (don't assign here).
427
        
428
        """
429
        
430
        index = argv;
431
        phase = 0;
432
        for k in range(0, self._num_osc):
433
            if (self.has_connection(index, k) == True):
434
                phase += math.sin(self._phases[k] - teta);
435
            
436
        return ( self._freq[index] + (phase * self._weight / self._num_osc) );
437
        
438
    
439
    def simulate(self, steps, time, solution = solve_type.FAST, collect_dynamic = True):
440
        """!
441
        @brief Performs static simulation of Sync oscillatory network.
442
        
443
        @param[in] steps (uint): Number steps of simulations during simulation.
444
        @param[in] time (double): Time of simulation.
445
        @param[in] solution (solve_type): Type of solution (solving).
446
        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
447
        
448
        @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
449
                otherwise returns only last values (last step of simulation) of dynamic.
450
        
451
        @see simulate_dynamic()
452
        @see simulate_static()
453
        
454
        """
455
        
456
        return self.simulate_static(steps, time, solution, collect_dynamic);
457
458
459
    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):
460
        """!
461
        @brief Performs dynamic simulation of the network until stop condition is not reached. Stop condition is defined by input argument 'order'.
462
        
463
        @param[in] order (double): Order of process synchronization, distributed 0..1.
464
        @param[in] solution (solve_type): Type of solution.
465
        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
466
        @param[in] step (double): Time step of one iteration of simulation.
467
        @param[in] int_step (double): Integration step, should be less than step.
468
        @param[in] threshold_changes (double): Additional stop condition that helps prevent infinite simulation, defines limit of changes of oscillators between current and previous steps.
469
        
470
        @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
471
                otherwise returns only last values (last step of simulation) of dynamic.
472
        
473
        @see simulate()
474
        @see simulate_static()
475
        
476
        """
477
        
478
        if (self._ccore_network_pointer is not None):
479
            ccore_instance_dynamic = wrapper.sync_simulate_dynamic(self._ccore_network_pointer, order, solution, collect_dynamic, step, int_step, threshold_changes);
480
            return sync_dynamic(None, None, ccore_instance_dynamic);
481
        
482
        # For statistics and integration
483
        time_counter = 0;
484
        
485
        # Prevent infinite loop. It's possible when required state cannot be reached.
486
        previous_order = 0;
487
        current_order = self.sync_local_order();
488
        
489
        # If requested input dynamics
490
        dyn_phase = [];
491
        dyn_time = [];
492
        if (collect_dynamic == True):
493
            dyn_phase.append(self._phases);
494
            dyn_time.append(0);
495
        
496
        # Execute until sync state will be reached
497
        while (current_order < order):                
498
            # update states of oscillators
499
            self._phases = self._calculate_phases(solution, time_counter, step, int_step);
500
            
501
            # update time
502
            time_counter += step;
503
            
504
            # if requested input dynamic
505
            if (collect_dynamic == True):
506
                dyn_phase.append(self._phases);
507
                dyn_time.append(time_counter);
508
                
509
            # update orders
510
            previous_order = current_order;
511
            current_order = self.sync_local_order();
512
            
513
            # hang prevention
514
            if (abs(current_order - previous_order) < threshold_changes):
515
                # print("Warning: sync_network::simulate_dynamic - simulation is aborted due to low level of convergence rate (order = " + str(current_order) + ").");
516
                break;
517
            
518
        if (collect_dynamic != True):
519
            dyn_phase.append(self._phases);
520
            dyn_time.append(time_counter);
521
522
        output_sync_dynamic = sync_dynamic(dyn_phase, dyn_time, None);
523
        return output_sync_dynamic;
524
525
526
    def simulate_static(self, steps, time, solution = solve_type.FAST, collect_dynamic = False):
527
        """!
528
        @brief Performs static simulation of oscillatory network.
529
        
530
        @param[in] steps (uint): Number steps of simulations during simulation.
531
        @param[in] time (double): Time of simulation.
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
        
535
        @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
536
                otherwise returns only last values (last step of simulation) of dynamic.
537
        
538
        @see simulate()
539
        @see simulate_dynamic()
540
        
541
        """
542
        
543
        if (self._ccore_network_pointer is not None):
544
            ccore_instance_dynamic = wrapper.sync_simulate_static(self._ccore_network_pointer, steps, time, solution, collect_dynamic);
545
            return sync_dynamic(None, None, ccore_instance_dynamic);
546
        
547
        dyn_phase = [];
548
        dyn_time = [];
549
        
550
        if (collect_dynamic == True):
551
            dyn_phase.append(self._phases);
552
            dyn_time.append(0);
553
        
554
        step = time / steps;
555
        int_step = step / 10.0;
556
        
557
        for t in numpy.arange(step, time + step, step):
558
            # update states of oscillators
559
            self._phases = self._calculate_phases(solution, t, step, int_step);
560
            
561
            # update states of oscillators
562
            if (collect_dynamic == True):
563
                dyn_phase.append(self._phases);
564
                dyn_time.append(t);
565
        
566
        if (collect_dynamic != True):
567
            dyn_phase.append(self._phases);
568
            dyn_time.append(time);
569
                        
570
        output_sync_dynamic = sync_dynamic(dyn_phase, dyn_time);
571
        return output_sync_dynamic;     
572
573
574
    def _calculate_phases(self, solution, t, step, int_step):
575
        """!
576
        @brief Calculates new phases for oscillators in the network in line with current step.
577
        
578
        @param[in] solution (solve_type): Type solver of the differential equation.
579
        @param[in] t (double): Time of simulation.
580
        @param[in] step (double): Step of solution at the end of which states of oscillators should be calculated.
581
        @param[in] int_step (double): Step differentiation that is used for solving differential equation.
582
        
583
        @return (list) New states (phases) for oscillators.
584
        
585
        """
586
        
587
        next_phases = [0] * self._num_osc;    # new oscillator _phases
588
        
589
        for index in range (0, self._num_osc, 1):
590
            if (solution == solve_type.FAST):
591
                result = self._phases[index] + self._phase_kuramoto(self._phases[index], 0, index);
592
                next_phases[index] = self._phase_normalization(result);
593
                
594
            elif (solution == solve_type.RK4):
595
                result = odeint(self._phase_kuramoto, self._phases[index], numpy.arange(t - step, t, int_step), (index , ));
596
                next_phases[index] = self._phase_normalization(result[len(result) - 1][0]);
597
            
598
            else:
599
                raise NameError("Solver '" + solution + "' is not supported");
600
        
601
        return next_phases;
602
        
603
604
    def _phase_normalization(self, teta):
605
        """!
606
        @brief Normalization of phase of oscillator that should be placed between [0; 2 * pi].
607
        
608
        @param[in] teta (double): phase of oscillator.
609
        
610
        @return (double) Normalized phase.
611
        
612
        """
613
        
614
        norm_teta = teta;
615
        while (norm_teta > (2.0 * pi)) or (norm_teta < 0):
616
            if (norm_teta > (2.0 * pi)):
617
                norm_teta -= 2.0 * pi;
618
            else:
619
                norm_teta += 2.0 * pi;
620
        
621
        return norm_teta;
622