Completed
Push — 0.7.dev ( 7db1ce...619631 )
by
unknown
01:10
created

GeneticAlgorithm._select()   B

Complexity

Conditions 3

Size

Total Lines 24

Duplication

Lines 5
Ratio 20.83 %

Importance

Changes 0
Metric Value
cc 3
c 0
b 0
f 0
dl 5
loc 24
rs 8.9713
1
"""!
2
3
@brief Clustering by Genetic Algorithm
4
5
@date 2014-2017
6
@copyright GNU Public License
7
8
@cond GNU_PUBLIC_LICENSE
9
    PyClustering is free software: you can redistribute it and/or modify
10
    it under the terms of the GNU General Public License as published by
11
    the Free Software Foundation, either version 3 of the License, or
12
    (at your option) any later version.
13
14
    PyClustering is distributed in the hope that it will be useful,
15
    but WITHOUT ANY WARRANTY; without even the implied warranty of
16
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
    GNU General Public License for more details.
18
19
    You should have received a copy of the GNU General Public License
20
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
@endcond
22
23
"""
24
25
import numpy as np
0 ignored issues
show
Configuration introduced by
The import numpy could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
26
import math
27
28
from pyclustering.cluster.ga_maths import GAMath
29
30
31
class GeneticAlgorithm:
32
    """!
33
    @brief Class represents Genetic clustering algorithm
34
35
    """
36
37
    def __init__(self, data, count_clusters,  chromosome_count, population_count, count_mutation_gens=2,
38
                 coeff_mutation_count=0.25, select_coeff=1.0):
39
40
        # Initialize random
41
        np.random.seed()
42
43
        # Clustering data
44
        if type(data) is list:
45
            self.data = np.array(data)
46
        else:
47
            self.data = data
48
49
        # Count clusters
50
        self.count_clusters = count_clusters
51
52
        # Home many chromosome in population
53
        self.chromosome_count = chromosome_count
54
55
        # How many populations
56
        self.population_count = population_count
57
58
        # Count mutation genes
59
        self.count_mutation_gens = count_mutation_gens
60
61
        # Crossover rate
62
        self.crossover_rate = 1.0
63
64
        # Count of chromosome for mutation (range [0, 1])
65
        self.coeff_mutation_count = coeff_mutation_count
66
67
        # Exponential coeff for selection
68
        self.select_coeff = select_coeff
69
70
    def clustering(self):
71
        """
72
73
        :return:
74
        """
75
76
        # Initialize population
77
        chromosomes = self._init_population(self.count_clusters, len(self.data), self.chromosome_count)
78
79
        # Initialize the Best solution
80
        best_chromosome, best_ff = self._get_best_chromosome(chromosomes, self.data, self.count_clusters)
81
82
        # Next population
83
        for _ in range(self.population_count):
84
85
            # Select
86
            chromosomes = self._select(chromosomes, self.data, self.count_clusters, self.select_coeff)
87
88
            # Crossover
89
            self._crossover(chromosomes)
90
91
            # Mutation
92
            self._mutation(chromosomes, self.count_clusters, self.count_mutation_gens, self.coeff_mutation_count)
93
94
            # Update the Best Solution
95
            new_best_chromosome, new_best_ff = self._get_best_chromosome(chromosomes, self.data, self.count_clusters)
96
97
            # Get best chromosome
98
            if new_best_ff < best_ff:
99
                best_ff = new_best_ff
100
                best_chromosome = new_best_chromosome
101
102
        return best_chromosome, best_ff
103
104
    @staticmethod
105
    def _select(chromosomes, data, count_clusters, select_coeff):
106
        """  """
107
108
        # Calc centers
109
        centres = GAMath.get_centres(chromosomes, data, count_clusters)
110
111
        # Calc fitness functions
112
        fitness = GeneticAlgorithm._calc_fitness_function(centres, data, chromosomes)
113
114
        for _idx in range(len(fitness)):
115
            fitness[_idx] = math.exp(1 + fitness[_idx] * select_coeff)
116
117
        # Calc probability vector
118
        probabilities = GAMath.calc_probability_vector(fitness)
119
120
        # Select P chromosomes with probabilities
121
        new_chromosomes = np.zeros(chromosomes.shape, dtype=np.int)
122
123 View Code Duplication
        # Selecting
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
124
        for _idx in range(len(chromosomes)):
125
            new_chromosomes[_idx] = chromosomes[GAMath.get_uniform(probabilities)]
126
127
        return new_chromosomes
128
129
    @staticmethod
130
    def _crossover(chromosomes):
131
        """  """
132
133
        # Get pairs to Crossover
134
        pairs_to_crossover = np.array(range(len(chromosomes)))
135
136
        # Set random pairs
137
        np.random.shuffle(pairs_to_crossover)
138
139
        # Index offset ( pairs_to_crossover split into 2 parts : [V1, V2, .. | P1, P2, ...] crossover between V<->P)
140
        offset_in_pair = int(len(pairs_to_crossover) / 2)
141
142
        # For each pair
143
        for _idx in range(offset_in_pair):
144
145
            # Generate random mask for crossover
146
            crossover_mask = GeneticAlgorithm._get_crossover_mask(len(chromosomes[_idx]))
147 View Code Duplication
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
148
            # Crossover a pair
149
            GeneticAlgorithm._crossover_a_pair(chromosomes[pairs_to_crossover[_idx]],
150
                                               chromosomes[pairs_to_crossover[_idx + offset_in_pair]],
151
                                               crossover_mask)
152
153
    @staticmethod
154
    def _mutation(chromosomes, count_clusters, count_gen_for_mutation, coeff_mutation_count):
155
        """  """
156
157
        # Count gens in Chromosome
158
        count_gens = len(chromosomes[0])
159
160
        # Get random chromosomes for mutation
161
        random_idx_chromosomes = np.array(range(len(chromosomes)))
162
        np.random.shuffle(random_idx_chromosomes)
163
164
        #
165
        for _idx_chromosome in range(int(len(random_idx_chromosomes) * coeff_mutation_count)):
166
167
            #
168
            for _ in range(count_gen_for_mutation):
169
170
                # Get random gen
171
                gen_num = np.random.randint(count_gens)
172
173
                # Set random cluster
174
                chromosomes[random_idx_chromosomes[_idx_chromosome]][gen_num] = np.random.randint(count_clusters)
175
176
    @staticmethod
177
    def _crossover_a_pair(chromosome_1, chromosome_2, mask):
178
        """  """
179
180
        for _idx in range(len(chromosome_1)):
181
182
            if mask[_idx] == 1:
183
                # Swap values
184
                chromosome_1[_idx], chromosome_2[_idx] = chromosome_2[_idx], chromosome_1[_idx]
185
186
    @staticmethod
187
    def _get_crossover_mask(mask_length):
188
        """  """
189
190
        # Initialize mask
191
        mask = np.zeros(mask_length)
192
193
        # Set a half of array to 1
194
        mask[:int(int(mask_length) / 6)] = 1
195
196
        # Random shuffle
197
        np.random.shuffle(mask)
198
199
        return mask
200
201
    @staticmethod
202
    def _init_population(count_clusters, count_data, chromosome_count):
203
        """ Returns first population as a uniform random choice """
204
205
        population = np.random.randint(count_clusters, size=(chromosome_count, count_data))
206
207
        return population
208
209
    @staticmethod
210
    def _get_best_chromosome(chromosomes, data, count_clusters):
211
        """  """
212
213
        # Calc centers
214
        centres = GAMath.get_centres(chromosomes, data, count_clusters)
215
216
        # Calc Fitness functions
217
        fitness_function = GeneticAlgorithm._calc_fitness_function(centres, data, chromosomes)
218
219
        # Index of the best chromosome
220
        best_chromosome_idx = fitness_function.argmin()
221
222
        # Get chromosome with the best fitness function
223
        return chromosomes[best_chromosome_idx], fitness_function[best_chromosome_idx]
224
225
    @staticmethod
226
    def _calc_fitness_function(centres, data, chromosomes):
227
        """  """
228
229
        # Get count of chromosomes and clusters
230
        count_chromosome = len(chromosomes)
231
232
        # Initialize fitness function values
233
        fitness_function = np.zeros(count_chromosome)
234
235
        # Calc fitness function for each chromosome
236
        for _idx_chromosome in range(count_chromosome):
237
238
            # Get centers for a selected chromosome
239
            centres_data = np.zeros(data.shape)
240
241
            # Fill data centres
242
            for _idx in range(len(data)):
243
                centres_data[_idx] = centres[_idx_chromosome][chromosomes[_idx_chromosome][_idx]]
244
245
            # Get City Block distance for a chromosome
246
            fitness_function[_idx_chromosome] += np.sum(abs(data - centres_data))
247
248
        return fitness_function
249