3 @brief Neural Network: Local Excitatory Global Inhibitory Oscillatory Network (LEGION)
4 @details Implementation based on paper @cite article::legion::1, @cite article::legion::2.
6 @authors Andrei Novikov (pyclustering@yandex.ru)
8 @copyright BSD-3-Clause
15 import pyclustering.core.legion_wrapper
as wrapper
17 from pyclustering.core.wrapper
import ccore_library
23 from scipy.integrate
import odeint
28 @brief Describes parameters of LEGION.
29 @details Contained parameters affect on output dynamic of each oscillator of the network.
37 @brief Default constructor of parameters for LEGION (local excitatory global inhibitory oscillatory network).
38 @details Constructor initializes parameters by default non-zero values that can be
39 used for simple simulation.
99 @brief Represents output dynamic of LEGION.
106 @brief Returns output dynamic of the network.
118 @brief Returns output dynamic of the global inhibitor of the network.
131 @brief Returns simulation time.
137 return list(range(len(self)));
140 def __init__(self, output, inhibitor, time, ccore = None):
142 @brief Constructor of legion dynamic.
144 @param[in] output (list): Output dynamic of the network represented by excitatory values of oscillators.
145 @param[in] inhibitor (list): Output dynamic of the global inhibitor of the network.
146 @param[in] time (list): Simulation time.
147 @param[in] ccore (POINTER): Pointer to CCORE legion_dynamic. If it is specified then others arguments can be omitted.
160 @brief Destructor of the dynamic of the legion network.
169 @brief Returns length of output dynamic.
175 return len(self.
_time);
180 @brief Allocate clusters in line with ensembles of synchronous oscillators where each synchronous ensemble corresponds to only one cluster.
182 @param[in] tolerance (double): Maximum error for allocation of synchronous ensemble oscillators.
184 @return (list) Grours of indexes of synchronous oscillators, for example, [ [index_osc1, index_osc3], [index_osc2], [index_osc4, index_osc5] ].
196 @brief Local excitatory global inhibitory oscillatory network (LEGION) that uses relaxation oscillator
197 based on Van der Pol model.
199 @details The model uses global inhibitor to de-synchronize synchronous ensembles of oscillators.
201 CCORE option can be used to use the pyclustering core - C/C++ shared library for processing that significantly increases performance.
205 # Create parameters of the network
206 parameters = legion_parameters();
210 stimulus = [1, 1, 0, 0, 0, 1, 1, 1];
212 # Create the network (use CCORE for fast solving)
213 net = legion_network(len(stimulus), parameters, conn_type.GRID_FOUR, ccore = True);
215 # Simulate network - result of simulation is output dynamic of the network
216 output_dynamic = net.simulate(1000, 750, stimulus);
218 # Draw output dynamic
219 draw_dynamics(output_dynamic.time, output_dynamic.output, x_title = "Time", y_title = "x(t)");
224 def __init__(self, num_osc, parameters = None, type_conn = conn_type.ALL_TO_ALL, type_conn_represent = conn_represent.MATRIX, ccore = True):
226 @brief Constructor of oscillatory network LEGION (local excitatory global inhibitory oscillatory network).
228 @param[in] num_osc (uint): Number of oscillators in the network.
229 @param[in] parameters (legion_parameters): Parameters of the network that are defined by structure 'legion_parameters'.
230 @param[in] type_conn (conn_type): Type of connection between oscillators in the network.
231 @param[in] type_conn_represent (conn_represent): Internal representation of connection in the network: matrix or list.
232 @param[in] ccore (bool): If True then all interaction with object will be performed via CCORE library (C++ implementation of pyclustering).
245 if ( (ccore
is True)
and ccore_library.workable() ):
249 super().
__init__(num_osc, type_conn, type_conn_represent);
270 @brief Default destructor of LEGION.
280 @brief (uint) Returns size of LEGION.
290 def __create_stimulus(self, stimulus):
292 @brief Create stimulus for oscillators in line with stimulus map and parameters.
294 @param[in] stimulus (list): Stimulus for oscillators that is represented by list, number of stimulus should be equal number of oscillators.
298 if (len(stimulus) != self.
_num_osc):
299 raise NameError(
"Number of stimulus should be equal number of oscillators in the network.");
308 def __create_dynamic_connections(self):
310 @brief Create dynamic connection in line with input stimulus.
315 raise NameError(
"Stimulus should initialed before creation of the dynamic connections in the network.");
322 if (len(neighbors) > 0)
and (self.
_stimulus[i] > 0):
323 number_stimulated_neighbors = 0.0
326 number_stimulated_neighbors += 1.0
328 if (number_stimulated_neighbors > 0):
329 dynamic_weight = self.
_params.Wt / number_stimulated_neighbors
335 def simulate(self, steps, time, stimulus, solution=solve_type.RK4, collect_dynamic=True):
337 @brief Performs static simulation of LEGION oscillatory network.
339 @param[in] steps (uint): Number steps of simulations during simulation.
340 @param[in] time (double): Time of simulation.
341 @param[in] stimulus (list): Stimulus for oscillators, number of stimulus should be equal to number of oscillators,
342 example of stimulus for 5 oscillators [0, 0, 1, 1, 0], value of stimulus is defined by parameter 'I'.
343 @param[in] solution (solve_type): Method that is used for differential equation.
344 @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
346 @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
347 otherwise returns only last values (last step of simulation) of dynamic.
352 pointer_dynamic = wrapper.legion_simulate(self.
__ccore_legion_pointer, steps, time, solution, collect_dynamic, stimulus)
356 if solution == solve_type.FAST:
357 raise NameError(
"Solver FAST is not support due to low accuracy that leads to huge error.")
359 elif solution == solve_type.RKF45:
360 raise NameError(
"Solver RKF45 is not support in python version. RKF45 is supported in CCORE implementation.")
373 if collect_dynamic
is True:
379 int_step = step / 10.0
381 for t
in numpy.arange(step, time + step, step):
386 if collect_dynamic
is True:
398 def _calculate_states(self, solution, t, step, int_step):
400 @brief Calculates new state of each oscillator in the network.
402 @param[in] solution (solve_type): Type solver of the differential equation.
403 @param[in] t (double): Current time of simulation.
404 @param[in] step (double): Step of solution at the end of which states of oscillators should be calculated.
405 @param[in] int_step (double): Step differentiation that is used for solving differential equation.
409 next_excitatory = [0.0] * self.
_num_osc;
410 next_inhibitory = [0.0] * self.
_num_osc;
413 if (self.
_params.ENABLE_POTENTIONAL
is True):
414 next_potential = [0.0] * self.
_num_osc;
417 for index
in range (0, self.
_num_osc, 1):
418 if (self.
_params.ENABLE_POTENTIONAL
is True):
420 [ next_excitatory[index], next_inhibitory[index], next_potential[index] ] = result[len(result) - 1][0:3];
424 [ next_excitatory[index], next_inhibitory[index] ] = result[len(result) - 1][0:2];
430 for index_neighbor
in neighbors:
444 if (self.
_params.ENABLE_POTENTIONAL
is True):
449 def _global_inhibitor_state(self, z, t, argv):
451 @brief Returns new value of global inhibitory
453 @param[in] z (dobule): Current value of inhibitory.
454 @param[in] t (double): Current time of simulation.
455 @param[in] argv (tuple): It's not used, can be ignored.
457 @return (double) New value if global inhibitory (not assign).
468 return self.
_params.fi * (sigma - z);
471 def _legion_state_simplify(self, inputs, t, argv):
473 @brief Returns new values of excitatory and inhibitory parts of oscillator of oscillator.
474 @details Simplify model doesn't consider oscillator potential.
476 @param[in] inputs (list): Initial values (current) of oscillator [excitatory, inhibitory].
477 @param[in] t (double): Current time of simulation.
478 @param[in] argv (uint): Extra arguments that are not used for integration - index of oscillator.
480 @return (list) New values of excitatoty and inhibitory part of oscillator (not assign).
495 for index_neighbor
in neighbors:
501 def _legion_state(self, inputs, t, argv):
503 @brief Returns new values of excitatory and inhibitory parts of oscillator and potential of oscillator.
505 @param[in] inputs (list): Initial values (current) of oscillator [excitatory, inhibitory, potential].
506 @param[in] t (double): Current time of simulation.
507 @param[in] argv (uint): Extra arguments that are not used for integration - index of oscillator.
509 @return (list) New values of excitatoty and inhibitory part of oscillator and new value of potential (not assign).
519 potential_influence = heaviside(p + math.exp(-self.
_params.alpha * t) - self.
_params.teta);
527 for index_neighbor
in neighbors:
530 dp = self.
_params.lamda * (1.0 - p) * heaviside(potential - self.
_params.teta_p) - self.
_params.mu * p;