moocore.

bounded_exponential_crossover#

moocore.bounded_exponential_crossover(parents, /, *, lower=None, upper=None, scale=0.1, swap_prob=0.0, pair_prob=1.0, seed=None)[source]#

Bounded exponential crossover (BEX).

Each crossover operation on a pair of parents \(x, y\) produces two offspring \(u, v\). With probability pair_prob, for each variable \(j\) with \(d_j = |x_j - y_j| > 0\),

\[u_j = x_j + \beta_j^x d_j, \qquad v_j = y_j + \beta_j^y d_j,\]

where \(\beta_j^x\) is negative or positive with probability 1/2 each. On each side, it follows an exponential distribution with scale \(\lambda\) (scale), truncated at the bound on that side, so that \(u_j\) stays within the bounds of the variable. Towards the lower bound \(l_j\), \(\beta = \lambda \ln(e_l + r(1 - e_l))\) with \(e_l = \exp((l_j - x_j) / (\lambda d_j))\). Towards the upper bound \(h_j\), \(\beta = -\lambda \ln(1 - r(1 - e_h))\) with \(e_h = \exp((x_j - h_j) / (\lambda d_j))\). The uniform random number \(r\) and the direction are shared by both offspring. Variables where the parents are equal are copied. After that, each variable is swapped between the two offspring with probability swap_prob, as in uniform_crossover().

Otherwise, with probability 1 - pair_prob, the offspring are copies of their parents.

Parameters:
  • parents (ArrayLike) – 2D array of numerical values, where each row is a decision vector. Rows 2i and 2i + 1 are crossed over. If the number of rows is odd, the last row is crossed over with the first one. Every value must be within the bounds.

  • lower (ArrayLike | None) – Lower bounds of the decision variables. Either a single value, which will be used for all variables, or one value per column. None means no lower bound. (default: None)

  • upper (ArrayLike | None) – Upper bounds of the decision variables. Either a single value, which will be used for all variables, or one value per column. None means no upper bound. (default: None)

  • scale (float) – Scale \(\lambda > 0\) of the exponential distribution. Smaller values keep the offspring closer to their parents. (default: 0.1)

  • swap_prob (float) – Probability of swapping each variable between the two offspring. (default: 0.0)

  • pair_prob (float) – Probability of applying the crossover operation to each pair of parents. (default: 1.0)

  • seed (int | Generator | None) – Either an integer to seed numpy.random.default_rng(), a Numpy default random number generator (RNG) or an instance of a Numpy-compatible RNG. None uses the equivalent of a random seed, as in numpy.random.default_rng(). (default: None)

Returns:

ndarray – Array with the same shape as parents, where rows 2i and 2i + 1 are the two offspring of rows 2i and 2i + 1 of parents. If the number of rows is odd, the second offspring of the last pair is dropped, so the number of rows does not change.

Notes

BEX was proposed by Thakur, Meghwani and Jalota (2014) as a bounded version of the Laplace crossover of Deep and Thakur (2007). Without bounds, \(\beta\) follows a Laplace distribution. A parent that sits on a bound is copied with probability 1/2, as in the paper.

The swap of variables (swap_prob) is not part of the original operator. Thakur, Meghwani and Jalota tuned \(\lambda = 0.65\) and a crossover probability of 0.5 for their genetic algorithm. The defaults here keep the offspring closer to their parents and cross over every pair.

References

M. Thakur, S. S. Meghwani and H. Jalota (2014). A modified real coded genetic algorithm for constrained optimization. Applied Mathematics and Computation, 235:292–317. https://doi.org/10.1016/j.amc.2014.02.093

K. Deep and M. Thakur (2007). A new crossover operator for real coded genetic algorithms. Applied Mathematics and Computation, 188(1):895–911. https://doi.org/10.1016/j.amc.2006.10.047

Examples

>>> parents = np.array([[0.2, 0.2], [0.6, 0.8]])
>>> moocore.bounded_exponential_crossover(parents, lower=0, upper=1, seed=42)
array([[0.32040486, 0.14833417],
       [0.72036984, 0.74522359]])