Completed
Push — master ( a63a82...61f52c )
by
unknown
01:32
created

sync_network   B

Complexity

Total Complexity 41

Size/Duplication

Total Lines 317
Duplicated Lines 0 %
Metric Value
dl 0
loc 317
rs 8.2769
wmc 41

10 Methods

Rating   Name   Duplication   Size   Complexity  
B sync_local_order() 0 26 6
C simulate_dynamic() 0 65 7
A __del__() 0 9 2
A simulate() 0 18 1
A _phase_normalization() 0 18 4
B simulate_static() 0 46 6
B __init__() 0 31 5
A _phase_kuramoto() 0 19 3
B _calculate_phases() 0 28 4
B sync_order() 0 24 3

How to fix   Complexity   

Complex Class

Complex classes like sync_network often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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
    _dynamic = None;
55
    _time = None;
56
    _ccore_sync_dynamic_pointer = None;
57
    
58
    @property
59
    def output(self):
60
        """!
61
        @brief (list) Returns outputs of oscillator during simulation.
62
        
63
        """
64
        if (self._ccore_sync_dynamic_pointer is not None):
65
            return wrapper.sync_dynamic_get_output(self._ccore_sync_dynamic_pointer);
66
            
67
        return self._dynamic;
68
    
69
    
70
    @property
71
    def time(self):
72
        """!
73
        @brief (list) Returns sampling times when dynamic is measured during simulation.
74
        
75
        """
76
        if (self._ccore_sync_dynamic_pointer is not None):
77
            return wrapper.sync_dynamic_get_time(self._ccore_sync_dynamic_pointer);
78
        
79
        return self._time;
80
    
81
    
82
    def __init__(self, phase, time, ccore = None):
83
        """!
84
        @brief Constructor of Sync dynamic.
85
        
86
        @param[in] phase (list): Dynamic of oscillators on each step of simulation. If ccore pointer is specified than it can be ignored.
87
        @param[in] time (list): Simulation time.
88
        @param[in] ccore (ctypes.pointer): Pointer to CCORE sync_dynamic instance in memory.
89
        
90
        """
91
        self._dynamic = phase;
92
        self._time = time;
93
        self._ccore_sync_dynamic_pointer = ccore;
94
    
95
    
96
    def __del__(self):
97
        """!
98
        @brief Default destructor of Sync dynamic.
99
        
100
        """
101
        if (self._ccore_sync_dynamic_pointer is not None):
102
            wrapper.sync_dynamic_destroy(self._ccore_sync_dynamic_pointer);
103
    
104
    
105
    def __len__(self):
106
        """!
107
        @brief (uint) Returns number of simulation steps that are stored in dynamic.
108
        
109
        """
110
        if (self._ccore_sync_dynamic_pointer is not None):
111
            return wrapper.sync_dynamic_get_size(self._ccore_sync_dynamic_pointer);
112
        
113
        return len(self._dynamic);
114
    
115
    
116
    def allocate_sync_ensembles(self, tolerance = 0.01):
117
        """!
118
        @brief Allocate clusters in line with ensembles of synchronous oscillators where each
119
               synchronous ensemble corresponds to only one cluster.
120
               
121
        @param[in] tolerance (double): Maximum error for allocation of synchronous ensemble oscillators.
122
        
123
        @return (list) Grours (lists) of indexes of synchronous oscillators.
124
                For example [ [index_osc1, index_osc3], [index_osc2], [index_osc4, index_osc5] ].
125
        
126
        """
127
        
128
        if (self._ccore_sync_dynamic_pointer is not None):
129
            return wrapper.sync_dynamic_allocate_sync_ensembles(self._ccore_sync_dynamic_pointer, tolerance);
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
        dynamic = self.output;
171
        current_dynamic = dynamic[len(dynamic) - 1];
172
        
173
        if (iteration is not None):
174
            current_dynamic = dynamic[iteration];
175
        
176
        number_oscillators = len(dynamic[0]);
177
        affinity_matrix = [ [ 0.0 for i in range(number_oscillators) ] for j in range(number_oscillators) ];  
178
        
179
        for i in range(number_oscillators):
180
            for j in range(number_oscillators):
181
                phase1 = current_dynamic[i];
182
                phase2 = current_dynamic[j];
183
                
184
                affinity_matrix[i][j] = math.sin(phase1 - phase2);
185
                
186
        return affinity_matrix;
187
188
189
class sync_visualizer:
190
    """!
191
    @brief Visualizer of output dynamic of sync network (Sync).
192
    
193
    """
194
        
195
    @staticmethod
196
    def show_output_dynamic(sync_output_dynamic):
197
        """!
198
        @brief Shows output dynamic (output of each oscillator) during simulation.
199
        
200
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
201
        
202
        """
203
        
204
        draw_dynamics(sync_output_dynamic.time, sync_output_dynamic.output, x_title = "t", y_title = "phase", y_lim = [0, 2 * 3.14]);
205
    
206
    
207
    @staticmethod
208
    def show_correlation_matrix(sync_output_dynamic, iteration = None):
209
        """!
210
        @brief Shows correlation matrix between oscillators at the specified iteration.
211
        
212
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
213
        @param[in] iteration (uint): Number of interation of simulation for which correlation matrix should be allocated.
214
                                      If iternation number is not specified, the last step of simulation is used for the matrix allocation.
215
        
216
        """
217
        
218
        figure = plt.figure();
0 ignored issues
show
Unused Code introduced by
The variable figure seems to be unused.
Loading history...
219
        correlation_matrix = sync_output_dynamic.allocate_correlation_matrix(iteration);
220
        
221
        plt.imshow(correlation_matrix, cmap = plt.get_cmap('cool'), interpolation='kaiser'); 
222
        plt.show();
223
        
224
    
225
    @staticmethod
226
    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...
227
        """!
228
        @brief Shows animation of output dynamic (output of each oscillator) during simulation on a circle from [0; 2pi].
229
        
230
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
231
        @param[in] animation_velocity (uint): Interval between frames in milliseconds. 
232
        
233
        """
234
        
235
        figure = plt.figure();
236
        
237
        xcircle = numpy.linspace(-1.0, 1.0, 500);
238
        ycircle_positive = [ (1.0 - x ** 2) ** 0.5 for x in xcircle ];
239
        ycircle_negative = [ -y for y in ycircle_positive ];
240
        
241
        def init_frame():
242
            artist1, = plt.plot(xcircle, ycircle_positive, 'b-');
243
            artist2, = plt.plot(xcircle, ycircle_negative, 'b-');
244
            artist3, = plt.plot([-1.1, 1.1], [0.0, 0.0], 'b-');
245
            artist4, = plt.plot([0.0, 0.0], [-1.1, 1.1], 'b-');
246
            
247
            text1 = plt.text(-1.1, 0.0, r'$\pi$');
248
            text2 = plt.text(1.1, 0.0, r'0');
249
            text3 = plt.text(0.0, 1.1, r'$\pi$/2');
250
            text4 = plt.text(0.0, -1.1, r'3$\pi$/2');
251
            
252
            return [ artist1, artist2, artist3, artist4, text1, text2, text3, text4 ];          
253
        
254
        def frame_generation(index_dynamic):
255
            dynamic = sync_output_dynamic.output[index_dynamic];
256
            
257
            xdata = [];
258
            ydata = [];
259
            
260
            for phase in dynamic:
261
                xcoord = math.cos(phase);
262
                ycoord = math.sin(phase);
263
                
264
                xdata.append(xcoord);
265
                ydata.append(ycoord);
266
            
267
            artist5, = plt.plot(xdata, ydata, 'ro');
268
            return [ artist5 ];
269
        
270
        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...
271
        plt.show();
272
    
273
    
274
    @staticmethod
275
    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...
276
        """!
277
        @brief Shows animation of correlation matrix between oscillators during simulation.
278
        
279
        @param[in] sync_output_dynamic (sync_dynamic): Output dynamic of the Sync network.
280
        @param[in] animation_velocity (uint): Interval between frames in milliseconds. 
281
        
282
        """
283
        
284
        figure = plt.figure();
285
        
286
        correlation_matrix = sync_output_dynamic.allocate_correlation_matrix(0);
287
        
288
        def init_frame(): 
289
            artist = plt.imshow(correlation_matrix, cmap = plt.get_cmap('cool'), interpolation='kaiser', hold = True);           
290
            return [ artist ];   
291
        
292
        def frame_generation(index_dynamic):
293
            correlation_matrix = sync_output_dynamic.allocate_correlation_matrix(index_dynamic);
294
            artist = plt.imshow(correlation_matrix, cmap = plt.get_cmap('cool'), interpolation='kaiser');
295
            
296
            return [ artist ];
297
298
        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...
299
        plt.show();        
300
301
302
class sync_network(network):    
303
    """!
304
    @brief Model of oscillatory network that is based on the Kuramoto model of synchronization.
305
    
306
    """
307
    
308
    # Protected members:
309
    _name = 'Phase Sync Network'
310
    _phases = None;                     # Current phases of oscillators.
311
    _freq = None;                       # Own frequencies of oscillators.
312
    _weight = 0;                        # Strength of connections between oscillators.
313
    
314
    _ccore_network_pointer = None;      # Pointer to CCORE Sync implementation of the network.
315
    
316
317
    def __init__(self, num_osc, weight = 1, frequency = 0, type_conn = conn_type.ALL_TO_ALL, conn_represent = conn_represent.MATRIX, initial_phases = initial_type.RANDOM_GAUSSIAN, ccore = False):
0 ignored issues
show
Comprehensibility Bug introduced by
conn_represent is re-defining a name which is already available in the outer-scope (previously defined on line 43).

It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior:

param = 5

class Foo:
    def __init__(self, param):   # "param" would be flagged here
        self.param = param
Loading history...
318
        """!
319
        @brief Constructor of oscillatory network is based on Kuramoto model.
320
        
321
        @param[in] num_osc (uint): Number of oscillators in the network.
322
        @param[in] weight (double): Coupling strength of the links between oscillators.
323
        @param[in] frequency (double): Multiplier of internal frequency of the oscillators.
324
        @param[in] type_conn (conn_type): Type of connection between oscillators in the network (all-to-all, grid, bidirectional list, etc.).
325
        @param[in] conn_represent (conn_represent): Internal representation of connection in the network: matrix or list.
326
        @param[in] initial_phases (initial_type): Type of initialization of initial phases of oscillators (random, uniformly distributed, etc.).
327
        @param[in] ccore (bool): If True simulation is performed by CCORE library (C++ implementation of pyclustering).
328
        
329
        """
330
        
331
        if (ccore is True):
332
            self._ccore_network_pointer = wrapper.sync_create_network(num_osc, weight, frequency, type_conn, initial_phases);
333
        else:   
334
            super().__init__(num_osc, type_conn, conn_represent);
335
            
336
            self._weight = weight;
337
            
338
            self._phases = list();
339
            self._freq = list();
340
            
341
            for index in range(0, num_osc, 1):    
342
                if (initial_phases == initial_type.RANDOM_GAUSSIAN):
343
                    self._phases.append(random.random() * 2.0 * pi);
344
                elif (initial_phases == initial_type.EQUIPARTITION):
345
                    self._phases.append( pi / num_osc * index);
346
                
347
                self._freq.append(random.random() * frequency);
348
    
349
    
350
    def __del__(self):
351
        """!
352
        @brief Destructor of oscillatory network is based on Kuramoto model.
353
        
354
        """
355
        
356
        if (self._ccore_network_pointer is not None):
357
            wrapper.sync_destroy_network(self._ccore_network_pointer);
358
            self._ccore_network_pointer = None;
359
    
360
    
361
    def sync_order(self):
362
        """!
363
        @brief Calculates level of global synchorization in the network.
364
        
365
        @return (double) Level of global synchronization.
366
        
367
        @see sync_local_order()
368
        
369
        """
370
        
371
        if (self._ccore_network_pointer is not None):
372
            return wrapper.sync_order(self._ccore_network_pointer);
373
        
374
        exp_amount = 0;
375
        average_phase = 0;
376
        
377
        for index in range(0, self._num_osc, 1):
378
            exp_amount += math.expm1( abs(1j * self._phases[index]) );
379
            average_phase += self._phases[index];
380
        
381
        exp_amount /= self._num_osc;
382
        average_phase = math.expm1( abs(1j * (average_phase / self._num_osc)) );
383
        
384
        return abs(average_phase) / abs(exp_amount);    
385
    
386
    
387
    def sync_local_order(self):
388
        """!
389
        @brief Calculates level of local (partial) synchronization in the network.
390
        
391
        @return (double) Level of local (partial) synchronization.
392
        
393
        @see sync_order()
394
        
395
        """
396
        
397
        if (self._ccore_network_pointer is not None):
398
            return wrapper.sync_local_order(self._ccore_network_pointer);
399
        
400
        exp_amount = 0.0;
401
        num_neigh = 0;
402
        
403
        for i in range(0, self._num_osc, 1):
404
            for j in range(0, self._num_osc, 1):
405
                if (self.has_connection(i, j) == True):
406
                    exp_amount += math.exp(-abs(self._phases[j] - self._phases[i]));
407
                    num_neigh += 1;
408
        
409
        if (num_neigh == 0):
410
            num_neigh = 1;
411
        
412
        return exp_amount / num_neigh;
413
    
414
    
415
    def _phase_kuramoto(self, teta, t, argv):
0 ignored issues
show
Unused Code introduced by
The argument t seems to be unused.
Loading history...
416
        """!
417
        @brief Returns result of phase calculation for specified oscillator in the network.
418
        
419
        @param[in] teta (double): Phase of the oscillator that is differentiated.
420
        @param[in] t (double): Current time of simulation.
421
        @param[in] argv (tuple): Index of the oscillator in the list.
422
        
423
        @return (double) New phase for specified oscillator (don't assign here).
424
        
425
        """
426
        
427
        index = argv;
428
        phase = 0;
429
        for k in range(0, self._num_osc):
430
            if (self.has_connection(index, k) == True):
431
                phase += math.sin(self._phases[k] - teta);
432
            
433
        return ( self._freq[index] + (phase * self._weight / self._num_osc) );
434
        
435
    
436
    def simulate(self, steps, time, solution = solve_type.FAST, collect_dynamic = True):
437
        """!
438
        @brief Performs static simulation of Sync oscillatory network.
439
        
440
        @param[in] steps (uint): Number steps of simulations during simulation.
441
        @param[in] time (double): Time of simulation.
442
        @param[in] solution (solve_type): Type of solution (solving).
443
        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
444
        
445
        @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
446
                otherwise returns only last values (last step of simulation) of dynamic.
447
        
448
        @see simulate_dynamic()
449
        @see simulate_static()
450
        
451
        """
452
        
453
        return self.simulate_static(steps, time, solution, collect_dynamic);
454
455
456
    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):
457
        """!
458
        @brief Performs dynamic simulation of the network until stop condition is not reached. Stop condition is defined by input argument 'order'.
459
        
460
        @param[in] order (double): Order of process synchronization, distributed 0..1.
461
        @param[in] solution (solve_type): Type of solution.
462
        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
463
        @param[in] step (double): Time step of one iteration of simulation.
464
        @param[in] int_step (double): Integration step, should be less than step.
465
        @param[in] threshold_changes (double): Additional stop condition that helps prevent infinite simulation, defines limit of changes of oscillators between current and previous steps.
466
        
467
        @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
468
                otherwise returns only last values (last step of simulation) of dynamic.
469
        
470
        @see simulate()
471
        @see simulate_static()
472
        
473
        """
474
        
475
        if (self._ccore_network_pointer is not None):
476
            ccore_instance_dynamic = wrapper.sync_simulate_dynamic(self._ccore_network_pointer, order, solution, collect_dynamic, step, int_step, threshold_changes);
477
            return sync_dynamic(None, None, ccore_instance_dynamic);
478
        
479
        # For statistics and integration
480
        time_counter = 0;
481
        
482
        # Prevent infinite loop. It's possible when required state cannot be reached.
483
        previous_order = 0;
484
        current_order = self.sync_local_order();
485
        
486
        # If requested input dynamics
487
        dyn_phase = [];
488
        dyn_time = [];
489
        if (collect_dynamic == True):
490
            dyn_phase.append(self._phases);
491
            dyn_time.append(0);
492
        
493
        # Execute until sync state will be reached
494
        while (current_order < order):                
495
            # update states of oscillators
496
            self._phases = self._calculate_phases(solution, time_counter, step, int_step);
497
            
498
            # update time
499
            time_counter += step;
500
            
501
            # if requested input dynamic
502
            if (collect_dynamic == True):
503
                dyn_phase.append(self._phases);
504
                dyn_time.append(time_counter);
505
                
506
            # update orders
507
            previous_order = current_order;
508
            current_order = self.sync_local_order();
509
            
510
            # hang prevention
511
            if (abs(current_order - previous_order) < threshold_changes):
512
                # print("Warning: sync_network::simulate_dynamic - simulation is aborted due to low level of convergence rate (order = " + str(current_order) + ").");
513
                break;
514
            
515
        if (collect_dynamic != True):
516
            dyn_phase.append(self._phases);
517
            dyn_time.append(time_counter);
518
519
        output_sync_dynamic = sync_dynamic(dyn_phase, dyn_time, None);
520
        return output_sync_dynamic;
521
522
523
    def simulate_static(self, steps, time, solution = solve_type.FAST, collect_dynamic = False):
524
        """!
525
        @brief Performs static simulation of oscillatory network.
526
        
527
        @param[in] steps (uint): Number steps of simulations during simulation.
528
        @param[in] time (double): Time of simulation.
529
        @param[in] solution (solve_type): Type of solution.
530
        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
531
        
532
        @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
533
                otherwise returns only last values (last step of simulation) of dynamic.
534
        
535
        @see simulate()
536
        @see simulate_dynamic()
537
        
538
        """
539
        
540
        if (self._ccore_network_pointer is not None):
541
            ccore_instance_dynamic = wrapper.sync_simulate_static(self._ccore_network_pointer, steps, time, solution, collect_dynamic);
542
            return sync_dynamic(None, None, ccore_instance_dynamic);
543
        
544
        dyn_phase = [];
545
        dyn_time = [];
546
        
547
        if (collect_dynamic == True):
548
            dyn_phase.append(self._phases);
549
            dyn_time.append(0);
550
        
551
        step = time / steps;
552
        int_step = step / 10.0;
553
        
554
        for t in numpy.arange(step, time + step, step):
555
            # update states of oscillators
556
            self._phases = self._calculate_phases(solution, t, step, int_step);
557
            
558
            # update states of oscillators
559
            if (collect_dynamic == True):
560
                dyn_phase.append(self._phases);
561
                dyn_time.append(t);
562
        
563
        if (collect_dynamic != True):
564
            dyn_phase.append(self._phases);
565
            dyn_time.append(t);
0 ignored issues
show
Bug introduced by
The loop variable t might not be defined here.
Loading history...
566
                        
567
        output_sync_dynamic = sync_dynamic(dyn_phase, dyn_time);
568
        return output_sync_dynamic;     
569
570
571
    def _calculate_phases(self, solution, t, step, int_step):
572
        """!
573
        @brief Calculates new phases for oscillators in the network in line with current step.
574
        
575
        @param[in] solution (solve_type): Type solver of the differential equation.
576
        @param[in] t (double): Time of simulation.
577
        @param[in] step (double): Step of solution at the end of which states of oscillators should be calculated.
578
        @param[in] int_step (double): Step differentiation that is used for solving differential equation.
579
        
580
        @return (list) New states (phases) for oscillators.
581
        
582
        """
583
        
584
        next_phases = [0] * self._num_osc;    # new oscillator _phases
585
        
586
        for index in range (0, self._num_osc, 1):
587
            if (solution == solve_type.FAST):
588
                result = self._phases[index] + self._phase_kuramoto(self._phases[index], 0, index);
589
                next_phases[index] = self._phase_normalization(result);
590
                
591
            elif (solution == solve_type.RK4):
592
                result = odeint(self._phase_kuramoto, self._phases[index], numpy.arange(t - step, t, int_step), (index , ));
593
                next_phases[index] = self._phase_normalization(result[len(result) - 1][0]);
594
            
595
            else:
596
                raise NameError("Solver '" + solution + "' is not supported");
597
        
598
        return next_phases;
599
        
600
601
    def _phase_normalization(self, teta):
602
        """!
603
        @brief Normalization of phase of oscillator that should be placed between [0; 2 * pi].
604
        
605
        @param[in] teta (double): phase of oscillator.
606
        
607
        @return (double) Normalized phase.
608
        
609
        """
610
        
611
        norm_teta = teta;
612
        while (norm_teta > (2.0 * pi)) or (norm_teta < 0):
613
            if (norm_teta > (2.0 * pi)):
614
                norm_teta -= 2.0 * pi;
615
            else:
616
                norm_teta += 2.0 * pi;
617
        
618
        return norm_teta;
619