Source code for gastop.crossover

"""crossover.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 Crossover class.

"""
import numpy as np
from gastop import Truss


[docs]class Crossover(): '''Mixes attributes belonging to two different parents to produce two children with specific characteristics from both parents. When creating a new Crossover() object, it must be initialized with dictionary crossover_params (containing crossover method). The Crossover() object can then be used as a function that produces children according to the specified crossover method, such as uniform_crossover, single_point_split or two_points_split. '''
[docs] def __init__(self, crossover_params): """Creates a Crossover object. Once instantiated, the Crossover object can be called as a function to combine two trusses using the specified methods and parameters. Args: crossover_params (dict): Dictionary containing: - ``'node_crossover_method'`` *(str)*: Name of method to use for node crossover. - ``'edge_crossover_method'`` *(str)*: Name of method to use for edge crossover. - ``'property_crossover_method'`` *(str)*: Name of method to use for property crossover. - ``'user_spec_nodes'`` *(ndarray)*: Array of user specified nodes that should be passed on unaltered. Returns: Crossover callable object. """ self.params = crossover_params self.node_method = getattr(self, self.params['node_crossover_method']) self.edge_method = getattr(self, self.params['edge_crossover_method']) self.property_method = getattr( self, self.params['property_crossover_method'])
[docs] @staticmethod def uniform_crossover(parent_1, parent_2): # Paul '''Performs a uniform crossover on the two parents The uniform crossover method creates two child arrays by randomly mixing together information taken from two parent arrays. To do this, the uniform crossover method creates two arrays of ones and zeros -one being the complement of the other- with the same shape as the parent arrays. The first array is multiplied with parent1 and the complementary array is multiplied with parent2 before adding the results together to make child1. The exact opposite multiplication is done to make child2. Args: parent_1 (ndarray): Numpy array containing information for parent 1. parent_2 (ndarray): Numpy array containing information for parent 2. Returns: child1, child2 (ndarrays): Numpy arrays containing information for children. ''' # find the shape of the parents nn = np.shape(parent_1) # making an array of ones and zeros unos_and_zeros = np.random.randint(2, size=nn) # creating an array of all ones to later create the complementary array unos = np.ones(nn, dtype=int) # creating the complementary array unos_and_zeros_c = unos - unos_and_zeros # making kids ;) child1 = (unos_and_zeros * parent_1) + (unos_and_zeros_c * parent_2) child2 = (unos_and_zeros_c * parent_1) + (unos_and_zeros * parent_2) return child1, child2
[docs] @staticmethod def single_point_split(array_1, array_2): # Amlan '''Performs a single point split crossover between two parents The single split crossover method takes specific information from two parents and returns two children containing characteristics from both parents. In order to achieve this, it chooses a random point and splits the two parents into two different parts. Then it merges the first half of the first parent with the second half of the second parent and vice versa. Args: array_1 (ndarray): Numpy array containing information for parent 1. array_2 (ndarray): Numpy array containing information for parent 2. Returns: child1, child2 (ndarrays): Numpy arrays containing information for children. ''' # Choosing a random point array_row = len(array_1) point = np.random.randint(0, array_row) # Mixing the two parents child_1 = np.concatenate((array_1[:point], array_2[point:]), axis=0) child_2 = np.concatenate((array_2[:point], array_1[point:]), axis=0) return child_1, child_2
[docs] @staticmethod def two_points_split(array_1, array_2): # Amlan '''Takes specific values of two parents and return two children containing characteristics from both parents. The two points split method chooses two random points and splits the two parents into three different parts. Then, it replaces the central part of the first parent with the central part of the second parent. Args: array_1 (ndarray): Numpy array containing information for parent 1. array_2 (ndarray): Numpy array containing information for parent 2. Returns: child_1, child_2 (ndarrays): Numpy arrays containing information for children. ''' # Choosing two random points p1 = np.random.randint(1, len(array_1)) p2 = np.random.randint(1, len(array_1)-1) # Preparing the two points such that p1<p2 if p2 >= p1: p2 += 1 else: p1, p2 = p2, p1 # # Mixing the two parents child_1 = array_1 child_2 = array_2 child_1[p1:p2, :], child_2[p1:p2, :] = child_2[p1:p2, :], child_1[p1:p2, :] return child_1, child_2
[docs] def __call__(self, truss_1, truss_2): """Calls a crossover object on two trusses to combine them. Crossover object must have been instantiated specifying which methods to use. Args: truss_1 (Truss object): First truss to be combined. truss_2 (Truss object): Second truss to be combined. Returns: child_1, child_2 (Truss objects): Children trusses produced by crossover. """ user_spec_nodes = self.params['user_spec_nodes'] child_1 = Truss(user_spec_nodes, 0, 0, 0) child_2 = Truss(user_spec_nodes, 0, 0, 0) child_1.rand_nodes, child_2.rand_nodes = self.node_method( truss_1.rand_nodes, truss_2.rand_nodes, **self.params['node_crossover_params']) child_1.edges, child_2.edges = self.edge_method( truss_1.edges, truss_2.edges, **self.params['edge_crossover_params']) child_1.properties, child_2.properties = self.property_method( truss_1.properties, truss_2.properties, **self.params['property_crossover_params']) return child_1, child_2