pyclustering  0.10.1
pyclustring is a Python, C++ data mining library.
syncnet.py
1 """!
2 
3 @brief Cluster analysis algorithm: Sync
4 @details Implementation based on paper @cite article::syncnet::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 
14 import matplotlib.pyplot as plt
15 import matplotlib.animation as animation
16 
17 from pyclustering.cluster.encoder import type_encoding
18 from pyclustering.cluster import cluster_visualizer
19 
20 from pyclustering.core.syncnet_wrapper import syncnet_create_network, syncnet_process, syncnet_destroy_network, syncnet_analyser_destroy
21 from pyclustering.core.sync_wrapper import sync_connectivity_matrix
22 from pyclustering.core.wrapper import ccore_library
23 
24 from pyclustering.nnet.sync import sync_dynamic, sync_network, sync_visualizer
25 from pyclustering.nnet import conn_represent, initial_type, conn_type, solve_type
26 
27 from pyclustering.utils import euclidean_distance
28 
29 
31  """!
32  @brief Performs analysis of output dynamic of the oscillatory network syncnet to extract information about cluster allocation.
33 
34  """
35 
36  def __init__(self, phase, time, pointer_sync_analyser):
37  """!
38  @brief Constructor of the analyser.
39 
40  @param[in] phase (list): Output dynamic of the oscillatory network, where one iteration consists of all phases of oscillators.
41  @param[in] time (list): Simulation time.
42  @param[in] pointer_sync_analyser (POINTER): Pointer to CCORE analyser, if specified then other arguments can be omitted.
43 
44  """
45  super().__init__(phase, time, pointer_sync_analyser)
46 
47 
48  def __del__(self):
49  """!
50  @brief Desctructor of the analyser.
51 
52  """
53 
54  if self._ccore_sync_dynamic_pointer is not None:
55  syncnet_analyser_destroy(self._ccore_sync_dynamic_pointer)
56  self._ccore_sync_dynamic_pointer = None
57 
58 
59  def allocate_clusters(self, eps = 0.01, indexes = None, iteration = None):
60  """!
61  @brief Returns list of clusters in line with state of oscillators (phases).
62 
63  @param[in] eps (double): Tolerance that defines the maximum difference between phases of oscillators that belong to one cluster.
64  @param[in] indexes (list): List of real object indexes and it should be equal to amount of oscillators (in case of 'None' - indexes are in range [0; amount_oscillators]).
65  @param[in] iteration (uint): Iteration of simulation that should be used for allocation.
66 
67  @return (list) List of clusters, for example [ [cluster1], [cluster2], ... ].)
68 
69  """
70 
71  return self.allocate_sync_ensembles(eps, indexes, iteration)
72 
73 
75  """!
76  @brief Returns clustering result representation type that indicate how clusters are encoded.
77 
78  @return (type_encoding) Clustering result representation.
79 
80  @see get_clusters()
81 
82  """
83 
84  return type_encoding.CLUSTER_INDEX_LIST_SEPARATION
85 
86 
87 
89  """!
90  @brief Visualizer of output dynamic of oscillatory network 'syncnet' for cluster analysis.
91 
92  """
93 
94  @staticmethod
95  def animate_cluster_allocation(dataset, analyser, animation_velocity=75, tolerance=0.1, save_movie=None, title=None):
96  """!
97  @brief Shows animation of output dynamic (output of each oscillator) during simulation on a circle from [0; 2pi].
98 
99  @param[in] dataset (list): Input data that was used for processing by the network.
100  @param[in] analyser (syncnet_analyser): Output dynamic analyser of the Sync network.
101  @param[in] animation_velocity (uint): Interval between frames in milliseconds.
102  @param[in] tolerance (double): Tolerance level that define maximal difference between phases of oscillators in one cluster.
103  @param[in] save_movie (string): If it is specified then animation will be stored to file that is specified in this parameter.
104  @param[in] title (string): If it is specified then title will be displayed on the animation plot.
105 
106  """
107 
108  figure = plt.figure()
109 
110  def init_frame():
111  return frame_generation(0)
112 
113  def frame_generation(index_dynamic):
114  figure.clf()
115  if title is not None:
116  figure.suptitle(title, fontsize = 26, fontweight = 'bold')
117 
118  ax1 = figure.add_subplot(121, projection='polar')
119 
120  clusters = analyser.allocate_clusters(eps = tolerance, iteration = index_dynamic)
121  dynamic = analyser.output[index_dynamic]
122 
123  visualizer = cluster_visualizer(size_row = 2)
124  visualizer.append_clusters(clusters, dataset)
125 
126  artist1, = ax1.plot(dynamic, [1.0] * len(dynamic), marker='o', color='blue', ls='')
127 
128  visualizer.show(figure, display = False)
129  artist2 = figure.gca()
130 
131  return [ artist1, artist2 ]
132 
133  cluster_animation = animation.\
134  FuncAnimation(figure, frame_generation, len(analyser), interval=animation_velocity, init_func=init_frame,
135  repeat_delay=5000)
136 
137  if save_movie is not None:
138 # plt.rcParams['animation.ffmpeg_path'] = 'D:\\Program Files\\ffmpeg-3.3.1-win64-static\\bin\\ffmpeg.exe';
139 # ffmpeg_writer = animation.FFMpegWriter(fps = 15);
140 # cluster_animation.save(save_movie, writer = ffmpeg_writer);
141  cluster_animation.save(save_movie, writer='ffmpeg', fps=15, bitrate=1500)
142  else:
143  plt.show()
144 
145 
147  """!
148  @brief Class represents clustering algorithm SyncNet.
149  @details SyncNet is bio-inspired algorithm that is based on oscillatory network that uses modified Kuramoto model. Each attribute of a data object
150  is considered as a phase oscillator.
151 
152  Example:
153  @code
154  from pyclustering.cluster import cluster_visualizer
155  from pyclustering.cluster.syncnet import syncnet, solve_type
156  from pyclustering.samples.definitions import SIMPLE_SAMPLES
157  from pyclustering.utils import read_sample
158 
159  # Read sample for clustering from some file.
160  sample = read_sample(SIMPLE_SAMPLES.SAMPLE_SIMPLE3)
161 
162  # Create oscillatory network with connectivity radius 1.0.
163  network = syncnet(sample, 1.0)
164 
165  # Run cluster analysis and collect output dynamic of the oscillatory network.
166  # Network simulation is performed by Runge Kutta 4.
167  analyser = network.process(0.998, solve_type.RK4)
168 
169  # Show oscillatory network.
170  network.show_network()
171 
172  # Obtain clustering results.
173  clusters = analyser.allocate_clusters()
174 
175  # Visualize clustering results.
176  visualizer = cluster_visualizer()
177  visualizer.append_clusters(clusters, sample)
178  visualizer.show()
179  @endcode
180 
181  """
182 
183  def __init__(self, sample, radius, conn_repr=conn_represent.MATRIX, initial_phases=initial_type.RANDOM_GAUSSIAN,
184  enable_conn_weight=False, ccore=True):
185  """!
186  @brief Contructor of the oscillatory network SYNC for cluster analysis.
187 
188  @param[in] sample (list): Input data that is presented as list of points (objects), each point should be represented by list or tuple.
189  @param[in] radius (double): Connectivity radius between points, points should be connected if distance between them less then the radius.
190  @param[in] conn_repr (conn_represent): Internal representation of connection in the network: matrix or list. Ignored in case of usage of CCORE library.
191  @param[in] initial_phases (initial_type): Type of initialization of initial phases of oscillators (random, uniformly distributed, etc.).
192  @param[in] enable_conn_weight (bool): If True - enable mode when strength between oscillators depends on distance between two oscillators.
193  If False - all connection between oscillators have the same strength that equals to 1 (True).
194  @param[in] ccore (bool): Defines should be CCORE C++ library used instead of Python code or not.
195 
196  """
197 
198  self._ccore_network_pointer = None
199  self._osc_loc = sample
200  self._num_osc = len(sample)
201 
202  self._verify_arguments()
203 
204  if (ccore is True) and ccore_library.workable():
205  self._ccore_network_pointer = syncnet_create_network(sample, radius, initial_phases, enable_conn_weight)
206 
207  # Default representation that is returned by CCORE is matrix.
208  self._conn_represent = conn_represent.MATRIX
209 
210  else:
211  super().__init__(len(sample), 1, 0, conn_type.DYNAMIC, conn_repr, initial_phases, False)
212 
213  self._conn_weight = None
214  self._ena_conn_weight = enable_conn_weight
215 
216  # Create connections.
217  if radius is not None:
218  self._create_connections(radius)
219 
220 
221  def __del__(self):
222  """!
223  @brief Destructor of oscillatory network is based on Kuramoto model.
224 
225  """
226 
227  if self._ccore_network_pointer is not None:
228  syncnet_destroy_network(self._ccore_network_pointer)
229  self._ccore_network_pointer = None
230 
231 
232  def _verify_arguments(self):
233  """!
234  @brief Verify input parameters for the algorithm and throw exception in case of incorrectness.
235 
236  """
237  if self._num_osc <= 0:
238  raise ValueError("Input data is empty (size: '%d')." % self._num_osc)
239 
240 
241  def _create_connections(self, radius):
242  """!
243  @brief Create connections between oscillators in line with input radius of connectivity.
244 
245  @param[in] radius (double): Connectivity radius between oscillators.
246 
247  """
248 
249  if self._ena_conn_weight is True:
250  self._conn_weight = [[0] * self._num_osc for _ in range(0, self._num_osc, 1)]
251 
252  maximum_distance = 0
253  minimum_distance = float('inf')
254 
255  # Create connections
256  for i in range(0, self._num_osc, 1):
257  for j in range(i + 1, self._num_osc, 1):
258  dist = euclidean_distance(self._osc_loc[i], self._osc_loc[j])
259 
260  if self._ena_conn_weight is True:
261  self._conn_weight[i][j] = dist
262  self._conn_weight[j][i] = dist
263 
264  if (dist > maximum_distance): maximum_distance = dist
265  if (dist < minimum_distance): minimum_distance = dist
266 
267  if dist <= radius:
268  self.set_connection(i, j)
269 
270  if self._ena_conn_weight is True:
271  multiplier = 1
272  subtractor = 0
273 
274  if maximum_distance != minimum_distance:
275  multiplier = (maximum_distance - minimum_distance)
276  subtractor = minimum_distance
277 
278  for i in range(0, self._num_osc, 1):
279  for j in range(i + 1, self._num_osc, 1):
280  value_conn_weight = (self._conn_weight[i][j] - subtractor) / multiplier
281 
282  self._conn_weight[i][j] = value_conn_weight
283  self._conn_weight[j][i] = value_conn_weight
284 
285 
286  def process(self, order = 0.998, solution=solve_type.FAST, collect_dynamic=True):
287  """!
288  @brief Peforms cluster analysis using simulation of the oscillatory network.
289 
290  @param[in] order (double): Order of synchronization that is used as indication for stopping processing.
291  @param[in] solution (solve_type): Specified type of solving diff. equation.
292  @param[in] collect_dynamic (bool): Specified requirement to collect whole dynamic of the network.
293 
294  @return (syncnet_analyser) Returns analyser of results of clustering.
295 
296  """
297 
298  if self._ccore_network_pointer is not None:
299  pointer_output_dynamic = syncnet_process(self._ccore_network_pointer, order, solution, collect_dynamic)
300  return syncnet_analyser(None, None, pointer_output_dynamic)
301  else:
302  output_sync_dynamic = self.simulate_dynamic(order, solution, collect_dynamic)
303  return syncnet_analyser(output_sync_dynamic.output, output_sync_dynamic.time, None)
304 
305 
306  def _phase_kuramoto(self, teta, t, argv):
307  """!
308  @brief Overrided method for calculation of oscillator phase.
309 
310  @param[in] teta (double): Current value of phase.
311  @param[in] t (double): Time (can be ignored).
312  @param[in] argv (uint): Index of oscillator whose phase represented by argument teta.
313 
314  @return (double) New value of phase of oscillator with index 'argv'.
315 
316  """
317 
318  index = argv # index of oscillator
319  phase = 0.0 # phase of a specified oscillator that will calculated in line with current env. states.
320 
321  neighbors = self.get_neighbors(index)
322  for k in neighbors:
323  conn_weight = 1.0
324  if self._ena_conn_weight is True:
325  conn_weight = self._conn_weight[index][k]
326 
327  phase += conn_weight * self._weight * math.sin(self._phases[k] - teta)
328 
329  divider = len(neighbors)
330  if divider == 0:
331  divider = 1.0
332 
333  return self._freq[index] + (phase / divider)
334 
335 
336  def show_network(self):
337  """!
338  @brief Shows connections in the network. It supports only 2-d and 3-d representation.
339 
340  """
341 
342  if (self._ccore_network_pointer is not None) and (self._osc_conn is None):
343  self._osc_conn = sync_connectivity_matrix(self._ccore_network_pointer)
344 
345  dimension = len(self._osc_loc[0])
346  if (dimension != 3) and (dimension != 2):
347  raise NameError('Network that is located in different from 2-d and 3-d dimensions can not be represented');
348 
349  from matplotlib.font_manager import FontProperties
350  from matplotlib import rcParams
351 
352  rcParams['font.sans-serif'] = ['Arial']
353  rcParams['font.size'] = 12
354 
355  fig = plt.figure()
356  axes = None
357  if dimension == 2:
358  axes = fig.add_subplot(111)
359  elif dimension == 3:
360  axes = fig.gca(projection='3d')
361 
362  surface_font = FontProperties()
363  surface_font.set_name('Arial')
364  surface_font.set_size('12')
365 
366  for i in range(0, self._num_osc, 1):
367  if dimension == 2:
368  axes.plot(self._osc_loc[i][0], self._osc_loc[i][1], 'bo')
369  if self._conn_represent == conn_represent.MATRIX:
370  for j in range(i, self._num_osc, 1): # draw connection between two points only one time
371  if self.has_connection(i, j) is True:
372  axes.plot([self._osc_loc[i][0], self._osc_loc[j][0]], [self._osc_loc[i][1], self._osc_loc[j][1]], 'b-', linewidth = 0.5)
373 
374  else:
375  for j in self.get_neighbors(i):
376  if (self.has_connection(i, j) is True) and (i > j): # draw connection between two points only one time
377  axes.plot([self._osc_loc[i][0], self._osc_loc[j][0]], [self._osc_loc[i][1], self._osc_loc[j][1]], 'b-', linewidth = 0.5)
378 
379  elif dimension == 3:
380  axes.scatter(self._osc_loc[i][0], self._osc_loc[i][1], self._osc_loc[i][2], c = 'b', marker = 'o')
381 
382  if self._conn_represent == conn_represent.MATRIX:
383  for j in range(i, self._num_osc, 1): # draw connection between two points only one time
384  if self.has_connection(i, j) is True:
385  axes.plot([self._osc_loc[i][0], self._osc_loc[j][0]], [self._osc_loc[i][1], self._osc_loc[j][1]], [self._osc_loc[i][2], self._osc_loc[j][2]], 'b-', linewidth = 0.5)
386 
387  else:
388  for j in self.get_neighbors(i):
389  if (self.has_connection(i, j) == True) and (i > j): # draw connection between two points only one time
390  axes.plot([self._osc_loc[i][0], self._osc_loc[j][0]], [self._osc_loc[i][1], self._osc_loc[j][1]], [self._osc_loc[i][2], self._osc_loc[j][2]], 'b-', linewidth = 0.5)
391 
392  plt.grid()
393  plt.show()
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.cluster.syncnet.syncnet._ccore_network_pointer
_ccore_network_pointer
Definition: syncnet.py:197
pyclustering.cluster.syncnet.syncnet_visualizer
Visualizer of output dynamic of oscillatory network 'syncnet' for cluster analysis.
Definition: syncnet.py:88
pyclustering.cluster.syncnet.syncnet_analyser.allocate_clusters
def allocate_clusters(self, eps=0.01, indexes=None, iteration=None)
Returns list of clusters in line with state of oscillators (phases).
Definition: syncnet.py:59
pyclustering.cluster.cluster_visualizer
Common visualizer of clusters on 1D, 2D or 3D surface.
Definition: __init__.py:370
pyclustering.nnet.sync.sync_network.simulate_dynamic
def simulate_dynamic(self, order=0.998, solution=solve_type.FAST, 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: sync.py:851
pyclustering.nnet.sync.sync_network._freq
_freq
Definition: sync.py:737
pyclustering.cluster.syncnet.syncnet._conn_weight
_conn_weight
Definition: syncnet.py:212
pyclustering.nnet.sync.sync_network
Model of oscillatory network that is based on the Kuramoto model of synchronization.
Definition: sync.py:702
pyclustering.cluster.syncnet.syncnet._num_osc
_num_osc
Definition: syncnet.py:199
pyclustering.cluster.syncnet.syncnet
Class represents clustering algorithm SyncNet.
Definition: syncnet.py:146
pyclustering.cluster.syncnet.syncnet._osc_loc
_osc_loc
Definition: syncnet.py:198
pyclustering.cluster.syncnet.syncnet_analyser
Performs analysis of output dynamic of the oscillatory network syncnet to extract information about c...
Definition: syncnet.py:30
pyclustering.cluster
pyclustering module for cluster analysis.
Definition: __init__.py:1
pyclustering.cluster.syncnet.syncnet_analyser.__init__
def __init__(self, phase, time, pointer_sync_analyser)
Constructor of the analyser.
Definition: syncnet.py:36
pyclustering.cluster.syncnet.syncnet.__init__
def __init__(self, sample, radius, conn_repr=conn_represent.MATRIX, initial_phases=initial_type.RANDOM_GAUSSIAN, enable_conn_weight=False, ccore=True)
Contructor of the oscillatory network SYNC for cluster analysis.
Definition: syncnet.py:183
pyclustering.cluster.syncnet.syncnet_analyser.get_cluster_encoding
def get_cluster_encoding(self)
Returns clustering result representation type that indicate how clusters are encoded.
Definition: syncnet.py:74
pyclustering.nnet.network.get_neighbors
def get_neighbors(self, index)
Finds neighbors of the oscillator with specified index.
Definition: __init__.py:394
pyclustering.cluster.syncnet.syncnet._conn_represent
_conn_represent
Definition: syncnet.py:207
pyclustering.nnet.network.has_connection
def has_connection(self, i, j)
Returns True if there is connection between i and j oscillators and False - if connection doesn't exi...
Definition: __init__.py:351
pyclustering.cluster.syncnet.syncnet.process
def process(self, order=0.998, solution=solve_type.FAST, collect_dynamic=True)
Peforms cluster analysis using simulation of the oscillatory network.
Definition: syncnet.py:286
pyclustering.cluster.syncnet.syncnet._ena_conn_weight
_ena_conn_weight
Definition: syncnet.py:213
pyclustering.nnet.network.set_connection
def set_connection(self, i, j)
Couples two specified oscillators in the network with dynamic connections.
Definition: __init__.py:372
pyclustering.cluster.syncnet.syncnet._osc_conn
_osc_conn
Definition: syncnet.py:343
pyclustering.nnet
Neural and oscillatory network module. Consists of models of bio-inspired networks.
Definition: __init__.py:1
pyclustering.cluster.syncnet.syncnet.show_network
def show_network(self)
Shows connections in the network.
Definition: syncnet.py:336
pyclustering.cluster.syncnet.syncnet_analyser._ccore_sync_dynamic_pointer
_ccore_sync_dynamic_pointer
Definition: syncnet.py:56
pyclustering.nnet.sync.sync_network._weight
_weight
Definition: sync.py:734
pyclustering.cluster.syncnet.syncnet_visualizer.animate_cluster_allocation
def animate_cluster_allocation(dataset, analyser, animation_velocity=75, tolerance=0.1, save_movie=None, title=None)
Shows animation of output dynamic (output of each oscillator) during simulation on a circle from [0; ...
Definition: syncnet.py:95
pyclustering.cluster.syncnet.syncnet_analyser.__del__
def __del__(self)
Desctructor of the analyser.
Definition: syncnet.py:48
pyclustering.nnet.sync.sync_visualizer
Visualizer of output dynamic of sync network (Sync).
Definition: sync.py:412
pyclustering.cluster.syncnet.syncnet._verify_arguments
def _verify_arguments(self)
Verify input parameters for the algorithm and throw exception in case of incorrectness.
Definition: syncnet.py:232
pyclustering.nnet.sync.sync_dynamic.allocate_sync_ensembles
def allocate_sync_ensembles(self, tolerance=0.01, indexes=None, iteration=None)
Allocate clusters in line with ensembles of synchronous oscillators where each synchronous ensemble c...
Definition: sync.py:174
pyclustering.cluster.syncnet.syncnet._create_connections
def _create_connections(self, radius)
Create connections between oscillators in line with input radius of connectivity.
Definition: syncnet.py:241
pyclustering.utils
Utils that are used by modules of pyclustering.
Definition: __init__.py:1
pyclustering.nnet.sync.sync_network._phases
_phases
Definition: sync.py:736
pyclustering.cluster.syncnet.syncnet.__del__
def __del__(self)
Destructor of oscillatory network is based on Kuramoto model.
Definition: syncnet.py:221
pyclustering.cluster.encoder
Module for representing clustering results.
Definition: encoder.py:1