Completed
Push — master ( aa8a9c...92a332 )
by Andrei
01:34
created

hysteresis_network.__init__()   B

Complexity

Conditions 2

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 2
dl 0
loc 28
rs 8.8571
c 1
b 0
f 1
1
"""!
2
3
@brief Neural Network: Hysteresis Oscillatory Network
4
@details Based on article description:
5
         - K.Jinno. Oscillatory Hysteresis Associative Memory. 2002.
6
         - K.Jinno, H.Taguchi, T.Yamamoto, H.Hirose. Dynamical Hysteresis Neural Network for Graph Coloring Problem. 2003.
7
8
@authors Andrei Novikov ([email protected])
9
@date 2014-2016
10
@copyright GNU Public License
11
12
@cond GNU_PUBLIC_LICENSE
13
    PyClustering is free software: you can redistribute it and/or modify
14
    it under the terms of the GNU General Public License as published by
15
    the Free Software Foundation, either version 3 of the License, or
16
    (at your option) any later version.
17
    
18
    PyClustering is distributed in the hope that it will be useful,
19
    but WITHOUT ANY WARRANTY; without even the implied warranty of
20
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
    GNU General Public License for more details.
22
    
23
    You should have received a copy of the GNU General Public License
24
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
25
@endcond
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
31
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...
32
33
from pyclustering.nnet import *;
0 ignored issues
show
Unused Code introduced by
IntEnum was imported with wildcard, but is not used.
Loading history...
Unused Code introduced by
math was imported with wildcard, but is not used.
Loading history...
Unused Code introduced by
initial_type was imported with wildcard, but is not used.
Loading history...
34
35
from pyclustering.utils import draw_dynamics;
36
37
38
class hysteresis_dynamic:
39
    """!
40
    @brief Represents output dynamic of hysteresis oscillatory network.
41
    
42
    """
43
    
44
    @property
45
    def output(self):
46
        """!
47
        @brief (list) Returns outputs of oscillator during simulation.
48
        
49
        """
50
        return self._dynamic;
51
    
52
    
53
    @property
54
    def time(self):
55
        """!
56
        @brief (list) Returns sampling times when dynamic is measured during simulation.
57
        
58
        """
59
        
60
        return self._time;
61
    
62
    
63
    def __init__(self, amplitudes, time):
64
        """!
65
        @brief Constructor of hysteresis neural network dynamic.
66
        
67
        @param[in] amplitudes (list): Dynamic (amplitudes) of oscillators on each step of simulation.
68
        @param[in] time (list): Simulation time (timestamps of simulation steps) when amplitudes are stored.
69
        
70
        """
71
        
72
        if (len(amplitudes) != len(time)):
73
            raise NameError("Length of list of dynamics of oscillators should be equal to length of simulation timestamps of steps.");
74
        
75
        self._dynamic = amplitudes;
76
        
77
        self._time = time;
78
79
80
    def __len__(self):
81
        """!
82
        @brief (uint) Returns number of simulation steps that are stored in dynamic.
83
        
84
        """
85
        
86
        return len(self._dynamic);
87
    
88
    
89
    def allocate_sync_ensembles(self, tolerance = 0.1, threshold_steps = 1):
90
        """!
91
        @brief Allocate clusters in line with ensembles of synchronous oscillators where each
92
               synchronous ensemble corresponds to only one cluster.
93
               
94
        @param[in] tolerance (double): Maximum error for allocation of synchronous ensemble oscillators.
95
        @param[in] threshold_steps (uint): Number of steps from the end of simulation that should be analysed for ensemble allocation.
96
                    If amout of simulation steps has been less than threshold steps than amount of steps will be reduced to amout
97
                    of simulation steps.
98
        
99
        @return (list) Grours of indexes of synchronous oscillators, for example, [ [index_osc1, index_osc3], [index_osc2], [index_osc4, index_osc5] ]."
100
        
101
        """
102
        
103
        clusters = [ [0] ];
104
        
105
        number_oscillators = len(self._dynamic[0]);
106
        
107
        for i in range(1, number_oscillators, 1):
108
            captured_neuron = True;
109
            for cluster in clusters:
110
                neuron_index = cluster[0];
111
                
112
                analysis_steps = threshold_steps;
113
                if (len(self._dynamic) < analysis_steps):
114
                    analysis_steps = len(self._dynamic);
115
                
116
                analysis_start_step_index = len(self._dynamic) - 1;
117
                
118
                for step in range(analysis_start_step_index, analysis_start_step_index - analysis_steps, -1):
119
                    neuron_amplitude = self._dynamic[step][neuron_index];
120
                    candidate_amplitude = self._dynamic[step][i];
121
                    
122
                    if ( not (candidate_amplitude < (neuron_amplitude + tolerance)) or not (candidate_amplitude > (neuron_amplitude - tolerance)) ):
123
                        captured_neuron = False;
124
                        break;
125
                    
126
                if ( captured_neuron is True ):
127
                    cluster.append(i);
128
                    break;
129
            
130
            if (captured_neuron is False):
131
                clusters.append([i]);
132
        
133
        return clusters;
134
135
136
class hysteresis_visualizer:
137
    """!
138
    @brief Visualizer of output dynamic of hysteresis oscillatory network.
139
    
140
    """
141
        
142
    @staticmethod
143
    def show_output_dynamic(hysteresis_output_dynamic):
144
        """!
145
        @brief Shows output dynamic (output of each oscillator) during simulation.
146
        
147
        @param[in] hysteresis_output_dynamic (hysteresis_dynamic): Output dynamic of the hysteresis oscillatory network.
148
        
149
        """
150
        
151
        draw_dynamics(hysteresis_output_dynamic.time, hysteresis_output_dynamic.output, x_title = "Time", y_title = "x(t)");
152
153
154
class hysteresis_network(network):
155
    """!
156
    @brief Hysteresis oscillatory network that uses relaxation oscillators.
157
    
158
    """
159
    
160
    @property
161
    def outputs(self):
162
        """!
163
        @brief Returns current outputs of neurons.
164
        
165
        @return (list) Current outputs of neurons.
166
        
167
        """
168
        
169
        return self._outputs;
170
    
171
    @outputs.setter
172
    def outputs(self, values):
173
        """!
174
        @brief Sets outputs of neurons.
175
        
176
        """
177
        
178
        self._outputs = [val for val in values];
179
        self._outputs_buffer = [val for val in values];
180
    
181
    @property
182
    def states(self):
183
        """!
184
        @brief Return current states of neurons.
185
        
186
        @return (list) States of neurons.
187
        
188
        """
189
        
190
        return self._states;
191
    
192
    @states.setter
193
    def states(self, values):
194
        """!
195
        @brief Set current states of neurons.
196
        
197
        """
198
        
199
        self._states = [val for val in values];
200
   
201
    
202
    def __init__(self, num_osc, own_weight = -4, neigh_weight = -1, type_conn = conn_type.ALL_TO_ALL, type_conn_represent = conn_represent.MATRIX):
203
        """!
204
        @brief Constructor of hysteresis oscillatory network.
205
        
206
        @param[in] num_osc (uint): Number of oscillators in the network.
207
        @param[in] own_weight (double): Weight of connection from oscillator to itself - own weight.
208
        @param[in] neigh_weight (double): Weight of connection between oscillators.
209
        @param[in] type_conn (conn_type): Type of connection between oscillators in the network.
210
        @param[in] type_conn_represent (conn_represent): Internal representation of connection in the network: matrix or list.
211
        
212
        """
213
        
214
        super().__init__(num_osc, type_conn, type_conn_represent);
215
        
216
        # list of states of neurons.
217
        self._states = [0] * self._num_osc;
218
        
219
        # list of current outputs of neurons.
220
        self._outputs = [-1] * self._num_osc;
221
        
222
        # list of previous outputs of neurons
223
        self._outputs_buffer = [-1] * self._num_osc;
224
        
225
        # matrix of connection weights between neurons.
226
        self._weight = list();
227
        for index in range(0, self._num_osc, 1):
228
            self._weight.append( [neigh_weight] * self._num_osc);
229
            self._weight[index][index] = own_weight;
230
231
    
232
    def _neuron_states(self, inputs, t, argv):
0 ignored issues
show
Unused Code introduced by
The argument t seems to be unused.
Loading history...
233
        """!
234
        @brief Returns new value of the neuron (oscillator).
235
        
236
        @param[in] inputs (list): Initial values (current) of the neuron - excitatory.
237
        @param[in] t (double): Current time of simulation.
238
        @param[in] argv (tuple): Extra arguments that are not used for integration - index of the neuron.
239
        
240
        @return (double) New value of the neuron.
241
        
242
        """
243
        
244
        xi = inputs[0];
245
        index = argv;
246
        
247
        # own impact
248
        impact = self._weight[index][index] * self._outputs[index];
249
        
250
        for i in range(0, self._num_osc, 1):
251
            if (self.has_connection(i, index)):
252
                impact += self._weight[index][i] * self._outputs[i];
253
254
        x = -xi + impact;
255
                
256
        if (xi > 1): self._outputs_buffer[index] = 1; 
257
        if (xi < -1): self._outputs_buffer[index] = -1;
258
       
259
        return x;
260
        
261
    
262
    def simulate(self, steps, time, solution = solve_type.RK4, collect_dynamic = True):
263
        """!
264
        @brief Performs static simulation of hysteresis oscillatory network.
265
        
266
        @param[in] steps (uint): Number steps of simulations during simulation.
267
        @param[in] time (double): Time of simulation.
268
        @param[in] solution (solve_type): Type of solution (solving).
269
        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
270
        
271
        @return (hysteresis_dynamic) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
272
                otherwise returns only last values (last step of simulation) of dynamic.
273
        """
274
                
275
        return self.simulate_static(steps, time, solution, collect_dynamic);
276
    
277
    
278
    def simulate_static(self, steps, time, solution = solve_type.RK4, collect_dynamic = False):
279
        """!
280
        @brief Performs static simulation of hysteresis oscillatory network.
281
        
282
        @param[in] steps (uint): Number steps of simulations during simulation.
283
        @param[in] time (double): Time of simulation.
284
        @param[in] solution (solve_type): Type of solution (solving).
285
        @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
286
        
287
        @return (hysteresis_dynamic) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
288
                otherwise returns only last values (last step of simulation) of dynamic.
289
        
290
        """
291
292
        # Check solver before simulation
293
        if (solution == solve_type.FAST):
294
            raise NameError("Solver FAST is not support due to low accuracy that leads to huge error.");
295
        elif (solution == solve_type.RKF45):
296
            raise NameError("Solver RKF45 is not support in python version.");
297
298
        dyn_state = None;
299
        dyn_time = None;
300
        
301
        if (collect_dynamic == True):
302
            dyn_state = [];
303
            dyn_time = [];
304
            
305
            dyn_state.append(self._states);
306
            dyn_time.append(0);
307
        
308
        step = time / steps;
309
        int_step = step / 10.0;
310
        
311
        for t in numpy.arange(step, time + step, step):
312
            # update states of oscillators
313
            self._states = self._calculate_states(solution, t, step, int_step);
314
            
315
            # update states of oscillators
316
            if (collect_dynamic is True):
317
                dyn_state.append(self._states);
318
                dyn_time.append(t);
319
        
320
        if (collect_dynamic is False):
321
            dyn_state.append(self._states);
322
            dyn_time.append(time);
323
        
324
        return hysteresis_dynamic(dyn_state, dyn_time);
325
326
327
    def _calculate_states(self, solution, t, step, int_step):
0 ignored issues
show
Unused Code introduced by
The argument solution seems to be unused.
Loading history...
328
        """!
329
        @brief Calculates new states for neurons using differential calculus. Returns new states for neurons.
330
        
331
        @param[in] solution (solve_type): Type solver of the differential equation.
332
        @param[in] t (double): Current time of simulation.
333
        @param[in] step (double): Step of solution at the end of which states of oscillators should be calculated.
334
        @param[in] int_step (double): Step differentiation that is used for solving differential equation.
335
        
336
        @return (list) New states for neurons.
337
        
338
        """
339
        
340
        next_states = [0] * self._num_osc;
341
        
342
        for index in range (0, self._num_osc, 1):            
343
            result = odeint(self._neuron_states, self._states[index], numpy.arange(t - step, t, int_step), (index , ));
344
            next_states[index] = result[len(result) - 1][0];
345
        
346
        self._outputs = [val for val in self._outputs_buffer];
347
        return next_states;