Source code for pyswarms.utils.functions.single_obj

# -*- coding: utf-8 -*-

"""single_obj.py: collection of single-objective functions

All objective functions :code:`obj_func()` must accept a
:code:`(numpy.ndarray)` with shape :code:`(n_particles, dimensions)`.
Thus, each row represents a  particle, and each column represents its
position on a specific dimension of the search-space.

In this context, :code:`obj_func()` must return an array :code:`j`
of size :code:`(n_particles, )` that contains all the computed fitness
for each particle.

Whenever you make changes to this file via an implementation
of a new objective function, be sure to perform unittesting
in order to check if all functions implemented adheres to
the design pattern stated above.

Function list:
- Ackley's, ackley
- Beale, beale
- Booth, booth
- Bukin's No 6, bukin6
- Cross-in-Tray, crossintray
- Easom, easom
- Eggholder, eggholder
- Goldstein, goldstein
- Himmelblau's, himmelblau
- Holder Table, holdertable
- Levi, levi
- Matyas, matyas
- Rastrigin, rastrigin
- Rosenbrock, rosenbrock
- Schaffer No 2, schaffer2
- Sphere, sphere
- Three Hump Camel, threehump
"""

# Import modules
import numpy as np


[docs]def ackley(x): """Ackley's objective function. Has a global minimum of `0` at :code:`f(0,0,...,0)` with a search domain of [-32, 32] Parameters ---------- x : numpy.ndarray set of inputs of shape :code:`(n_particles, dimensions)` Returns ------- numpy.ndarray computed cost of size :code:`(n_particles, )` ------ ValueError When the input is out of bounds with respect to the function domain """ if not np.logical_and(x >= -32, x <= 32).all(): raise ValueError("Input for Ackley function must be within [-32, 32].") d = x.shape[1] j = ( -20.0 * np.exp(-0.2 * np.sqrt((1 / d) * (x ** 2).sum(axis=1))) - np.exp((1 / float(d)) * np.cos(2 * np.pi * x).sum(axis=1)) + 20.0 + np.exp(1) ) return j
[docs]def beale(x): """Beale objective function. Only takes two dimensions and has a global minimum of `0` at :code:`f([3,0.5])` Its domain is bounded between :code:`[-4.5, 4.5]` Parameters ---------- x : numpy.ndarray set of inputs of shape :code:`(n_particles, dimensions)` Returns ------- numpy.ndarray computed cost of size :code:`(n_particles, )` Raises ------ IndexError When the input dimensions is greater than what the function allows ValueError When the input is out of bounds with respect to the function domain """ if not x.shape[1] == 2: raise IndexError("Beale function only takes two-dimensional input.") if not np.logical_and(x >= -4.5, x <= 4.5).all(): raise ValueError( "Input for Beale function must be within " "[-4.5, 4.5]." ) x_ = x[:, 0] y_ = x[:, 1] j = ( (1.5 - x_ + x_ * y_) ** 2.0 + (2.25 - x_ + x_ * y_ ** 2.0) ** 2.0 + (2.625 - x_ + x_ * y_ ** 3.0) ** 2.0 ) return j
[docs]def booth(x): """Booth's objective function. Only takes two dimensions and has a global minimum of `0` at :code:`f([1,3])`. Its domain is bounded between :code:`[-10, 10]` Parameters ---------- x : numpy.ndarray set of inputs of shape :code:`(n_particles, dimensions)` Returns ------- numpy.ndarray computed cost of size :code:`(n_particles, )` Raises ------ IndexError When the input dimensions is greater than what the function allows ValueError When the input is out of bounds with respect to the function domain """ if not x.shape[1] == 2: raise IndexError("Booth function only takes two-dimensional input.") if not np.logical_and(x >= -10, x <= 10).all(): raise ValueError("Input for Booth function must be within [-10, 10].") x_ = x[:, 0] y_ = x[:, 1] j = (x_ + 2 * y_ - 7) ** 2.0 + (2 * x_ + y_ - 5) ** 2.0 return j
[docs]def bukin6(x): """Bukin N. 6 Objective Function Only takes two dimensions and has a global minimum of `0` at :code:`f([-10,1])`. Its coordinates are bounded by: * x[:,0] must be within [-15, -5] * x[:,1] must be within [-3, 3] Parameters ---------- x : numpy.ndarray set of inputs of shape :code:`(n_particles, dimensions)` Returns ------- numpy.ndarray computed cost of size :code:`(n_particles, )` Raises ------ IndexError When the input dimensions is greater than what the function allows ValueError When the input is out of bounds with respect to the function domain """ if not x.shape[1] == 2: raise IndexError( "Bukin N. 6 function only takes two-dimensional " "input." ) if not np.logical_and(x[:, 0] >= -15, x[:, 0] <= -5).all(): raise ValueError( "x-coord for Bukin N. 6 function must be within " "[-15, -5]." ) if not np.logical_and(x[:, 1] >= -3, x[:, 1] <= 3).all(): raise ValueError( "y-coord for Bukin N. 6 function must be within " "[-3, 3]." ) x_ = x[:, 0] y_ = x[:, 1] j = 100 * np.sqrt(np.absolute(y_ - 0.01 * x_ ** 2.0)) + 0.01 * np.absolute( x_ + 10 ) return j
[docs]def crossintray(x): """Cross-in-tray objective function. Only takes two dimensions and has a four equal global minimums of `-2.06261` at :code:`f([1.34941, -1.34941])`, :code:`f([1.34941, 1.34941])`, :code:`f([-1.34941, 1.34941])`, and :code:`f([-1.34941, -1.34941])`. Its coordinates are bounded within :code:`[-10,10]`. Best visualized in the full domain and a range of :code:`[-2.0, -0.5]`. Parameters ---------- x : numpy.ndarray set of inputs of shape :code:`(n_particles, dimensions)` Returns ------- numpy.ndarray computed cost of size :code:`(n_particles, )` Raises ------ IndexError When the input dimensions is greater than what the function allows ValueError When the input is out of bounds with respect to the function domain """ if not x.shape[1] == 2: raise IndexError( "Cross-in-tray function only takes two-dimensional input." ) if not np.logical_and(x >= -10, x <= 10).all(): raise ValueError( "Input for cross-in-tray function must be within [-10, 10]." ) x_ = x[:, 0] y_ = x[:, 1] j = -0.0001 * np.power( np.abs( np.sin(x_) * np.sin(y_) * np.exp(np.abs(100 - (np.sqrt(x_ ** 2 + y_ ** 2) / np.pi))) ) + 1, 0.1, ) return j
[docs]def easom(x): """Easom objective function. Only takes two dimensions and has a global minimum of `-1` at :code:`f([pi, pi])`. Its coordinates are bounded within :code:`[-100,100]`. Best visualized in the domain of :code:`[-5, 5]` and a range of :code:`[-1, 0.2]`. Parameters ---------- x : numpy.ndarray set of inputs of shape :code:`(n_particles, dimensions)` Returns ------- numpy.ndarray computed cost of size :code:`(n_particles, )` Raises ------ IndexError When the input dimensions is greater than what the function allows ValueError When the input is out of bounds with respect to the function domain """ if not x.shape[1] == 2: raise IndexError("Easom function only takes two-dimensional input.") if not np.logical_and(x >= -100, x <= 100).all(): raise ValueError( "Input for Easom function must be within [-100, 100]." ) x_ = x[:, 0] y_ = x[:, 1] j = ( -1 * np.cos(x_) * np.cos(y_) * np.exp(-1 * ((x_ - np.pi) ** 2 + (y_ - np.pi) ** 2)) ) return j
[docs]def eggholder(x): """Eggholder objective function. Only takes two dimensions and has a global minimum of `-959.6407` at :code:`f([512, 404.3219])`. Its coordinates are bounded within :code:`[-512, 512]`. Best visualized in the full domain and a range of :code:`[-1000, 1000]`. Parameters ---------- x : numpy.ndarray set of inputs of shape :code:`(n_particles, dimensions)` Returns ------- numpy.ndarray computed cost of size :code:`(n_particles, )` Raises ------ IndexError When the input dimensions is greater than what the function allows ValueError When the input is out of bounds with respect to the function domain """ if not x.shape[1] == 2: raise IndexError( "Eggholder function only takes two-dimensional input." ) if not np.logical_and(x >= -512, x <= 512).all(): raise ValueError( "Input for Eggholder function must be within [-512, 512]." ) x_ = x[:, 0] y_ = x[:, 1] j = -(y_ + 47) * np.sin(np.sqrt(np.abs((x_ / 2) + y_ + 47))) - x_ * np.sin( np.sqrt(np.abs(x_ - (y_ + 47))) ) return j
[docs]def goldstein(x): """Goldstein-Price's objective function. Only takes two dimensions and has a global minimum at :code:`f([0,-1])`. Its domain is bounded between :code:`[-2, 2]` Best visualized in the domain of :code:`[-1.3,1.3]` and range :code:`[-1,8000]` Parameters ---------- x : numpy.ndarray set of inputs of shape :code:`(n_particles, dimensions)` Returns ------- numpy.ndarray computed cost of size :code:`(n_particles, )` Raises ------ IndexError When the input dimensions is greater than what the function allows ValueError When the input is out of bounds with respect to the function domain """ if not x.shape[1] == 2: raise IndexError( "Goldstein function only takes two-dimensional " "input." ) if not np.logical_and(x >= -2, x <= 2).all(): raise ValueError( "Input for Goldstein-Price function must be within " "[-2, 2]." ) x_ = x[:, 0] y_ = x[:, 1] j = ( 1 + (x_ + y_ + 1) ** 2.0 * ( 19 - 14 * x_ + 3 * x_ ** 2.0 - 14 * y_ + 6 * x_ * y_ + 3 * y_ ** 2.0 ) ) * ( 30 + (2 * x_ - 3 * y_) ** 2.0 * ( 18 - 32 * x_ + 12 * x_ ** 2.0 + 48 * y_ - 36 * x_ * y_ + 27 * y_ ** 2.0 ) ) return j
[docs]def himmelblau(x): """Himmelblau's objective function Only takes two dimensions and has a four equal global minimums of zero at :code:`f([3.0,2.0])`, :code:`f([-2.805118,3.131312])`, :code:`f([-3.779310,-3.283186])`, and :code:`f([3.584428,-1.848126])`. Its coordinates are bounded within :code:`[-5,5]`. Best visualized with the full domain and a range of :code:`[0,1000]` Parameters ---------- x : numpy.ndarray set of inputs of shape :code:`(n_particles, dimensions)` Returns ------- numpy.ndarray computed cost of size :code:`(n_particles, )` Raises ------ IndexError When the input dimensions is greater than what the function allows ValueError When the input is out of bounds with respect to the function domain """ if not x.shape[1] == 2: raise IndexError( "Himmelblau function only takes two-dimensional input." ) if not np.logical_and(x >= -5, x <= 5).all(): raise ValueError( "Input for Himmelblau function must be within [-5,5]." ) x_ = x[:, 0] y_ = x[:, 1] j = (x_ ** 2 + y_ - 11) ** 2 + (x_ + y_ ** 2 - 7) ** 2 return j
[docs]def holdertable(x): """Holder Table objective function Only takes two dimensions and has a four equal global minimums of `-19.2085` at :code:`f([8.05502, 9.66459])`, :code:`f([-8.05502, 9.66459])`, :code:`f([8.05502, -9.66459])`, and :code:`f([-8.05502, -9.66459])`. Its coordinates are bounded within :code:`[-10, 10]`. Best visualized with the full domain and a range of :code:`[-20, 0]` Parameters ---------- x : numpy.ndarray set of inputs of shape :code:`(n_particles, dimensions)` Returns ------- numpy.ndarray computed cost of size :code:`(n_particles, )` Raises ------ IndexError When the input dimensions is greater than what the function allows ValueError When the input is out of bounds with respect to the function domain """ if not x.shape[1] == 2: raise IndexError( "Holder Table function only takes two-dimensional input." ) if not np.logical_and(x >= -10, x <= 10).all(): raise ValueError( "Input for Holder Table function must be within [-10,10]." ) x_ = x[:, 0] y_ = x[:, 1] j = -np.abs( np.sin(x_) * np.cos(y_) * np.exp(np.abs(1 - np.sqrt(x_ ** 2 + y_ ** 2) / np.pi)) ) return j
[docs]def levi(x): """Levi objective function Only takes two dimensions and has a global minimum at :code:`f([1,1])`. Its coordinates are bounded within :code:`[-10,10]`. Parameters ---------- x : numpy.ndarray set of inputs of shape :code:`(n_particles, dimensions)` Returns ------- numpy.ndarray computed cost of size :code:`(n_particles, )` Raises ------ IndexError When the input dimensions is greater than what the function allows ValueError When the input is out of bounds with respect to the function domain """ if not x.shape[1] == 2: raise IndexError("Levi function only takes two-dimensional input.") if not np.logical_and(x >= -10, x <= 10).all(): raise ValueError("Input for Levi function must be within [-10, 10].") mask = np.full(x.shape, False) mask[:, -1] = True masked_x = np.ma.array(x, mask=mask) w_ = 1 + (x - 1) / 4 masked_w_ = np.ma.array(w_, mask=mask) d_ = x.shape[1] - 1 j = ( np.sin(np.pi * w_[:, 0]) ** 2.0 + ((masked_x - 1) ** 2.0).sum(axis=1) * (1 + 10 * np.sin(np.pi * (masked_w_).sum(axis=1) + 1) ** 2.0) + (w_[:, d_] - 1) ** 2.0 * (1 + np.sin(2 * np.pi * w_[:, d_]) ** 2.0) ) return j
[docs]def matyas(x): """Matyas objective function Only takes two dimensions and has a global minimum at :code:`f([0,0])`. Its coordinates are bounded within :code:`[-10,10]`. Parameters ---------- x : numpy.ndarray set of inputs of shape :code:`(n_particles, dimensions)` Returns ------- numpy.ndarray """ if not x.shape[1] == 2: raise IndexError("Matyas function only takes two-dimensional input.") if not np.logical_and(x >= -10, x <= 10).all(): raise ValueError( "Input for Matyas function must be within " "[-10, 10]." ) x_ = x[:, 0] y_ = x[:, 1] j = 0.26 * (x_ ** 2.0 + y_ ** 2.0) - 0.48 * x_ * y_ return j
[docs]def rastrigin(x): """Rastrigin objective function. Has a global minimum at :code:`f(0,0,...,0)` with a search domain of :code:`[-5.12, 5.12]` Parameters ---------- x : numpy.ndarray set of inputs of shape :code:`(n_particles, dimensions)` Returns ------- numpy.ndarray computed cost of size :code:`(n_particles, )` Raises ------ ValueError When the input is out of bounds with respect to the function domain """ if not np.logical_and(x >= -5.12, x <= 5.12).all(): raise ValueError( "Input for Rastrigin function must be within " "[-5.12, 5.12]." ) d = x.shape[1] j = 10.0 * d + (x ** 2.0 - 10.0 * np.cos(2.0 * np.pi * x)).sum(axis=1) return j
[docs]def rosenbrock(x): """Rosenbrock objective function. Also known as the Rosenbrock's valley or Rosenbrock's banana function. Has a global minimum of :code:`np.ones(dimensions)` where :code:`dimensions` is :code:`x.shape[1]`. The search domain is :code:`[-inf, inf]`. Parameters ---------- x : numpy.ndarray set of inputs of shape :code:`(n_particles, dimensions)` Returns ------- numpy.ndarray computed cost of size :code:`(n_particles, )` """ r = np.sum( 100 * (x.T[1:] - x.T[:-1] ** 2.0) ** 2 + (1 - x.T[:-1]) ** 2.0, axis=0 ) return r
[docs]def schaffer2(x): """Schaffer N.2 objective function Only takes two dimensions and has a global minimum at :code:`f([0,0])`. Its coordinates are bounded within :code:`[-100,100]`. Parameters ---------- x : numpy.ndarray set of inputs of shape :code:`(n_particles, dimensions)` Returns ------- numpy.ndarray computed cost of size :code:`(n_particles, )` Raises ------ IndexError When the input dimensions is greater than what the function allows ValueError When the input is out of bounds with respect to the function domain """ if not x.shape[1] == 2: raise IndexError( "Schaffer N. 2 function only takes two-dimensional " "input." ) if not np.logical_and(x >= -100, x <= 100).all(): raise ValueError( "Input for Schaffer function must be within " "[-100, 100]." ) x_ = x[:, 0] y_ = x[:, 1] j = 0.5 + ( (np.sin(x_ ** 2.0 - y_ ** 2.0) ** 2.0 - 0.5) / ((1 + 0.001 * (x_ ** 2.0 + y_ ** 2.0)) ** 2.0) ) return j
[docs]def sphere(x): """Sphere objective function. Has a global minimum at :code:`0` and with a search domain of :code:`[-inf, inf]` Parameters ---------- x : numpy.ndarray set of inputs of shape :code:`(n_particles, dimensions)` Returns ------- numpy.ndarray computed cost of size :code:`(n_particles, )` """ j = (x ** 2.0).sum(axis=1) return j
[docs]def threehump(x): """Three-hump camel objective function Only takes two dimensions and has a global minimum of `0` at :code:`f([0, 0])`. Its coordinates are bounded within :code:`[-5, 5]`. Best visualized in the full domin and a range of :code:`[0, 2000]`. Parameters ---------- x : numpy.ndarray set of inputs of shape :code:`(n_particles, dimensions)` Returns ------- numpy.ndarray computed cost of size :code:`(n_particles, )` Raises ------ IndexError When the input dimensions is greater than what the function allows ValueError When the input is out of bounds with respect to the function domain """ if not x.shape[1] == 2: raise IndexError( "Three-hump camel function only takes two-dimensional input." ) if not np.logical_and(x >= -5, x <= 5).all(): raise ValueError( "Input for Three-hump camel function must be within [-5, 5]." ) x_ = x[:, 0] y_ = x[:, 1] j = 2 * x_ ** 2 - 1.05 * (x_ ** 4) + (x_ ** 6) / 6 + x_ * y_ + y_ ** 2 return j