Completed
Push — 0.7.dev ( 2e222e...d9bef7 )
by Andrei
01:11
created

fsync_network.simulate()   C

Complexity

Conditions 7

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
dl 0
loc 34
rs 5.5
c 0
b 0
f 0
1
"""!
2
3
@brief Oscillatory Neural Network based on Kuramoto model in frequency domain.
4
@details Based on article description:
5
         - Y.Kuramoto. Chemical Oscillations, Waves, and Turbulence. 1984.
6
7
@authors Andrei Novikov ([email protected])
8
@date 2014-2017
9
@copyright GNU Public License
10
11
@cond GNU_PUBLIC_LICENSE
12
    PyClustering is free software: you can redistribute it and/or modify
13
    it under the terms of the GNU General Public License as published by
14
    the Free Software Foundation, either version 3 of the License, or
15
    (at your option) any later version.
16
    
17
    PyClustering is distributed in the hope that it will be useful,
18
    but WITHOUT ANY WARRANTY; without even the implied warranty of
19
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
    GNU General Public License for more details.
21
    
22
    You should have received a copy of the GNU General Public License
23
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24
@endcond
25
26
"""
27
28
29
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...
30
import random;
31
import pyclustering.utils;
32
33
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...
34
35
from pyclustering.nnet import network, conn_type, conn_represent;
36
37
38
class fsync_dynamic:
39
    """!
40
    @brief Represents output dynamic of Sync in frequency domain.
41
    
42
    """
43
44
45
    def __init__(self, amplitude, time):
46
        """!
47
        @brief Constructor of Sync dynamic in frequency domain.
48
        
49
        @param[in] amplitude (list): Dynamic of oscillators on each step of simulation.
50
        @param[in] time (list): Simulation time where each time-point corresponds to amplitude-point.
51
        
52
        """
53
54
        self.__amplitude = amplitude;
55
        self.__time = time;
56
57
58
    @property
59
    def output(self):
60
        """!
61
        @brief (list) Returns output dynamic of the Sync network (amplitudes of each oscillator in the network) during simulation.
62
        
63
        """
64
65
        return self.__amplitude;
66
67
68
    @property
69
    def time(self):
70
        """!
71
        @brief (list) Returns time-points corresponds to dynamic-points points.
72
        
73
        """
74
75
        return self.__time;
76
77
78
    def __len__(self):
79
        """!
80
        @brief (uint) Returns number of simulation steps that are stored in dynamic.
81
        
82
        """
83
        
84
        return len(self.__amplitude);
85
86
87
    def __getitem__(self, index):
88
        """!
89
        @brief Indexing of the dynamic.
90
        
91
        """
92
        if (index is 0):
93
            return self.__time;
94
        
95
        elif (index is 1):
96
            return self.__amplitude;
97
        
98
        else:
99
            raise NameError('Out of range ' + index + ': only indexes 0 and 1 are supported.');
100
101
102
    def allocate_sync_ensembles(self, tolerance = 0.1):
103
        """!
104
        @brief Allocate clusters in line with ensembles of synchronous oscillators where each synchronous ensemble corresponds to only one cluster.
105
        
106
        @param[in] tolerance (double): Maximum error for allocation of synchronous ensemble oscillators.
107
        
108
        @return (list) Grours of indexes of synchronous oscillators, for example, [ [index_osc1, index_osc3], [index_osc2], [index_osc4, index_osc5] ].
109
        
110
        """
111
        
112
        return pyclustering.utils.allocate_sync_ensembles(self.__amplitude, tolerance);
113
114
115
    def extract_number_oscillations(self, index, amplitude_threshold):
116
        """!
117
        @brief Extracts number of oscillations of specified oscillator.
118
        
119
        @param[in] index (uint): Index of oscillator whose dynamic is considered.
120
        @param[in] amplitude_threshold (double): Amplitude threshold when oscillation is taken into account, for example,
121
                    when oscillator amplitude is greater than threshold then oscillation is incremented.
122
        
123
        @return (uint) Number of oscillations of specified oscillator.
124
        
125
        """
126
        
127
        return pyclustering.utils.extract_number_oscillations(self.__amplitude, index, amplitude_threshold);
128
129
130
131
class fsync_visualizer:
132
    """!
133
    @brief Visualizer of output dynamic of sync network in frequency domain.
134
    
135
    """
136
137
    @staticmethod
138
    def show_output_dynamic(fsync_output_dynamic):
139
        """!
140
        @brief Shows output dynamic (output of each oscillator) during simulation.
141
        
142
        @param[in] fsync_output_dynamic (fsync_dynamic): Output dynamic of the fSync network.
143
        
144
        @see show_output_dynamics
145
        
146
        """
147
        
148
        pyclustering.utils.draw_dynamics(fsync_output_dynamic.time, fsync_output_dynamic.output, x_title = "t", y_title = "amplitude");
149
150
151
    @staticmethod
152
    def show_output_dynamics(fsync_output_dynamics):
153
        """!
154
        @brief Shows several output dynamics (output of each oscillator) during simulation.
155
        @details Each dynamic is presented on separate plot.
156
        
157
        @param[in] fsync_output_dynamics (list): list of output dynamics 'fsync_dynamic' of the fSync network.
158
        
159
        @see show_output_dynamic
160
        
161
        """
162
        
163
        pyclustering.utils.draw_dynamics_set(fsync_output_dynamics, "t", "amplitude", None, None, False, False);
164
165
166
167
class fsync_network(network):
168
    """!
0 ignored issues
show
Bug introduced by
A suspicious escape sequence \d was found. Did you maybe forget to add an r prefix?

Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with r or R are they interpreted as regular expressions.

The escape sequence that was used indicates that you might have intended to write a regular expression.

Learn more about the available escape sequences. in the Python documentation.

Loading history...
Bug introduced by
A suspicious escape sequence \o was found. Did you maybe forget to add an r prefix?

Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with r or R are they interpreted as regular expressions.

The escape sequence that was used indicates that you might have intended to write a regular expression.

Learn more about the available escape sequences. in the Python documentation.

Loading history...
Bug introduced by
A suspicious escape sequence \s was found. Did you maybe forget to add an r prefix?

Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with r or R are they interpreted as regular expressions.

The escape sequence that was used indicates that you might have intended to write a regular expression.

Learn more about the available escape sequences. in the Python documentation.

Loading history...
169
    @brief Model of oscillatory network that uses Landau-Stuart oscillator and Kuramoto model as a synchronization mechanism.
170
    @details Dynamic of each oscillator in the network is described by following differential Landau-Stuart equation with feedback:
171
    
172
    \f[
173
    \dot{z}_{i} = (i\omega_{i} + \rho^{2}_{i} - |z_{i}|^{2} )z_{i} + \frac{1}{N}\sum_{j=0}^{N}k_{ij}(z_{j} - z_{i});
174
    \f]
175
    
176
    Where left part of the equation is Landau-Stuart equation and the right is a Kuramoto model for synchronization.
177
    For solving this equation Runge-Kutta 4 method is used by default.
178
    
179
    Example:
180
    @code
181
        # Prepare oscillatory network parameters.
182
        amount_oscillators = 3;
183
        frequency = 1.0;
184
        radiuses = [1.0, 2.0, 3.0];
185
        coupling_strength = 1.0;
186
        
187
        # Create oscillatory network
188
        oscillatory_network = fsync_network(amount_oscillators, frequency, radiuses, coupling_strength);
189
        
190
        # Simulate network during 200 steps on 10 time-units of time-axis.
191
        output_dynamic = oscillatory_network.simulate(200, 10, True);    # True is to collect whole output dynamic.
192
        
193
        # Visualize output result
194
        fsync_visualizer.show_output_dynamic(output_dynamic);
195
    @endcode
196
    
197
    Example of output dynamic of the network:
198
    @image html fsync_sync_examples.png
199
    
200
    """
201
    
202
    __DEFAULT_FREQUENCY_VALUE = 1.0;
203
    __DEFAULT_RADIUS_VALUE = 1.0;
204
    __DEFAULT_COUPLING_STRENGTH = 1.0;
205
206
207
    def __init__(self, num_osc, factor_frequency = 1.0, factor_radius = 1.0, factor_coupling = 1.0, type_conn = conn_type.ALL_TO_ALL, representation = conn_represent.MATRIX):
208
        """!
209
        @brief Constructor of oscillatory network based on synchronization Kuramoto model and Landau-Stuart oscillator.
210
        
211
        @param[in] num_osc (uint): Amount oscillators in the network.
212
        @param[in] factor_frequency (double|list): Frequency of oscillators, it can be specified as common value for all oscillators by
213
                    single double value and for each separately by list.
214
        @param[in] factor_radius (double|list): Radius of oscillators that affects amplitude, it can be specified as common value for all oscillators by
215
                    single double value and for each separately by list.
216
        @param[in] factor_coupling (double): Coupling strength between oscillators.
217
        @param[in] type_conn (conn_type): Type of connection between oscillators in the network (all-to-all, grid, bidirectional list, etc.).
218
        @param[in] representation (conn_represent): Internal representation of connection in the network: matrix or list.
219
        
220
        """
221
        
222
        super().__init__(num_osc, type_conn, representation);
223
        
224
        self.__frequency = factor_frequency if isinstance(factor_frequency, list) else [ fsync_network.__DEFAULT_FREQUENCY_VALUE * factor_frequency for _ in range(num_osc) ];
225
        self.__radius = factor_radius if isinstance(factor_radius, list) else [ fsync_network.__DEFAULT_RADIUS_VALUE * factor_radius for _ in range(num_osc) ];
226
        self.__coupling_strength = fsync_network.__DEFAULT_COUPLING_STRENGTH * factor_coupling;
227
        self.__properties = [ self.__oscillator_property(index) for index in range(self._num_osc) ];
228
        
229
        random.seed();
230
        self.__amplitude = [ random.random() for _ in range(num_osc) ];
231
232
233
    def simulate(self, steps, time, collect_dynamic = False):
234
        """!
235
        @brief Performs static simulation of oscillatory network.
236
        
237
        @param[in] steps (uint): Number simulation steps.
238
        @param[in] time (double): Time of simulation.
239
        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
240
        
241
        @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' is True, than return dynamic for the whole simulation time,
242
                 otherwise returns only last values (last step of simulation) of output dynamic.
243
        
244
        @see simulate()
245
        @see simulate_dynamic()
246
        
247
        """
248
        
249
        dynamic_amplitude, dynamic_time = ([], []) if collect_dynamic is False else ([self.__amplitude], [0]);
250
        
251
        step = time / steps;
252
        int_step = step / 10.0;
253
        
254
        for t in numpy.arange(step, time + step, step):
255
            self.__amplitude = self.__calculate(t, step, int_step);
256
            
257
            if (collect_dynamic == True):
258
                dynamic_amplitude.append([ numpy.real(amplitude)[0] for amplitude in self.__amplitude ]);
259
                dynamic_time.append(t);
260
        
261
        if (collect_dynamic != True):
262
            dynamic_amplitude.append([ numpy.real(amplitude)[0] for amplitude in self.__amplitude ]);
263
            dynamic_time.append(time);
264
265
        output_sync_dynamic = fsync_dynamic(dynamic_amplitude, dynamic_time);
266
        return output_sync_dynamic;
267
268
269
    def __calculate(self, t, step, int_step):
270
        """!
271
        @brief Calculates new amplitudes for oscillators in the network in line with current step.
272
        
273
        @param[in] t (double): Time of simulation.
274
        @param[in] step (double): Step of solution at the end of which states of oscillators should be calculated.
275
        @param[in] int_step (double): Step differentiation that is used for solving differential equation.
276
        
277
        @return (list) New states (phases) for oscillators.
278
        
279
        """
280
        
281
        next_amplitudes = [0.0] * self._num_osc;
282
        
283
        for index in range (0, self._num_osc, 1):
284
            z = numpy.array(self.__amplitude[index], dtype = numpy.complex128, ndmin = 1);
285
            result = odeint(self.__calculate_amplitude, z.view(numpy.float64), numpy.arange(t - step, t, int_step), (index , ));
286
            next_amplitudes[index] = (result[len(result) - 1]).view(numpy.complex128);
287
        
288
        return next_amplitudes;
289
290
291
    def __oscillator_property(self, index):
292
        """!
293
        @brief Calculate Landau-Stuart oscillator constant property that is based on frequency and radius.
294
        
295
        @param[in] index (uint): Oscillator index whose property is calculated.
296
        
297
        @return (double) Oscillator property.
298
        
299
        """
300
        
301
        return numpy.array(1j * self.__frequency[index] + self.__radius[index]**2, dtype = numpy.complex128, ndmin = 1);
302
303
304
    def __landau_stuart(self, amplitude, index):
305
        """!
306
        @brief Calculate Landau-Stuart state.
307
        
308
        @param[in] amplitude (double): Current amplitude of oscillator.
309
        @param[in] index (uint): Oscillator index whose state is calculated. 
310
        
311
        @return (double) Landau-Stuart state.
312
        
313
        """
314
        
315
        return (self.__properties[index] - numpy.absolute(amplitude) ** 2) * amplitude;
316
317
318
    def __synchronization_mechanism(self, amplitude, index):
319
        """!
320
        @brief Calculate synchronization part using Kuramoto synchronization mechanism.
321
        
322
        @param[in] amplitude (double): Current amplitude of oscillator.
323
        @param[in] index (uint): Oscillator index whose synchronization influence is calculated.
324
        
325
        @return (double) Synchronization influence for the specified oscillator.
326
        
327
        """
328
        
329
        sync_influence = 0.0;
330
        
331
        for k in range(self._num_osc):
332
            if (self.has_connection(index, k) == True):
333
                amplitude_neighbor = numpy.array(self.__amplitude[k], dtype = numpy.complex128, ndmin = 1);
334
                sync_influence += amplitude_neighbor - amplitude;
335
        
336
        return sync_influence * self.__coupling_strength / self._num_osc;
337
338
339
    def __calculate_amplitude(self, amplitude, t, argv):
0 ignored issues
show
Unused Code introduced by
The argument t seems to be unused.
Loading history...
340
        """!
341
        @brief Returns new amplitude value for particular oscillator that is defined by index that is in 'argv' argument.
342
        @details The method is used for differential calculation.
343
        
344
        @param[in] amplitude (double): Current amplitude of oscillator.
345
        @param[in] t (double): Current time of simulation.
346
        @param[in] argv (uint): Index of the current oscillator.
347
        
348
        @return (double) New amplitude of the oscillator.
349
        
350
        """
351
        
352
        z = amplitude.view(numpy.complex);
353
        dzdt = self.__landau_stuart(z, argv) + self.__synchronization_mechanism(z, argv);
354
        
355
        return dzdt.view(numpy.float64);