3 @brief Neural Network: Hysteresis Oscillatory Network
4 @details Implementation based on paper @cite article::nnet::hysteresis::1.
6 @authors Andrei Novikov (pyclustering@yandex.ru)
8 @copyright BSD-3-Clause
14 from scipy.integrate
import odeint
23 @brief Represents output dynamic of hysteresis oscillatory network.
30 @brief (list) Returns outputs of oscillator during simulation.
39 @brief (list) Returns sampling times when dynamic is measured during simulation.
48 @brief Constructor of hysteresis neural network dynamic.
50 @param[in] amplitudes (list): Dynamic (amplitudes) of oscillators on each step of simulation.
51 @param[in] time (list): Simulation time (timestamps of simulation steps) when amplitudes are stored.
55 if (len(amplitudes) != len(time)):
56 raise NameError(
"Length of list of dynamics of oscillators should be equal to length of simulation timestamps of steps.");
65 @brief (uint) Returns number of simulation steps that are stored in dynamic.
74 @brief Allocate clusters in line with ensembles of synchronous oscillators where each
75 synchronous ensemble corresponds to only one cluster.
77 @param[in] tolerance (double): Maximum error for allocation of synchronous ensemble oscillators.
78 @param[in] threshold_steps (uint): Number of steps from the end of simulation that should be analysed for ensemble allocation.
79 If amout of simulation steps has been less than threshold steps than amount of steps will be reduced to amout
82 @return (list) Grours of indexes of synchronous oscillators, for example, [ [index_osc1, index_osc3], [index_osc2], [index_osc4, index_osc5] ]."
88 number_oscillators = len(self.
_dynamic[0]);
90 for i
in range(1, number_oscillators, 1):
91 captured_neuron =
True;
92 for cluster
in clusters:
93 neuron_index = cluster[0];
95 analysis_steps = threshold_steps;
96 if (len(self.
_dynamic) < analysis_steps):
99 analysis_start_step_index = len(self.
_dynamic) - 1;
101 for step
in range(analysis_start_step_index, analysis_start_step_index - analysis_steps, -1):
102 neuron_amplitude = self.
_dynamic[step][neuron_index];
103 candidate_amplitude = self.
_dynamic[step][i];
105 if (
not (candidate_amplitude < (neuron_amplitude + tolerance))
or not (candidate_amplitude > (neuron_amplitude - tolerance)) ):
106 captured_neuron =
False;
109 if ( captured_neuron
is True ):
113 if (captured_neuron
is False):
114 clusters.append([i]);
121 @brief Visualizer of output dynamic of hysteresis oscillatory network.
128 @brief Shows output dynamic (output of each oscillator) during simulation.
130 @param[in] hysteresis_output_dynamic (hysteresis_dynamic): Output dynamic of the hysteresis oscillatory network.
134 draw_dynamics(hysteresis_output_dynamic.time, hysteresis_output_dynamic.output, x_title =
"Time", y_title =
"x(t)");
139 @brief Hysteresis oscillatory network that uses relaxation oscillators that are represented by objective hysteresis neurons whose output in range [-1, +1].
143 # create hysteresis oscillatory network with 5 oscillators.
144 network = hysteresis_network(5);
146 # set initial states (from range [-1, +1]).
147 network.states = [1 0 1 0 1];
149 # set initial outputs.
150 network.outputs = [1 1 1 1 1];
152 # perform simulation of the network during 1000 steps in 10 time units.
153 output_dynamic = network.simulate(1000, 10);
155 # show output dynamic of the network.
156 hysteresis_visualizer.show_output_dynamic(output_dynamic);
158 # obtain synchronous ensembles.
159 ensembles = output_dynamic.allocate_sync_ensembles(tolerance = 0.5, threshold_steps = 5);
168 @brief Returns current outputs of neurons.
170 @return (list) Current outputs of neurons.
179 @brief Sets outputs of neurons.
183 self.
_outputs = [val
for val
in values];
189 @brief Return current states of neurons.
191 @return (list) States of neurons.
200 @brief Set current states of neurons.
204 self.
_states = [val
for val
in values];
207 def __init__(self, num_osc, own_weight = -4, neigh_weight = -1, type_conn = conn_type.ALL_TO_ALL, type_conn_represent = conn_represent.MATRIX):
209 @brief Constructor of hysteresis oscillatory network.
211 @param[in] num_osc (uint): Number of oscillators in the network.
212 @param[in] own_weight (double): Weight of connection from oscillator to itself - own weight.
213 @param[in] neigh_weight (double): Weight of connection between oscillators.
214 @param[in] type_conn (conn_type): Type of connection between oscillators in the network.
215 @param[in] type_conn_represent (conn_represent): Internal representation of connection in the network: matrix or list.
219 super().
__init__(num_osc, type_conn, type_conn_represent);
232 for index
in range(0, self.
_num_osc, 1):
234 self.
_weight[index][index] = own_weight;
237 def _neuron_states(self, inputs, t, argv):
239 @brief Returns new value of the neuron (oscillator).
241 @param[in] inputs (list): Initial values (current) of the neuron - excitatory.
242 @param[in] t (double): Current time of simulation.
243 @param[in] argv (tuple): Extra arguments that are not used for integration - index of the neuron.
245 @return (double) New value of the neuron.
255 for i
in range(0, self.
_num_osc, 1):
267 def simulate(self, steps, time, solution = solve_type.RK4, collect_dynamic = True):
269 @brief Performs static simulation of hysteresis oscillatory network.
271 @param[in] steps (uint): Number steps of simulations during simulation.
272 @param[in] time (double): Time of simulation.
273 @param[in] solution (solve_type): Type of solution (solving).
274 @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
276 @return (hysteresis_dynamic) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
277 otherwise returns only last values (last step of simulation) of dynamic.
283 def simulate_static(self, steps, time, solution = solve_type.RK4, collect_dynamic = False):
285 @brief Performs static simulation of hysteresis oscillatory network.
287 @param[in] steps (uint): Number steps of simulations during simulation.
288 @param[in] time (double): Time of simulation.
289 @param[in] solution (solve_type): Type of solution (solving).
290 @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
292 @return (hysteresis_dynamic) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
293 otherwise returns only last values (last step of simulation) of dynamic.
298 if (solution == solve_type.FAST):
299 raise NameError(
"Solver FAST is not support due to low accuracy that leads to huge error.");
300 elif (solution == solve_type.RKF45):
301 raise NameError(
"Solver RKF45 is not support in python version.");
306 if (collect_dynamic ==
True):
310 dyn_state.append(self.
_states);
314 int_step = step / 10.0;
316 for t
in numpy.arange(step, time + step, step):
321 if (collect_dynamic
is True):
322 dyn_state.append(self.
_states);
325 if (collect_dynamic
is False):
326 dyn_state.append(self.
_states);
327 dyn_time.append(time);
332 def _calculate_states(self, solution, t, step, int_step):
334 @brief Calculates new states for neurons using differential calculus. Returns new states for neurons.
336 @param[in] solution (solve_type): Type solver of the differential equation.
337 @param[in] t (double): Current time of simulation.
338 @param[in] step (double): Step of solution at the end of which states of oscillators should be calculated.
339 @param[in] int_step (double): Step differentiation that is used for solving differential equation.
341 @return (list) New states for neurons.
347 for index
in range (0, self.
_num_osc, 1):
348 result = odeint(self.
_neuron_states, self.
_states[index], numpy.arange(t - step, t, int_step), (index , ));
349 next_states[index] = result[len(result) - 1][0];