def test_constraints_func_experimental_warning() -> None: study = optuna.create_study(directions=["minimize", "minimize"]) with pytest.warns(optuna.exceptions.ExperimentalWarning): _get_pareto_front_info( study=study, constraints_func=lambda _: [1.0], )
def test_get_pareto_front_info_unsupported_dimensions( n_dims: int, include_dominated_trials: bool, constraints_func: Optional[Callable[[FrozenTrial], Sequence[float]]], ) -> None: study = optuna.create_study(directions=["minimize"] * n_dims) with pytest.raises(ValueError): _get_pareto_front_info( study=study, include_dominated_trials=include_dominated_trials, constraints_func=constraints_func, )
def test_get_pareto_front_info_using_axis_order_and_targets( include_dominated_trials: bool, constraints_func: Optional[Callable[[FrozenTrial], Sequence[float]]], ) -> None: study = optuna.create_study(directions=["minimize", "minimize", "minimize"]) with pytest.raises(ValueError): _get_pareto_front_info( study=study, axis_order=[0, 1, 2], targets=lambda t: (t.values[0], t.values[1], t.values[2]), include_dominated_trials=include_dominated_trials, constraints_func=constraints_func, )
def test_get_pareto_front_info_invalid_target_values( include_dominated_trials: bool, constraints_func: Optional[Callable[[FrozenTrial], Sequence[float]]], ) -> None: study = optuna.create_study(directions=["minimize", "minimize"]) study.optimize(lambda _: [0, 0], n_trials=3) with pytest.raises(ValueError): _get_pareto_front_info( study=study, targets=lambda t: t.values[0], include_dominated_trials=include_dominated_trials, constraints_func=constraints_func, )
def test_get_pareto_front_info_invalid_axis_order( axis_order: List[int], include_dominated_trials: bool, constraints_func: Optional[Callable[[FrozenTrial], Sequence[float]]], ) -> None: study = optuna.create_study(directions=["minimize", "minimize"]) with pytest.raises(ValueError): _get_pareto_front_info( study=study, include_dominated_trials=include_dominated_trials, axis_order=axis_order, constraints_func=constraints_func, )
def test_get_pareto_front_info_unconstrained( include_dominated_trials: bool, axis_order: Optional[List[int]], targets: Optional[Callable[[FrozenTrial], Sequence[float]]], target_names: Optional[List[str]], ) -> None: if axis_order is not None and targets is not None: pytest.skip("skip using both axis_order and targets") study = create_study_2d() trials = study.get_trials(deepcopy=False) info = _get_pareto_front_info( study=study, include_dominated_trials=include_dominated_trials, axis_order=axis_order, targets=targets, target_names=target_names, ) assert info == _ParetoFrontInfo( n_targets=2, target_names=target_names or ["Objective 0", "Objective 1"], best_trials_with_values=[(trials[2], [0, 2]), (trials[3], [1, 0])], non_best_trials_with_values=[(trials[0], [1, 2]), (trials[1], [1, 1])] if include_dominated_trials else [], infeasible_trials_with_values=[], axis_order=axis_order or [0, 1], include_dominated_trials=include_dominated_trials, has_constraints_func=False, )
def test_get_pareto_front_info_infer_n_targets() -> None: study = optuna.create_study(directions=["minimize", "minimize"]) assert _get_pareto_front_info(study).n_targets == 2 study = optuna.create_study(directions=["minimize"] * 5) assert ( _get_pareto_front_info( study, target_names=["target1", "target2"], targets=lambda _: [0.0, 1.0] ).n_targets == 2 ) study = optuna.create_study(directions=["minimize"] * 5) study.optimize(lambda _: [0] * 5, n_trials=1) assert _get_pareto_front_info(study, targets=lambda _: [0.0, 1.0]).n_targets == 2 study = optuna.create_study(directions=["minimize"] * 2) with pytest.raises(ValueError): _get_pareto_front_info(study, targets=lambda _: [0.0, 1.0])
def plot_pareto_front( study: Study, *, target_names: Optional[List[str]] = None, include_dominated_trials: bool = True, axis_order: Optional[List[int]] = None, targets: Optional[Callable[[FrozenTrial], Sequence[float]]] = None, ) -> "Axes": """Plot the Pareto front of a study. .. seealso:: Please refer to :func:`optuna.visualization.plot_pareto_front` for an example. Example: The following code snippet shows how to plot the Pareto front of a study. .. plot:: import optuna def objective(trial): x = trial.suggest_float("x", 0, 5) y = trial.suggest_float("y", 0, 3) v0 = 4 * x ** 2 + 4 * y ** 2 v1 = (x - 5) ** 2 + (y - 5) ** 2 return v0, v1 study = optuna.create_study(directions=["minimize", "minimize"]) study.optimize(objective, n_trials=50) optuna.visualization.matplotlib.plot_pareto_front(study) Args: study: A :class:`~optuna.study.Study` object whose trials are plotted for their objective values. ``study.n_objectives`` must be eigher 2 or 3. target_names: Objective name list used as the axis titles. If :obj:`None` is specified, "Objective {objective_index}" is used instead. If ``targets`` is specified for a study that does not contain any completed trial, ``target_name`` must be specified. include_dominated_trials: A flag to include all dominated trial's objective values. axis_order: A list of indices indicating the axis order. If :obj:`None` is specified, default order is used. ``axis_order`` and ``targets`` cannot be used at the same time. .. warning:: Deprecated in v3.0.0. This feature will be removed in the future. The removal of this feature is currently scheduled for v5.0.0, but this schedule is subject to change. See https://github.com/optuna/optuna/releases/tag/v3.0.0. targets: A function that returns a tuple of target values to display. The argument to this function is :class:`~optuna.trial.FrozenTrial`. ``targets`` must be :obj:`None` or return 2 or 3 values. ``axis_order`` and ``targets`` cannot be used at the same time. If your study has more than 4 objectives, ``targets`` must be specified. .. note:: Added in v3.0.0 as an experimental feature. The interface may change in newer versions without prior notice. See https://github.com/optuna/optuna/releases/tag/v3.0.0. Returns: A :class:`matplotlib.axes.Axes` object. """ _imports.check() info = _get_pareto_front_info(study, target_names, include_dominated_trials, axis_order, None, targets) if info.n_targets == 2: return _get_pareto_front_2d(info) elif info.n_targets == 3: return _get_pareto_front_3d(info) else: raise ValueError( "`plot_pareto_front` function only supports 2 or 3 targets." " you used {} targets now.".format(info.n_targets))
def plot_pareto_front( study: Study, *, target_names: Optional[List[str]] = None, include_dominated_trials: bool = True, axis_order: Optional[List[int]] = None, constraints_func: Optional[Callable[[FrozenTrial], Sequence[float]]] = None, targets: Optional[Callable[[FrozenTrial], Sequence[float]]] = None, ) -> "Axes": """Plot the Pareto front of a study. .. seealso:: Please refer to :func:`optuna.visualization.plot_pareto_front` for an example. Example: The following code snippet shows how to plot the Pareto front of a study. .. plot:: import optuna def objective(trial): x = trial.suggest_float("x", 0, 5) y = trial.suggest_float("y", 0, 3) v0 = 4 * x ** 2 + 4 * y ** 2 v1 = (x - 5) ** 2 + (y - 5) ** 2 return v0, v1 study = optuna.create_study(directions=["minimize", "minimize"]) study.optimize(objective, n_trials=50) optuna.visualization.matplotlib.plot_pareto_front(study) Args: study: A :class:`~optuna.study.Study` object whose trials are plotted for their objective values. ``study.n_objectives`` must be either 2 or 3 when ``targets`` is :obj:`None`. target_names: Objective name list used as the axis titles. If :obj:`None` is specified, "Objective {objective_index}" is used instead. If ``targets`` is specified for a study that does not contain any completed trial, ``target_name`` must be specified. include_dominated_trials: A flag to include all dominated trial's objective values. axis_order: A list of indices indicating the axis order. If :obj:`None` is specified, default order is used. ``axis_order`` and ``targets`` cannot be used at the same time. .. warning:: Deprecated in v3.0.0. This feature will be removed in the future. The removal of this feature is currently scheduled for v5.0.0, but this schedule is subject to change. See https://github.com/optuna/optuna/releases/tag/v3.0.0. constraints_func: An optional function that computes the objective constraints. It must take a :class:`~optuna.trial.FrozenTrial` and return the constraints. The return value must be a sequence of :obj:`float` s. A value strictly larger than 0 means that a constraint is violated. A value equal to or smaller than 0 is considered feasible. This specification is the same as in, for example, :class:`~optuna.integration.NSGAIISampler`. If given, trials are classified into three categories: feasible and best, feasible but non-best, and infeasible. Categories are shown in different colors. Here, whether a trial is best (on Pareto front) or not is determined ignoring all infeasible trials. targets: A function that returns a tuple of target values to display. The argument to this function is :class:`~optuna.trial.FrozenTrial`. ``targets`` must be :obj:`None` or return 2 or 3 values. ``axis_order`` and ``targets`` cannot be used at the same time. If ``study.n_objectives`` is neither 2 nor 3, ``targets`` must be specified. .. note:: Added in v3.0.0 as an experimental feature. The interface may change in newer versions without prior notice. See https://github.com/optuna/optuna/releases/tag/v3.0.0. Returns: A :class:`matplotlib.axes.Axes` object. """ _imports.check() info = _get_pareto_front_info(study, target_names, include_dominated_trials, axis_order, constraints_func, targets) if info.n_targets == 2: return _get_pareto_front_2d(info) elif info.n_targets == 3: return _get_pareto_front_3d(info) else: assert False, "Must not reach here"
def test_get_pareto_front_info_invalid_number_of_target_names() -> None: study = optuna.create_study(directions=["minimize", "minimize"]) with pytest.raises(ValueError): _get_pareto_front_info(study=study, target_names=["Foo"])
study=study, constraints_func=lambda _: [1.0], ) @pytest.mark.parametrize( "plotter", [ optuna.visualization._pareto_front._get_pareto_front_plot, optuna.visualization.matplotlib._pareto_front._get_pareto_front_plot, ], ) @pytest.mark.parametrize( "info_template", [ _get_pareto_front_info(create_study_2d()), _get_pareto_front_info(create_study_3d()), ], ) @pytest.mark.parametrize("include_dominated_trials", [True, False]) @pytest.mark.parametrize("has_constraints_func", [True, False]) def test_get_pareto_front_plot( plotter: Callable[[_ParetoFrontInfo], Any], info_template: _ParetoFrontInfo, include_dominated_trials: bool, has_constraints_func: bool, ) -> None: info = info_template if not include_dominated_trials: info = info._replace(include_dominated_trials=False, non_best_trials_with_values=[])