Source code for optuna.samplers.nsgaii._crossovers._vsbx

from typing import Optional

import numpy as np

from optuna._experimental import experimental_class
from optuna.samplers.nsgaii._crossovers._base import BaseCrossover
from optuna.study import Study


[docs]@experimental_class("3.0.0") class VSBXCrossover(BaseCrossover): """Modified Simulated Binary Crossover operation used by :class:`~optuna.samplers.NSGAIISampler`. vSBX generates child individuals without excluding any region of the parameter space, while maintaining the excellent properties of SBX. - `Pedro J. Ballester, Jonathan N. Carter. Real-Parameter Genetic Algorithms for Finding Multiple Optimal Solutions in Multi-modal Optimization. GECCO 2003: 706-717 <https://link.springer.com/chapter/10.1007/3-540-45105-6_86>`_ Args: eta: Distribution index. A small value of ``eta`` allows distant solutions to be selected as children solutions. If not specified, takes default value of ``2`` for single objective functions and ``20`` for multi objective. """ n_parents = 2 def __init__(self, eta: Optional[float] = None) -> None: self._eta = eta
[docs] def crossover( self, parents_params: np.ndarray, rng: np.random.RandomState, study: Study, search_space_bounds: np.ndarray, ) -> np.ndarray: # https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.422.952&rep=rep1&type=pdf # Section 3.2 Crossover Schemes (vSBX) if self._eta is None: eta = 20.0 if study._is_multi_objective() else 2.0 else: eta = self._eta us = rng.rand(len(search_space_bounds)) beta_1 = np.power(1 / 2 * us, 1 / (eta + 1)) beta_2 = np.power(1 / 2 * (1 - us), 1 / (eta + 1)) mask = us > 0.5 c1 = 0.5 * ((1 + beta_1) * parents_params[0] + (1 - beta_1) * parents_params[1]) c1[mask] = ( 0.5 * ((1 - beta_1) * parents_params[0] + (1 + beta_1) * parents_params[1])[mask] ) c2 = 0.5 * ((3 - beta_2) * parents_params[0] - (1 - beta_2) * parents_params[1]) c2[mask] = ( 0.5 * (-(1 - beta_2) * parents_params[0] + (3 - beta_2) * parents_params[1])[mask] ) # vSBX applies crossover with establishment 0.5, and with probability 0.5, # the gene of the parent individual is the gene of the child individual. # The original SBX creates two child individuals, # but optuna's implementation creates only one child individual. # Therefore, when there is no crossover, # the gene is selected with equal probability from the parent individuals x1 and x2. child_params_list = [] for c1_i, c2_i, x1_i, x2_i in zip(c1, c2, parents_params[0], parents_params[1]): if rng.rand() < 0.5: if rng.rand() < 0.5: child_params_list.append(c1_i) else: child_params_list.append(c2_i) else: if rng.rand() < 0.5: child_params_list.append(x1_i) else: child_params_list.append(x2_i) child_params = np.array(child_params_list) return child_params