def test_population_size() -> None: # Set `population_size` to 10. sampler = NSGAIISampler(population_size=10) study = optuna.create_study(directions=["minimize"], sampler=sampler) study.optimize(lambda t: [t.suggest_uniform("x", 0, 9)], n_trials=40) generations = Counter([ t.system_attrs[optuna.samplers._nsga2._GENERATION_KEY] for t in study.trials ]) assert generations == {0: 10, 1: 10, 2: 10, 3: 10} # Set `population_size` to 2. sampler = NSGAIISampler(population_size=2) study = optuna.create_study(directions=["minimize"], sampler=sampler) study.optimize(lambda t: [t.suggest_uniform("x", 0, 9)], n_trials=40) generations = Counter([ t.system_attrs[optuna.samplers._nsga2._GENERATION_KEY] for t in study.trials ]) assert generations == {i: 2 for i in range(20)} # Invalid population size. with pytest.raises(ValueError): # Less than 2. NSGAIISampler(population_size=1) with pytest.raises(TypeError): # Not an integer. NSGAIISampler(population_size=2.5) # type: ignore
def test_fast_non_dominated_sort_constrained_feasible_infeasible() -> None: with warnings.catch_warnings(): warnings.simplefilter("ignore", optuna.exceptions.ExperimentalWarning) sampler = NSGAIISampler(constraints_func=lambda _: [0]) # Single objective. directions = [StudyDirection.MINIMIZE] trials = [ _create_frozen_trial(0, [10], [0]), _create_frozen_trial(1, [20], [-1]), _create_frozen_trial(2, [20], [-2]), _create_frozen_trial(3, [30], [1]), ] population_per_rank = sampler._fast_non_dominated_sort(trials, directions) assert [{t.number for t in population} for population in population_per_rank] == [ {0}, {1, 2}, {3}, ] # Two objective. directions = [StudyDirection.MAXIMIZE, StudyDirection.MAXIMIZE] trials = [ _create_frozen_trial(0, [10, 30], [-1, -1]), _create_frozen_trial(1, [10, 10], [-2, -2]), _create_frozen_trial(2, [20, 20], [3, 3]), _create_frozen_trial(3, [30, 10], [6, -1]), _create_frozen_trial(4, [15, 15], [4, 4]), ] population_per_rank = sampler._fast_non_dominated_sort(trials, directions) assert [{t.number for t in population} for population in population_per_rank] == [ {0}, {1}, {2, 3}, {4}, ] # Three objective. directions = [ StudyDirection.MAXIMIZE, StudyDirection.MAXIMIZE, StudyDirection.MINIMIZE ] trials = [ _create_frozen_trial(0, [5, 5, 4], [-1, -1, -1]), _create_frozen_trial(1, [5, 5, 5], [1, 1, -1]), _create_frozen_trial(2, [9, 9, 0], [1, -1, -1]), _create_frozen_trial(3, [5, 7, 5], [-1, -1, -1]), _create_frozen_trial(4, [0, 0, 9], [-1, -1, -1]), _create_frozen_trial(5, [0, 9, 9], [-1, -1, -1]), ] population_per_rank = sampler._fast_non_dominated_sort(trials, directions) assert [{t.number for t in population} for population in population_per_rank] == [ {0, 3, 5}, {4}, {2}, {1}, ]
def test_reseed_rng() -> None: sampler = NSGAIISampler(population_size=10) original_seed = sampler._rng.seed original_random_sampler_seed = sampler._random_sampler._rng.seed sampler.reseed_rng() assert original_seed != sampler._rng.seed assert original_random_sampler_seed != sampler._random_sampler._rng.seed
def test_fast_non_dominated_sort_empty(n_dims: int) -> None: for directions in itertools.product( [StudyDirection.MINIMIZE, StudyDirection.MAXIMIZE], repeat=n_dims): trials: List[FrozenTrial] = [] sampler = NSGAIISampler() population_per_rank = sampler._fast_non_dominated_sort( trials, list(directions)) assert population_per_rank == []
def test_fast_non_dominated_sort() -> None: sampler = NSGAIISampler() # Single objective. directions = [StudyDirection.MINIMIZE] trials = [ _create_frozen_trial(0, [10]), _create_frozen_trial(1, [20]), _create_frozen_trial(2, [20]), _create_frozen_trial(3, [30]), ] population_per_rank = sampler._fast_non_dominated_sort(trials, directions) assert [{t.number for t in population} for population in population_per_rank] == [ {0}, {1, 2}, {3}, ] # Two objective. directions = [StudyDirection.MAXIMIZE, StudyDirection.MAXIMIZE] trials = [ _create_frozen_trial(0, [10, 30]), _create_frozen_trial(1, [10, 10]), _create_frozen_trial(2, [20, 20]), _create_frozen_trial(3, [30, 10]), _create_frozen_trial(4, [15, 15]), ] population_per_rank = sampler._fast_non_dominated_sort(trials, directions) assert [{t.number for t in population} for population in population_per_rank] == [ {0, 2, 3}, {4}, {1}, ] # Three objective. directions = [ StudyDirection.MAXIMIZE, StudyDirection.MAXIMIZE, StudyDirection.MINIMIZE ] trials = [ _create_frozen_trial(0, [5, 5, 4]), _create_frozen_trial(1, [5, 5, 5]), _create_frozen_trial(2, [9, 9, 0]), _create_frozen_trial(3, [5, 7, 5]), _create_frozen_trial(4, [0, 0, 9]), _create_frozen_trial(5, [0, 9, 9]), ] population_per_rank = sampler._fast_non_dominated_sort(trials, directions) assert [{t.number for t in population} for population in population_per_rank] == [ {2}, {0, 3, 5}, {1}, {4}, ]
def test_crossover_prob() -> None: NSGAIISampler(crossover_prob=0.0) NSGAIISampler(crossover_prob=0.5) NSGAIISampler(crossover_prob=1.0) with pytest.raises(ValueError): NSGAIISampler(crossover_prob=-0.5) with pytest.raises(ValueError): NSGAIISampler(crossover_prob=1.1)
def test_swapping_prob() -> None: NSGAIISampler(swapping_prob=0.0) NSGAIISampler(swapping_prob=0.5) NSGAIISampler(swapping_prob=1.0) with pytest.raises(ValueError): NSGAIISampler(swapping_prob=-0.5) with pytest.raises(ValueError): NSGAIISampler(swapping_prob=1.1)
def test_fast_non_dominated_sort_no_constraints( direction1: StudyDirection, direction2: StudyDirection) -> None: sampler = NSGAIISampler() directions = [direction1, direction2] value_list = [10, 20, 20, 30, float("inf"), float("inf"), -float("inf")] values = [[v1, v2] for v1 in value_list for v2 in value_list] trials = [_create_frozen_trial(i, v) for i, v in enumerate(values)] population_per_rank = sampler._fast_non_dominated_sort( copy.copy(trials), directions) _assert_population_per_rank(trials, directions, population_per_rank)
def test_study_system_attr_for_population_cache() -> None: sampler = NSGAIISampler(population_size=10) study = optuna.create_study(directions=["minimize"], sampler=sampler) def get_cached_entries( study: optuna.study.Study, ) -> List[Tuple[int, List[int]]]: return [ v for k, v in study.system_attrs.items() if k.startswith( optuna.samplers._nsga2._POPULATION_CACHE_KEY_PREFIX) ] study.optimize(lambda t: [t.suggest_uniform("x", 0, 9)], n_trials=10) cached_entries = get_cached_entries(study) assert len(cached_entries) == 0 study.optimize(lambda t: [t.suggest_uniform("x", 0, 9)], n_trials=1) cached_entries = get_cached_entries(study) assert len(cached_entries) == 1 assert cached_entries[0][0] == 0 # Cached generation. assert len(cached_entries[0][1]) == 10 # Population size. study.optimize(lambda t: [t.suggest_uniform("x", 0, 9)], n_trials=10) cached_entries = get_cached_entries(study) assert len(cached_entries) == 1 assert cached_entries[0][0] == 1 # Cached generation. assert len(cached_entries[0][1]) == 10 # Population size.
def test_constraints_func_nan() -> None: n_trials = 4 n_objectives = 2 def constraints_func(_: FrozenTrial) -> Sequence[float]: return (float("nan"), ) with warnings.catch_warnings(): warnings.simplefilter("ignore", optuna.exceptions.ExperimentalWarning) sampler = NSGAIISampler(population_size=2, constraints_func=constraints_func) study = optuna.create_study(directions=["minimize"] * n_objectives, sampler=sampler) with pytest.raises(ValueError): study.optimize( lambda t: [t.suggest_float(f"x{i}", 0, 1) for i in range(n_objectives)], n_trials=n_trials, ) trials = study.get_trials() assert len( trials ) == 1 # The error stops optimization, but completed trials are recorded. assert all(0 <= x <= 1 for x in trials[0].params.values()) # The params are normal. assert trials[0].values == list( trials[0].params.values()) # The values are normal. assert trials[0].system_attrs[ _CONSTRAINTS_KEY] is None # None is set for constraints.
def test_constraints_func() -> None: n_trials = 4 n_objectives = 2 constraints_func_call_count = 0 def constraints_func(trial: FrozenTrial) -> Sequence[float]: nonlocal constraints_func_call_count constraints_func_call_count += 1 return (trial.number, ) with warnings.catch_warnings(): warnings.simplefilter("ignore", optuna.exceptions.ExperimentalWarning) sampler = NSGAIISampler(population_size=2, constraints_func=constraints_func) study = optuna.create_study(directions=["minimize"] * n_objectives, sampler=sampler) study.optimize( lambda t: [t.suggest_float(f"x{i}", 0, 1) for i in range(n_objectives)], n_trials=n_trials) assert len(study.trials) == n_trials assert constraints_func_call_count == n_trials for trial in study.trials: assert trial.system_attrs[_CONSTRAINTS_KEY] == (trial.number, )
def test_call_after_trial_of_random_sampler() -> None: sampler = NSGAIISampler() study = optuna.create_study(sampler=sampler) with patch.object( sampler._random_sampler, "after_trial", wraps=sampler._random_sampler.after_trial ) as mock_object: study.optimize(lambda _: 1.0, n_trials=1) assert mock_object.call_count == 1
def test_fast_non_dominated_sort_missing_constraint_values() -> None: with warnings.catch_warnings(): warnings.simplefilter("ignore", optuna.exceptions.ExperimentalWarning) sampler = NSGAIISampler(constraints_func=lambda _: [0]) # Single objective. directions = [StudyDirection.MINIMIZE] trials = [ _create_frozen_trial(0, [10]), _create_frozen_trial(1, [20]), _create_frozen_trial(2, [20], [0]), _create_frozen_trial(3, [20], [1]), _create_frozen_trial(4, [30], [-1]), ] with pytest.warns(UserWarning): population_per_rank = sampler._fast_non_dominated_sort( trials, directions) assert [{t.number for t in population} for population in population_per_rank] == [ {2}, {4}, {3}, {0}, {1}, ] # Two objectives. directions = [StudyDirection.MAXIMIZE, StudyDirection.MAXIMIZE] trials = [ _create_frozen_trial(0, [50, 30]), _create_frozen_trial(1, [30, 50]), _create_frozen_trial(2, [20, 20], [3, 3]), _create_frozen_trial(3, [30, 10], [0, -1]), _create_frozen_trial(4, [15, 15], [4, 4]), ] with pytest.warns(UserWarning): population_per_rank = sampler._fast_non_dominated_sort( trials, directions) assert [{t.number for t in population} for population in population_per_rank] == [ {3}, {2}, {4}, {0, 1}, ]
def test_crossover() -> None: NSGAIISampler() NSGAIISampler(crossover="uniform") NSGAIISampler(crossover="blxalpha") NSGAIISampler(crossover="sbx") NSGAIISampler(crossover="vsbx") NSGAIISampler(crossover="undx") NSGAIISampler(crossover="spx") with pytest.raises(ValueError): NSGAIISampler(crossover="no_imp_crossover")
def test_fast_non_dominated_sort_missing_constraint_values( values_and_constraints: List[Tuple[List[float], List[float]]]) -> None: with warnings.catch_warnings(): warnings.simplefilter("ignore", optuna.exceptions.ExperimentalWarning) sampler = NSGAIISampler(constraints_func=lambda _: [0]) values_dim = len(values_and_constraints[0][0]) for directions in itertools.product( [StudyDirection.MINIMIZE, StudyDirection.MAXIMIZE], repeat=values_dim): trials = [ _create_frozen_trial(i, v, c) for i, (v, c) in enumerate(values_and_constraints) ] with pytest.warns(UserWarning): population_per_rank = sampler._fast_non_dominated_sort( copy.copy(trials), list(directions)) _assert_population_per_rank(trials, list(directions), population_per_rank)
def test_crossover_invalid_population(crossover: BaseCrossover) -> None: n_objectives = 2 n_trials = 8 with pytest.raises(ValueError): sampler = NSGAIISampler(population_size=2, crossover=crossover) study = optuna.create_study(directions=["minimize"] * n_objectives, sampler=sampler) study.optimize( lambda t: [t.suggest_float(f"x{i}", 0, 1) for i in range(n_objectives)], n_trials=n_trials, )
def test_fast_non_dominated_sort_with_constraints() -> None: with warnings.catch_warnings(): warnings.simplefilter("ignore", optuna.exceptions.ExperimentalWarning) sampler = NSGAIISampler(constraints_func=lambda _: [0]) value_list = [10, 20, 20, 30, float("inf"), float("inf"), -float("inf")] values = [[v1, v2] for v1 in value_list for v2 in value_list] constraint_list = [-float("inf"), -2, 0, 1, 2, 3, float("inf")] constraints = [[c1, c2] for c1 in constraint_list for c2 in constraint_list] trials = [ _create_frozen_trial(i, v, c) for i, (v, c) in enumerate(itertools.product(values, constraints)) ] directions = [StudyDirection.MINIMIZE, StudyDirection.MAXIMIZE] population_per_rank = sampler._fast_non_dominated_sort( copy.copy(trials), directions) _assert_population_per_rank(trials, directions, population_per_rank)
def test_constraints_func_none() -> None: n_trials = 4 n_objectives = 2 sampler = NSGAIISampler(population_size=2) study = optuna.create_study(directions=["minimize"] * n_objectives, sampler=sampler) study.optimize( lambda t: [t.suggest_float(f"x{i}", 0, 1) for i in range(n_objectives)], n_trials=n_trials ) assert len(study.trials) == n_trials for trial in study.trials: assert _CONSTRAINTS_KEY not in trial.system_attrs
def test_mutation_prob() -> None: NSGAIISampler(mutation_prob=None) NSGAIISampler(mutation_prob=0.0) NSGAIISampler(mutation_prob=0.5) NSGAIISampler(mutation_prob=1.0) with pytest.raises(ValueError): NSGAIISampler(mutation_prob=-0.5) with pytest.raises(ValueError): NSGAIISampler(mutation_prob=1.1)
def test_crossover_invalid_population(crossover: BaseCrossover, population_size: int) -> None: with pytest.raises(ValueError): NSGAIISampler(population_size=population_size, crossover=crossover)
NSGAIISampler() NSGAIISampler(crossover="uniform") NSGAIISampler(crossover="blxalpha") NSGAIISampler(crossover="sbx") NSGAIISampler(crossover="vsbx") NSGAIISampler(crossover="undx") NSGAIISampler(crossover="spx") with pytest.raises(ValueError): NSGAIISampler(crossover="no_imp_crossover") parametrize_nsga2_sampler = pytest.mark.parametrize( "sampler_class", [ lambda: NSGAIISampler(population_size=2, crossover="uniform"), lambda: NSGAIISampler(population_size=2, crossover="blxalpha"), lambda: NSGAIISampler(population_size=2, crossover="sbx"), lambda: NSGAIISampler(population_size=2, crossover="vsbx"), lambda: NSGAIISampler(population_size=3, crossover="undx"), lambda: NSGAIISampler(population_size=3, crossover="spx"), ], ) @parametrize_nsga2_sampler @pytest.mark.parametrize("n_objectives", [1, 2, 3]) def test_crossover_objectives( n_objectives: int, sampler_class: Callable[[], BaseSampler]) -> None: n_trials = 8
def test_constraints_func_experimental_warning() -> None: with pytest.warns(optuna.exceptions.ExperimentalWarning): NSGAIISampler(constraints_func=lambda _: [0])
def test_call_after_trial_of_random_sampler() -> None: sampler = NSGAIISampler() study = optuna.create_study(sampler=sampler) with patch.object( sampler._random_sampler, "after_trial", wraps=sampler._random_sampler.after_trial) as mock_object: study.optimize(lambda _: 1.0, n_trials=1) assert mock_object.call_count == 1 parametrize_nsga2_sampler = pytest.mark.parametrize( "sampler_class", [ lambda: NSGAIISampler(population_size=2, crossover=UniformCrossover()), lambda: NSGAIISampler(population_size=2, crossover=BLXAlphaCrossover() ), lambda: NSGAIISampler(population_size=2, crossover=SBXCrossover()), lambda: NSGAIISampler(population_size=2, crossover=VSBXCrossover()), lambda: NSGAIISampler(population_size=3, crossover=UNDXCrossover()), lambda: NSGAIISampler(population_size=3, crossover=UNDXCrossover()), ], ) @parametrize_nsga2_sampler @pytest.mark.parametrize("n_objectives", [1, 2, 3]) def test_crossover_objectives( n_objectives: int, sampler_class: Callable[[], BaseSampler]) -> None: n_trials = 8