Source code for gastop.selector

"""selector.py
This file is a part of GASTOp
Authors: Amlan Sinha, Cristian Lacey, Daniel Shaw, Paul Kaneelil, Rory Conlin, Susan Redmond
Licensed under GNU GPLv3.
This module implements the Selector class.

"""
import numpy as np


[docs]class Selector(): # Cristian ''' Selects parents to be used for crossover and mutation. When creating a new Selector() obejct, must be initialized with dictionary sel_params (containing selection method). Object can then be used as a function that selects parents according to the specified method. '''
[docs] def __init__(self, sel_params): """Creates a Selector object. Args: sel_params(dict): Dictionary containing: - ``'method'`` *(str)*: Name of chosen selection method. - ``'method_params'`` *(dict)*: Dictionary of parameters required by chosen method. Returns: selector (Selector object) """ self.sel_params = sel_params self.method = getattr(self, sel_params['method'])
[docs] @staticmethod def inverse_square_rank_probability(num_parents, population): ''' Selects parents according to inverse square rank method. Creates a cdf, with each entry the cumulative sum of 1/sqrt(N) for N = 1, ... Random values are then produced between the largest and smallest elements of the list. Each parent is chosen as the index in the cdf that the corresponding random value falls. In this way, the most probable parents are those with the highest fitness scores. Args: num_parents (int): The number of parents to select. population (list): List of Truss objects that constitutes the current generation. Returns: parents (ndarray): Numpy array of indices in population corresponding to selected parents. ''' pop_size = len(population) # Build cdf (cumulative distribution function) a = np.array(range(1, pop_size+1)) pdf = 1/np.sqrt(a) cdf = np.cumsum(pdf) cdf_upperbound = cdf[-1] # Initialize an array of random floats between 1 and the largest entry # in the cdf. Find the indices in the cdf array that the random values # would fall. For example, if one random value were 1.9, the corresponding # index would be 1 since 1.9 lies between 1+1/sqrt(2) and # 1+1/sqrt(2)+1/sqrt(3). rand_vals = np.random.uniform(0, cdf_upperbound, num_parents) parents = np.searchsorted(cdf, rand_vals, side='left') parents = parents.astype(int) return parents
[docs] @staticmethod def tournament(num_parents, population, tourn_size, tourn_prob): ''' Selects parents according to tournament method. Randomly selects truss indices from population in groups called tournaments according to "tourn_size." Each tournament is then sorted by index (lower means more fit) in ascending order and a single index from each tournament is selected. The selection from each tournament is chosen probabilistically, assigning the first, most fit, index with probability p = tourn_prob, and then subsequent indices by p*(1-p)^n. The winners of each tournament are then returned as the parents array. Args: num_parents (int): The number of parents to select. population (list): List of Truss objects that constitutes the current generation. tourn_size (int): Number of trusses to include in a given tournament. Must be <= 31. tourn_prob (float): Probability of selecting first index in each tournament. Must be between 0 and 1. Returns: parents (ndarray): Numpy array of indices in population corresponding to selected parents. ''' pop_size = len(population) # Build ndarray of randomly selected parent indices. Each row in the # array corresponds to a tournament, one tournament for each parent. rand_dimen = (num_parents, tourn_size) rand_vals = np.random.randint(0, pop_size, rand_dimen) rand_vals.sort(axis=1) # print(rand_vals,rand_vals.shape,rand_vals.max()) # Build probability array generator = (tourn_prob*(1-tourn_prob)**x for x in range(tourn_size)) a = np.fromiter(generator, float) tourn_distribution = a/np.sum(a) # normalize probabilities # Randomly select indices from each row of rand_vals assigning the # corresponding probability in tourn_distribution to each element in # the row. select_ids = np.random.choice( tourn_size, num_parents, p=tourn_distribution) # print(select_ids,select_ids.shape,select_ids.max()) # Build parents array, selecting an element from each row of rand_vals parents = np.choose(select_ids, rand_vals.T) return parents
[docs] def __call__(self, num_parents, population): """Calls selector object on a population to get parent indices. Args: num_parents (int): Number of parents to select. population (list): Population of trusses to select from, must be sorted by fitness score in ascending order. Returns: parents (ndarray): Array of indices of parents in population list. """ parents = self.method(num_parents, population, ** self.sel_params['method_params']) return parents