def __update_cluster_dendrogram(self, index_cluster, blocks):

if len(self.__dendrogram) <= index_cluster:<EOL><INDENT>self.__dendrogram.append([])<EOL><DEDENT>blocks = sorted(blocks, key=lambda block: block.get_density(), reverse=True)<EOL>self.__dendrogram[index_cluster] += blocks<EOL>

!
        @brief Append clustered blocks to dendrogram.

        @param[in] index_cluster (uint): Cluster index that was assigned to blocks.
        @param[in] blocks (list): Blocks that were clustered.


def __init__(self, data, clusters, **kwargs):

self.__data = data<EOL>self.__clusters = clusters<EOL>self.__metric = kwargs.get('<STR_LIT>', distance_metric(type_metric.EUCLIDEAN_SQUARE))<EOL>if self.__metric.get_type() != type_metric.USER_DEFINED:<EOL><INDENT>self.__metric.enable_numpy_usage()<EOL><DEDENT>else:<EOL><INDENT>self.__metric.disable_numpy_usage()<EOL><DEDENT>self.__score = [<NUM_LIT:0.0>] * len(data)<EOL>self.__ccore = kwargs.get('<STR_LIT>', True) and self.__metric.get_type() != type_metric.USER_DEFINED<EOL>if self.__ccore:<EOL><INDENT>self.__ccore = ccore_library.workable()<EOL><DEDENT>if self.__ccore is False:<EOL><INDENT>self.__data = numpy.array(data)<EOL><DEDENT>

!
        @brief Initializes Silhouette method for analysis.

        @param[in] data (array_like): Input data that was used for cluster analysis.
        @param[in] clusters (list): Cluster that have been obtained after cluster analysis.
        @param[in] **kwargs: Arbitrary keyword arguments (available arguments: 'metric').

        Keyword Args:

            - metric (distance_metric): Metric that was used for cluster analysis and should be used for Silhouette
               score calculation (by default Square Euclidean distance).
            - ccore (bool): If True then CCORE (C++ implementation of pyclustering library) is used (by default True).


if self.__ccore is True:<EOL><INDENT>self.__process_by_ccore()<EOL><DEDENT>else:<EOL><INDENT>self.__process_by_python()<EOL><DEDENT>return self<EOL>

!
        @brief Calculates Silhouette score for each object from input data.

        @return (silhouette) Instance of the method (self).


ccore_metric = metric_wrapper.create_instance(self.__metric)<EOL>self.__score = wrapper.silhoeutte(self.__data, self.__clusters, ccore_metric.get_pointer())<EOL>

!
        @brief Performs processing using CCORE (C/C++ part of pyclustering library).


for index_cluster in range(len(self.__clusters)):<EOL><INDENT>for index_point in self.__clusters[index_cluster]:<EOL><INDENT>self.__score[index_point] = self.__calculate_score(index_point, index_cluster)<EOL><DEDENT><DEDENT>

!
        @brief Performs processing using python code.


return self.__score<EOL>

!
        @brief Returns Silhouette score for each object from input data.

        @see process


def __calculate_score(self, index_point, index_cluster):

difference = self.__calculate_dataset_difference(index_point)<EOL>a_score = self.__calculate_within_cluster_score(index_cluster, difference)<EOL>b_score = self.__caclulate_optimal_neighbor_cluster_score(index_cluster, difference)<EOL>return (b_score - a_score) / max(a_score, b_score)<EOL>

!
        @brief Calculates Silhouette score for the specific object defined by index_point.

        @param[in] index_point (uint): Index point from input data for which Silhouette score should be calculated.
        @param[in] index_cluster (uint): Index cluster to which the point belongs to.

        @return (float) Silhouette score for the object.


def __calculate_within_cluster_score(self, index_cluster, difference):

score = self.__calculate_cluster_difference(index_cluster, difference)<EOL>if len(self.__clusters[index_cluster]) == <NUM_LIT:1>:<EOL><INDENT>return float('<STR_LIT>')<EOL><DEDENT>return score / (len(self.__clusters[index_cluster]) - <NUM_LIT:1>)<EOL>

!
        @brief Calculates 'A' score for the specific object in cluster to which it belongs to.

        @param[in] index_point (uint): Index point from input data for which 'A' score should be calculated.
        @param[in] index_cluster (uint): Index cluster to which the point is belong to.

        @return (float) 'A' score for the object.


def __calculate_cluster_score(self, index_cluster, difference):

score = self.__calculate_cluster_difference(index_cluster, difference)<EOL>return score / len(self.__clusters[index_cluster])<EOL>

!
        @brief Calculates 'B*' score for the specific object for specific cluster.

        @param[in] index_point (uint): Index point from input data for which 'B*' score should be calculated.
        @param[in] index_cluster (uint): Index cluster to which the point is belong to.

        @return (float) 'B*' score for the object for specific cluster.


def __caclulate_optimal_neighbor_cluster_score(self, index_cluster, difference):

optimal_score = float('<STR_LIT>')<EOL>for index_neighbor_cluster in range(len(self.__clusters)):<EOL><INDENT>if index_cluster != index_neighbor_cluster:<EOL><INDENT>candidate_score = self.__calculate_cluster_score(index_neighbor_cluster, difference)<EOL>if candidate_score < optimal_score:<EOL><INDENT>optimal_score = candidate_score<EOL><DEDENT><DEDENT><DEDENT>if optimal_score == float('<STR_LIT>'):<EOL><INDENT>optimal_score = -<NUM_LIT:1.0><EOL><DEDENT>return optimal_score<EOL>

!
        @brief Calculates 'B' score for the specific object for the nearest cluster.

        @param[in] index_point (uint): Index point from input data for which 'B' score should be calculated.
        @param[in] index_cluster (uint): Index cluster to which the point is belong to.

        @return (float) 'B' score for the object.


def __calculate_cluster_difference(self, index_cluster, difference):

cluster_difference = <NUM_LIT:0.0><EOL>for index_point in self.__clusters[index_cluster]:<EOL><INDENT>cluster_difference += difference[index_point]<EOL><DEDENT>return cluster_difference<EOL>

!
        @brief Calculates distance from each object in specified cluster to specified object.

        @param[in] index_point (uint): Index point for which difference is calculated.

        @return (list) Distance from specified object to each object from input data in specified cluster.


if self.__metric.get_type() != type_metric.USER_DEFINED:<EOL><INDENT>dataset_differences = self.__metric(self.__data, self.__data[index_point])<EOL><DEDENT>else:<EOL><INDENT>dataset_differences = [self.__metric(point, self.__data[index_point]) for point in self.__data]<EOL><DEDENT>return dataset_differences<EOL>

!
        @brief Calculate distance from each object to specified object.

        @param[in] index_point (uint): Index point for which difference with other points is calculated.

        @return (list) Distance to each object from input data from the specified.


if self == silhouette_ksearch_type.KMEANS:<EOL><INDENT>return kmeans<EOL><DEDENT>elif self == silhouette_ksearch_type.KMEDIANS:<EOL><INDENT>return kmedians<EOL><DEDENT>elif self == silhouette_ksearch_type.KMEDOIDS:<EOL><INDENT>return kmedoids<EOL><DEDENT>else:<EOL><INDENT>return None<EOL><DEDENT>

!
        @brief Returns algorithm type that corresponds to specified enumeration value.

        @return (type) Algorithm type for cluster analysis.


def __init__(self, data, kmin, kmax, **kwargs):

self.__data = data<EOL>self.__kmin = kmin<EOL>self.__kmax = kmax<EOL>self.__algorithm = kwargs.get('<STR_LIT>', silhouette_ksearch_type.KMEANS)<EOL>self.__return_index = self.__algorithm == silhouette_ksearch_type.KMEDOIDS<EOL>self.__amount = -<NUM_LIT:1><EOL>self.__score = -<NUM_LIT:1.0><EOL>self.__scores = {}<EOL>self.__verify_arguments()<EOL>self.__ccore = kwargs.get('<STR_LIT>', True)<EOL>if self.__ccore:<EOL><INDENT>self.__ccore = ccore_library.workable()<EOL><DEDENT>

!
        @brief Initialize Silhouette search algorithm to find out optimal amount of clusters.

        @param[in] data (array_like): Input data that is used for searching optimal amount of clusters.
        @param[in] kmin (uint): Amount of clusters from which search is performed. Should be equal or greater than 2.
        @param[in] kmax (uint): Amount of clusters to which search is performed. Should be equal or less than amount of
                    points in input data.
        @param[in] **kwargs: Arbitrary keyword arguments (available arguments: 'algorithm').

        Keyword Args:

            - algorithm (silhouette_ksearch_type): Defines algorithm that is used for searching optimal number of
               clusters (by default K-Means).
            - ccore (bool): If True then CCORE (C++ implementation of pyclustering library) is used (by default True).


!
        @brief Performs analysis to find optimal amount of clusters.

        @see get_amount, get_score, get_scores

        @return (silhouette_search) Itself instance (silhouette_search)


results = wrapper.silhoeutte_ksearch(self.__data, self.__kmin, self.__kmax, self.__algorithm)<EOL>self.__amount = results[<NUM_LIT:0>]<EOL>self.__score = results[<NUM_LIT:1>]<EOL>self.__scores = results[<NUM_LIT:2>]<EOL>


self.__scores = {}<EOL>for k in range(self.__kmin, self.__kmax):<EOL><INDENT>clusters = self.__calculate_clusters(k)<EOL>if len(clusters) != k:<EOL><INDENT>self.__scores[k] = float('<STR_LIT>')<EOL>continue<EOL><DEDENT>score = silhouette(self.__data, clusters).process().get_score()<EOL>self.__scores[k] = sum(score) / len(score)<EOL>if self.__scores[k] > self.__score:<EOL><INDENT>self.__score = self.__scores[k]<EOL>self.__amount = k<EOL><DEDENT><DEDENT>


return self.__amount<EOL>

!
        @brief Returns optimal amount of clusters that has been found during analysis.

        @return (uint) Optimal amount of clusters.

        @see process


return self.__score<EOL>

!
        @brief Returns silhouette score that belongs to optimal amount of clusters (k).

        @return (float) Score that belong to optimal amount of clusters.

        @see process, get_scores


return self.__scores<EOL>

!
        @brief Returns silhouette score for each K value (amount of clusters).

        @return (dict) Silhouette score for each K value, where key is a K value and value is a silhouette score.

        @see process, get_score


initial_values = kmeans_plusplus_initializer(self.__data, k).initialize(return_index=self.__return_index)<EOL>algorithm_type = self.__algorithm.get_type()<EOL>return algorithm_type(self.__data, initial_values).process().get_clusters()<EOL>

!
        @brief Performs cluster analysis using specified K value.

        @param[in] k (uint): Amount of clusters that should be allocated.

        @return (array_like) Allocated clusters.


if self.__kmax > len(self.__data):<EOL><INDENT>raise ValueError("<STR_LIT>" + str(self.__kmax) + "<STR_LIT>" +<EOL>str(len(self.__data)) + "<STR_LIT>")<EOL><DEDENT>if self.__kmin <= <NUM_LIT:1>:<EOL><INDENT>raise ValueError("<STR_LIT>" + str(self.__kmin) + "<STR_LIT>"<EOL>"<STR_LIT>")<EOL><DEDENT>

!
        @brief Checks algorithm's arguments and if some of them is incorrect then exception is thrown.


def __init__(self, amount_clusters, dimension, cluster_sizes, cluster_centers=None, cluster_width=<NUM_LIT:1.0>):

self.__amount_clusters = amount_clusters<EOL>self.__dimension = dimension<EOL>self.__cluster_sizes = cluster_sizes<EOL>if not isinstance(self.__cluster_sizes, collections.Iterable):<EOL><INDENT>self.__cluster_sizes = [self.__cluster_sizes] * amount_clusters<EOL><DEDENT>self.__cluster_width = cluster_width<EOL>if not isinstance(self.__cluster_width, collections.Iterable):<EOL><INDENT>self.__cluster_width = [self.__cluster_width] * amount_clusters<EOL><DEDENT>self.__cluster_centers = cluster_centers<EOL>if self.__cluster_centers is None:<EOL><INDENT>self.__cluster_centers = self.__generate_cluster_centers(self.__cluster_width)<EOL><DEDENT>

!
        @brief Constructs data generator for generating data-sets.

        @param[in] amount_clusters (uint): Amount of clusters that should be generated.
        @param[in] dimension (uint): Dimension of each generated point.
        @param[in] cluster_sizes (uint|array_like): Size of each cluster. In case of 'array_like' input clusters with
                    corresponding sizes are generated.
        @param[in] cluster_centers (array_like): Optional parameter that defines cluster centers (means).
        @param[in] cluster_width (uint|array_like): Optional parameter that defines cluster width (standard deviation).
                    In case of 'array_like' input each cluster has own standard deviation.


data_points = []<EOL>for index_cluster in range(self.__amount_clusters):<EOL><INDENT>for _ in range(self.__cluster_sizes[index_cluster]):<EOL><INDENT>point = self.__generate_point(index_cluster)<EOL>data_points.append(point)<EOL><DEDENT><DEDENT>return data_points<EOL>

!
        @brief Generates data in line with generator parameters.


return [ random.gauss(self.__cluster_centers[index_cluster][index_dimension],<EOL>self.__cluster_width[index_cluster] / <NUM_LIT>)<EOL>for index_dimension in range(self.__dimension) ]<EOL>

!
        @brief Generates point in line with parameters of specified cluster.

        @param[in] index_cluster (uint): Index of cluster whose parameters are used for point generation.

        @return (list) New generated point in line with normal distribution and cluster parameters.


centers = []<EOL>default_offset = max(width) * <NUM_LIT><EOL>for i in range(self.__amount_clusters):<EOL><INDENT>center = [ random.gauss(i * default_offset, width[i] / <NUM_LIT>) for _ in range(self.__dimension) ]<EOL>centers.append(center)<EOL><DEDENT>return centers<EOL>

!
        @brief Generates centers (means in statistical term) for clusters.

        @param[in] width (list): Width of generated clusters.

        @return (list) Generated centers in line with normal distribution.


def __init__(self, data, initial_index_medoids, tolerance=<NUM_LIT>, ccore=True, **kwargs):

self.__pointer_data = data<EOL>self.__clusters = []<EOL>self.__medoid_indexes = initial_index_medoids<EOL>self.__tolerance = tolerance<EOL>self.__metric = kwargs.get('<STR_LIT>', distance_metric(type_metric.EUCLIDEAN_SQUARE))<EOL>self.__data_type = kwargs.get('<STR_LIT>', '<STR_LIT>')<EOL>self.__itermax = kwargs.get('<STR_LIT>', <NUM_LIT:200>)<EOL>self.__distance_calculator = self.__create_distance_calculator()<EOL>self.__ccore = ccore and self.__metric.get_type() != type_metric.USER_DEFINED<EOL>if self.__ccore:<EOL><INDENT>self.__ccore = ccore_library.workable()<EOL><DEDENT>

!
        @brief Constructor of clustering algorithm K-Medoids.

        @param[in] data (list): Input data that is presented as list of points (objects), each point should be represented by list or tuple.
        @param[in] initial_index_medoids (list): Indexes of intial medoids (indexes of points in input data).
        @param[in] tolerance (double): Stop condition: if maximum value of distance change of medoids of clusters is less than tolerance than algorithm will stop processing.
        @param[in] ccore (bool): If specified than CCORE library (C++ pyclustering library) is used for clustering instead of Python code.
        @param[in] **kwargs: Arbitrary keyword arguments (available arguments: 'metric', 'data_type', 'itermax').

        Keyword Args:

            - metric (distance_metric): Metric that is used for distance calculation between two points.
            - data_type (string): Data type of input sample 'data' that is processed by the algorithm ('points', 'distance_matrix').
            - itermax (uint): Maximum number of iteration for cluster analysis.


if self.__ccore is True:<EOL><INDENT>ccore_metric = metric_wrapper.create_instance(self.__metric)<EOL>self.__clusters, self.__medoid_indexes = wrapper.kmedoids(self.__pointer_data, self.__medoid_indexes, self.__tolerance, self.__itermax, ccore_metric.get_pointer(), self.__data_type)<EOL><DEDENT>else:<EOL><INDENT>changes = float('<STR_LIT>')<EOL>iterations = <NUM_LIT:0><EOL>while changes > self.__tolerance and iterations < self.__itermax:<EOL><INDENT>self.__clusters = self.__update_clusters()<EOL>update_medoid_indexes = self.__update_medoids()<EOL>changes = max([self.__distance_calculator(self.__medoid_indexes[index], update_medoid_indexes[index]) for index in range(len(update_medoid_indexes))])<EOL>self.__medoid_indexes = update_medoid_indexes<EOL>iterations += <NUM_LIT:1><EOL><DEDENT><DEDENT>return self<EOL>

!
        @brief Performs cluster analysis in line with rules of K-Medoids algorithm.

        @return (kmedoids) Returns itself (K-Medoids instance).

        @remark Results of clustering can be obtained using corresponding get methods.

        @see get_clusters()
        @see get_medoids()


return self.__clusters<EOL>

!
        @brief Returns list of allocated clusters, each cluster contains indexes of objects in list of data.

        @see process()
        @see get_medoids()


return self.__medoid_indexes<EOL>

!
        @brief Returns list of medoids of allocated clusters represented by indexes from the input data.

        @see process()
        @see get_clusters()


return type_encoding.CLUSTER_INDEX_LIST_SEPARATION<EOL>

!
        @brief Returns clustering result representation type that indicate how clusters are encoded.

        @return (type_encoding) Clustering result representation.

        @see get_clusters()


if self.__data_type == '<STR_LIT>':<EOL><INDENT>return lambda index1, index2: self.__metric(self.__pointer_data[index1], self.__pointer_data[index2])<EOL><DEDENT>elif self.__data_type == '<STR_LIT>':<EOL><INDENT>if isinstance(self.__pointer_data, numpy.matrix):<EOL><INDENT>return lambda index1, index2: self.__pointer_data.item((index1, index2))<EOL><DEDENT>return lambda index1, index2: self.__pointer_data[index1][index2]<EOL><DEDENT>else:<EOL><INDENT>raise TypeError("<STR_LIT>" % self.__data_type)<EOL><DEDENT>

!
        @brief Creates distance calculator in line with algorithms parameters.

        @return (callable) Distance calculator.


clusters = [[self.__medoid_indexes[i]] for i in range(len(self.__medoid_indexes))]<EOL>for index_point in range(len(self.__pointer_data)):<EOL><INDENT>if index_point in self.__medoid_indexes:<EOL><INDENT>continue<EOL><DEDENT>index_optim = -<NUM_LIT:1><EOL>dist_optim = float('<STR_LIT>')<EOL>for index in range(len(self.__medoid_indexes)):<EOL><INDENT>dist = self.__distance_calculator(index_point, self.__medoid_indexes[index])<EOL>if dist < dist_optim:<EOL><INDENT>index_optim = index<EOL>dist_optim = dist<EOL><DEDENT><DEDENT>clusters[index_optim].append(index_point)<EOL><DEDENT>return clusters<EOL>

!
        @brief Calculate distance to each point from the each cluster.
        @details Nearest points are captured by according clusters and as a result clusters are updated.

        @return (list) updated clusters as list of clusters where each cluster contains indexes of objects from data.


medoid_indexes = [-<NUM_LIT:1>] * len(self.__clusters)<EOL>for index in range(len(self.__clusters)):<EOL><INDENT>medoid_index = medoid(self.__pointer_data, self.__clusters[index], metric=self.__metric, data_type=self.__data_type)<EOL>medoid_indexes[index] = medoid_index<EOL><DEDENT>return medoid_indexes<EOL>

!
        @brief Find medoids of clusters in line with contained objects.

        @return (list) list of medoids for current number of clusters.


def __init__(self, data, maximum_clusters, threshold, ccore=True, **kwargs):

super().__init__(data, maximum_clusters, threshold, ccore, **kwargs)<EOL>

!
        @brief Creates MBSAS algorithm.

        @param[in] data (list): Input data that is presented as list of points (objects), each point should be represented by list or tuple.
        @param[in] maximum_clusters: Maximum allowable number of clusters that can be allocated during processing.
        @param[in] threshold: Threshold of dissimilarity (maximum distance) between points.
        @param[in] ccore (bool): If True than DLL CCORE (C++ solution) will be used for solving.
        @param[in] **kwargs: Arbitrary keyword arguments (available arguments: 'metric').

        Keyword Args:

            - metric (distance_metric): Metric that is used for distance calculation between two points.


if self._ccore is True:<EOL><INDENT>self.__process_by_ccore();<EOL><DEDENT>else:<EOL><INDENT>self.__prcess_by_python();<EOL><DEDENT>

!
        @brief Performs cluster analysis in line with rules of BSAS algorithm.

        @remark Results of clustering can be obtained using corresponding get methods.

        @see get_clusters()
        @see get_representatives()


def __init__(self, data, initial_centers = None, kmax = <NUM_LIT:20>, tolerance = <NUM_LIT>, criterion = splitting_type.BAYESIAN_INFORMATION_CRITERION, ccore = True):

self.__pointer_data = data<EOL>self.__clusters = []<EOL>if initial_centers is not None:<EOL><INDENT>self.__centers = initial_centers[:]<EOL><DEDENT>else:<EOL><INDENT>self.__centers = [ [random.random() for _ in range(len(data[<NUM_LIT:0>])) ] ]<EOL><DEDENT>self.__kmax = kmax<EOL>self.__tolerance = tolerance<EOL>self.__criterion = criterion<EOL>self.__ccore = ccore<EOL>if self.__ccore:<EOL><INDENT>self.__ccore = ccore_library.workable()<EOL><DEDENT>

!
        @brief Constructor of clustering algorithm X-Means.

        @param[in] data (list): Input data that is presented as list of points (objects), each point should be represented by list or tuple.
        @param[in] initial_centers (list): Initial coordinates of centers of clusters that are represented by list: [center1, center2, ...],
                    if it is not specified then X-Means starts from the random center.
        @param[in] kmax (uint): Maximum number of clusters that can be allocated.
        @param[in] tolerance (double): Stop condition for each iteration: if maximum value of change of centers of clusters is less than tolerance than algorithm will stop processing.
        @param[in] criterion (splitting_type): Type of splitting creation.
        @param[in] ccore (bool): Defines should be CCORE (C++ pyclustering library) used instead of Python code or not.


if (self.__ccore is True):<EOL><INDENT>self.__clusters, self.__centers = wrapper.xmeans(self.__pointer_data, self.__centers, self.__kmax, self.__tolerance, self.__criterion)<EOL><DEDENT>else:<EOL><INDENT>self.__clusters = []<EOL>while len(self.__centers) <= self.__kmax:<EOL><INDENT>current_cluster_number = len(self.__centers)<EOL>self.__clusters, self.__centers = self.__improve_parameters(self.__centers)<EOL>allocated_centers = self.__improve_structure(self.__clusters, self.__centers)<EOL>if current_cluster_number == len(allocated_centers):<EOL><INDENT>break<EOL><DEDENT>else:<EOL><INDENT>self.__centers = allocated_centers<EOL><DEDENT><DEDENT>self.__clusters, self.__centers = self.__improve_parameters(self.__centers)<EOL><DEDENT>

!
        @brief Performs cluster analysis in line with rules of X-Means algorithm.

        @remark Results of clustering can be obtained using corresponding gets methods.

        @see get_clusters()
        @see get_centers()


return self.__clusters<EOL>

!
        @brief Returns list of allocated clusters, each cluster contains indexes of objects in list of data.

        @return (list) List of allocated clusters.

        @see process()
        @see get_centers()


return self.__centers<EOL>

!
        @brief Returns list of centers for allocated clusters.

        @return (list) List of centers for allocated clusters.

        @see process()
        @see get_clusters()


def __improve_parameters(self, centers, available_indexes = None):

if available_indexes and len(available_indexes) == <NUM_LIT:1>:<EOL><INDENT>index_center = available_indexes[<NUM_LIT:0>]<EOL>return [ available_indexes ], self.__pointer_data[index_center]<EOL><DEDENT>local_data = self.__pointer_data<EOL>if available_indexes:<EOL><INDENT>local_data = [ self.__pointer_data[i] for i in available_indexes ]<EOL><DEDENT>local_centers = centers<EOL>if centers is None:<EOL><INDENT>local_centers = kmeans_plusplus_initializer(local_data, <NUM_LIT:2>, kmeans_plusplus_initializer.FARTHEST_CENTER_CANDIDATE).initialize()<EOL><DEDENT>kmeans_instance = kmeans(local_data, local_centers, tolerance=self.__tolerance, ccore=False)<EOL>kmeans_instance.process()<EOL>local_centers = kmeans_instance.get_centers()<EOL>clusters = kmeans_instance.get_clusters()<EOL>if available_indexes:<EOL><INDENT>clusters = self.__local_to_global_clusters(clusters, available_indexes)<EOL><DEDENT>return clusters, local_centers<EOL>

!
        @brief Performs k-means clustering in the specified region.

        @param[in] centers (list): Centers of clusters.
        @param[in] available_indexes (list): Indexes that defines which points can be used for k-means clustering, if None - then all points are used.

        @return (list) List of allocated clusters, each cluster contains indexes of objects in list of data.


def __local_to_global_clusters(self, local_clusters, available_indexes):

clusters = []<EOL>for local_cluster in local_clusters:<EOL><INDENT>current_cluster = []<EOL>for index_point in local_cluster:<EOL><INDENT>current_cluster.append(available_indexes[index_point])<EOL><DEDENT>clusters.append(current_cluster)<EOL><DEDENT>return clusters<EOL>

!
        @brief Converts clusters in local region define by 'available_indexes' to global clusters.

        @param[in] local_clusters (list): Local clusters in specific region.
        @param[in] available_indexes (list): Map between local and global point's indexes.

        @return Global clusters.


def __improve_structure(self, clusters, centers):

allocated_centers = []<EOL>amount_free_centers = self.__kmax - len(centers)<EOL>for index_cluster in range(len(clusters)):<EOL><INDENT>(parent_child_clusters, parent_child_centers) = self.__improve_parameters(None, clusters[index_cluster])<EOL>if len(parent_child_clusters) > <NUM_LIT:1>:<EOL><INDENT>parent_scores = self.__splitting_criterion([ clusters[index_cluster] ], [ centers[index_cluster] ])<EOL>child_scores = self.__splitting_criterion([ parent_child_clusters[<NUM_LIT:0>], parent_child_clusters[<NUM_LIT:1>] ], parent_child_centers)<EOL>split_require = False<EOL>if self.__criterion == splitting_type.BAYESIAN_INFORMATION_CRITERION:<EOL><INDENT>if parent_scores < child_scores: split_require = True<EOL><DEDENT>elif self.__criterion == splitting_type.MINIMUM_NOISELESS_DESCRIPTION_LENGTH:<EOL><INDENT>if parent_scores > child_scores: split_require = True;<EOL><DEDENT>if (split_require is True) and (amount_free_centers > <NUM_LIT:0>):<EOL><INDENT>allocated_centers.append(parent_child_centers[<NUM_LIT:0>])<EOL>allocated_centers.append(parent_child_centers[<NUM_LIT:1>])<EOL>amount_free_centers -= <NUM_LIT:1><EOL><DEDENT>else:<EOL><INDENT>allocated_centers.append(centers[index_cluster])<EOL><DEDENT><DEDENT>else:<EOL><INDENT>allocated_centers.append(centers[index_cluster])<EOL><DEDENT><DEDENT>return allocated_centers<EOL>

!
        @brief Check for best structure: divides each cluster into two and checks for best results using splitting criterion.

        @param[in] clusters (list): Clusters that have been allocated (each cluster contains indexes of points from data).
        @param[in] centers (list): Centers of clusters.

        @return (list) Allocated centers for clustering.


def __splitting_criterion(self, clusters, centers):

if self.__criterion == splitting_type.BAYESIAN_INFORMATION_CRITERION:<EOL><INDENT>return self.__bayesian_information_criterion(clusters, centers)<EOL><DEDENT>elif self.__criterion == splitting_type.MINIMUM_NOISELESS_DESCRIPTION_LENGTH:<EOL><INDENT>return self.__minimum_noiseless_description_length(clusters, centers)<EOL><DEDENT>else:<EOL><INDENT>assert <NUM_LIT:0>;<EOL><DEDENT>

!
        @brief Calculates splitting criterion for input clusters.

        @param[in] clusters (list): Clusters for which splitting criterion should be calculated.
        @param[in] centers (list): Centers of the clusters.

        @return (double) Returns splitting criterion. High value of splitting cretion means that current structure is much better.

        @see __bayesian_information_criterion(clusters, centers)
        @see __minimum_noiseless_description_length(clusters, centers)


def __minimum_noiseless_description_length(self, clusters, centers):

scores = float('<STR_LIT>')<EOL>W = <NUM_LIT:0.0><EOL>K = len(clusters)<EOL>N = <NUM_LIT:0.0><EOL>sigma_sqrt = <NUM_LIT:0.0><EOL>alpha = <NUM_LIT><EOL>betta = <NUM_LIT><EOL>for index_cluster in range(<NUM_LIT:0>, len(clusters), <NUM_LIT:1>):<EOL><INDENT>Ni = len(clusters[index_cluster])<EOL>if Ni == <NUM_LIT:0>:<EOL><INDENT>return float('<STR_LIT>')<EOL><DEDENT>Wi = <NUM_LIT:0.0><EOL>for index_object in clusters[index_cluster]:<EOL><INDENT>Wi += euclidean_distance(self.__pointer_data[index_object], centers[index_cluster])<EOL><DEDENT>sigma_sqrt += Wi<EOL>W += Wi / Ni<EOL>N += Ni<EOL><DEDENT>if N - K > <NUM_LIT:0>:<EOL><INDENT>sigma_sqrt /= (N - K)<EOL>sigma = sigma_sqrt ** <NUM_LIT:0.5><EOL>Kw = (<NUM_LIT:1.0> - K / N) * sigma_sqrt<EOL>Ks = ( <NUM_LIT> * alpha * sigma / (N ** <NUM_LIT:0.5>) ) * ( (alpha ** <NUM_LIT>) * sigma_sqrt / N + W - Kw / <NUM_LIT> ) ** <NUM_LIT:0.5><EOL>scores = sigma_sqrt * (<NUM_LIT:2> * K)**<NUM_LIT:0.5> * ((<NUM_LIT:2> * K)**<NUM_LIT:0.5> + betta) / N + W - sigma_sqrt + Ks + <NUM_LIT:2> * alpha**<NUM_LIT:0.5> * sigma_sqrt / N<EOL><DEDENT>return scores<EOL>

!
        @brief Calculates splitting criterion for input clusters using minimum noiseless description length criterion.

        @param[in] clusters (list): Clusters for which splitting criterion should be calculated.
        @param[in] centers (list): Centers of the clusters.

        @return (double) Returns splitting criterion in line with bayesian information criterion.
                Low value of splitting cretion means that current structure is much better.

        @see __bayesian_information_criterion(clusters, centers)


def __bayesian_information_criterion(self, clusters, centers):

scores = [float('<STR_LIT>')] * len(clusters)     <EOL>dimension = len(self.__pointer_data[<NUM_LIT:0>])<EOL>sigma_sqrt = <NUM_LIT:0.0><EOL>K = len(clusters)<EOL>N = <NUM_LIT:0.0><EOL>for index_cluster in range(<NUM_LIT:0>, len(clusters), <NUM_LIT:1>):<EOL><INDENT>for index_object in clusters[index_cluster]:<EOL><INDENT>sigma_sqrt += euclidean_distance_square(self.__pointer_data[index_object], centers[index_cluster]);<EOL><DEDENT>N += len(clusters[index_cluster])<EOL><DEDENT>if N - K > <NUM_LIT:0>:<EOL><INDENT>sigma_sqrt /= (N - K)<EOL>p = (K - <NUM_LIT:1>) + dimension * K + <NUM_LIT:1><EOL>sigma_multiplier = <NUM_LIT:0.0><EOL>if sigma_sqrt <= <NUM_LIT:0.0>:<EOL><INDENT>sigma_multiplier = float('<STR_LIT>')<EOL><DEDENT>else:<EOL><INDENT>sigma_multiplier = dimension * <NUM_LIT:0.5> * log(sigma_sqrt)<EOL><DEDENT>for index_cluster in range(<NUM_LIT:0>, len(clusters), <NUM_LIT:1>):<EOL><INDENT>n = len(clusters[index_cluster])<EOL>L = n * log(n) - n * log(N) - n * <NUM_LIT:0.5> * log(<NUM_LIT> * numpy.pi) - n * sigma_multiplier - (n - K) * <NUM_LIT:0.5><EOL>scores[index_cluster] = L - p * <NUM_LIT:0.5> * log(N)<EOL><DEDENT><DEDENT>return sum(scores)<EOL>

!
        @brief Calculates splitting criterion for input clusters using bayesian information criterion.

        @param[in] clusters (list): Clusters for which splitting criterion should be calculated.
        @param[in] centers (list): Centers of the clusters.

        @return (double) Splitting criterion in line with bayesian information criterion.
                High value of splitting criterion means that current structure is much better.

        @see __minimum_noiseless_description_length(clusters, centers)


self.__evolution_clusters   = []<EOL>self.__evolution_centers    = []<EOL>self.__initial_centers      = []<EOL>

!
        @brief Initializer of observer of K-Means algorithm.


return len(self.__evolution_clusters)<EOL>

!
        @brief Returns amount of steps that were observer during clustering process in K-Means algorithm.


self.__evolution_clusters.append(clusters)<EOL>self.__evolution_centers.append(centers)<EOL>

!
        @brief This method is called by K-Means algorithm to notify about changes.

        @param[in] clusters (array_like): Allocated clusters by K-Means algorithm.
        @param[in] centers (array_like): Allocated centers by K-Means algorithm.


self.__evolution_centers = evolution_centers<EOL>

!
        @brief Set evolution of changes of centers during clustering process.

        @param[in] evolution_centers (array_like): Evolution of changes of centers during clustering process.


return self.__evolution_centers[iteration]<EOL>

!
        @brief Get method to return centers at specific iteration of clustering process.

        @param[in] iteration (uint): Clustering process iteration at which centers are required.

        @return (array_like) Centers at specific iteration.


self.__evolution_clusters = evolution_clusters<EOL>

!
        @brief Set evolution of changes of centers during clustering process.

        @param[in] evolution_clusters (array_like): Evolution of changes of clusters during clustering process.


return self.__evolution_clusters[iteration]<EOL>

!
        @brief Get method to return allocated clusters at specific iteration of clustering process.

        @param[in] iteration (uint): Clustering process iteration at which clusters are required.

        @return (array_like) Clusters at specific iteration.


@staticmethod<EOL><INDENT>def show_clusters(sample, clusters, centers, initial_centers = None, **kwargs):<DEDENT>

visualizer = cluster_visualizer()<EOL>visualizer.append_clusters(clusters, sample)<EOL>offset = kwargs.get('<STR_LIT>', <NUM_LIT:0>)<EOL>figure = kwargs.get('<STR_LIT>', None)<EOL>display = kwargs.get('<STR_LIT>', True)<EOL>if figure is None:<EOL><INDENT>figure = visualizer.show(display = False)<EOL><DEDENT>else:<EOL><INDENT>visualizer.show(figure = figure, display = False)<EOL><DEDENT>kmeans_visualizer.__draw_centers(figure, offset, visualizer, centers, initial_centers)<EOL>kmeans_visualizer.__draw_rays(figure, offset, visualizer, sample, clusters, centers)<EOL>if display is True:<EOL><INDENT>plt.show()<EOL><DEDENT>return figure<EOL>

!
        @brief Display K-Means clustering results.

        @param[in] sample (list): Dataset that was used for clustering.
        @param[in] clusters (array_like): Clusters that were allocated by the algorithm.
        @param[in] centers (array_like): Centers that were allocated by the algorithm.
        @param[in] initial_centers (array_like): Initial centers that were used by the algorithm, if 'None' then initial centers are not displyed.
        @param[in] **kwargs: Arbitrary keyword arguments (available arguments: 'figure', 'display', 'offset').

        Keyword Args:

            - figure (figure): If 'None' then new is figure is created, otherwise specified figure is used for visualization.
            - display (bool): If 'True' then figure will be shown by the method, otherwise it should be shown manually using matplotlib function 'plt.show()'.
            - offset (uint): Specify axes index on the figure where results should be drawn (only if argument 'figure' is specified).

        @return (figure) Figure where clusters were drawn.


@staticmethod<EOL><INDENT>def animate_cluster_allocation(data, observer, animation_velocity = <NUM_LIT>, movie_fps = <NUM_LIT:1>, save_movie = None):<DEDENT>

figure = plt.figure()<EOL>def init_frame():<EOL><INDENT>return frame_generation(<NUM_LIT:0>)<EOL><DEDENT>def frame_generation(index_iteration):<EOL><INDENT>figure.clf()<EOL>figure.suptitle("<STR_LIT>" + str(index_iteration) + "<STR_LIT:)>", fontsize=<NUM_LIT>, fontweight='<STR_LIT>')<EOL>clusters = observer.get_clusters(index_iteration)<EOL>centers = observer.get_centers(index_iteration)<EOL>kmeans_visualizer.show_clusters(data, clusters, centers, None, figure=figure, display=False)<EOL>figure.subplots_adjust(top=<NUM_LIT>)<EOL>return [figure.gca()]<EOL><DEDENT>iterations = len(observer)<EOL>cluster_animation = animation.FuncAnimation(figure, frame_generation, iterations, interval=animation_velocity,<EOL>init_func=init_frame, repeat_delay=<NUM_LIT>)<EOL>if save_movie is not None:<EOL><INDENT>cluster_animation.save(save_movie, writer='<STR_LIT>', fps=movie_fps, bitrate=<NUM_LIT>)<EOL><DEDENT>else:<EOL><INDENT>plt.show()<EOL><DEDENT>

!
        @brief Animates clustering process that is performed by K-Means algorithm.

        @param[in] data (list): Dataset that is used for clustering.
        @param[in] observer (kmeans_observer): EM observer that was used for collection information about clustering process.
        @param[in] animation_velocity (uint): Interval between frames in milliseconds (for run-time animation only).
        @param[in] movie_fps (uint): Defines frames per second (for rendering movie only).
        @param[in] save_movie (string): If it is specified then animation will be stored to file that is specified in this parameter.


def __init__(self, data, initial_centers, tolerance=<NUM_LIT>, ccore=True, **kwargs):

self.__pointer_data = numpy.array(data)<EOL>self.__clusters = []<EOL>self.__centers = numpy.array(initial_centers)<EOL>self.__tolerance = tolerance<EOL>self.__total_wce = <NUM_LIT:0><EOL>self.__observer = kwargs.get('<STR_LIT>', None)<EOL>self.__metric = kwargs.get('<STR_LIT>', distance_metric(type_metric.EUCLIDEAN_SQUARE))<EOL>self.__itermax = kwargs.get('<STR_LIT>', <NUM_LIT:100>)<EOL>if self.__metric.get_type() != type_metric.USER_DEFINED:<EOL><INDENT>self.__metric.enable_numpy_usage()<EOL><DEDENT>else:<EOL><INDENT>self.__metric.disable_numpy_usage()<EOL><DEDENT>self.__ccore = ccore and self.__metric.get_type() != type_metric.USER_DEFINED<EOL>if self.__ccore is True:<EOL><INDENT>self.__ccore = ccore_library.workable()<EOL><DEDENT>

!
        @brief Constructor of clustering algorithm K-Means.
        @details Center initializer can be used for creating initial centers, for example, K-Means++ method.

        @param[in] data (array_like): Input data that is presented as array of points (objects), each point should be represented by array_like data structure.
        @param[in] initial_centers (array_like): Initial coordinates of centers of clusters that are represented by array_like data structure: [center1, center2, ...].
        @param[in] tolerance (double): Stop condition: if maximum value of change of centers of clusters is less than tolerance then algorithm stops processing.
        @param[in] ccore (bool): Defines should be CCORE library (C++ pyclustering library) used instead of Python code or not.
        @param[in] **kwargs: Arbitrary keyword arguments (available arguments: 'observer', 'metric', 'itermax').

        Keyword Args:

            - observer (kmeans_observer): Observer of the algorithm to collect information about clustering process on each iteration.
            - metric (distance_metric): Metric that is used for distance calculation between two points (by default euclidean square distance).
            - itermax (uint): Maximum number of iterations that is used for clustering process (by default: 200).

        @see center_initializer


if len(self.__pointer_data[<NUM_LIT:0>]) != len(self.__centers[<NUM_LIT:0>]):<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT>if self.__ccore is True:<EOL><INDENT>self.__process_by_ccore()<EOL><DEDENT>else:<EOL><INDENT>self.__process_by_python()<EOL><DEDENT>return self<EOL>

!
        @brief Performs cluster analysis in line with rules of K-Means algorithm.

        @return (kmeans) Returns itself (K-Means instance).

        @remark Results of clustering can be obtained using corresponding get methods.

        @see get_clusters()
        @see get_centers()


ccore_metric = metric_wrapper.create_instance(self.__metric)<EOL>results = wrapper.kmeans(self.__pointer_data, self.__centers, self.__tolerance, self.__itermax, (self.__observer is not None), ccore_metric.get_pointer())<EOL>self.__clusters = results[<NUM_LIT:0>]<EOL>self.__centers = results[<NUM_LIT:1>]<EOL>if self.__observer is not None:<EOL><INDENT>self.__observer.set_evolution_clusters(results[<NUM_LIT:2>])<EOL>self.__observer.set_evolution_centers(results[<NUM_LIT:3>])<EOL><DEDENT>self.__total_wce = results[<NUM_LIT:4>][<NUM_LIT:0>]<EOL>

!
        @brief Performs cluster analysis using CCORE (C/C++ part of pyclustering library).


maximum_change = float('<STR_LIT>')<EOL>iteration = <NUM_LIT:0><EOL>if self.__observer is not None:<EOL><INDENT>initial_clusters = self.__update_clusters()<EOL>self.__observer.notify(initial_clusters, self.__centers.tolist())<EOL><DEDENT>while maximum_change > self.__tolerance and iteration < self.__itermax:<EOL><INDENT>self.__clusters = self.__update_clusters()<EOL>updated_centers = self.__update_centers()  <EOL>if self.__observer is not None:<EOL><INDENT>self.__observer.notify(self.__clusters, updated_centers.tolist())<EOL><DEDENT>maximum_change = self.__calculate_changes(updated_centers)<EOL>self.__centers = updated_centers    <EOL>iteration += <NUM_LIT:1><EOL><DEDENT>self.__calculate_total_wce()<EOL>

!
        @brief Performs cluster analysis using python code.


return self.__clusters<EOL>

!
        @brief Returns list of allocated clusters, each cluster contains indexes of objects in list of data.

        @see process()
        @see get_centers()


if isinstance(self.__centers, list):<EOL><INDENT>return self.__centers<EOL><DEDENT>return self.__centers.tolist()<EOL>

!
        @brief Returns list of centers of allocated clusters.

        @see process()
        @see get_clusters()


return self.__total_wce<EOL>

!
        @brief Returns sum of metric errors that depends on metric that was used for clustering (by default SSE - Sum of Squared Errors).
        @details Sum of metric errors is calculated using distance between point and its center:
                 \f[error=\sum_{i=0}^{N}distance(x_{i}-center(x_{i}))\f]

        @see process()
        @see get_clusters()


return type_encoding.CLUSTER_INDEX_LIST_SEPARATION<EOL>

!
        @brief Returns clustering result representation type that indicate how clusters are encoded.

        @return (type_encoding) Clustering result representation.

        @see get_clusters()


clusters = [[] for _ in range(len(self.__centers))]<EOL>dataset_differences = self.__calculate_dataset_difference(len(clusters))<EOL>optimum_indexes = numpy.argmin(dataset_differences, axis=<NUM_LIT:0>)<EOL>for index_point in range(len(optimum_indexes)):<EOL><INDENT>index_cluster = optimum_indexes[index_point]<EOL>clusters[index_cluster].append(index_point)<EOL><DEDENT>clusters = [cluster for cluster in clusters if len(cluster) > <NUM_LIT:0>]<EOL>return clusters<EOL>

!
        @brief Calculate distance (in line with specified metric) to each point from the each cluster. Nearest points
                are captured by according clusters and as a result clusters are updated.

        @return (list) Updated clusters as list of clusters. Each cluster contains indexes of objects from data.


dimension = self.__pointer_data.shape[<NUM_LIT:1>]<EOL>centers = numpy.zeros((len(self.__clusters), dimension))<EOL>for index in range(len(self.__clusters)):<EOL><INDENT>cluster_points = self.__pointer_data[self.__clusters[index], :]<EOL>centers[index] = cluster_points.mean(axis=<NUM_LIT:0>)<EOL><DEDENT>return numpy.array(centers)<EOL>

!
        @brief Calculate centers of clusters in line with contained objects.

        @return (numpy.array) Updated centers.


dataset_differences = self.__calculate_dataset_difference(len(self.__clusters))<EOL>self.__total_wce = <NUM_LIT:0><EOL>for index_cluster in range(len(self.__clusters)):<EOL><INDENT>for index_point in self.__clusters[index_cluster]:<EOL><INDENT>self.__total_wce += dataset_differences[index_cluster][index_point]<EOL><DEDENT><DEDENT>

!
        @brief Calculate total within cluster errors that is depend on metric that was chosen for K-Means algorithm.


dataset_differences = numpy.zeros((amount_clusters, len(self.__pointer_data)))<EOL>for index_center in range(amount_clusters):<EOL><INDENT>if self.__metric.get_type() != type_metric.USER_DEFINED:<EOL><INDENT>dataset_differences[index_center] = self.__metric(self.__pointer_data, self.__centers[index_center])<EOL><DEDENT>else:<EOL><INDENT>dataset_differences[index_center] = [ self.__metric(point, self.__centers[index_center])<EOL>for point in self.__pointer_data ]<EOL><DEDENT><DEDENT>return dataset_differences<EOL>

!
        @brief Calculate distance from each point to each cluster center.


if len(self.__centers) != len(updated_centers):<EOL><INDENT>maximum_change = float('<STR_LIT>')<EOL><DEDENT>else:<EOL><INDENT>changes = self.__metric(self.__centers, updated_centers)<EOL>maximum_change = numpy.max(changes)<EOL><DEDENT>return maximum_change<EOL>

!
        @brief Calculates changes estimation between previous and current iteration using centers for that purpose.

        @param[in] updated_centers (array_like): New cluster centers.

        @return (float) Maximum changes between centers.


dimension = float(len(data[<NUM_LIT:0>]))<EOL>if dimension != <NUM_LIT:1.0>:<EOL><INDENT>inv_variance = numpy.linalg.pinv(covariance)<EOL><DEDENT>else:<EOL><INDENT>inv_variance = <NUM_LIT:1.0> / covariance<EOL><DEDENT>divider = (pi * <NUM_LIT>) ** (dimension / <NUM_LIT>) * numpy.sqrt(numpy.linalg.norm(covariance))<EOL>if divider != <NUM_LIT:0.0>:<EOL><INDENT>right_const = <NUM_LIT:1.0> / divider<EOL><DEDENT>else:<EOL><INDENT>right_const = float('<STR_LIT>')<EOL><DEDENT>result = []<EOL>for point in data:<EOL><INDENT>mean_delta = point - mean<EOL>point_gaussian = right_const * numpy.exp( -<NUM_LIT:0.5> * mean_delta.dot(inv_variance).dot(numpy.transpose(mean_delta)) )<EOL>result.append(point_gaussian)<EOL><DEDENT>return result<EOL>

!
    @brief Calculates gaussian for dataset using specified mean (mathematical expectation) and variance or covariance in case
            multi-dimensional data.

    @param[in] data (list): Data that is used for gaussian calculation.
    @param[in] mean (float|numpy.array): Mathematical expectation used for calculation.
    @param[in] covariance (float|numpy.array): Variance or covariance matrix for calculation.

    @return (list) Value of gaussian function for each point in dataset.


self.__sample = sample<EOL>self.__amount = amount<EOL>

!
        @brief Constructs EM initializer.

        @param[in] sample (list): Data that will be used by the EM algorithm.
        @param[in] amount (uint): Amount of clusters that should be allocated by the EM algorithm.


def initialize(self, init_type = ema_init_type.KMEANS_INITIALIZATION):

if init_type == ema_init_type.KMEANS_INITIALIZATION:<EOL><INDENT>return self.__initialize_kmeans()<EOL><DEDENT>elif init_type == ema_init_type.RANDOM_INITIALIZATION:<EOL><INDENT>return self.__initialize_random()<EOL><DEDENT>raise NameError("<STR_LIT>")<EOL>

!
        @brief Calculates initial parameters for EM algorithm: means and covariances using
                specified strategy.

        @param[in] init_type (ema_init_type): Strategy for initialization.

        @return (float|list, float|numpy.array) Initial means and variance (covariance matrix in case multi-dimensional data).


clusters = [[] for _ in range(len(centers))]<EOL>for index_point in range(len(self.__sample)):<EOL><INDENT>index_optim, dist_optim = -<NUM_LIT:1>, <NUM_LIT:0.0><EOL>for index in range(len(centers)):<EOL><INDENT>dist = euclidean_distance_square(self.__sample[index_point], centers[index])<EOL>if (dist < dist_optim) or (index is <NUM_LIT:0>):<EOL><INDENT>index_optim, dist_optim = index, dist<EOL><DEDENT><DEDENT>clusters[index_optim].append(index_point)<EOL><DEDENT>return clusters<EOL>

!
        @brief Calculate Euclidean distance to each point from the each cluster.
        @brief Nearest points are captured by according clusters and as a result clusters are updated.

        @return (list) updated clusters as list of clusters. Each cluster contains indexes of objects from data.


self.__means_evolution = []<EOL>self.__covariances_evolution = []<EOL>self.__clusters_evolution = []<EOL>

!
        @brief Initializes EM observer.


return len(self.__means_evolution)<EOL>

!
        @return (uint) Amount of iterations that were done by the EM algorithm.


return len(self.__means_evolution)<EOL>


return self.__means_evolution<EOL>

!
        @return (list) Mean of each cluster on each step of clustering.


return self.__covariances_evolution<EOL>

!
        @return (list) Covariance matrix (or variance in case of one-dimensional data) of each cluster on each step of clustering.


return self.__clusters_evolution<EOL>

!
        @return (list) Allocated clusters on each step of clustering.


def notify(self, means, covariances, clusters):

self.__means_evolution.append(means)<EOL>self.__covariances_evolution.append(covariances)<EOL>self.__clusters_evolution.append(clusters)<EOL>

!
        @brief This method is used by the algorithm to notify observer about changes where the algorithm
                should provide new values: means, covariances and allocated clusters.

        @param[in] means (list): Mean of each cluster on currect step.
        @param[in] covariances (list): Covariances of each cluster on current step.
        @param[in] clusters (list): Allocated cluster on current step.


@staticmethod<EOL><INDENT>def show_clusters(clusters, sample, covariances, means, figure = None, display = True):<DEDENT>

visualizer = cluster_visualizer()<EOL>visualizer.append_clusters(clusters, sample)<EOL>if figure is None:<EOL><INDENT>figure = visualizer.show(display = False)<EOL><DEDENT>else:<EOL><INDENT>visualizer.show(figure = figure, display = False)<EOL><DEDENT>if len(sample[<NUM_LIT:0>]) == <NUM_LIT:2>:<EOL><INDENT>ema_visualizer.__draw_ellipses(figure, visualizer, clusters, covariances, means)<EOL><DEDENT>if display is True:<EOL><INDENT>plt.show()<EOL><DEDENT>return figure<EOL>

!
        @brief Draws clusters and in case of two-dimensional dataset draws their ellipses.

        @param[in] clusters (list): Clusters that were allocated by the algorithm.
        @param[in] sample (list): Dataset that were used for clustering.
        @param[in] covariances (list): Covariances of the clusters.
        @param[in] means (list): Means of the clusters.
        @param[in] figure (figure): If 'None' then new is figure is creater, otherwise specified figure is used
                    for visualization.
        @param[in] display (bool): If 'True' then figure will be shown by the method, otherwise it should be
                    shown manually using matplotlib function 'plt.show()'.

        @return (figure) Figure where clusters were drawn.


figure = plt.figure()<EOL>def init_frame():<EOL><INDENT>return frame_generation(<NUM_LIT:0>)<EOL><DEDENT>def frame_generation(index_iteration):<EOL><INDENT>figure.clf()<EOL>figure.suptitle("<STR_LIT>" + str(index_iteration) +"<STR_LIT:)>", fontsize = <NUM_LIT>, fontweight = '<STR_LIT>')<EOL>clusters = observer.get_evolution_clusters()[index_iteration]<EOL>covariances = observer.get_evolution_covariances()[index_iteration]<EOL>means = observer.get_evolution_means()[index_iteration]<EOL>ema_visualizer.show_clusters(clusters, data, covariances, means, figure, False)<EOL>figure.subplots_adjust(top = <NUM_LIT>)<EOL>return [ figure.gca() ]<EOL><DEDENT>iterations = len(observer)<EOL>cluster_animation = animation.FuncAnimation(figure, frame_generation, iterations, interval = animation_velocity, init_func = init_frame, repeat_delay = <NUM_LIT>)<EOL>if save_movie is not None:<EOL><INDENT>cluster_animation.save(save_movie, writer = '<STR_LIT>', fps = movie_fps, bitrate = <NUM_LIT>)<EOL><DEDENT>else:<EOL><INDENT>plt.show()<EOL><DEDENT>

!
        @brief Animates clustering process that is performed by EM algorithm.

        @param[in] data (list): Dataset that is used for clustering.
        @param[in] observer (ema_observer): EM observer that was used for collection information about clustering process.
        @param[in] animation_velocity (uint): Interval between frames in milliseconds (for run-time animation only).
        @param[in] movie_fps (uint): Defines frames per second (for rendering movie only).
        @param[in] save_movie (string): If it is specified then animation will be stored to file that is specified in this parameter.


def __init__(self, data, amount_clusters, means = None, variances = None, observer = None, tolerance = <NUM_LIT>, iterations = <NUM_LIT:100>):

self.__data = numpy.array(data)<EOL>self.__amount_clusters = amount_clusters<EOL>self.__tolerance = tolerance<EOL>self.__iterations = iterations<EOL>self.__observer = observer<EOL>self.__means = means<EOL>self.__variances = variances<EOL>if (means is None) or (variances is None):<EOL><INDENT>self.__means, self.__variances = ema_initializer(data, amount_clusters).initialize(ema_init_type.KMEANS_INITIALIZATION)<EOL>if len(self.__means) != amount_clusters:<EOL><INDENT>self.__amount_clusters = len(self.__means)<EOL><DEDENT><DEDENT>self.__rc = [ [<NUM_LIT:0.0>] * len(self.__data) for _ in range(amount_clusters) ]<EOL>self.__pic = [<NUM_LIT:1.0>] * amount_clusters<EOL>self.__clusters = []<EOL>self.__gaussians = [ [] for _ in range(amount_clusters) ]<EOL>self.__stop = False<EOL>

!
        @brief Initializes Expectation-Maximization algorithm for cluster analysis.

        @param[in] data (list): Dataset that should be analysed and where each point (object) is represented by the list of coordinates.
        @param[in] amount_clusters (uint): Amount of clusters that should be allocated.
        @param[in] means (list): Initial means of clusters (amount of means should be equal to amount of clusters for allocation).
                    If this parameter is 'None' then K-Means algorithm with K-Means++ method will be used for initialization by default.
        @param[in] variances (list): Initial cluster variances (or covariances in case of multi-dimensional data). Amount of
                    covariances should be equal to amount of clusters that should be allocated. If this parameter is 'None' then
                    K-Means algorithm with K-Means++ method will be used for initialization by default.
        @param[in] observer (ema_observer): Observer for gathering information about clustering process.
        @param[in] tolerance (float): Defines stop condition of the algorithm (when difference between current and
                    previous log-likelihood estimation is less then 'tolerance' then clustering is over).
        @param[in] iterations (uint): Additional stop condition parameter that defines maximum number of steps that can be
                    performed by the algorithm during clustering process.


previous_likelihood = -<NUM_LIT><EOL>current_likelihood = -<NUM_LIT><EOL>current_iteration = <NUM_LIT:0><EOL>while(self.__stop is False) and (abs(previous_likelihood - current_likelihood) > self.__tolerance) and (current_iteration < self.__iterations):<EOL><INDENT>self.__expectation_step()<EOL>self.__maximization_step()<EOL>current_iteration += <NUM_LIT:1><EOL>self.__extract_clusters()<EOL>self.__notify()<EOL>previous_likelihood = current_likelihood<EOL>current_likelihood = self.__log_likelihood()<EOL>self.__stop = self.__get_stop_condition()<EOL><DEDENT>self.__normalize_probabilities()<EOL>

!
        @brief Run clustering process of the algorithm.
        @details This method should be called before call 'get_clusters()'.


return self.__clusters<EOL>

!
        @return (list) Allocated clusters where each cluster is represented by list of indexes of points from dataset,
                        for example, two cluster may have following representation [[0, 1, 4], [2, 3, 5, 6]].


return self.__means<EOL>

!
        @return (list) Corresponding centers (means) of clusters.


return self.__variances<EOL>

!
        @return (list) Corresponding variances (or covariances in case of multi-dimensional data) of clusters.


return self.__rc<EOL>

!
        @brief Returns 2-dimensional list with belong probability of each object from data to cluster correspondingly,
                where that first index is for cluster and the second is for point.

        @code
            # Get belong probablities
            probabilities = ema_instance.get_probabilities();

            # Show porbability of the fifth element in the first and in the second cluster
            index_point = 5;
            print("Probability in the first cluster:", probabilities[0][index_point]);
            print("Probability in the first cluster:", probabilities[1][index_point]);
        @endcode

        @return (list) 2-dimensional list with belong probability of each object from data to cluster.


def __init__(self, need_global_best=False, need_population_best=False, need_mean_ff=False):

<EOL>self._global_best_result = {'<STR_LIT>': [], '<STR_LIT>': []}<EOL>self._best_population_result = {'<STR_LIT>': [], '<STR_LIT>': []}<EOL>self._mean_ff_result = []<EOL>self._need_global_best = need_global_best<EOL>self._need_population_best = need_population_best<EOL>self._need_mean_ff = need_mean_ff<EOL>

!
        @brief Constructs genetic algorithm observer to collect specific information.

        @param[in] need_global_best (bool): If 'True' then the best chromosomes and its fitness function value (global optimum) for each iteration are stored.
        @param[in] need_population_best (bool): If 'True' then current (on each iteration) best chromosomes and its fitness function value (local optimum) are stored.
        @param[in] need_mean_ff (bool): If 'True' then average value of fitness function on each iteration is stored.


global_length = len(self._global_best_result['<STR_LIT>'])<EOL>local_length = len(self._best_population_result['<STR_LIT>'])<EOL>average_length = len(self._mean_ff_result)<EOL>return max(global_length, local_length, average_length)<EOL>

!
        @brief Returns amount of iterations that genetic algorithm was observed.


def collect_global_best(self, best_chromosome, best_fitness_function):

!
        @brief Stores the best chromosome and its fitness function's value.

        @param[in] best_chromosome (list): The best chromosome that were observed.
        @param[in] best_fitness_function (float): Fitness function value of the best chromosome.


def collect_population_best(self, best_chromosome, best_fitness_function):

!
        @brief Stores the best chromosome for current specific iteration and its fitness function's value.

        @param[in] best_chromosome (list): The best chromosome on specific iteration.
        @param[in] best_fitness_function (float): Fitness function value of the chromosome.


if not self._need_mean_ff:<EOL><INDENT>return<EOL><DEDENT>self._mean_ff_result.append(np.mean(fitness_functions))<EOL>

!
        @brief Stores average value of fitness function among chromosomes on specific iteration.

        @param[in] fitness_functions (float): Average value of fitness functions among chromosomes.


return self._global_best_result<EOL>

!
        @return (dict) Returns dictionary with keys 'chromosome' and 'fitness_function' where evolution of the best chromosome
                 and its fitness function's value (evolution of global optimum) are stored in lists.


return self._best_population_result<EOL>

!
        @brief (dict) Returns dictionary with keys 'chromosome' and 'fitness_function' where evolution of the current best chromosome
                 and its fitness function's value (evolution of local optimum) are stored in lists.


return self._mean_ff_result<EOL>

!
        @brief (list) Returns fitness function's values on each iteration.


@staticmethod<EOL><INDENT>def show_evolution(observer, start_iteration = <NUM_LIT:0>, stop_iteration=None, ax=None, display=True):<DEDENT>

if (ax is None):<EOL><INDENT>_, ax = plt.subplots(<NUM_LIT:1>)<EOL>ax.set_title("<STR_LIT>")<EOL><DEDENT>if stop_iteration is None:<EOL><INDENT>stop_iteration = len(observer)<EOL><DEDENT>line_best, = ax.plot(observer.get_global_best()['<STR_LIT>'][start_iteration:stop_iteration], '<STR_LIT:r>')<EOL>line_current, = ax.plot(observer.get_population_best()['<STR_LIT>'][start_iteration:stop_iteration], '<STR_LIT:k>')<EOL>line_mean, = ax.plot(observer.get_mean_fitness_function()[start_iteration:stop_iteration], '<STR_LIT:c>')<EOL>if start_iteration < (stop_iteration - <NUM_LIT:1>):<EOL><INDENT>ax.set_xlim([start_iteration, (stop_iteration - <NUM_LIT:1>)])<EOL><DEDENT>ax.set_xlabel("<STR_LIT>")<EOL>ax.set_ylabel("<STR_LIT>")<EOL>ax.legend([line_best, line_current, line_mean], ["<STR_LIT>", "<STR_LIT>", "<STR_LIT>"], prop={'<STR_LIT:size>': <NUM_LIT:10>})<EOL>ax.grid()<EOL>if display is True:<EOL><INDENT>plt.show()<EOL><DEDENT>return ax<EOL>

!
        @brief Displays evolution of fitness function for the best chromosome, for the current best chromosome and
                average value among all chromosomes.

        @param[in] observer (ga_observer): Genetic algorithm observer that was used for collecting evolution in the algorithm and
                    where whole required information for visualization is stored.
        @param[in] start_iteration (uint): Iteration from that evolution should be shown.
        @param[in] stop_iteration (uint): Iteration after that evolution shouldn't be shown.
        @param[in] ax (Axes): Canvas where evolution should be displayed.
        @param[in] display (bool): If 'True' then visualization of the evolution will be shown by the function.
                    This argument should be 'False' if you want to add something else to the canvas and display it later.

        @return (Axis) Canvas where evolution was shown.


@staticmethod<EOL><INDENT>def show_clusters(data, observer, marker='<STR_LIT:.>', markersize=None):<DEDENT>

figure = plt.figure()<EOL>ax1 = figure.add_subplot(<NUM_LIT>)<EOL>clusters = ga_math.get_clusters_representation(observer.get_global_best()['<STR_LIT>'][-<NUM_LIT:1>])<EOL>visualizer = cluster_visualizer(<NUM_LIT:1>, <NUM_LIT:2>)<EOL>visualizer.append_clusters(clusters, data, <NUM_LIT:0>, marker, markersize)<EOL>visualizer.show(figure, display=False)<EOL>ga_visualizer.show_evolution(observer, <NUM_LIT:0>, None, ax1, True)<EOL>

!
        @brief Shows allocated clusters by the genetic algorithm.

        @param[in] data (list): Input data that was used for clustering process by the algorithm.
        @param[in] observer (ga_observer): Observer that was used for collection information about clustering process.
        @param[in] marker (char): Type of marker that should be used for object (point) representation.
        @param[in] markersize (uint): Size of the marker that is used for object (point) representation.

        @note If you have clusters instead of observer then 'cluster_visualizer' can be used for visualization purposes.

        @see cluster_visualizer


@staticmethod<EOL><INDENT>def animate_cluster_allocation(data, observer, animation_velocity=<NUM_LIT>, movie_fps=<NUM_LIT:5>, save_movie=None):<DEDENT>

figure = plt.figure()<EOL>def init_frame():<EOL><INDENT>return frame_generation(<NUM_LIT:0>)<EOL><DEDENT>def frame_generation(index_iteration):<EOL><INDENT>figure.clf()<EOL>figure.suptitle("<STR_LIT>" + str(index_iteration) + "<STR_LIT:)>", fontsize=<NUM_LIT>, fontweight='<STR_LIT>')<EOL>visualizer = cluster_visualizer(<NUM_LIT:4>, <NUM_LIT:2>, ["<STR_LIT>" + str(index_iteration), "<STR_LIT>"])<EOL>local_minimum_clusters = ga_math.get_clusters_representation(observer.get_population_best()['<STR_LIT>'][index_iteration])<EOL>visualizer.append_clusters(local_minimum_clusters, data, <NUM_LIT:0>)<EOL>global_minimum_clusters = ga_math.get_clusters_representation(observer.get_global_best()['<STR_LIT>'][index_iteration])<EOL>visualizer.append_clusters(global_minimum_clusters, data, <NUM_LIT:1>)<EOL>ax1 = plt.subplot2grid((<NUM_LIT:2>, <NUM_LIT:2>), (<NUM_LIT:1>, <NUM_LIT:0>), colspan=<NUM_LIT:2>)<EOL>ga_visualizer.show_evolution(observer, <NUM_LIT:0>, index_iteration + <NUM_LIT:1>, ax1, False)<EOL>visualizer.show(figure, shift=<NUM_LIT:0>, display=False)<EOL>figure.subplots_adjust(top=<NUM_LIT>)<EOL>return [figure.gca()]<EOL><DEDENT>iterations = len(observer)<EOL>cluster_animation = animation.FuncAnimation(figure, frame_generation, iterations, interval=animation_velocity, init_func=init_frame, repeat_delay=<NUM_LIT>)<EOL>if save_movie is not None:<EOL><INDENT>cluster_animation.save(save_movie, writer='<STR_LIT>', fps=movie_fps, bitrate=<NUM_LIT>)<EOL><DEDENT>else:<EOL><INDENT>plt.show()<EOL><DEDENT>

!
        @brief Animate clustering process of genetic clustering algorithm.
        @details This method can be also used for rendering movie of clustering process and 'ffmpeg' is required for that purpuse.

        @param[in] data (list): Input data that was used for clustering process by the algorithm.
        @param[in] observer (ga_observer): Observer that was used for collection information about clustering process.
                    Be sure that whole information was collected by the observer.
        @param[in] animation_velocity (uint): Interval between frames in milliseconds (for run-time animation only).
        @param[in] movie_fps (uint): Defines frames per second (for rendering movie only).
        @param[in] save_movie (string): If it is specified then animation will be stored to file that is specified in this parameter.


def __init__(self, data, count_clusters, chromosome_count, population_count, **kwargs):

<EOL>np.random.seed()<EOL>if type(data) is list:<EOL><INDENT>self._data = np.array(data)<EOL><DEDENT>else:<EOL><INDENT>self._data = data<EOL><DEDENT>self._count_clusters = count_clusters<EOL>self._chromosome_count = chromosome_count<EOL>self._population_count = population_count<EOL>self._count_mutation_gens = kwargs.get('<STR_LIT>', <NUM_LIT:2>)<EOL>self._crossover_rate = kwargs.get('<STR_LIT>', <NUM_LIT:1.0>)<EOL>self._coeff_mutation_count = kwargs.get('<STR_LIT>', <NUM_LIT>)<EOL>self._select_coeff = kwargs.get('<STR_LIT>', <NUM_LIT:1.0>)<EOL>self._result_clustering = {'<STR_LIT>': [],<EOL>'<STR_LIT>': <NUM_LIT:0.0>}<EOL>self._observer = kwargs.get('<STR_LIT>', ga_observer())<EOL>

!
        @brief Initialize genetic clustering algorithm for cluster analysis.

        @param[in] data (numpy.array|list): Input data for clustering that is represented by two dimensional array
                    where each row is a point, for example, [[0.0, 2.1], [0.1, 2.0], [-0.2, 2.4]].
        @param[in] count_clusters (uint): Amount of clusters that should be allocated in the data.
        @param[in] chromosome_count (uint): Amount of chromosomes in each population.
        @param[in] population_count (uint): Amount of populations.
        @param[in] **kwargs: Arbitrary keyword arguments (available arguments: 'count_mutation_gens', 'coeff_mutation_count', 'select_coeff', 'crossover_rate', 'observer').

        Keyword Args:

            - count_mutation_gens (uint): Amount of genes in chromosome that is mutated on each step.
            - coeff_mutation_count (float): Percent of chromosomes for mutation, distributed in range (0, 1] and
               thus amount of chromosomes is defined as follows: 'chromosome_count' * 'coeff_mutation_count'.
            - select_coeff (float): Exponential coefficient for selection procedure that is used as follows:
               math.exp(1 + fitness(chromosome) * select_coeff).
            - crossover_rate (float): Crossover rate.
            - observer (ga_observer): Observer that is used for collecting information of about clustering process on each step.


<EOL>chromosomes = self._init_population(self._count_clusters, len(self._data), self._chromosome_count)<EOL>best_chromosome, best_ff, first_fitness_functions= self._get_best_chromosome(chromosomes, self._data, self._count_clusters)<EOL>if self._observer is not None:<EOL><INDENT>self._observer.collect_global_best(best_chromosome, best_ff)<EOL>self._observer.collect_population_best(best_chromosome, best_ff)<EOL>self._observer.collect_mean(first_fitness_functions)<EOL><DEDENT>for _ in range(self._population_count):<EOL><INDENT>chromosomes = self._select(chromosomes, self._data, self._count_clusters, self._select_coeff)<EOL>self._crossover(chromosomes)<EOL>self._mutation(chromosomes, self._count_clusters, self._count_mutation_gens, self._coeff_mutation_count)<EOL>new_best_chromosome, new_best_ff, fitness_functions= self._get_best_chromosome(chromosomes, self._data, self._count_clusters)<EOL>if new_best_ff < best_ff:<EOL><INDENT>best_ff = new_best_ff<EOL>best_chromosome = new_best_chromosome<EOL><DEDENT>if self._observer is not None:<EOL><INDENT>self._observer.collect_global_best(best_chromosome, best_ff)<EOL>self._observer.collect_population_best(new_best_chromosome, new_best_ff)<EOL>self._observer.collect_mean(fitness_functions)<EOL><DEDENT><DEDENT>self._result_clustering['<STR_LIT>'] = best_chromosome<EOL>self._result_clustering['<STR_LIT>'] = best_ff<EOL>return best_chromosome, best_ff<EOL>

!
        @brief Perform clustering procedure in line with rule of genetic clustering algorithm.

        @see get_clusters()


return self._observer<EOL>

!
        @brief Returns genetic algorithm observer.


return ga_math.get_clusters_representation(self._result_clustering['<STR_LIT>'], self._count_clusters)<EOL>

!
        @brief Returns list of allocated clusters, each cluster contains indexes of objects from the data.

        @return (list) List of allocated clusters.

        @see process()


@staticmethod<EOL><INDENT>def _select(chromosomes, data, count_clusters, select_coeff):<DEDENT>

<EOL>centres = ga_math.get_centres(chromosomes, data, count_clusters)<EOL>fitness = genetic_algorithm._calc_fitness_function(centres, data, chromosomes)<EOL>for _idx in range(len(fitness)):<EOL><INDENT>fitness[_idx] = math.exp(<NUM_LIT:1> + fitness[_idx] * select_coeff)<EOL><DEDENT>probabilities = ga_math.calc_probability_vector(fitness)<EOL>new_chromosomes = np.zeros(chromosomes.shape, dtype=np.int)<EOL>for _idx in range(len(chromosomes)):<EOL><INDENT>new_chromosomes[_idx] = chromosomes[ga_math.get_uniform(probabilities)]<EOL><DEDENT>return new_chromosomes<EOL>

!
        @brief Performs selection procedure where new chromosomes are calculated.

        @param[in] chromosomes (numpy.array): Chromosomes


<EOL>pairs_to_crossover = np.array(range(len(chromosomes)))<EOL>np.random.shuffle(pairs_to_crossover)<EOL>offset_in_pair = int(len(pairs_to_crossover) / <NUM_LIT:2>)<EOL>for _idx in range(offset_in_pair):<EOL><INDENT>crossover_mask = genetic_algorithm._get_crossover_mask(len(chromosomes[_idx]))<EOL>genetic_algorithm._crossover_a_pair(chromosomes[pairs_to_crossover[_idx]],<EOL>chromosomes[pairs_to_crossover[_idx + offset_in_pair]],<EOL>crossover_mask)<EOL><DEDENT>


@staticmethod<EOL><INDENT>def _mutation(chromosomes, count_clusters, count_gen_for_mutation, coeff_mutation_count):<DEDENT>

<EOL>count_gens = len(chromosomes[<NUM_LIT:0>])<EOL>random_idx_chromosomes = np.array(range(len(chromosomes)))<EOL>np.random.shuffle(random_idx_chromosomes)<EOL>for _idx_chromosome in range(int(len(random_idx_chromosomes) * coeff_mutation_count)):<EOL><INDENT>for _ in range(count_gen_for_mutation):<EOL><INDENT>gen_num = np.random.randint(count_gens)<EOL>chromosomes[random_idx_chromosomes[_idx_chromosome]][gen_num] = np.random.randint(count_clusters)<EOL><DEDENT><DEDENT>


@staticmethod<EOL><INDENT>def _crossover_a_pair(chromosome_1, chromosome_2, mask):<DEDENT>

for _idx in range(len(chromosome_1)):<EOL><INDENT>if mask[_idx] == <NUM_LIT:1>:<EOL><INDENT>chromosome_1[_idx], chromosome_2[_idx] = chromosome_2[_idx], chromosome_1[_idx]<EOL><DEDENT><DEDENT>

!
        @brief Crossovers a pair of chromosomes.

        @param[in] chromosome_1 (numpy.array): The first chromosome for crossover.
        @param[in] chromosome_2 (numpy.array): The second chromosome for crossover.
        @param[in] mask (numpy.array): Crossover mask that defines which genes should be swapped.


<EOL>mask = np.zeros(mask_length)<EOL>mask[:int(int(mask_length) / <NUM_LIT:2>)] = <NUM_LIT:1><EOL>np.random.shuffle(mask)<EOL>return mask<EOL>

!
        @brief Crossover mask to crossover a pair of chromosomes.

        @param[in] mask_length (uint): Length of the mask.


@staticmethod<EOL><INDENT>def _init_population(count_clusters, count_data, chromosome_count):<DEDENT>

population = np.random.randint(count_clusters, size=(chromosome_count, count_data))<EOL>return population<EOL>

!
        @brief Returns first population as a uniform random choice.

        @param[in] count_clusters (uint): Amount of clusters that should be allocated.
        @param[in] count_data (uint): Data size that is used for clustering process.
        @param[in] chromosome_count (uint):Amount of chromosome that is used for clustering.


@staticmethod<EOL><INDENT>def _get_best_chromosome(chromosomes, data, count_clusters):<DEDENT>

<EOL>centres = ga_math.get_centres(chromosomes, data, count_clusters)<EOL>fitness_functions = genetic_algorithm._calc_fitness_function(centres, data, chromosomes)<EOL>best_chromosome_idx = fitness_functions.argmin()<EOL>return chromosomes[best_chromosome_idx], fitness_functions[best_chromosome_idx], fitness_functions<EOL>

!
        @brief Returns the current best chromosome.

        @param[in] chromosomes (list): Chromosomes that are used for searching.
        @param[in] data (list): Input data that is used for clustering process.
        @param[in] count_clusters (uint): Amount of clusters that should be allocated.

        @return (list, float, list) The best chromosome, its fitness function value and fitness function values for
                 all chromosomes.


@staticmethod<EOL><INDENT>def _calc_fitness_function(centres, data, chromosomes):<DEDENT>

<EOL>count_chromosome = len(chromosomes)<EOL>fitness_function = np.zeros(count_chromosome)<EOL>for _idx_chromosome in range(count_chromosome):<EOL><INDENT>centres_data = np.zeros(data.shape)<EOL>for _idx in range(len(data)):<EOL><INDENT>centres_data[_idx] = centres[_idx_chromosome][chromosomes[_idx_chromosome][_idx]]<EOL><DEDENT>fitness_function[_idx_chromosome] += np.sum(abs(data - centres_data))<EOL><DEDENT>return fitness_function<EOL>

!
        @brief Calculate fitness function values for chromosomes.

        @param[in] centres (list): Cluster centers.
        @param[in] data (list): Input data that is used for clustering process.
        @param[in] chromosomes (list): Chromosomes whose fitness function's values are calculated.

        @return (list) Fitness function value for each chromosome correspondingly.


def som_create(rows, cols, conn_type, parameters):

ccore = ccore_library.get()<EOL>c_params = c_som_parameters()<EOL>c_params.init_type = parameters.init_type<EOL>c_params.init_radius = parameters.init_radius<EOL>c_params.init_learn_rate = parameters.init_learn_rate<EOL>c_params.adaptation_threshold = parameters.adaptation_threshold<EOL>ccore.som_create.restype = POINTER(c_void_p)<EOL>som_pointer = ccore.som_create(c_uint(rows), c_uint(cols), c_uint(conn_type), pointer(c_params))<EOL>return som_pointer<EOL>

!
    @brief Create of self-organized map using CCORE pyclustering library.

    @param[in] rows (uint): Number of neurons in the column (number of rows).
    @param[in] cols (uint): Number of neurons in the row (number of columns).
    @param[in] conn_type (type_conn): Type of connection between oscillators in the network (grid four, grid eight, honeycomb, function neighbour).
    @param[in] parameters (som_parameters): Other specific parameters.

    @return (POINTER) C-pointer to object of self-organized feature in memory.


def som_load(som_pointer, weights, award, capture_objects):

if len(weights) == <NUM_LIT:0>:<EOL><INDENT>return<EOL><DEDENT>ccore = ccore_library.get()<EOL>package_weights = package_builder(weights, c_double).create()<EOL>package_award = package_builder(award, c_size_t).create()<EOL>package_capture_objects = package_builder(capture_objects, c_size_t).create()<EOL>ccore.som_load(som_pointer, package_weights, package_award, package_capture_objects)<EOL>

!
    @brief Load dump of the network to SOM.
    @details Initialize SOM using existed weights, amount of captured objects by each neuron, captured
              objects by each neuron. Initialization is not performed if weights are empty.

    @param[in] som_pointer (POINTER): pointer to object of self-organized map.
    @param[in] weights (list): weights that should assigned to neurons.
    @param[in] awards (list): amount of captured objects by each neuron.
    @param[in] capture_objects (list): captured objects by each neuron.


ccore = ccore_library.get()<EOL>ccore.som_destroy(som_pointer)<EOL>

!
    @brief Destroys self-organized map.

    @param[in] som_pointer (POINTER): pointer to object of self-organized map.


def som_train(som_pointer, data, epochs, autostop):

pointer_data = package_builder(data, c_double).create()<EOL>ccore = ccore_library.get()<EOL>ccore.som_train.restype = c_size_t<EOL>return ccore.som_train(som_pointer, pointer_data, c_uint(epochs), autostop)<EOL>

!
    @brief Trains self-organized feature map (SOM) using CCORE pyclustering library.

    @param[in] data (list): Input data - list of points where each point is represented by list of features, for example coordinates.
    @param[in] epochs (uint): Number of epochs for training.
    @param[in] autostop (bool): Automatic termination of learining process when adaptation is not occurred.

    @return (uint) Number of learining iterations.


pointer_data = package_builder(pattern, c_double).create()<EOL>ccore = ccore_library.get()<EOL>ccore.som_simulate.restype = c_size_t<EOL>return ccore.som_simulate(som_pointer, pointer_data)<EOL>

!
    @brief Processes input pattern (no learining) and returns index of neuron-winner.
    @details Using index of neuron winner catched object can be obtained using property capture_objects.

    @param[in] som_pointer (c_pointer): pointer to object of self-organized map.
    @param[in] pattern (list): input pattern.

    @return Returns index of neuron-winner.


ccore = ccore_library.get()<EOL>ccore.som_get_winner_number.restype = c_size_t<EOL>return ccore.som_get_winner_number(som_pointer)<EOL>

!
    @brief Returns of number of winner at the last step of learning process.

    @param[in] som_pointer (c_pointer): pointer to object of self-organized map.


ccore = ccore_library.get()<EOL>ccore.som_get_size.restype = c_size_t<EOL>return ccore.som_get_size(som_pointer)<EOL>

!
    @brief Returns size of self-organized map (number of neurons).

    @param[in] som_pointer (c_pointer): pointer to object of self-organized map.


ccore = ccore_library.get()<EOL>ccore.som_get_capture_objects.restype = POINTER(pyclustering_package)<EOL>package = ccore.som_get_capture_objects(som_pointer)<EOL>result = package_extractor(package).extract()<EOL>return result<EOL>

!
    @brief Returns list of indexes of captured objects by each neuron.

    @param[in] som_pointer (c_pointer): pointer to object of self-organized map.


ccore = ccore_library.get()<EOL>ccore.som_get_weights.restype = POINTER(pyclustering_package)<EOL>package = ccore.som_get_weights(som_pointer)<EOL>result = package_extractor(package).extract()<EOL>return result<EOL>

!
    @brief Returns list of weights of each neuron.

    @param[in] som_pointer (c_pointer): pointer to object of self-organized map.


ccore = ccore_library.get()<EOL>ccore.som_get_awards.restype = POINTER(pyclustering_package)<EOL>package = ccore.som_get_awards(som_pointer)<EOL>result = package_extractor(package).extract()<EOL>return result<EOL>

!
    @brief Returns list of amount of captured objects by each neuron.

    @param[in] som_pointer (c_pointer): pointer to object of self-organized map.


ccore = ccore_library.get()<EOL>ccore.som_get_neighbors.restype = POINTER(pyclustering_package)<EOL>package = ccore.som_get_neighbors(som_pointer)<EOL>result = package_extractor(package).extract()<EOL>return result<EOL>

!
    @brief Returns list of indexes of neighbors of each neuron.

    @param[in] som_pointer (c_pointer): pointer to object of self-organized map.


return pyclustering_type_data.__PYCLUSTERING_CTYPE_MAP[pyclustering_package_type]<EOL>

!
        @return (ctype) Return ctype that corresponds to pyclustering type data.


return pyclustering_type_data.__CTYPE_PYCLUSTERING_MAP[data_ctype]<EOL>

!
        @return (unit) Return pyclustering data type that corresponds to ctype.


def __init__(self, dataset, c_data_type):

self.__dataset = dataset<EOL>self.__c_data_type = c_data_type<EOL>

!
        @brief Initialize package builder object by dataset.

        @param[in] dataset (list): Data that should be packed in 'pyclustering_package'.
        @param[in] c_data_type (ctype.type): If specified than specified data type is used for data storing in package.


return self.__create_package(self.__dataset)<EOL>

!
        @brief Performs packing procedure of the data to the package.

        @return (pointer) ctype-pointer to pyclustering package.


self.__package_pointer = package_pointer<EOL>

!
        @brief Initialize package extractor object by ctype-pointer to 'pyclustering_package'.

        @param[in] package_pointer (pointer): ctype-pointer to 'pyclustering_package' that should be used for unpacking.


return self.__extract_data(self.__package_pointer)<EOL>

!
        @brief Performs unpacking procedure of the pyclustering package to the data.

        @return (list) Extracted data from the pyclustering package.


def rock(sample, eps, number_clusters, threshold):

pointer_data = package_builder(sample, c_double).create();<EOL>ccore = ccore_library.get();<EOL>ccore.rock_algorithm.restype = POINTER(pyclustering_package);<EOL>package = ccore.rock_algorithm(pointer_data, c_double(eps), c_size_t(number_clusters), c_double(threshold));<EOL>list_of_clusters = package_extractor(package).extract();<EOL>ccore.free_pyclustering_package(package);<EOL>return list_of_clusters<EOL>

@brief Clustering algorithm ROCK returns allocated clusters and noise that are consisted from input data.
@details Calculation is performed via CCORE (C/C++ part of the pyclustering)."

@param[in] sample: input data - list of points where each point is represented by list of coordinates.
@param[in] eps: connectivity radius (similarity threshold), points are neighbors if distance between them is less than connectivity radius.
@param[in] number_clusters: defines number of clusters that should be allocated from the input data set.
@param[in] threshold: value that defines degree of normalization that influences on choice of clusters for merging during processing.

@return List of allocated clusters, each cluster contains indexes of objects in list of data.


def __init__(self, x_title=None, y_title=None, x_lim=None, y_lim=None, x_labels=True, y_labels=True):

<EOL>self.x_title  = x_title;<EOL>self.y_title  = y_title;<EOL>self.x_lim    = x_lim;<EOL>self.y_lim    = y_lim;<EOL>self.x_labels = x_labels;<EOL>self.y_labels = y_labels<EOL>

!
        @brief Constructor of canvas.

        @param[in] x_title (string): Title for X axis, if 'None', then nothing is displayed.
        @param[in] y_title (string): Title for Y axis, if 'None', then nothing is displayed.
        @param[in] x_lim (list): Defines borders of X axis like [from, to], for example [0, 3.14], if 'None' then
                    borders are calculated automatically.
        @param[in] y_lim (list): Defines borders of Y axis like [from, to], if 'None' then borders are calculated
                    automatically.
        @param[in] x_labels (bool): If True then labels of X axis are displayed.
        @param[in] y_labels (bool): If True then labels of Y axis are displayed.


def __init__(self, canvas, time, dynamics, separate, color):

<EOL>self.canvas     = canvas;<EOL>self.time       = time;<EOL>self.dynamics   = dynamics;<EOL>self.separate   = self.__get_canonical_separate(separate);<EOL>self.color      = color<EOL>

!
        @brief Constructor of output dynamic descriptor.

        @param[in] canvas (uint): Index of canvas where dynamic should be displayed, in case of 'separate'
                    representation this argument is considered as a first canvas from that displaying should be done.
        @param[in] time (list): Time points that are considered as a X axis.
        @param[in] dynamics (list): Dynamic or dynamics that should be displayed.
        @param[in] separate (bool|list): If 'True' then each dynamic is displayed on separate canvas, if it is defined
                    by list, for example, [ [1, 2], [3, 4] ], then the first and the second dynamics are displayed on
                    the canvas with index 'canvas' and the third and forth are displayed on the next 'canvas + 1'
                    canvas.
        @param[in] color (string): Color that is used to display output dynamic(s).


return self.separate[index_dynamic]<EOL>

!
        @brief Returns index of canvas where specified dynamic (by index 'index_dynamic') should be displayed.

        @param[in] index_dynamic (uint): Index of dynamic that should be displayed.

        @return (uint) Index of canvas.


if (isinstance(input_separate, list)):<EOL><INDENT>separate = [<NUM_LIT:0>] * len(self.dynamics[<NUM_LIT:0>]);<EOL>for canvas_index in range(len(input_separate)):<EOL><INDENT>dynamic_indexes = input_separate[canvas_index];<EOL>for dynamic_index in dynamic_indexes:<EOL><INDENT>separate[dynamic_index] = canvas_index;<EOL><DEDENT><DEDENT>return separate;<EOL><DEDENT>elif (input_separate is False):<EOL><INDENT>if (isinstance(self.dynamics[<NUM_LIT:0>], list) is True):<EOL><INDENT>return [ self.canvas ] * len(self.dynamics[<NUM_LIT:0>]);<EOL><DEDENT>else:<EOL><INDENT>return [ self.canvas ];<EOL><DEDENT><DEDENT>elif (input_separate is True):<EOL><INDENT>if (isinstance(self.dynamics[<NUM_LIT:0>], list) is True):<EOL><INDENT>return range(self.canvas, self.canvas + len(self.dynamics[<NUM_LIT:0>]));<EOL><DEDENT>else:<EOL><INDENT>return [ self.canvas ];<EOL><DEDENT><DEDENT>else:<EOL><INDENT>raise Exception("<STR_LIT>" % type(input_separate));<EOL><DEDENT>

!
        @brief Return unified representation of separation value.
        @details It represents list whose size is equal to amount of dynamics, where index of dynamic will show
                  where it should be displayed.

        @param[in] input_separate (bool|list): Input separate representation that should transformed.

        @return (list) Indexes where each dynamic should be displayed.


def __init__(self, canvas, x_title=None, y_title=None, x_lim=None, y_lim=None, x_labels=True, y_labels=True):

self.__size = canvas;<EOL>self.__canvases = [ canvas_descr(x_title, y_title, x_lim, y_lim, x_labels, y_labels) for _ in range(canvas) ];<EOL>self.__dynamic_storage = []<EOL>

!
        @brief Construct dynamic visualizer.
        @details Default properties that are generalized in the constructor, for example, X axis title, can be
                  changed by corresponding method: 'set_canvas_properties'.

        @param[in] canvas (uint): Amount of canvases that is used for visualization.
        @param[in] x_title (string): Title for X axis of canvases, if 'None', then nothing is displayed.
        @param[in] y_title (string): Title for Y axis of canvases, if 'None', then nothing is displayed.
        @param[in] x_lim (list): Defines borders of X axis like [from, to], for example [0, 3.14], if 'None' then
                    borders are calculated automatically.
        @param[in] y_lim (list): Defines borders of Y axis like [from, to], if 'None' then borders are calculated
                    automatically.
        @param[in] x_labels (bool): If True then labels of X axis are displayed.
        @param[in] y_labels (bool): If True then labels of Y axis are displayed.


def set_canvas_properties(self, canvas, x_title=None, y_title=None, x_lim=None, y_lim=None, x_labels=True, y_labels=True):

self.__canvases[canvas] = canvas_descr(x_title, y_title, x_lim, y_lim, x_labels, y_labels)<EOL>

!
        @brief Set properties for specified canvas.

        @param[in] canvas (uint): Index of canvas whose properties should changed.
        @param[in] x_title (string): Title for X axis, if 'None', then nothing is displayed.
        @param[in] y_title (string): Title for Y axis, if 'None', then nothing is displayed.
        @param[in] x_lim (list): Defines borders of X axis like [from, to], for example [0, 3.14], if 'None' then
                    borders are calculated automatically.
        @param[in] y_lim (list): Defines borders of Y axis like [from, to], if 'None' then borders are calculated
                    automatically.
        @param[in] x_labels (bool): If True then labels of X axis are displayed.
        @param[in] y_labels (bool): If True then labels of Y axis are displayed.


def append_dynamic(self, t, dynamic, canvas=<NUM_LIT:0>, color='<STR_LIT>'):

description = dynamic_descr(canvas, t, dynamic, False, color);<EOL>self.__dynamic_storage.append(description);<EOL>self.__update_canvas_xlim(description.time, description.separate)<EOL>

!
        @brief Append single dynamic to specified canvas (by default to the first with index '0').

        @param[in] t (list): Time points that corresponds to dynamic values and considered on a X axis.
        @param[in] dynamic (list): Value points of dynamic that are considered on an Y axis.
        @param[in] canvas (uint): Canvas where dynamic should be displayed.
        @param[in] color (string): Color that is used for drawing dynamic on the canvas.


def append_dynamics(self, t, dynamics, canvas=<NUM_LIT:0>, separate=False, color='<STR_LIT>'):

description = dynamic_descr(canvas, t, dynamics, separate, color);<EOL>self.__dynamic_storage.append(description);<EOL>self.__update_canvas_xlim(description.time, description.separate)<EOL>

!
        @brief Append several dynamics to canvas or canvases (defined by 'canvas' and 'separate' arguments).

        @param[in] t (list): Time points that corresponds to dynamic values and considered on a X axis.
        @param[in] dynamics (list): Dynamics where each of them is considered on Y axis.
        @param[in] canvas (uint): Index of canvas where dynamic should be displayed, in case of 'separate'
                    representation this argument is considered as a first canvas from that displaying should be done.
        @param[in] separate (bool|list): If 'True' then each dynamic is displayed on separate canvas, if it is defined
                    by list, for example, [ [1, 2], [3, 4] ], then the first and the second dynamics are displayed on
                    the canvas with index 'canvas' and the third and forth are displayed on the next 'canvas + 1'
                    canvas.
        @param[in] color (string): Color that is used to display output dynamic(s).


def show(self, axis=None, display=True):

if (not axis):<EOL><INDENT>(_, axis) = plt.subplots(self.__size, <NUM_LIT:1>);<EOL><DEDENT>self.__format_canvases(axis);<EOL>for dynamic in self.__dynamic_storage:<EOL><INDENT>self.__display_dynamic(axis, dynamic);<EOL><DEDENT>if (display):<EOL><INDENT>plt.show();<EOL><DEDENT>

!
        @brief Draw and show output dynamics.

        @param[in] axis (axis): If is not 'None' then user specified axis is used to display output dynamic.
        @param[in] display (bool): Whether output dynamic should be displayed or not, if not, then user
                    should call 'plt.show()' by himself.


exp_amount = <NUM_LIT:0.0>;<EOL>average_phase = <NUM_LIT:0.0>;<EOL>for phase in oscillator_phases:<EOL><INDENT>exp_amount += math.expm1( abs(<NUM_LIT> * phase) );<EOL>average_phase += phase;<EOL><DEDENT>exp_amount /= len(oscillator_phases);<EOL>average_phase = math.expm1( abs(<NUM_LIT> * (average_phase / len(oscillator_phases))) );<EOL>return abs(average_phase) / abs(exp_amount)<EOL>

!
        @brief Calculates level of global synchronization (order parameter) for input phases.
        @details This parameter is tend 1.0 when the oscillatory network close to global synchronization and it tend to 0.0 when
                  desynchronization is observed in the network.

        @param[in] oscillator_phases (list): List of oscillator phases that are used for level of global synchronization.

        @return (double) Level of global synchronization (order parameter).

        @see calculate_order_parameter()


exp_amount = <NUM_LIT:0.0>;<EOL>num_neigh = <NUM_LIT:0.0>;<EOL>for i in range(<NUM_LIT:0>, len(oscillatory_network), <NUM_LIT:1>):<EOL><INDENT>for j in range(<NUM_LIT:0>, len(oscillatory_network), <NUM_LIT:1>):<EOL><INDENT>if (oscillatory_network.has_connection(i, j) == True):<EOL><INDENT>exp_amount += math.exp(-abs(oscillator_phases[j] - oscillator_phases[i]));<EOL>num_neigh += <NUM_LIT:1.0>;<EOL><DEDENT><DEDENT><DEDENT>if (num_neigh == <NUM_LIT:0>):<EOL><INDENT>num_neigh = <NUM_LIT:1.0>;<EOL><DEDENT>return exp_amount / num_neigh<EOL>

!
        @brief Calculates level of local synchorization (local order parameter) for input phases for the specified network.
        @details This parameter is tend 1.0 when the oscillatory network close to local synchronization and it tend to 0.0 when
                  desynchronization is observed in the network.

        @param[in] oscillator_phases (list): List of oscillator phases that are used for level of local (partial) synchronization.
        @param[in] oscillatory_network (sync): Instance of oscillatory network whose connections are required for calculation.

        @return (double) Level of local synchronization (local order parameter).


if ( (self._ccore_sync_dynamic_pointer is not None) and ( (self._dynamic is None) or (len(self._dynamic) == <NUM_LIT:0>) ) ):<EOL><INDENT>self._dynamic = wrapper.sync_dynamic_get_output(self._ccore_sync_dynamic_pointer);<EOL><DEDENT>return self._dynamic<EOL>

!
        @brief (list) Returns output dynamic of the Sync network (phase coordinates of each oscillator in the network) during simulation.


if ( (self._ccore_sync_dynamic_pointer is not None) and ( (self._time is None) or (len(self._time) == <NUM_LIT:0>) ) ):<EOL><INDENT>self._time = wrapper.sync_dynamic_get_time(self._ccore_sync_dynamic_pointer);<EOL><DEDENT>return self._time<EOL>

!
        @brief (list) Returns sampling times when dynamic is measured during simulation.


def __init__(self, phase, time, ccore = None):

self._dynamic = phase;<EOL>self._time = time;<EOL>self._ccore_sync_dynamic_pointer = ccore<EOL>

!
        @brief Constructor of Sync dynamic.

        @param[in] phase (list): Dynamic of oscillators on each step of simulation. If ccore pointer is specified than it can be ignored.
        @param[in] time (list): Simulation time.
        @param[in] ccore (ctypes.pointer): Pointer to CCORE sync_dynamic instance in memory.


if (self._ccore_sync_dynamic_pointer is not None):<EOL><INDENT>wrapper.sync_dynamic_destroy(self._ccore_sync_dynamic_pointer);<EOL><DEDENT>

!
        @brief Default destructor of Sync dynamic.


if (self._ccore_sync_dynamic_pointer is not None):<EOL><INDENT>return wrapper.sync_dynamic_get_size(self._ccore_sync_dynamic_pointer);<EOL><DEDENT>return len(self._dynamic)<EOL>

!
        @brief Returns number of simulation steps that are stored in dynamic.
        @return (uint) Number of simulation steps that are stored in dynamic.


if (index is <NUM_LIT:0>):<EOL><INDENT>return self.time;<EOL><DEDENT>elif (index is <NUM_LIT:1>):<EOL><INDENT>return self.output;<EOL><DEDENT>else:<EOL><INDENT>raise NameError('<STR_LIT>' + index + '<STR_LIT>');<EOL><DEDENT>

!
        @brief Indexing of the dynamic.


def allocate_sync_ensembles(self, tolerance = <NUM_LIT>, indexes = None, iteration = None):

if (self._ccore_sync_dynamic_pointer is not None):<EOL><INDENT>ensembles = wrapper.sync_dynamic_allocate_sync_ensembles(self._ccore_sync_dynamic_pointer, tolerance, iteration);<EOL>if (indexes is not None):<EOL><INDENT>for ensemble in ensembles:<EOL><INDENT>for index in range(len(ensemble)):<EOL><INDENT>ensemble[index] = indexes[ ensemble[index] ];<EOL><DEDENT><DEDENT><DEDENT>return ensembles;<EOL><DEDENT>if ( (self._dynamic is None) or (len(self._dynamic) == <NUM_LIT:0>) ):<EOL><INDENT>return [];<EOL><DEDENT>number_oscillators = len(self._dynamic[<NUM_LIT:0>]);<EOL>last_state = None;<EOL>if (iteration is None):<EOL><INDENT>last_state = self._dynamic[len(self._dynamic) - <NUM_LIT:1>];<EOL><DEDENT>else:<EOL><INDENT>last_state = self._dynamic[iteration];<EOL><DEDENT>clusters = [];<EOL>if (number_oscillators > <NUM_LIT:0>):<EOL><INDENT>clusters.append([<NUM_LIT:0>]);<EOL><DEDENT>for i in range(<NUM_LIT:1>, number_oscillators, <NUM_LIT:1>):<EOL><INDENT>cluster_allocated = False;<EOL>for cluster in clusters:<EOL><INDENT>for neuron_index in cluster:<EOL><INDENT>last_state_shifted = abs(last_state[i] - <NUM_LIT:2> * pi);<EOL>if ( ( (last_state[i] < (last_state[neuron_index] + tolerance)) and (last_state[i] > (last_state[neuron_index] - tolerance)) ) or<EOL>( (last_state_shifted < (last_state[neuron_index] + tolerance)) and (last_state_shifted > (last_state[neuron_index] - tolerance)) ) ):<EOL><INDENT>cluster_allocated = True;<EOL>real_index = i;<EOL>if (indexes is not None):<EOL><INDENT>real_index = indexes[i];<EOL><DEDENT>cluster.append(real_index);<EOL>break;<EOL><DEDENT><DEDENT>if (cluster_allocated == True):<EOL><INDENT>break;<EOL><DEDENT><DEDENT>if (cluster_allocated == False):<EOL><INDENT>clusters.append([i]);<EOL><DEDENT><DEDENT>return clusters<EOL>

!
        @brief Allocate clusters in line with ensembles of synchronous oscillators where each synchronous ensemble corresponds to only one cluster.

        @param[in] tolerance (double): Maximum error for allocation of synchronous ensemble oscillators.
        @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]).
        @param[in] iteration (uint): Iteration of simulation that should be used for allocation.

        @return (list) Grours (lists) of indexes of synchronous oscillators.
                For example [ [index_osc1, index_osc3], [index_osc2], [index_osc4, index_osc5] ].