diff --git a/package/benchmarks/wfg/LICENSE b/package/benchmarks/wfg/LICENSE new file mode 100644 index 00000000..380b0c20 --- /dev/null +++ b/package/benchmarks/wfg/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Preferred Networks, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/package/benchmarks/wfg/README.md b/package/benchmarks/wfg/README.md new file mode 100644 index 00000000..8967d3b7 --- /dev/null +++ b/package/benchmarks/wfg/README.md @@ -0,0 +1,83 @@ +--- +author: Optuna team +title: The WFG Problem Collection +description: The WFG Problem Collection (Huband et al. 2006) is a widely-used benchmark suite for multi-objective optimization. This package is a wrapper of the optproblems library. +tags: [benchmark, continuous optimization, multi-objective, WFG, optproblems] +optuna_versions: [4.1.0] +license: MIT License +--- + +## Abstract + +This package provides a wrapper of the [optproblems](https://www.simonwessing.de/optproblems/doc/index.html) library's WFG test suite, which consists of 9 kinds of continuous problems with variadic objectives and variables. For the details of the benchmark problems, please take a look at the original paper (Huband et al., 2006) in the reference section. + +## APIs + +### class `Problem(function_id: int, n_objectives: int, dimension: int, k: int | None = None, **kwargs: Any)` + +- `function_id`: Function ID of the WFG problem in \[1, 9\]. +- `n_objectives`: Number of objectives. +- `dimension`: Number of variables. +- `k`: Number of position parameters. It must hold k \< dimension and k must be a multiple of n_objectives - 1. Huband et al. recommend k = 4 for two objectives and k = 2 * (m - 1) for m objectives. If `None` is set, this recommendation is used. +- `kwargs`: Arbitrary keyword arguments, please refer to [the optproblems documentation](https://www.simonwessing.de/optproblems/doc/wfg.html) for more details. + +#### Methods and Properties + +- `search_space`: Return the search space. + - Returns: `dict[str, optuna.distributions.BaseDistribution]` +- `directions`: Return the optimization directions. + - Returns: `list[optuna.study.StudyDirection]` +- `__call__(trial: optuna.Trial)`: Evaluate the objective functions and return the objective values. + - Args: + - `trial`: Optuna trial object. + - Returns: `float` +- `evaluate(params: dict[str, float])`: Evaluate the objective functions and return the objective values. + - Args: + - `params`: Decision variable like `{"x0": x1_value, "x1": x1_value, ..., "xn": xn_value}`. The number of parameters must be equal to `dimension`. + - Returns: `float` + +The properties defined by [optproblems](https://www.simonwessing.de/optproblems/doc/wfg.html) are also available such as `get_optimal_solutions`. + +## Installation + +Please install the [optproblems](https://pypi.org/project/optproblems/) package. + +```shell +pip install -U optproblems +``` + +## Example + +```python +import optuna +import optunahub + + +wfg = optunahub.load_module("benchmarks/wfg") +wfg4 = wfg.Problem(function_id=4, n_objectives=2, dimension=3, k=1) + +study_pareto = optuna.create_study( + study_name="ParetoFront", directions=wfg4.directions +) +for x in wfg4.get_optimal_solutions(1000): # Generate 1000 Pareto optimal solutions + study_pareto.enqueue_trial(params={ + f"x{i}": x.phenome[i] for i in range(3) + }) +study_pareto.optimize(wfg4, n_trials=1000) + +study_tpe = optuna.create_study( + study_name="TPESampler", + sampler=optuna.samplers.TPESampler(seed=42), directions=wfg4.directions +) +study_tpe.optimize(wfg4, n_trials=1000) + +optunahub.load_module("visualization/plot_pareto_front_multi").plot_pareto_front( + [study_pareto, study_tpe] +).show() +``` + +![Result](images/wfg4.png) + +## Reference + +Huband, S., Hingston, P., Barone, L., & While, L. (2006). [A review of multiobjective test problems and a scalable test problem toolkit](https://doi.org/10.1109/TEVC.2005.861417). IEEE Transactions on Evolutionary Computation, 10(5), 477-506. diff --git a/package/benchmarks/wfg/__init__.py b/package/benchmarks/wfg/__init__.py new file mode 100644 index 00000000..590f7247 --- /dev/null +++ b/package/benchmarks/wfg/__init__.py @@ -0,0 +1,4 @@ +from ._wfg import Problem + + +__all__ = ["Problem"] diff --git a/package/benchmarks/wfg/_wfg.py b/package/benchmarks/wfg/_wfg.py new file mode 100644 index 00000000..3cb168a1 --- /dev/null +++ b/package/benchmarks/wfg/_wfg.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +from typing import Any + +import optproblems.wfg +import optuna +import optunahub + + +class Problem(optunahub.benchmarks.BaseProblem): + """Wrapper class for the WFG test suite of optproblems.""" + + def __init__( + self, + function_id: int, + n_objectives: int, + dimension: int, + k: int | None = None, + **kwargs: Any, + ) -> None: + """Initialize the problem. + Args: + function_id: Function ID of the WFG problem in [1, 9]. + n_objectives: Number of objectives. + dimension: Number of variables. + k: Number of position parameters. It must hold k < dimension and k must be a multiple of n_objectives - 1. Huband et al. recommend k = 4 for two objectives and k = 2 * (m - 1) for m objectives. + kwargs: Arbitrary keyword arguments, please refer to the optproblems documentation for more details. + + Please refer to the optproblems documentation for the details of the available properties. + https://www.simonwessing.de/optproblems/doc/wfg.html + """ + assert 1 <= function_id <= 9, "function_id must be in [1, 9]" + + if k is None: + k = 2 * (n_objectives - 1) if n_objectives > 2 else 4 + + self._problem = optproblems.wfg.WFG(n_objectives, dimension, k, **kwargs)[function_id - 1] + + self._search_space = { + f"x{i}": optuna.distributions.FloatDistribution( + self._problem.min_bounds[i], self._problem.max_bounds[i] + ) + for i in range(dimension) + } + + @property + def search_space(self) -> dict[str, optuna.distributions.BaseDistribution]: + """Return the search space.""" + return self._search_space.copy() + + @property + def directions(self) -> list[optuna.study.StudyDirection]: + """Return the optimization directions.""" + direction = ( + optuna.study.StudyDirection.MAXIMIZE + if self._problem.do_maximize + else optuna.study.StudyDirection.MINIMIZE + ) + return [direction] * self._problem.num_objectives + + def evaluate(self, params: dict[str, float]) -> float: + """Evaluate the objective function. + Args: + params: + Decision variable, e.g., evaluate({"x0": 1.0, "x1": 2.0}). + The number of parameters must be equal to the dimension of the problem. + Returns: + The objective value. + + """ + return self._problem.objective_function([params[name] for name in self._search_space]) + + def __getattr__(self, name: str) -> Any: + return getattr(self._problem, name) diff --git a/package/benchmarks/wfg/images/wfg4.png b/package/benchmarks/wfg/images/wfg4.png new file mode 100644 index 00000000..fa3b2079 Binary files /dev/null and b/package/benchmarks/wfg/images/wfg4.png differ diff --git a/package/benchmarks/wfg/requirements.txt b/package/benchmarks/wfg/requirements.txt new file mode 100644 index 00000000..1c7349a1 --- /dev/null +++ b/package/benchmarks/wfg/requirements.txt @@ -0,0 +1 @@ +optproblems \ No newline at end of file