3 @brief Cluster analysis algorithm: Sync
4 @details Implementation based on paper @cite article::syncnet::1.
6 @authors Andrei Novikov (pyclustering@yandex.ru)
8 @copyright BSD-3-Clause
14 import matplotlib.pyplot
as plt
15 import matplotlib.animation
as animation
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
32 @brief Performs analysis of output dynamic of the oscillatory network syncnet to extract information about cluster allocation.
36 def __init__(self, phase, time, pointer_sync_analyser):
38 @brief Constructor of the analyser.
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.
45 super().
__init__(phase, time, pointer_sync_analyser)
50 @brief Desctructor of the analyser.
61 @brief Returns list of clusters in line with state of oscillators (phases).
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.
67 @return (list) List of clusters, for example [ [cluster1], [cluster2], ... ].)
76 @brief Returns clustering result representation type that indicate how clusters are encoded.
78 @return (type_encoding) Clustering result representation.
84 return type_encoding.CLUSTER_INDEX_LIST_SEPARATION
90 @brief Visualizer of output dynamic of oscillatory network 'syncnet' for cluster analysis.
97 @brief Shows animation of output dynamic (output of each oscillator) during simulation on a circle from [0; 2pi].
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.
108 figure = plt.figure()
111 return frame_generation(0)
113 def frame_generation(index_dynamic):
115 if title
is not None:
116 figure.suptitle(title, fontsize = 26, fontweight =
'bold')
118 ax1 = figure.add_subplot(121, projection=
'polar')
120 clusters = analyser.allocate_clusters(eps = tolerance, iteration = index_dynamic)
121 dynamic = analyser.output[index_dynamic]
124 visualizer.append_clusters(clusters, dataset)
126 artist1, = ax1.plot(dynamic, [1.0] * len(dynamic), marker=
'o', color=
'blue', ls=
'')
128 visualizer.show(figure, display =
False)
129 artist2 = figure.gca()
131 return [ artist1, artist2 ]
133 cluster_animation = animation.\
134 FuncAnimation(figure, frame_generation, len(analyser), interval=animation_velocity, init_func=init_frame,
137 if save_movie
is not None:
141 cluster_animation.save(save_movie, writer=
'ffmpeg', fps=15, bitrate=1500)
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.
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
159 # Read sample for clustering from some file.
160 sample = read_sample(SIMPLE_SAMPLES.SAMPLE_SIMPLE3)
162 # Create oscillatory network with connectivity radius 1.0.
163 network = syncnet(sample, 1.0)
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)
169 # Show oscillatory network.
170 network.show_network()
172 # Obtain clustering results.
173 clusters = analyser.allocate_clusters()
175 # Visualize clustering results.
176 visualizer = cluster_visualizer()
177 visualizer.append_clusters(clusters, sample)
183 def __init__(self, sample, radius, conn_repr=conn_represent.MATRIX, initial_phases=initial_type.RANDOM_GAUSSIAN,
184 enable_conn_weight=False, ccore=True):
186 @brief Contructor of the oscillatory network SYNC for cluster analysis.
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.
204 if (ccore
is True)
and ccore_library.workable():
211 super().
__init__(len(sample), 1, 0, conn_type.DYNAMIC, conn_repr, initial_phases,
False)
217 if radius
is not None:
223 @brief Destructor of oscillatory network is based on Kuramoto model.
232 def _verify_arguments(self):
234 @brief Verify input parameters for the algorithm and throw exception in case of incorrectness.
238 raise ValueError(
"Input data is empty (size: '%d')." % self.
_num_osc)
241 def _create_connections(self, radius):
243 @brief Create connections between oscillators in line with input radius of connectivity.
245 @param[in] radius (double): Connectivity radius between oscillators.
253 minimum_distance = float(
'inf')
256 for i
in range(0, self.
_num_osc, 1):
257 for j
in range(i + 1, self.
_num_osc, 1):
264 if (dist > maximum_distance): maximum_distance = dist
265 if (dist < minimum_distance): minimum_distance = dist
274 if maximum_distance != minimum_distance:
275 multiplier = (maximum_distance - minimum_distance)
276 subtractor = minimum_distance
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
286 def process(self, order = 0.998, solution=solve_type.FAST, collect_dynamic=True):
288 @brief Peforms cluster analysis using simulation of the oscillatory network.
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.
294 @return (syncnet_analyser) Returns analyser of results of clustering.
302 output_sync_dynamic = self.
simulate_dynamic(order, solution, collect_dynamic)
303 return syncnet_analyser(output_sync_dynamic.output, output_sync_dynamic.time,
None)
306 def _phase_kuramoto(self, teta, t, argv):
308 @brief Overrided method for calculation of oscillator phase.
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.
314 @return (double) New value of phase of oscillator with index 'argv'.
327 phase += conn_weight * self.
_weight * math.sin(self.
_phases[k] - teta)
329 divider = len(neighbors)
333 return self.
_freq[index] + (phase / divider)
338 @brief Shows connections in the network. It supports only 2-d and 3-d representation.
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');
349 from matplotlib.font_manager
import FontProperties
350 from matplotlib
import rcParams
352 rcParams[
'font.sans-serif'] = [
'Arial']
353 rcParams[
'font.size'] = 12
358 axes = fig.add_subplot(111)
360 axes = fig.gca(projection=
'3d')
362 surface_font = FontProperties()
363 surface_font.set_name(
'Arial')
364 surface_font.set_size(
'12')
366 for i
in range(0, self.
_num_osc, 1):
370 for j
in range(i, self.
_num_osc, 1):
383 for j
in range(i, self.
_num_osc, 1):