diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 8132ec16f..9ba74a928 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -20,14 +20,11 @@ Ex: ```python from bayes_opt import BayesianOptimization -black_box_function = lambda x, y: -x ** 2 - (y - 1) ** 2 + 1 +black_box_function = lambda x, y: -(x**2) - (y - 1) ** 2 + 1 -pbounds = {'x': (2, 4), 'y': (-3, 3)} +pbounds = {"x": (2, 4), "y": (-3, 3)} -optimizer = BayesianOptimization( - f=black_box_function, - pbounds=pbounds -) +optimizer = BayesianOptimization(f=black_box_function, pbounds=pbounds) optimizer.maximize() ``` diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml index 335ed9c1c..929fe91ff 100644 --- a/.github/workflows/build_docs.yml +++ b/.github/workflows/build_docs.yml @@ -21,7 +21,7 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v6 with: - python-version: "3.10" + python-version: "3.11" - name: Get tag uses: olegtarasov/get-tag@v2.1 - name: Install pandoc diff --git a/.github/workflows/format_and_lint.yml b/.github/workflows/format_and_lint.yml index 2f7665f1d..51a292561 100644 --- a/.github/workflows/format_and_lint.yml +++ b/.github/workflows/format_and_lint.yml @@ -16,7 +16,7 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v6 with: - python-version: "3.9" + python-version: "3.11" - name: Install dependencies run: uv sync --extra dev - name: Run pre-commit diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 4b3a061b5..41759f203 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.14t"] + python-version: ["3.11", "3.12", "3.13", "3.14", "3.14t"] numpy-major: ["1", "2"] exclude: - python-version: "3.13" # numpy<2 is not supported on 3.13+ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d86185dec..47397c58a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,4 +6,4 @@ repos: name: ruff-format args: [--check] repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.12.3 \ No newline at end of file + rev: v0.17.1 \ No newline at end of file diff --git a/README.md b/README.md index c485d11ca..17daa9a4b 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ def black_box_function(x, y): purposes think of the internals of this function, i.e.: the process which generates its output values, as unknown. """ - return -x ** 2 - (y - 1) ** 2 + 1 + return -(x**2) - (y - 1) ** 2 + 1 ``` ### 2. Getting Started @@ -82,13 +82,9 @@ All we need to get started is to instantiate a `BayesianOptimization` object spe from bayes_opt import BayesianOptimization # Bounded region of parameter space -pbounds = {'x': (2, 4), 'y': (-3, 3)} +pbounds = {"x": (2, 4), "y": (-3, 3)} -optimizer = BayesianOptimization( - f=black_box_function, - pbounds=pbounds, - random_state=1, -) +optimizer = BayesianOptimization(f=black_box_function, pbounds=pbounds, random_state=1) ``` The BayesianOptimization object will work out of the box without much tuning needed. The main method you should be aware of is `maximize`, which does exactly what you think it does. @@ -99,10 +95,7 @@ There are many parameters you can pass to maximize, nonetheless, the most import ```python -optimizer.maximize( - init_points=2, - n_iter=3, -) +optimizer.maximize(init_points=2, n_iter=3) ``` | iter | target | x | y | diff --git a/bayes_opt/acquisition.py b/bayes_opt/acquisition.py index ce31d27a1..4cd6828af 100644 --- a/bayes_opt/acquisition.py +++ b/bayes_opt/acquisition.py @@ -1024,7 +1024,7 @@ def _copy_target_space(self, target_space: TargetSpace) -> TargetSpace: A copy of the target space. """ keys = target_space.keys - pbounds = {key: bound for key, bound in zip(keys, target_space.bounds)} + pbounds = {key: bound for key, bound in zip(keys, target_space.bounds, strict=False)} target_space_copy = TargetSpace( None, pbounds=pbounds, allow_duplicate_points=target_space._allow_duplicate_points ) @@ -1351,7 +1351,7 @@ def set_acquisition_params(self, params: dict[str, Any]) -> None: params : dict Dictionary containing the acquisition function parameters. """ - for acq, acq_params in zip(self.base_acquisitions, params["base_acquisitions_params"]): + for acq, acq_params in zip(self.base_acquisitions, params["base_acquisitions_params"], strict=False): acq.set_acquisition_params(acq_params) self.gains = np.array(params["gains"]) diff --git a/bayes_opt/bayesian_optimization.py b/bayes_opt/bayesian_optimization.py index 737cb0e84..f3f151fd6 100644 --- a/bayes_opt/bayesian_optimization.py +++ b/bayes_opt/bayesian_optimization.py @@ -33,7 +33,6 @@ from bayes_opt.acquisition import AcquisitionFunction from bayes_opt.constraint import ConstraintModel - from bayes_opt.domain_reduction import DomainTransformer from bayes_opt.parameter import BoundsMapping, ParamsType Float = np.floating[Any] @@ -476,7 +475,8 @@ def _load_state_dict(self, state: dict[str, Any]) -> None: if state.get("transformed_bounds") and self._bounds_transformer: new_bounds = { - key: bounds for key, bounds in zip(self._space.keys, np.array(state["transformed_bounds"])) + key: bounds + for key, bounds in zip(self._space.keys, np.array(state["transformed_bounds"]), strict=False) } self._space.set_bounds(new_bounds) self._bounds_transformer.initialize(self._space) diff --git a/bayes_opt/domain_reduction.py b/bayes_opt/domain_reduction.py index 243a51bd5..634fffdb1 100644 --- a/bayes_opt/domain_reduction.py +++ b/bayes_opt/domain_reduction.py @@ -90,7 +90,7 @@ def initialize(self, target_space: TargetSpace) -> None: else: self.minimum_window_value = self.minimum_window_value - any_not_float = any([not isinstance(p, FloatParameter) for p in target_space._params_config.values()]) + any_not_float = any(not isinstance(p, FloatParameter) for p in target_space._params_config.values()) if any_not_float: msg = "Domain reduction is only supported for all-FloatParameter optimization." raise ValueError(msg) @@ -180,11 +180,8 @@ def _trim(self, new_bounds: NDArray[Float], global_bounds: NDArray[Float]) -> ND for i, pbounds in enumerate(new_bounds): # If the one of the bounds is outside the global bounds, reset the bound to the global bound # This is expected to happen when the window is near the global bounds, no warning is issued - if pbounds[0] < global_bounds[i, 0]: - pbounds[0] = global_bounds[i, 0] - - if pbounds[1] > global_bounds[i, 1]: - pbounds[1] = global_bounds[i, 1] + pbounds[0] = max(pbounds[0], global_bounds[i, 0]) + pbounds[1] = min(pbounds[1], global_bounds[i, 1]) # If a lower bound is greater than the associated global upper bound, # reset it to the global lower bound diff --git a/bayes_opt/parameter.py b/bayes_opt/parameter.py index 2eaa7a44e..18b9d34f6 100644 --- a/bayes_opt/parameter.py +++ b/bayes_opt/parameter.py @@ -3,10 +3,10 @@ from __future__ import annotations import abc -from collections.abc import Sequence +from collections.abc import Callable, Sequence from inspect import signature from numbers import Number -from typing import TYPE_CHECKING, Any, Callable, Union +from typing import TYPE_CHECKING, Any import numpy as np from sklearn.gaussian_process import kernels @@ -23,15 +23,15 @@ FloatBoundsWithoutType = tuple[float, float] FloatBoundsWithType = tuple[float, float, type[float]] - FloatBounds = Union[FloatBoundsWithoutType, FloatBoundsWithType] - IntBounds = tuple[Union[int, float], Union[int, float], type[int]] + FloatBounds = FloatBoundsWithoutType | FloatBoundsWithType + IntBounds = tuple[int | float, int | float, type[int]] CategoricalBounds = Sequence[Any] - Bounds = Union[FloatBounds, IntBounds, CategoricalBounds] + Bounds = FloatBounds | IntBounds | CategoricalBounds BoundsMapping = Mapping[str, Bounds] # FIXME: categorical parameters can be of any type. # This will make static type checking for parameters difficult. - ParamsType = Union[Mapping[str, Any], Sequence[Any], NDArray[Float]] + ParamsType = Mapping[str, Any] | Sequence[Any] | NDArray[Float] def is_numeric(value: Any) -> bool: @@ -280,7 +280,7 @@ def random_sample( random_state = ensure_rng(random_state) return random_state.randint(self.bounds[0], self.bounds[1] + 1, n_samples).astype(float) - def to_float(self, value: int | float) -> float: + def to_float(self, value: float) -> float: """Convert a parameter value to a float. Parameters @@ -290,7 +290,7 @@ def to_float(self, value: int | float) -> float: """ return float(value) - def to_param(self, value: int | float | NDArray[Int] | NDArray[Float]) -> int: + def to_param(self, value: float | NDArray[Int] | NDArray[Float]) -> int: """Convert a float value to a parameter. Parameters diff --git a/bayes_opt/target_space.py b/bayes_opt/target_space.py index 33bf0687b..cb3081418 100644 --- a/bayes_opt/target_space.py +++ b/bayes_opt/target_space.py @@ -670,7 +670,10 @@ def res(self) -> list[dict[str, Any]]: if self._constraint is None: params = [self.array_to_params(p) for p in self.params] - return [{"target": target, "params": param} for target, param in zip(self.target, params)] + return [ + {"target": target, "params": param} + for target, param in zip(self.target, params, strict=False) + ] params = [self.array_to_params(p) for p in self.params] @@ -681,6 +684,7 @@ def res(self) -> list[dict[str, Any]]: self._constraint_values, params, self._constraint.allowed(self._constraint_values), + strict=False, ) ] diff --git a/pyproject.toml b/pyproject.toml index 38115e192..34273510d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,13 +5,11 @@ description = "Bayesian Optimization package" authors = [{ name = "Fernando Nogueira", email = "fmfnogueira@gmail.com" }] license = { file = "LICENSE" } readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.11" classifiers = [ "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", @@ -43,12 +41,10 @@ dev = [ "pre-commit>=3.7.1", "pytest>=8.0.0", "pytest-cov>=4.1.0", - "ruff>=0.15.6", + "ruff==0.17.1", "sphinx-immaterial>=0.12.0", - "sphinx>=7.0.0; python_version<'3.10'", - "sphinx>=8.0.0; python_version>='3.10'", - "sphinx-autodoc-typehints>=2.3.0; python_version<'3.10'", - "sphinx-autodoc-typehints>=2.4.0; python_version>='3.10'", + "sphinx>=8.0.0", + "sphinx-autodoc-typehints>=2.4.0", ] [project.urls] Repository = "https://github.com/bayesian-optimization/BayesianOptimization" diff --git a/ruff.toml b/ruff.toml index 9c08e69ce..c069649e4 100644 --- a/ruff.toml +++ b/ruff.toml @@ -1,6 +1,6 @@ line-length = 110 unsafe-fixes = false -target-version = "py39" +target-version = "py311" extend = "./pyproject.toml" exclude = [ # docs diff --git a/tests/test_bayesian_optimization.py b/tests/test_bayesian_optimization.py index 2788e192d..29b7a79ce 100644 --- a/tests/test_bayesian_optimization.py +++ b/tests/test_bayesian_optimization.py @@ -329,13 +329,8 @@ def test_duplicate_points(): # register once (should work) optimizer.register(params=next_point_to_probe, target=target) # register twice (should throw error) - try: + with pytest.raises(NotUniqueError): optimizer.register(params=next_point_to_probe, target=target) - duplicate_point_error = None # should be overwritten below - except Exception as e: - duplicate_point_error = e - - assert isinstance(duplicate_point_error, NotUniqueError) # OK, now let's test that it DOESNT fail when allow_duplicate_points=True optimizer = BayesianOptimization( @@ -384,7 +379,7 @@ def test_load_state_rejects_incompatible_keys(tmp_path, saved_points, existing_p restored.probe(dict.fromkeys(pbounds, 0), lazy=False) before = restored.res - with pytest.raises(ValueError, match="parameter keys.*same order"): + with pytest.raises(ValueError, match=r"parameter keys.*same order"): restored.load_state(state_path) assert restored.res == before diff --git a/tests/test_notebooks_run.py b/tests/test_notebooks_run.py index f1fbf6eaa..c42509da7 100644 --- a/tests/test_notebooks_run.py +++ b/tests/test_notebooks_run.py @@ -19,7 +19,7 @@ @pytest.mark.parametrize("notebook", sorted(this_file_loc.with_name("examples").glob("*.ipynb"))) def test_all_notebooks_run(notebook: Path): as_string = str(notebook) - if any([nb in as_string for nb in _NOTEBOOKS_NOT_TO_RUN]): + if any(nb in as_string for nb in _NOTEBOOKS_NOT_TO_RUN): pytest.skip(f"skipping [{notebook!s}]") print(f"running: {notebook}...")