From 3ae9b9731233477bcc39ea25196bd06e165f8f3a Mon Sep 17 00:00:00 2001 From: Direk Kakkar Date: Mon, 23 Mar 2026 23:47:16 +0530 Subject: [PATCH 01/12] started the `lipo.py` --- src/hyperactive/opt/lipo.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/hyperactive/opt/lipo.py diff --git a/src/hyperactive/opt/lipo.py b/src/hyperactive/opt/lipo.py new file mode 100644 index 00000000..e69de29b From 7c90f99116c91e728528117d0efd20b8139d03f7 Mon Sep 17 00:00:00 2001 From: Direk Kakkar Date: Mon, 23 Mar 2026 23:48:02 +0530 Subject: [PATCH 02/12] imports --- src/hyperactive/opt/lipo.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hyperactive/opt/lipo.py b/src/hyperactive/opt/lipo.py index e69de29b..4c130876 100644 --- a/src/hyperactive/opt/lipo.py +++ b/src/hyperactive/opt/lipo.py @@ -0,0 +1,2 @@ +import numpy as np +from lipo import GlobalOptimizer From 1d1087f35918eda603ceade8ffca07121dd8a069 Mon Sep 17 00:00:00 2001 From: Direk Kakkar Date: Mon, 23 Mar 2026 23:49:06 +0530 Subject: [PATCH 03/12] `__init__()` --- src/hyperactive/opt/lipo.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/hyperactive/opt/lipo.py b/src/hyperactive/opt/lipo.py index 4c130876..1a900e89 100644 --- a/src/hyperactive/opt/lipo.py +++ b/src/hyperactive/opt/lipo.py @@ -1,2 +1,10 @@ import numpy as np from lipo import GlobalOptimizer + +class LIPOOptimizer: + """Parameter-free global optimizer via the lipo package.""" + def __init__(self, search_space, n_iter, experiment, maximize=True): + self.search_space = search_space + self.n_iter = n_iter + self.experiment = experiment + self.maximize = maximize \ No newline at end of file From f09a824b8a2179895b76330938d1578ac0874c49 Mon Sep 17 00:00:00 2001 From: Direk Kakkar Date: Mon, 23 Mar 2026 23:50:02 +0530 Subject: [PATCH 04/12] `_parse_search_spaces()` --- src/hyperactive/opt/lipo.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/hyperactive/opt/lipo.py b/src/hyperactive/opt/lipo.py index 1a900e89..d14e91f9 100644 --- a/src/hyperactive/opt/lipo.py +++ b/src/hyperactive/opt/lipo.py @@ -3,8 +3,24 @@ class LIPOOptimizer: """Parameter-free global optimizer via the lipo package.""" + def __init__(self, search_space, n_iter, experiment, maximize=True): self.search_space = search_space self.n_iter = n_iter self.experiment = experiment - self.maximize = maximize \ No newline at end of file + self.maximize = maximize + + def _parse_search_space(self): + lower, upper, cats = {}, {}, {} + for key, values in self.search_space.items(): + # Categorical: list of strings + if isinstance(values, list) and isinstance(values[0], str): + cats[key] = values + else: + arr = np.array(values) + lower[key] = float(arr.min()) + upper[key] = float(arr.max()) + # Store grid so we can snap results back later + self._grids = getattr(self, "_grids", {}) + self._grids[key] = arr + return lower, upper, cats \ No newline at end of file From b679f04362a4efdc9aa3769cca2b3a42aabace64 Mon Sep 17 00:00:00 2001 From: Direk Kakkar Date: Mon, 23 Mar 2026 23:50:38 +0530 Subject: [PATCH 05/12] `_snap_to_grid()` --- src/hyperactive/opt/lipo.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/hyperactive/opt/lipo.py b/src/hyperactive/opt/lipo.py index d14e91f9..82f6c90b 100644 --- a/src/hyperactive/opt/lipo.py +++ b/src/hyperactive/opt/lipo.py @@ -23,4 +23,15 @@ def _parse_search_space(self): # Store grid so we can snap results back later self._grids = getattr(self, "_grids", {}) self._grids[key] = arr - return lower, upper, cats \ No newline at end of file + return lower, upper, cats + + def _snap_to_grid(self, params): + """Snap lipo's continuous output to nearest valid grid point.""" + snapped = {} + for key, val in params.items(): + if key in getattr(self, "_grids", {}): + grid = self._grids[key] + snapped[key] = grid[np.argmin(np.abs(grid - val))] + else: + snapped[key] = val # categorical, pass through + return snapped From 8e5aef64388e57f8bce512ca7b4e58411ae7485f Mon Sep 17 00:00:00 2001 From: Direk Kakkar Date: Mon, 23 Mar 2026 23:51:07 +0530 Subject: [PATCH 06/12] `solve()` --- src/hyperactive/opt/lipo.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/hyperactive/opt/lipo.py b/src/hyperactive/opt/lipo.py index 82f6c90b..d8297519 100644 --- a/src/hyperactive/opt/lipo.py +++ b/src/hyperactive/opt/lipo.py @@ -35,3 +35,19 @@ def _snap_to_grid(self, params): else: snapped[key] = val # categorical, pass through return snapped + + def solve(self): + lower, upper, cats = self._parse_search_space() + + def wrapped(**kwargs): + return self.experiment(self._snap_to_grid(kwargs)) + + opt = GlobalOptimizer( + wrapped, + lower_bounds=lower, + upper_bounds=upper, + categories=cats, + maximize=self.maximize, + ) + opt.run(self.n_iter) + return self._snap_to_grid(opt.maximum["x"]) \ No newline at end of file From 171e23466de95e1422691d40e9c5a7e328e656a4 Mon Sep 17 00:00:00 2001 From: Direk Kakkar Date: Mon, 23 Mar 2026 23:53:16 +0530 Subject: [PATCH 07/12] Made LIPOOptimizer importable --- src/hyperactive/opt/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hyperactive/opt/__init__.py b/src/hyperactive/opt/__init__.py index da303a23..ed5ad2b2 100644 --- a/src/hyperactive/opt/__init__.py +++ b/src/hyperactive/opt/__init__.py @@ -4,6 +4,7 @@ from hyperactive.opt.gridsearch import GridSearchSk from hyperactive.opt.random_search import RandomSearchSk +from .lipo import LIPOOoptimizer from .gfo import ( BayesianOptimizer, From 34e66ded0c1a787761e29a4c9d75c1fd18ddf140 Mon Sep 17 00:00:00 2001 From: Direk Kakkar Date: Mon, 23 Mar 2026 23:54:41 +0530 Subject: [PATCH 08/12] added `lipo` to `pyproject.toml` --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index f77e7362..a145f4a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -99,6 +99,7 @@ all_extras = [ "optuna<5", "cmaes", # Required for CmaEsOptimizer (optuna's CMA-ES sampler) "lightning", + "lipo", ] From e652fa76c7f6fb22f29af5a3ecbfb1049158f9db Mon Sep 17 00:00:00 2001 From: Direk Kakkar Date: Mon, 23 Mar 2026 23:58:11 +0530 Subject: [PATCH 09/12] added test for `lipo` in `src/hyperactive/tests` --- src/hyperactive/tests/test_lipo.py | 41 ++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/hyperactive/tests/test_lipo.py diff --git a/src/hyperactive/tests/test_lipo.py b/src/hyperactive/tests/test_lipo.py new file mode 100644 index 00000000..1f7adfa8 --- /dev/null +++ b/src/hyperactive/tests/test_lipo.py @@ -0,0 +1,41 @@ +import numpy as np +import pytest +from hyperactive.opt.lipo import LIPOOptimizer + + +def sphere(params): + return -(params["x"] ** 2 + params["y"] ** 2) + + +def test_lipo_basic(): + opt = LIPOOptimizer( + search_space={ + "x": np.arange(-5, 5, 0.1), + "y": np.arange(-5, 5, 0.1), + }, + n_iter=50, + experiment=sphere, + ) + best = opt.solve() + assert "x" in best and "y" in best + assert abs(best["x"]) < 1.5 # should be near 0 + + +def test_lipo_categorical(): + def fn(p): return 1.0 if p["kernel"] == "rbf" else 0.0 + opt = LIPOOptimizer( + search_space={"kernel": ["linear", "rbf", "poly"]}, + n_iter=20, experiment=fn, + ) + best = opt.solve() + assert best["kernel"] == "rbf" + + +def test_lipo_snap_to_grid(): + def fn(p): return -abs(p["x"] - 3) + opt = LIPOOptimizer( + search_space={"x": np.array([1, 2, 3, 4, 5])}, + n_iter=30, experiment=fn, + ) + best = opt.solve() + assert best["x"] in [1, 2, 3, 4, 5] # must be on the grid \ No newline at end of file From 54e225dc014e55e784390b884f18057be1730078 Mon Sep 17 00:00:00 2001 From: Direk Kakkar Date: Tue, 24 Mar 2026 00:00:40 +0530 Subject: [PATCH 10/12] added usage exmaple in `examples/lipo/` --- examples/lipo/lipo_examples.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 examples/lipo/lipo_examples.py diff --git a/examples/lipo/lipo_examples.py b/examples/lipo/lipo_examples.py new file mode 100644 index 00000000..98e0d296 --- /dev/null +++ b/examples/lipo/lipo_examples.py @@ -0,0 +1,18 @@ +import numpy as np +from hyperactive.opt.lipo import LIPOOptimizer + + +def objective(params): + x, y = params["x"], params["y"] + return -(x ** 2 + y ** 2) # max at (0, 0) + + +opt = LIPOOptimizer( + search_space={ + "x": np.arange(-5, 5, 0.1), + "y": np.arange(-5, 5, 0.1), + }, + n_iter=100, + experiment=objective, +) +print(opt.solve()) # {'x': ~0.0, 'y': ~0.0} \ No newline at end of file From 2a464b92120ab2df426278472eda809b4e13912d Mon Sep 17 00:00:00 2001 From: Direk Kakkar Date: Tue, 24 Mar 2026 00:05:05 +0530 Subject: [PATCH 11/12] made changes in `README.md` --- README.md | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0562174b..5e734982 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ pip install hyperactive ```bash pip install hyperactive[sklearn-integration] # scikit-learn integration pip install hyperactive[sktime-integration] # sktime/skpro integration +pip install hyperactive[lipo-integration] # lipo global optimizer pip install hyperactive[all_extras] # Everything including Optuna ``` @@ -111,7 +112,7 @@ pip install hyperactive[all_extras] # Everything including Optuna Multiple Backends
- GFO algorithms, Optuna samplers, and sklearn search methods through one unified API. + GFO algorithms, Optuna samplers, sklearn search methods, and lipo's parameter-free global optimizer through one unified API. Stable & Tested
@@ -177,13 +178,13 @@ flowchart TB GFO["GFO
21 algorithms"] OPTUNA["Optuna
8 algorithms"] SKL["sklearn
2 algorithms"] - MORE["...
more to come"] + LIPO["LIPO
1 algorithm"] end OPT --> GFO OPT --> OPTUNA OPT --> SKL - OPT --> MORE + OPT --> LIPO end subgraph OUT["Output"] @@ -366,6 +367,34 @@ best_params = optimizer.solve() +
+LIPO Global Optimizer + +```python +import numpy as np +from hyperactive.opt.lipo import LIPOOptimizer + +def objective(params): + x, y = params["x"], params["y"] + return -(x**2 + y**2) + +search_space = { + "x": np.arange(-5, 5, 0.1), + "y": np.arange(-5, 5, 0.1), +} + +optimizer = LIPOOptimizer( + search_space=search_space, + n_iter=100, + experiment=objective, +) +best_params = optimizer.solve() +``` + +
+ + +
Time Series Forecasting with sktime @@ -508,4 +537,4 @@ If you use this software in your research, please cite: ## License -[MIT License](./LICENSE) - Free for commercial and academic use. +[MIT License](./LICENSE) - Free for commercial and academic use. \ No newline at end of file From 3b0aa727487b4dfddd93fa3d4c55e4572f85d7ca Mon Sep 17 00:00:00 2001 From: Direk Kakkar Date: Tue, 24 Mar 2026 00:34:28 +0530 Subject: [PATCH 12/12] fixed the errors --- src/hyperactive/opt/__init__.py | 2 +- src/hyperactive/opt/lipo.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/hyperactive/opt/__init__.py b/src/hyperactive/opt/__init__.py index ed5ad2b2..4daa4ebc 100644 --- a/src/hyperactive/opt/__init__.py +++ b/src/hyperactive/opt/__init__.py @@ -4,7 +4,7 @@ from hyperactive.opt.gridsearch import GridSearchSk from hyperactive.opt.random_search import RandomSearchSk -from .lipo import LIPOOoptimizer +from .lipo import LIPOOptimizer from .gfo import ( BayesianOptimizer, diff --git a/src/hyperactive/opt/lipo.py b/src/hyperactive/opt/lipo.py index d8297519..51cb7f10 100644 --- a/src/hyperactive/opt/lipo.py +++ b/src/hyperactive/opt/lipo.py @@ -35,7 +35,7 @@ def _snap_to_grid(self, params): else: snapped[key] = val # categorical, pass through return snapped - + def solve(self): lower, upper, cats = self._parse_search_space() @@ -50,4 +50,5 @@ def wrapped(**kwargs): maximize=self.maximize, ) opt.run(self.n_iter) - return self._snap_to_grid(opt.maximum["x"]) \ No newline at end of file + + return self._snap_to_grid(opt.optimum[0]) \ No newline at end of file