pyclustering  0.10.1
pyclustring is a Python, C++ data mining library.
syncpr.py
1 """!
2 
3 @brief Phase oscillatory network for patten recognition based on modified Kuramoto model.
4 @details Implementation based on paper @cite article::nnet::syncpr::1.
5 
6 @authors Andrei Novikov (pyclustering@yandex.ru)
7 @date 2014-2020
8 @copyright BSD-3-Clause
9 
10 """
11 
12 import math
13 import cmath
14 import numpy
15 
16 from pyclustering.nnet import solve_type, initial_type, conn_type,conn_represent
17 from pyclustering.nnet.sync import sync_network, sync_dynamic, sync_visualizer
18 
19 import pyclustering.core.syncpr_wrapper as wrapper
20 
21 from pyclustering.core.wrapper import ccore_library
22 
23 from PIL import Image
24 
25 import matplotlib.pyplot as plt
26 import matplotlib.animation as animation
27 
28 
30  """!
31  @brief Represents output dynamic of syncpr (Sync for Pattern Recognition).
32 
33  """
34 
35  def __init__(self, phase, time, ccore):
36  """!
37  @brief Constructor of syncpr dynamic.
38 
39  @param[in] phase (list): Dynamic of oscillators on each step of simulation. If ccore pointer is specified than it can be ignored.
40  @param[in] time (list): Simulation time.
41  @param[in] ccore (ctypes.pointer): Pointer to CCORE sync_dynamic instance in memory.
42 
43  """
44  super().__init__(phase, time, ccore);
45 
46 
48  """!
49  @brief Visualizer of output dynamic of syncpr network (Sync for Pattern Recognition).
50 
51  """
52 
53  @staticmethod
54  def show_pattern(syncpr_output_dynamic, image_height, image_width):
55  """!
56  @brief Displays evolution of phase oscillators as set of patterns where the last one means final result of recognition.
57 
58  @param[in] syncpr_output_dynamic (syncpr_dynamic): Output dynamic of a syncpr network.
59  @param[in] image_height (uint): Height of the pattern (image_height * image_width should be equal to number of oscillators).
60  @param[in] image_width (uint): Width of the pattern.
61 
62  """
63  number_pictures = len(syncpr_output_dynamic);
64  iteration_math_step = 1.0;
65  if (number_pictures > 50):
66  iteration_math_step = number_pictures / 50.0;
67  number_pictures = 50;
68 
69  number_cols = int(numpy.ceil(number_pictures ** 0.5));
70  number_rows = int(numpy.ceil(number_pictures / number_cols));
71 
72  real_index = 0, 0;
73  double_indexer = True;
74  if ( (number_cols == 1) or (number_rows == 1) ):
75  real_index = 0;
76  double_indexer = False;
77 
78  (_, axarr) = plt.subplots(number_rows, number_cols);
79 
80  if (number_pictures > 1):
81  plt.setp([ax for ax in axarr], visible = False);
82 
83  iteration_display = 0.0;
84  for iteration in range(len(syncpr_output_dynamic)):
85  if (iteration >= iteration_display):
86  iteration_display += iteration_math_step;
87 
88  ax_handle = axarr;
89  if (number_pictures > 1):
90  ax_handle = axarr[real_index];
91 
92  syncpr_visualizer.__show_pattern(ax_handle, syncpr_output_dynamic, image_height, image_width, iteration);
93 
94  if (double_indexer is True):
95  real_index = real_index[0], real_index[1] + 1;
96  if (real_index[1] >= number_cols):
97  real_index = real_index[0] + 1, 0;
98  else:
99  real_index += 1;
100 
101  plt.show();
102 
103 
104  @staticmethod
105  def animate_pattern_recognition(syncpr_output_dynamic, image_height, image_width, animation_velocity = 75, title = None, save_movie = None):
106  """!
107  @brief Shows animation of pattern recognition process that has been preformed by the oscillatory network.
108 
109  @param[in] syncpr_output_dynamic (syncpr_dynamic): Output dynamic of a syncpr network.
110  @param[in] image_height (uint): Height of the pattern (image_height * image_width should be equal to number of oscillators).
111  @param[in] image_width (uint): Width of the pattern.
112  @param[in] animation_velocity (uint): Interval between frames in milliseconds.
113  @param[in] title (string): Title of the animation that is displayed on a figure if it is specified.
114  @param[in] save_movie (string): If it is specified then animation will be stored to file that is specified in this parameter.
115 
116  """
117  figure = plt.figure();
118 
119  def init_frame():
120  return frame_generation(0);
121 
122  def frame_generation(index_dynamic):
123  figure.clf();
124 
125  if (title is not None):
126  figure.suptitle(title, fontsize = 26, fontweight = 'bold')
127 
128  ax1 = figure.add_subplot(121, projection='polar');
129  ax2 = figure.add_subplot(122);
130 
131  dynamic = syncpr_output_dynamic.output[index_dynamic];
132 
133  artist1, = ax1.plot(dynamic, [1.0] * len(dynamic), marker = 'o', color = 'blue', ls = '');
134  artist2 = syncpr_visualizer.__show_pattern(ax2, syncpr_output_dynamic, image_height, image_width, index_dynamic);
135 
136  return [ artist1, artist2 ];
137 
138  cluster_animation = animation.FuncAnimation(figure, frame_generation, len(syncpr_output_dynamic), interval = animation_velocity, init_func = init_frame, repeat_delay = 5000);
139 
140  if (save_movie is not None):
141 # plt.rcParams['animation.ffmpeg_path'] = 'C:\\Users\\annoviko\\programs\\ffmpeg-win64-static\\bin\\ffmpeg.exe';
142 # ffmpeg_writer = animation.FFMpegWriter();
143 # cluster_animation.save(save_movie, writer = ffmpeg_writer, fps = 15);
144  cluster_animation.save(save_movie, writer = 'ffmpeg', fps = 15, bitrate = 1500);
145  else:
146  plt.show();
147 
148 
149  @staticmethod
150  def __show_pattern(ax_handle, syncpr_output_dynamic, image_height, image_width, iteration):
151  """!
152  @brief Draws pattern on specified ax.
153 
154  @param[in] ax_handle (Axis): Axis where pattern should be drawn.
155  @param[in] syncpr_output_dynamic (syncpr_dynamic): Output dynamic of a syncpr network.
156  @param[in] image_height (uint): Height of the pattern (image_height * image_width should be equal to number of oscillators).
157  @param[in] image_width (uint): Width of the pattern.
158  @param[in] iteration (uint): Simulation iteration that should be used for extracting pattern.
159 
160  @return (matplotlib.artist) Artist (pattern) that is rendered in the canvas.
161 
162  """
163 
164  current_dynamic = syncpr_output_dynamic.output[iteration];
165  stage_picture = [(255, 255, 255)] * (image_height * image_width);
166  for index_phase in range(len(current_dynamic)):
167  phase = current_dynamic[index_phase];
168 
169  pixel_color = math.floor( phase * (255 / (2 * math.pi)) );
170  stage_picture[index_phase] = (pixel_color, pixel_color, pixel_color);
171 
172  stage = numpy.array(stage_picture, numpy.uint8);
173  stage = numpy.reshape(stage, (image_height, image_width) + ((3),)); # ((3),) it's size of RGB - third dimension.
174 
175  image_cluster = Image.fromarray(stage);
176 
177  artist = ax_handle.imshow(image_cluster, interpolation = 'none');
178  plt.setp(ax_handle, visible = True);
179 
180  ax_handle.xaxis.set_ticklabels([]);
181  ax_handle.yaxis.set_ticklabels([]);
182  ax_handle.xaxis.set_ticks_position('none');
183  ax_handle.yaxis.set_ticks_position('none');
184 
185  return artist;
186 
187 
189  """!
190  @brief Model of phase oscillatory network for pattern recognition that is based on the Kuramoto model.
191  @details The model uses second-order and third-order modes of the Fourier components.
192 
193  CCORE option can be used to use the pyclustering core - C/C++ shared library for processing that significantly increases performance.
194 
195  Example:
196  @code
197  # Network size should be equal to size of pattern for learning.
198  net = syncpr(size_network, 0.3, 0.3);
199 
200  # Train network using list of patterns (input images).
201  net.train(image_samples);
202 
203  # Recognize image using 10 steps during 10 seconds of simulation.
204  sync_output_dynamic = net.simulate(10, 10, pattern, solve_type.RK4, True);
205 
206  # Display output dynamic.
207  syncpr_visualizer.show_output_dynamic(sync_output_dynamic);
208 
209  # Display evolution of recognition of the pattern.
210  syncpr_visualizer.show_pattern(sync_output_dynamic, image_height, image_width);
211 
212  @endcode
213 
214  """
215 
216  def __init__(self, num_osc, increase_strength1, increase_strength2, ccore = True):
217  """!
218  @brief Constructor of oscillatory network for pattern recognition based on Kuramoto model.
219 
220  @param[in] num_osc (uint): Number of oscillators in the network.
221  @param[in] increase_strength1 (double): Parameter for increasing strength of the second term of the Fourier component.
222  @param[in] increase_strength2 (double): Parameter for increasing strength of the third term of the Fourier component.
223  @param[in] ccore (bool): If True simulation is performed by CCORE library (C++ implementation of pyclustering).
224 
225  """
226 
227  if ( (ccore is True) and ccore_library.workable() ):
228  self._ccore_network_pointer = wrapper.syncpr_create(num_osc, increase_strength1, increase_strength2);
229 
230  else:
231  self._increase_strength1 = increase_strength1;
232  self._increase_strength2 = increase_strength2;
233  self._coupling = [ [0.0 for i in range(num_osc)] for j in range(num_osc) ];
234 
235  super().__init__(num_osc, 1, 0, conn_type.ALL_TO_ALL, conn_represent.MATRIX, initial_type.RANDOM_GAUSSIAN, ccore)
236 
237 
238  def __del__(self):
239  """!
240  @brief Default destructor of syncpr.
241 
242  """
243 
244  if (self._ccore_network_pointer is not None):
245  wrapper.syncpr_destroy(self._ccore_network_pointer);
246  self._ccore_network_pointer = None;
247 
248 
249  def __len__(self):
250  """!
251  @brief Returns size of the network.
252 
253  """
254  if (self._ccore_network_pointer is not None):
255  return wrapper.syncpr_get_size(self._ccore_network_pointer);
256 
257  else:
258  return self._num_osc;
259 
260 
261  def train(self, samples):
262  """!
263  @brief Trains syncpr network using Hebbian rule for adjusting strength of connections between oscillators during training.
264 
265  @param[in] samples (list): list of patterns where each pattern is represented by list of features that are equal to [-1; 1].
266 
267  """
268 
269  # Verify pattern for learning
270  for pattern in samples:
271  self.__validate_pattern(pattern);
272 
273  if (self._ccore_network_pointer is not None):
274  return wrapper.syncpr_train(self._ccore_network_pointer, samples);
275 
276  length = len(self);
277  number_samples = len(samples);
278 
279  for i in range(length):
280  for j in range(i + 1, len(self), 1):
281 
282  # go through via all patterns
283  for p in range(number_samples):
284  value1 = samples[p][i];
285  value2 = samples[p][j];
286 
287  self._coupling[i][j] += value1 * value2;
288 
289  self._coupling[i][j] /= length;
290  self._coupling[j][i] = self._coupling[i][j];
291 
292 
293  def simulate(self, steps, time, pattern, solution = solve_type.RK4, collect_dynamic = True):
294  """!
295  @brief Performs static simulation of syncpr oscillatory network.
296  @details In other words network performs pattern recognition during simulation.
297 
298  @param[in] steps (uint): Number steps of simulations during simulation.
299  @param[in] time (double): Time of simulation.
300  @param[in] pattern (list): Pattern for recognition represented by list of features that are equal to [-1; 1].
301  @param[in] solution (solve_type): Type of solver that should be used for simulation.
302  @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
303 
304  @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
305  otherwise returns only last values (last step of simulation) of dynamic.
306 
307  @see simulate_dynamic()
308  @see simulate_static()
309 
310  """
311 
312  return self.simulate_static(steps, time, pattern, solution, collect_dynamic);
313 
314 
315  def simulate_dynamic(self, pattern, order = 0.998, solution = solve_type.RK4, collect_dynamic = False, step = 0.1, int_step = 0.01, threshold_changes = 0.0000001):
316  """!
317  @brief Performs dynamic simulation of the network until stop condition is not reached.
318  @details In other words network performs pattern recognition during simulation.
319  Stop condition is defined by input argument 'order' that represents memory order, but
320  process of simulation can be stopped if convergance rate is low whose threshold is defined
321  by the argument 'threshold_changes'.
322 
323  @param[in] pattern (list): Pattern for recognition represented by list of features that are equal to [-1; 1].
324  @param[in] order (double): Order of process synchronization, distributed 0..1.
325  @param[in] solution (solve_type): Type of solution.
326  @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
327  @param[in] step (double): Time step of one iteration of simulation.
328  @param[in] int_step (double): Integration step, should be less than step.
329  @param[in] threshold_changes (double): Additional stop condition that helps prevent infinite simulation, defines limit of changes of oscillators between current and previous steps.
330 
331  @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
332  otherwise returns only last values (last step of simulation) of dynamic.
333 
334  @see simulate()
335  @see simulate_static()
336 
337  """
338 
339  self.__validate_pattern(pattern);
340 
341  if (self._ccore_network_pointer is not None):
342  ccore_instance_dynamic = wrapper.syncpr_simulate_dynamic(self._ccore_network_pointer, pattern, order, solution, collect_dynamic, step);
343  return syncpr_dynamic(None, None, ccore_instance_dynamic);
344 
345  for i in range(0, len(pattern), 1):
346  if (pattern[i] > 0.0):
347  self._phases[i] = 0.0;
348  else:
349  self._phases[i] = math.pi / 2.0;
350 
351  # For statistics and integration
352  time_counter = 0;
353 
354  # Prevent infinite loop. It's possible when required state cannot be reached.
355  previous_order = 0;
356  current_order = self.__calculate_memory_order(pattern);
357 
358  # If requested input dynamics
359  dyn_phase = [];
360  dyn_time = [];
361  if (collect_dynamic == True):
362  dyn_phase.append(self._phases);
363  dyn_time.append(0);
364 
365  # Execute until sync state will be reached
366  while (current_order < order):
367  # update states of oscillators
368  self._phases = self._calculate_phases(solution, time_counter, step, int_step);
369 
370  # update time
371  time_counter += step;
372 
373  # if requested input dynamic
374  if (collect_dynamic == True):
375  dyn_phase.append(self._phases);
376  dyn_time.append(time_counter);
377 
378  # update orders
379  previous_order = current_order;
380  current_order = self.__calculate_memory_order(pattern);
381 
382  # hang prevention
383  if (abs(current_order - previous_order) < threshold_changes):
384  break;
385 
386  if (collect_dynamic != True):
387  dyn_phase.append(self._phases);
388  dyn_time.append(time_counter);
389 
390  output_sync_dynamic = syncpr_dynamic(dyn_phase, dyn_time, None);
391  return output_sync_dynamic;
392 
393 
394  def simulate_static(self, steps, time, pattern, solution = solve_type.FAST, collect_dynamic = False):
395  """!
396  @brief Performs static simulation of syncpr oscillatory network.
397  @details In other words network performs pattern recognition during simulation.
398 
399  @param[in] steps (uint): Number steps of simulations during simulation.
400  @param[in] time (double): Time of simulation.
401  @param[in] pattern (list): Pattern for recognition represented by list of features that are equal to [-1; 1].
402  @param[in] solution (solve_type): Type of solution.
403  @param[in] collect_dynamic (bool): If True - returns whole dynamic of oscillatory network, otherwise returns only last values of dynamics.
404 
405  @return (list) Dynamic of oscillatory network. If argument 'collect_dynamic' = True, than return dynamic for the whole simulation time,
406  otherwise returns only last values (last step of simulation) of dynamic.
407 
408  @see simulate()
409  @see simulate_dynamic()
410 
411  """
412 
413  self.__validate_pattern(pattern);
414 
415  if (self._ccore_network_pointer is not None):
416  ccore_instance_dynamic = wrapper.syncpr_simulate_static(self._ccore_network_pointer, steps, time, pattern, solution, collect_dynamic);
417  return syncpr_dynamic(None, None, ccore_instance_dynamic);
418 
419  for i in range(0, len(pattern), 1):
420  if (pattern[i] > 0.0):
421  self._phases[i] = 0.0;
422  else:
423  self._phases[i] = math.pi / 2.0;
424 
425  return super().simulate_static(steps, time, solution, collect_dynamic);
426 
427 
428  def memory_order(self, pattern):
429  """!
430  @brief Calculates function of the memorized pattern.
431  @details Throws exception if length of pattern is not equal to size of the network or if it consists feature with value that are not equal to [-1; 1].
432 
433  @param[in] pattern (list): Pattern for recognition represented by list of features that are equal to [-1; 1].
434 
435  @return (double) Order of memory for the specified pattern.
436 
437  """
438 
439  self.__validate_pattern(pattern);
440 
441  if (self._ccore_network_pointer is not None):
442  return wrapper.syncpr_memory_order(self._ccore_network_pointer, pattern);
443 
444  else:
445  return self.__calculate_memory_order(pattern);
446 
447 
448  def __calculate_memory_order(self, pattern):
449  """!
450  @brief Calculates function of the memorized pattern without any pattern validation.
451 
452  @param[in] pattern (list): Pattern for recognition represented by list of features that are equal to [-1; 1].
453 
454  @return (double) Order of memory for the specified pattern.
455 
456  """
457 
458  memory_order = 0.0;
459  for index in range(len(self)):
460  memory_order += pattern[index] * cmath.exp( 1j * self._phases[index] );
461 
462  memory_order /= len(self);
463  return abs(memory_order);
464 
465 
466  def _phase_kuramoto(self, teta, t, argv):
467  """!
468  @brief Returns result of phase calculation for specified oscillator in the network.
469 
470  @param[in] teta (double): Phase of the oscillator that is differentiated.
471  @param[in] t (double): Current time of simulation.
472  @param[in] argv (tuple): Index of the oscillator in the list.
473 
474  @return (double) New phase for specified oscillator (don't assign it here).
475 
476  """
477 
478  index = argv;
479 
480  phase = 0.0;
481  term = 0.0;
482 
483  for k in range(0, self._num_osc):
484  if (k != index):
485  phase_delta = self._phases[k] - teta;
486 
487  phase += self._coupling[index][k] * math.sin(phase_delta);
488 
489  term1 = self._increase_strength1 * math.sin(2.0 * phase_delta);
490  term2 = self._increase_strength2 * math.sin(3.0 * phase_delta);
491 
492  term += (term1 - term2);
493 
494  return ( phase + term / len(self) );
495 
496 
497  def __validate_pattern(self, pattern):
498  """!
499  @brief Validates pattern.
500  @details Throws exception if length of pattern is not equal to size of the network or if it consists feature with value that are not equal to [-1; 1].
501 
502  @param[in] pattern (list): Pattern for recognition represented by list of features that are equal to [-1; 1].
503 
504  """
505  if (len(pattern) != len(self)):
506  raise NameError('syncpr: length of the pattern (' + len(pattern) + ') should be equal to size of the network');
507 
508  for feature in pattern:
509  if ( (feature != -1.0) and (feature != 1.0) ):
510  raise NameError('syncpr: patten feature (' + feature + ') should be distributed in [-1; 1]');
pyclustering.nnet.syncpr.syncpr.memory_order
def memory_order(self, pattern)
Calculates function of the memorized pattern.
Definition: syncpr.py:428
pyclustering.nnet.sync.sync_dynamic
Represents output dynamic of Sync.
Definition: sync.py:93
pyclustering.nnet.sync
Neural Network: Oscillatory Neural Network based on Kuramoto model.
Definition: sync.py:1
pyclustering.nnet.syncpr.syncpr
Model of phase oscillatory network for pattern recognition that is based on the Kuramoto model.
Definition: syncpr.py:188
pyclustering.nnet.syncpr.syncpr.__len__
def __len__(self)
Returns size of the network.
Definition: syncpr.py:249
pyclustering.nnet.sync.sync_network._ccore_network_pointer
_ccore_network_pointer
Definition: sync.py:724
pyclustering.nnet.sync.sync_network
Model of oscillatory network that is based on the Kuramoto model of synchronization.
Definition: sync.py:702
pyclustering.nnet.sync.sync_network._calculate_phases
def _calculate_phases(self, solution, t, step, int_step)
Calculates new phases for oscillators in the network in line with current step.
Definition: sync.py:966
pyclustering.nnet.network._num_osc
int _num_osc
Definition: __init__.py:88
pyclustering.nnet.syncpr.syncpr._increase_strength1
_increase_strength1
Definition: syncpr.py:231
pyclustering.nnet.syncpr.syncpr_visualizer.show_pattern
def show_pattern(syncpr_output_dynamic, image_height, image_width)
Displays evolution of phase oscillators as set of patterns where the last one means final result of r...
Definition: syncpr.py:54
pyclustering.nnet.syncpr.syncpr.__del__
def __del__(self)
Default destructor of syncpr.
Definition: syncpr.py:238
pyclustering.nnet.syncpr.syncpr.simulate_dynamic
def simulate_dynamic(self, pattern, order=0.998, solution=solve_type.RK4, collect_dynamic=False, step=0.1, int_step=0.01, threshold_changes=0.0000001)
Performs dynamic simulation of the network until stop condition is not reached.
Definition: syncpr.py:315
pyclustering.nnet.syncpr.syncpr_dynamic.__init__
def __init__(self, phase, time, ccore)
Constructor of syncpr dynamic.
Definition: syncpr.py:35
pyclustering.nnet.sync.sync_network.simulate_static
def simulate_static(self, steps, time, solution=solve_type.FAST, collect_dynamic=False)
Performs static simulation of oscillatory network.
Definition: sync.py:918
pyclustering.nnet.syncpr.syncpr._increase_strength2
_increase_strength2
Definition: syncpr.py:232
pyclustering.nnet.syncpr.syncpr._coupling
_coupling
Definition: syncpr.py:233
pyclustering.nnet.syncpr.syncpr_dynamic
Represents output dynamic of syncpr (Sync for Pattern Recognition).
Definition: syncpr.py:29
pyclustering.nnet.syncpr.syncpr.simulate_static
def simulate_static(self, steps, time, pattern, solution=solve_type.FAST, collect_dynamic=False)
Performs static simulation of syncpr oscillatory network.
Definition: syncpr.py:394
pyclustering.nnet.syncpr.syncpr.train
def train(self, samples)
Trains syncpr network using Hebbian rule for adjusting strength of connections between oscillators du...
Definition: syncpr.py:261
pyclustering.nnet.syncpr.syncpr_visualizer.animate_pattern_recognition
def animate_pattern_recognition(syncpr_output_dynamic, image_height, image_width, animation_velocity=75, title=None, save_movie=None)
Shows animation of pattern recognition process that has been preformed by the oscillatory network.
Definition: syncpr.py:105
pyclustering.nnet
Neural and oscillatory network module. Consists of models of bio-inspired networks.
Definition: __init__.py:1
pyclustering.nnet.syncpr.syncpr.__validate_pattern
def __validate_pattern(self, pattern)
Validates pattern.
Definition: syncpr.py:497
pyclustering.nnet.syncpr.syncpr.__calculate_memory_order
def __calculate_memory_order(self, pattern)
Calculates function of the memorized pattern without any pattern validation.
Definition: syncpr.py:448
pyclustering.nnet.sync.sync_visualizer
Visualizer of output dynamic of sync network (Sync).
Definition: sync.py:412
pyclustering.nnet.sync.sync_network._phases
_phases
Definition: sync.py:736
pyclustering.nnet.syncpr.syncpr.__init__
def __init__(self, num_osc, increase_strength1, increase_strength2, ccore=True)
Constructor of oscillatory network for pattern recognition based on Kuramoto model.
Definition: syncpr.py:216
pyclustering.nnet.syncpr.syncpr.simulate
def simulate(self, steps, time, pattern, solution=solve_type.RK4, collect_dynamic=True)
Performs static simulation of syncpr oscillatory network.
Definition: syncpr.py:293
pyclustering.nnet.syncpr.syncpr_visualizer
Visualizer of output dynamic of syncpr network (Sync for Pattern Recognition).
Definition: syncpr.py:47