def test_dominates_complete_vs_incomplete(t1_state: TrialState) -> None: directions = [StudyDirection.MINIMIZE, StudyDirection.MAXIMIZE] t1 = create_trial(values=[0, 2], state=t1_state) t2 = create_trial(values=[1, 1], state=TrialState.COMPLETE) assert _dominates(t2, t1, list(directions)) assert not _dominates(t1, t2, list(directions))
def test_dominates_1d_not_equal(v1: float, v2: float) -> None: t1 = create_trial(values=[v1]) t2 = create_trial(values=[v2]) assert _dominates(t1, t2, [StudyDirection.MINIMIZE]) assert not _dominates(t2, t1, [StudyDirection.MINIMIZE]) assert _dominates(t2, t1, [StudyDirection.MAXIMIZE]) assert not _dominates(t1, t2, [StudyDirection.MAXIMIZE])
def test_dominates_invalid() -> None: directions = [StudyDirection.MINIMIZE, StudyDirection.MAXIMIZE] # The numbers of objectives for `t1` and `t2` don't match. t1 = create_trial(values=[1]) # One objective. t2 = create_trial(values=[1, 2]) # Two objectives. with pytest.raises(ValueError): _dominates(t1, t2, directions) # The numbers of objectives and directions don't match. t1 = create_trial(values=[1]) # One objective. t2 = create_trial(values=[1]) # One objective. with pytest.raises(ValueError): _dominates(t1, t2, directions)
def test_dominates_2d() -> None: directions = [StudyDirection.MINIMIZE, StudyDirection.MAXIMIZE] # Check all pairs of trials consisting of these values, i.e., # [-inf, -inf], [-inf, -1], [-inf, 1], [-inf, inf], [-1, -inf], ... # These values should be specified in ascending order. vals = [-float("inf"), -1, 1, float("inf")] # The following table illustrates an example of dominance relations. # "d" cells in the table dominates the "t" cell in (MINIMIZE, MAXIMIZE) setting. # # value1 # ╔═════╤═════╤═════╤═════╤═════╗ # ║ │ -∞ │ -1 │ 1 │ ∞ ║ # ╟─────┼─────┼─────┼─────┼─────╢ # ║ -∞ │ │ │ d │ d ║ # ╟─────┼─────┼─────┼─────┼─────╢ # ║ -1 │ │ │ d │ d ║ # value0 ╟─────┼─────┼─────┼─────┼─────╢ # ║ 1 │ │ │ t │ d ║ # ╟─────┼─────┼─────┼─────┼─────╢ # ║ ∞ │ │ │ │ ║ # ╚═════╧═════╧═════╧═════╧═════╝ # # In the following code, we check that for each position of "t" cell, the relation # above holds. # Generate the set of all possible indices. all_indices = set( (i, j) for i in range(len(vals)) for j in range(len(vals))) for (t_i, t_j) in all_indices: # Generate the set of all indices that dominates the current index. dominating_indices = set((d_i, d_j) for d_i in range(t_i + 1) for d_j in range(t_j, len(vals))) dominating_indices -= {(t_i, t_j)} for (d_i, d_j) in dominating_indices: trial1 = create_trial(values=[vals[t_i], vals[t_j]]) trial2 = create_trial(values=[vals[d_i], vals[d_j]]) assert _dominates(trial2, trial1, directions) for (d_i, d_j) in all_indices - dominating_indices: trial1 = create_trial(values=[vals[t_i], vals[t_j]]) trial2 = create_trial(values=[vals[d_i], vals[d_j]]) assert not _dominates(trial2, trial1, directions)
def test_constrained_dominates_feasible_vs_feasible( direction1: StudyDirection, direction2: StudyDirection, constraints_list: List[List[float]]) -> None: directions = [direction1, direction2] # Check all pairs of trials consisting of these values, i.e., # [-inf, -inf], [-inf, -1], [-inf, 1], [-inf, inf], [-1, -inf], ... values_list = [ [x, y] for x in [-float("inf"), -1, 1, float("inf")] for y in [-float("inf"), -1, 1, float("inf")] ] values_constraints_list = [(vs, cs) for vs in values_list for cs in constraints_list] # The results of _constrained_dominates match _dominates in all feasible cases. for (values1, constraints1) in values_constraints_list: for (values2, constraints2) in values_constraints_list: t1 = _create_frozen_trial(0, values1, constraints1) t2 = _create_frozen_trial(1, values2, constraints2) assert _constrained_dominates(t1, t2, directions) == _dominates( t1, t2, directions)
def _constrained_dominates(trial0: FrozenTrial, trial1: FrozenTrial, directions: Sequence[StudyDirection]) -> bool: """Checks constrained-domination. A trial x is said to constrained-dominate a trial y, if any of the following conditions is true: 1) Trial x is feasible and trial y is not. 2) Trial x and y are both infeasible, but solution x has a smaller overall constraint violation. 3) Trial x and y are feasible and trial x dominates trial y. """ constraints0 = trial0.system_attrs.get(_CONSTRAINTS_KEY) constraints1 = trial1.system_attrs.get(_CONSTRAINTS_KEY) if constraints0 is None: warnings.warn(f"Trial {trial0.number} does not have constraint values." " It will be dominated by the other trials.") if constraints1 is None: warnings.warn(f"Trial {trial1.number} does not have constraint values." " It will be dominated by the other trials.") if constraints0 is None and constraints1 is None: # Neither Trial x nor y has constraints values return _dominates(trial0, trial1, directions) if constraints0 is not None and constraints1 is None: # Trial x has constraint values, but y doesn't. return True if constraints0 is None and constraints1 is not None: # If Trial y has constraint values, but x doesn't. return False assert isinstance(constraints0, (list, tuple)) assert isinstance(constraints1, (list, tuple)) if len(constraints0) != len(constraints1): raise ValueError( "Trials with different numbers of constraints cannot be compared.") if trial0.state != TrialState.COMPLETE: return False if trial1.state != TrialState.COMPLETE: return True satisfy_constraints0 = all(v <= 0 for v in constraints0) satisfy_constraints1 = all(v <= 0 for v in constraints1) if satisfy_constraints0 and satisfy_constraints1: # Both trials satisfy the constraints. return _dominates(trial0, trial1, directions) if satisfy_constraints0: # trial0 satisfies the constraints, but trial1 violates them. return True if satisfy_constraints1: # trial1 satisfies the constraints, but trial0 violates them. return False # Both trials violate the constraints. violation0 = sum(v for v in constraints0 if v > 0) violation1 = sum(v for v in constraints1 if v > 0) return violation0 < violation1
def test_dominates_1d_equal(v: float, direction: StudyDirection) -> None: assert not _dominates(create_trial(values=[v]), create_trial(values=[v]), [direction])