"""fitness.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 FitnessFunction class.
"""
import numpy as np
[docs]class FitnessFunction():
"""Implements fitness functions for computing fitness scores.
The fitness function assigns a single value to each truss based
on various parameters, so that comparisons between trusses can be made.
The class is designed to be instantiated as a FitnessFunction object
which operates on Truss objects to assign a fitness score, though methods
from the class may also be called directly.
"""
[docs] def __init__(self, equation, parameters):
"""Creates a FitnessFunction object
Once created, the object acts like a function and can be called on a Truss
object to assign it a fitness score.
Args:
equation (string): The name of the method to be used to compute
fitness, as a string. eg, ``'weighted_sum'`` or
``'rosenbrock'``.
parameters (dict): Dictionary of keyword parameter values for the
method specified in *equation*.
Returns:
FitnessFunction callable object
"""
self.equation = getattr(self, equation)
self.parameters = parameters # dictionary
[docs] @staticmethod
def weighted_sum(truss, goal_fos, critical_nodes, w_fos, w_mass, w_deflection):
"""Computes fitness score using a weighted sum of parameters.
Args:
truss (Truss object): truss to be scored. Must have *mass*,
*fos*, and *deflections* attributes defined (for example, by using evaluator).
goal_fos (float >= 0): Desired factor of safety. Trusses with
a smaller fos will be penalized according to *w_fos*.
critical_nodes (int, array): Array of nodes #s for which
deflection should be minimized. If empty, defaults to all.
w_fos (float >= 0): Penalty weight for low fos. Only applied
if truss.fos < *goal_fos*.
w_mass (float >= 0): Penalty applied to mass. Relative
magnitude of *w_mass* and *w_fos* determines importance of
minimizing mass vs maximizing fos.
w_deflection (float >=0, array): Penalty applied to deflections.
If scalar, applies the same penalty to all critical nodes.
Can also be an array the same size as *critical_nodes* in
which case different penalties will be applied to each node.
Returns:
float: Fitness score. Computed as:
:math:`f = w_{m} m + w_{fos}\max{(\mathrm{fos}_{goal}-\mathrm{fos}_{min}, 0)}
+ w_{def} ||\mathrm{deflections}||_2`
:math:`m` is the mass of the stucture, :math:`\mathrm{fos}_{min}` is the
lowest fos for the structure under all load conditions.
If :math:`\mathrm{fos}_{min} > \mathrm{fos}_{goal}`, no fos penalty is applied, so f
depends only on mass and deflections.
"""
if truss.fos.size:
minfos = truss.fos.min()
else:
minfos = 0
if critical_nodes.size:
deflections = truss.deflection[critical_nodes][:, :3]
else:
deflections = truss.deflection[:, :3]
deflection_score = np.sum(
w_deflection*np.sqrt(np.sum(deflections**2, axis=1)))
fs = np.maximum(goal_fos - minfos, 0)
f = w_mass*truss.mass + w_fos*fs + deflection_score
return f
[docs] @staticmethod
def sphere(truss):
"""Sum of squares of node array elements. aka, sphere function.
Global min at x = 0 where f = 0.
This method is primarily supplied for testing of the genetic algorithm,
and should not be used for structural design.
Args:
truss (Truss object): only uses truss object as container for
nodes. No other attributes needed.
Returns:
float: Fitness score. Computed as
:math:`f(x) = \Sigma_{i=1}^n x_i^2`
:math:`n` is determined from size of nodes array,
:math:`x_i` are entries of node array.
"""
x = truss.rand_nodes.flatten()
f = np.sum(x**2)
return f
[docs] @staticmethod
def rosenbrock(truss):
"""n-dimensional Rosenbrock function.
Sum of n/2 2D Rosenbrock functions.
Global min at x=1 where f=0.
This method is primarily supplied for testing of the genetic algorithm,
and should not be used for structural design.
Args:
truss (Truss object): only uses truss object as container for
nodes. No other attributes needed.
Returns:
float: Fitness score. Computed as
:math:`f(x) = \Sigma_{i=1}^{n/2}(100*(x_{2i-1}^2 - x_{2i}^2)^2 + (x_{2i-1} - 1)^2)`
:math:`n` is determined from size of nodes array,
:math:`x_i` are entries of node array.
"""
x = truss.rand_nodes.flatten()
if x.size % 2:
x = x[1:] # need an even number of elements
n = x.size
f = 0
for i in range(int(n/2)):
f += 100*(x[2*i]**2 - x[2*i+1])**2 + (x[2*i] - 1)**2
return f
[docs] @staticmethod
def rastrigin(truss):
"""n-dimensional Restrigin function.
Global min at x=0 where f=0.
This method is primarily supplied for testing of the genetic algorithm,
and should not be used for structural design.
Args:
truss (Truss object): only uses truss object as container for
nodes. No other attributes needed.
Returns:
float: fitness score. Computed as
:math:`f(x) = 10n + \Sigma_{i=1}^n (x_i^2 -10\cos{(2 \pi x_i)})`
:math:`n` is determined from size of nodes array,
:math:`x_i` are entries of node array.
"""
A = 10 # normalizing parameters
x = truss.rand_nodes.flatten()
n = x.size
x2 = x**2
cosx = np.cos(2*np.pi*x)
f = A*n + np.sum(x2-A*cosx)
return f
[docs] def __call__(self, truss):
"""Computes fitness score and stores it in truss object.
Used when a FitnessFunction object has been created with the
method to be used and any necessary parameters.
Args:
truss (Truss object): truss to be scored.
Returns:
None
"""
f = self.equation(truss, **self.parameters)
truss.fitness_score = f
return truss