moocore.

parent_centric_crossover#

moocore.parent_centric_crossover(population, /, *, targets=None, lower=None, upper=None, sigma_zeta=0.1, sigma_eta=0.1, seed=None)[source]#

Parent-centric crossover (PCX).

Each target \(x\) is an index parent. Two more parents \(a, b\) are drawn from population, distinct from each other and from the target. Let \(d = x - g\), where \(g\) is the centroid of the three parents, and let \(\bar{D}\) be the mean distance from \(a\) and \(b\) to the line through \(x\) along \(d\). The offspring is

\[x + \zeta d + \bar{D} \eta,\]

where \(\zeta \sim N(0, \sigma_\zeta^2)\) and \(\eta\) is a vector of independent \(N(0, \sigma_\eta^2)\) values projected onto the subspace orthogonal to \(d\). Finally, the offspring is clipped to the bounds.

Parameters:
  • population (ArrayLike) – 2D array of numerical values, where each row is a decision vector. It must have at least 3 rows.

  • targets (ArrayLike | None) – 1D vector of row indices of population. Each index produces one offspring. None uses every row. (default: None)

  • 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)

  • sigma_zeta (float) – Standard deviation \(\sigma_\zeta > 0\) of the step along \(d\). (default: 0.1)

  • sigma_eta (float) – Standard deviation \(\sigma_\eta > 0\) of the steps orthogonal to \(d\). (default: 0.1)

  • 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 one row per target, where row i is the offspring of index parent targets[i].

Notes

PCX was proposed by Deb, Anand and Joshi (2002), who use three parents and \(\sigma_\zeta = \sigma_\eta = 0.1\). Their equation (2) writes the step orthogonal to \(d\) as a sum of normal steps along \(\mu - 1\) orthonormal vectors, where \(\mu\) is the number of parents. Like pymoo, which bases its implementation on the authors’ C code, this function projects an isotropic normal vector onto the whole subspace orthogonal to \(d\), so the step covers every direction orthogonal to \(d\), not only \(\mu - 1\) of them. If \(x\) is (almost) the centroid, the projection is skipped.

References

K. Deb, A. Anand and D. Joshi (2002). A computationally efficient evolutionary algorithm for real-parameter optimization. Evolutionary Computation, 10(4):371–395. https://doi.org/10.1162/106365602760972767

Examples

>>> population = np.array([[0.2, 0.2], [0.4, 0.9], [0.8, 0.3], [0.5, 0.5]])
>>> moocore.parent_centric_crossover(
...     population, targets=[0, 3], lower=0, upper=1, seed=42
... )
array([[0.18832072, 0.19642879],
       [0.50136187, 0.44711562]])