Source code for surrogate.selection.selSPEA2

# Copyright 2016 Quan Pan
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#    http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Author: Quan Pan <quanpan302@hotmail.com>
# License: Apache License, Version 2.0
# Create: 2016-12-02

import math

from .utils import randomizedSelect

[docs]def selSPEA2(individuals, k): """Apply SPEA-II selection operator on the *individuals*. Usually, the size of *individuals* will be larger than *n* because any individual present in *individuals* will appear in the returned list at most once. Having the size of *individuals* equals to *n* will have no effect other than sorting the population according to a strength Pareto scheme. The list returned contains references to the input *individuals*. For more details on the SPEA-II operator see [Zitzler2001]_. :param individuals: A list of individuals to select from. :param k: The number of individuals to select. :returns: A list of selected individuals. .. [Zitzler2001] Zitzler, Laumanns and Thiele, "SPEA 2: Improving the strength Pareto evolutionary algorithm", 2001. """ N = len(individuals) L = len(individuals[0].fitness.values) K = math.sqrt(N) strength_fits = [0] * N fits = [0] * N dominating_inds = [list() for i in xrange(N)] for i, ind_i in enumerate(individuals): for j, ind_j in enumerate(individuals[i + 1:], i + 1): if ind_i.fitness.dominates(ind_j.fitness): strength_fits[i] += 1 dominating_inds[j].append(i) elif ind_j.fitness.dominates(ind_i.fitness): strength_fits[j] += 1 dominating_inds[i].append(j) for i in xrange(N): for j in dominating_inds[i]: fits[i] += strength_fits[j] # Choose all non-dominated individuals chosen_indices = [i for i in xrange(N) if fits[i] < 1] if len(chosen_indices) < k: # The archive is too small for i in xrange(N): distances = [0.0] * N for j in xrange(i + 1, N): dist = 0.0 for l in xrange(L): val = individuals[i].fitness.values[l] - \ individuals[j].fitness.values[l] dist += val * val distances[j] = dist kth_dist = randomizedSelect(distances, 0, N - 1, K) density = 1.0 / (kth_dist + 2.0) fits[i] += density next_indices = [(fits[i], i) for i in xrange(N) if not i in chosen_indices] next_indices.sort() # print next_indices chosen_indices += [i for _, i in next_indices[:k - len(chosen_indices)]] elif len(chosen_indices) > k: # The archive is too large N = len(chosen_indices) distances = [[0.0] * N for i in xrange(N)] sorted_indices = [[0] * N for i in xrange(N)] for i in xrange(N): for j in xrange(i + 1, N): dist = 0.0 for l in xrange(L): val = individuals[chosen_indices[i]].fitness.values[l] - \ individuals[chosen_indices[j]].fitness.values[l] dist += val * val distances[i][j] = dist distances[j][i] = dist distances[i][i] = -1 # Insert sort is faster than quick sort for short arrays for i in xrange(N): for j in xrange(1, N): l = j while l > 0 and distances[i][j] < distances[i][sorted_indices[i][l - 1]]: sorted_indices[i][l] = sorted_indices[i][l - 1] l -= 1 sorted_indices[i][l] = j size = N to_remove = [] while size > k: # Search for minimal distance min_pos = 0 for i in xrange(1, N): for j in xrange(1, size): dist_i_sorted_j = distances[i][sorted_indices[i][j]] dist_min_sorted_j = distances[min_pos][sorted_indices[min_pos][j]] if dist_i_sorted_j < dist_min_sorted_j: min_pos = i break elif dist_i_sorted_j > dist_min_sorted_j: break # Remove minimal distance from sorted_indices for i in xrange(N): distances[i][min_pos] = float("inf") distances[min_pos][i] = float("inf") for j in xrange(1, size - 1): if sorted_indices[i][j] == min_pos: sorted_indices[i][j] = sorted_indices[i][j + 1] sorted_indices[i][j + 1] = min_pos # Remove corresponding individual from chosen_indices to_remove.append(min_pos) size -= 1 for index in reversed(sorted(to_remove)): del chosen_indices[index] return [individuals[i] for i in chosen_indices]