# 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
```