hoptimal 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hoptimal-0.1.0/.dockerignore +26 -0
- hoptimal-0.1.0/.github/workflows/ci.yml +39 -0
- hoptimal-0.1.0/.github/workflows/release.yml +86 -0
- hoptimal-0.1.0/.gitignore +35 -0
- hoptimal-0.1.0/CMakeLists.txt +121 -0
- hoptimal-0.1.0/Dockerfile +48 -0
- hoptimal-0.1.0/LICENSE +21 -0
- hoptimal-0.1.0/PKG-INFO +188 -0
- hoptimal-0.1.0/README.md +138 -0
- hoptimal-0.1.0/benchmarks/python/bench_speed.py +138 -0
- hoptimal-0.1.0/benchmarks/python/competitors/__init__.py +0 -0
- hoptimal-0.1.0/benchmarks/python/competitors/bench_competitors.py +218 -0
- hoptimal-0.1.0/benchmarks/python/ml/__init__.py +0 -0
- hoptimal-0.1.0/benchmarks/python/ml/bench_sklearn.py +100 -0
- hoptimal-0.1.0/benchmarks/python/results/regret_curves.png +0 -0
- hoptimal-0.1.0/benchmarks/python/results/regret_curves.svg +12350 -0
- hoptimal-0.1.0/benchmarks/python/run_all.py +69 -0
- hoptimal-0.1.0/benchmarks/python/synthetic/__init__.py +0 -0
- hoptimal-0.1.0/benchmarks/python/synthetic/bench_synthetic.py +66 -0
- hoptimal-0.1.0/benchmarks/python/synthetic/functions.py +120 -0
- hoptimal-0.1.0/benchmarks/python/visualize.py +106 -0
- hoptimal-0.1.0/examples/demo.ipynb +280 -0
- hoptimal-0.1.0/examples/test_catboost.py +30 -0
- hoptimal-0.1.0/examples/test_jax.py +36 -0
- hoptimal-0.1.0/examples/test_lightgbm.py +30 -0
- hoptimal-0.1.0/examples/test_pytorch.py +50 -0
- hoptimal-0.1.0/examples/test_sklearn.py +36 -0
- hoptimal-0.1.0/examples/test_tensorflow.py +40 -0
- hoptimal-0.1.0/examples/test_xgboost.py +31 -0
- hoptimal-0.1.0/include/hoptimal/core/pruner.hpp +21 -0
- hoptimal-0.1.0/include/hoptimal/core/sampler.hpp +43 -0
- hoptimal-0.1.0/include/hoptimal/core/search_space.hpp +79 -0
- hoptimal-0.1.0/include/hoptimal/core/storage.hpp +34 -0
- hoptimal-0.1.0/include/hoptimal/core/study.hpp +105 -0
- hoptimal-0.1.0/include/hoptimal/core/trial.hpp +109 -0
- hoptimal-0.1.0/include/hoptimal/core/types.hpp +25 -0
- hoptimal-0.1.0/include/hoptimal/gp/acquisition.hpp +48 -0
- hoptimal-0.1.0/include/hoptimal/gp/gaussian_process.hpp +59 -0
- hoptimal-0.1.0/include/hoptimal/gp/kernel.hpp +86 -0
- hoptimal-0.1.0/include/hoptimal/pruners/hyperband_pruner.hpp +42 -0
- hoptimal-0.1.0/include/hoptimal/pruners/median_pruner.hpp +26 -0
- hoptimal-0.1.0/include/hoptimal/samplers/gp_sampler.hpp +79 -0
- hoptimal-0.1.0/include/hoptimal/samplers/random_sampler.hpp +34 -0
- hoptimal-0.1.0/include/hoptimal/storage/memory_storage.hpp +32 -0
- hoptimal-0.1.0/include/hoptimal/utils/lbfgs.hpp +36 -0
- hoptimal-0.1.0/include/hoptimal/utils/linalg.hpp +37 -0
- hoptimal-0.1.0/include/hoptimal/utils/statistics.hpp +20 -0
- hoptimal-0.1.0/pyproject.toml +59 -0
- hoptimal-0.1.0/python/bindings/bindings.cpp +199 -0
- hoptimal-0.1.0/python/hoptimal/__init__.py +66 -0
- hoptimal-0.1.0/python/hoptimal/integrations/__init__.py +1 -0
- hoptimal-0.1.0/python/hoptimal/integrations/catboost.py +126 -0
- hoptimal-0.1.0/python/hoptimal/integrations/jax_flax.py +97 -0
- hoptimal-0.1.0/python/hoptimal/integrations/lightgbm.py +152 -0
- hoptimal-0.1.0/python/hoptimal/integrations/pytorch.py +115 -0
- hoptimal-0.1.0/python/hoptimal/integrations/sklearn.py +156 -0
- hoptimal-0.1.0/python/hoptimal/integrations/tensorflow.py +165 -0
- hoptimal-0.1.0/python/hoptimal/integrations/xgboost.py +166 -0
- hoptimal-0.1.0/src/core/search_space.cpp +133 -0
- hoptimal-0.1.0/src/core/study.cpp +265 -0
- hoptimal-0.1.0/src/core/trial.cpp +84 -0
- hoptimal-0.1.0/src/gp/acquisition.cpp +133 -0
- hoptimal-0.1.0/src/gp/gaussian_process.cpp +139 -0
- hoptimal-0.1.0/src/gp/kernel.cpp +97 -0
- hoptimal-0.1.0/src/pruners/hyperband_pruner.cpp +70 -0
- hoptimal-0.1.0/src/pruners/median_pruner.cpp +51 -0
- hoptimal-0.1.0/src/samplers/gp_sampler.cpp +203 -0
- hoptimal-0.1.0/src/samplers/random_sampler.cpp +69 -0
- hoptimal-0.1.0/src/storage/memory_storage.cpp +50 -0
- hoptimal-0.1.0/src/utils/lbfgs.cpp +128 -0
- hoptimal-0.1.0/tests/integration/CMakeLists.txt +18 -0
- hoptimal-0.1.0/tests/integration/test_sampler_convergence.cpp +117 -0
- hoptimal-0.1.0/tests/integration/test_study_e2e.cpp +166 -0
- hoptimal-0.1.0/tests/unit/CMakeLists.txt +24 -0
- hoptimal-0.1.0/tests/unit/test_acquisition.cpp +117 -0
- hoptimal-0.1.0/tests/unit/test_gaussian_process.cpp +145 -0
- hoptimal-0.1.0/tests/unit/test_kernel.cpp +122 -0
- hoptimal-0.1.0/tests/unit/test_pruners.cpp +109 -0
- hoptimal-0.1.0/tests/unit/test_search_space.cpp +195 -0
- hoptimal-0.1.0/tests/unit/test_storage.cpp +218 -0
- hoptimal-0.1.0/tests/unit/test_study.cpp +258 -0
- hoptimal-0.1.0/tests/unit/test_trial.cpp +226 -0
- hoptimal-0.1.0/vcpkg.json +9 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Build artifacts — rebuilt inside the image
|
|
2
|
+
build/
|
|
3
|
+
**/__pycache__/
|
|
4
|
+
*.so
|
|
5
|
+
*.pyd
|
|
6
|
+
*.o
|
|
7
|
+
*.a
|
|
8
|
+
|
|
9
|
+
# Local logs / scratch captured during the Windows dev session
|
|
10
|
+
logs*.txt
|
|
11
|
+
test_results.txt
|
|
12
|
+
|
|
13
|
+
# Benchmark outputs (regenerated)
|
|
14
|
+
benchmarks/python/results/*.json
|
|
15
|
+
|
|
16
|
+
# VCS / editor
|
|
17
|
+
.git/
|
|
18
|
+
.gitignore
|
|
19
|
+
.vs/
|
|
20
|
+
.vscode/
|
|
21
|
+
.idea/
|
|
22
|
+
|
|
23
|
+
# Python packaging cruft
|
|
24
|
+
*.egg-info/
|
|
25
|
+
dist/
|
|
26
|
+
.pytest_cache/
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch: # allow manual runs
|
|
8
|
+
schedule:
|
|
9
|
+
- cron: "0 6 * * 1" # weekly Monday 06:00 UTC — runs the heavy DL job
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
# ── Core + classic ML integrations ──────────────────────────────────────────
|
|
13
|
+
# Reuses the project Dockerfile so CI and local `docker build` are identical.
|
|
14
|
+
# The `integrations` stage builds the C++ core, runs all 130 tests, builds the
|
|
15
|
+
# pybind11 module, and runs the sklearn/xgboost/lightgbm/catboost smoke tests.
|
|
16
|
+
build-and-test:
|
|
17
|
+
name: build + test (core + integrations)
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Build and test via Docker (target=integrations)
|
|
23
|
+
run: docker build --target integrations -t hoptimal:ci .
|
|
24
|
+
|
|
25
|
+
# ── Deep-learning integrations (heavy; weekly + manual only) ─────────────────
|
|
26
|
+
# torch + tensorflow + jax are multi-GB, so this doesn't gate every push.
|
|
27
|
+
deep-learning:
|
|
28
|
+
name: deep-learning integrations (torch / jax / tensorflow)
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v4
|
|
33
|
+
|
|
34
|
+
- name: Free up disk space
|
|
35
|
+
run: |
|
|
36
|
+
sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/lib/android || true
|
|
37
|
+
|
|
38
|
+
- name: Build and test via Docker (target=dl)
|
|
39
|
+
run: docker build --target dl -t hoptimal:dl .
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# Builds wheels for Linux/macOS/Windows + an sdist, then publishes to PyPI.
|
|
4
|
+
# Triggers when you publish a GitHub Release (recommended), or manually.
|
|
5
|
+
on:
|
|
6
|
+
release:
|
|
7
|
+
types: [published]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
# ── Build binary wheels on every platform ────────────────────────────────────
|
|
12
|
+
wheels:
|
|
13
|
+
name: wheels on ${{ matrix.os }}
|
|
14
|
+
runs-on: ${{ matrix.os }}
|
|
15
|
+
strategy:
|
|
16
|
+
fail-fast: false
|
|
17
|
+
matrix:
|
|
18
|
+
# macos-14 (Apple Silicon) cross-builds BOTH arm64 and x86_64 via
|
|
19
|
+
# CIBW_ARCHS_MACOS below, so a separate macos-13 (Intel) runner would
|
|
20
|
+
# only produce duplicate wheels — and those Intel runners are queue-
|
|
21
|
+
# constrained as GitHub winds them down. One macOS runner covers both.
|
|
22
|
+
os: [ubuntu-latest, windows-latest, macos-14]
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
|
|
26
|
+
- name: Build wheels
|
|
27
|
+
uses: pypa/cibuildwheel@v2.21
|
|
28
|
+
env:
|
|
29
|
+
# Skip PyPy and musllinux to keep the matrix lean; add back if wanted.
|
|
30
|
+
CIBW_SKIP: "pp* *-musllinux_*"
|
|
31
|
+
# 64-bit only — the C++20 core has no reason to ship 32-bit wheels,
|
|
32
|
+
# and the 32-bit manylinux/Windows images use an older toolchain.
|
|
33
|
+
CIBW_ARCHS_LINUX: x86_64
|
|
34
|
+
CIBW_ARCHS_WINDOWS: AMD64
|
|
35
|
+
CIBW_ARCHS_MACOS: "x86_64 arm64"
|
|
36
|
+
# libc++ marks std::variant/std::visit "unavailable" below macOS 10.13,
|
|
37
|
+
# which this code uses. Raise the x86_64 deployment target (cibuildwheel
|
|
38
|
+
# clamps arm64 to its 11.0 minimum automatically).
|
|
39
|
+
CIBW_ENVIRONMENT_MACOS: MACOSX_DEPLOYMENT_TARGET=10.13
|
|
40
|
+
# C++20 needs a modern toolchain — manylinux2014's GCC 10 is too old
|
|
41
|
+
# for parts of the standard library, so build on the GCC 12 image.
|
|
42
|
+
CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28
|
|
43
|
+
# Surface the real CMake/compiler error if a build fails.
|
|
44
|
+
CIBW_BUILD_VERBOSITY: "1"
|
|
45
|
+
# Smoke-test each built wheel actually imports + runs.
|
|
46
|
+
CIBW_TEST_REQUIRES: numpy
|
|
47
|
+
CIBW_TEST_COMMAND: >-
|
|
48
|
+
python -c "import hoptimal; s = hoptimal.create_study();
|
|
49
|
+
print('hoptimal', getattr(hoptimal, '__version__', 'ok'))"
|
|
50
|
+
|
|
51
|
+
- uses: actions/upload-artifact@v4
|
|
52
|
+
with:
|
|
53
|
+
name: wheels-${{ matrix.os }}
|
|
54
|
+
path: ./wheelhouse/*.whl
|
|
55
|
+
|
|
56
|
+
# ── Build the source distribution ────────────────────────────────────────────
|
|
57
|
+
sdist:
|
|
58
|
+
name: sdist
|
|
59
|
+
runs-on: ubuntu-latest
|
|
60
|
+
steps:
|
|
61
|
+
- uses: actions/checkout@v4
|
|
62
|
+
- name: Build sdist
|
|
63
|
+
run: pipx run build --sdist
|
|
64
|
+
- uses: actions/upload-artifact@v4
|
|
65
|
+
with:
|
|
66
|
+
name: sdist
|
|
67
|
+
path: ./dist/*.tar.gz
|
|
68
|
+
|
|
69
|
+
# ── Publish to PyPI via Trusted Publishing (OIDC, no API tokens) ─────────────
|
|
70
|
+
publish:
|
|
71
|
+
name: publish to PyPI
|
|
72
|
+
needs: [wheels, sdist]
|
|
73
|
+
runs-on: ubuntu-latest
|
|
74
|
+
# Only publish for real releases, not manual dry-runs.
|
|
75
|
+
if: github.event_name == 'release'
|
|
76
|
+
environment:
|
|
77
|
+
name: pypi
|
|
78
|
+
url: https://pypi.org/p/hoptimal
|
|
79
|
+
permissions:
|
|
80
|
+
id-token: write # required for Trusted Publishing
|
|
81
|
+
steps:
|
|
82
|
+
- uses: actions/download-artifact@v4
|
|
83
|
+
with:
|
|
84
|
+
path: dist
|
|
85
|
+
merge-multiple: true
|
|
86
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Build artifacts
|
|
2
|
+
build/
|
|
3
|
+
dist/
|
|
4
|
+
*.so
|
|
5
|
+
*.pyd
|
|
6
|
+
*.o
|
|
7
|
+
*.a
|
|
8
|
+
*.egg-info/
|
|
9
|
+
|
|
10
|
+
# Python
|
|
11
|
+
__pycache__/
|
|
12
|
+
*.pyc
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.venv/
|
|
15
|
+
venv/
|
|
16
|
+
.ipynb_checkpoints/
|
|
17
|
+
|
|
18
|
+
# Local logs / scratch from the dev session
|
|
19
|
+
logs*.txt
|
|
20
|
+
test_results.txt
|
|
21
|
+
|
|
22
|
+
# Benchmark run outputs (regenerated; commit curated results only)
|
|
23
|
+
benchmarks/python/results/*.json
|
|
24
|
+
|
|
25
|
+
# Planning / design docs kept outside the public repo.
|
|
26
|
+
PORTFOLIO_PLAN.md
|
|
27
|
+
hoptimal_architecture.md
|
|
28
|
+
docs/
|
|
29
|
+
|
|
30
|
+
# Editor / OS
|
|
31
|
+
.vs/
|
|
32
|
+
.vscode/
|
|
33
|
+
.idea/
|
|
34
|
+
.DS_Store
|
|
35
|
+
Thumbs.db
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.20)
|
|
2
|
+
project(hoptimal VERSION 0.1.0 LANGUAGES CXX)
|
|
3
|
+
|
|
4
|
+
set(CMAKE_CXX_STANDARD 20)
|
|
5
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
6
|
+
set(CMAKE_CXX_EXTENSIONS OFF)
|
|
7
|
+
|
|
8
|
+
option(HOPTIMAL_BUILD_TESTS "Build C++ unit/integration tests" ON)
|
|
9
|
+
option(HOPTIMAL_BUILD_BENCHMARKS "Build C++ microbenchmarks" OFF)
|
|
10
|
+
option(HOPTIMAL_BUILD_PYTHON "Build pybind11 Python bindings" OFF)
|
|
11
|
+
option(HOPTIMAL_USE_SANITIZERS "Enable ASan + UBSan (non-MSVC)" OFF)
|
|
12
|
+
|
|
13
|
+
# ── Dependencies (try find_package first, fall back to FetchContent) ──────────
|
|
14
|
+
include(FetchContent)
|
|
15
|
+
|
|
16
|
+
# Eigen3 — header-only. Prefer a system/vcpkg install; otherwise fetch the
|
|
17
|
+
# source from git. We need only the headers, so we populate the source WITHOUT
|
|
18
|
+
# running Eigen's own CMake: SOURCE_SUBDIR points at a nonexistent directory so
|
|
19
|
+
# FetchContent_MakeAvailable downloads but skips add_subdirectory(). That avoids
|
|
20
|
+
# (a) GitLab archive-checksum drift (we clone a tag instead of pinning a tarball
|
|
21
|
+
# hash) and (b) a duplicate-target clash on Eigen3::Eigen that Eigen's own CMake
|
|
22
|
+
# would otherwise create.
|
|
23
|
+
find_package(Eigen3 3.4 QUIET)
|
|
24
|
+
if(NOT Eigen3_FOUND)
|
|
25
|
+
message(STATUS "Eigen3 not found — fetching 3.4.0 via FetchContent (needs internet)")
|
|
26
|
+
FetchContent_Declare(eigen3
|
|
27
|
+
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
|
|
28
|
+
GIT_TAG 3.4.0
|
|
29
|
+
GIT_SHALLOW TRUE
|
|
30
|
+
SOURCE_SUBDIR cmake-do-not-configure
|
|
31
|
+
)
|
|
32
|
+
FetchContent_MakeAvailable(eigen3)
|
|
33
|
+
if(NOT TARGET Eigen3::Eigen)
|
|
34
|
+
add_library(Eigen3::Eigen INTERFACE IMPORTED)
|
|
35
|
+
target_include_directories(Eigen3::Eigen INTERFACE ${eigen3_SOURCE_DIR})
|
|
36
|
+
endif()
|
|
37
|
+
endif()
|
|
38
|
+
|
|
39
|
+
# GoogleTest (only when tests are requested)
|
|
40
|
+
if(HOPTIMAL_BUILD_TESTS)
|
|
41
|
+
find_package(GTest QUIET)
|
|
42
|
+
if(NOT GTest_FOUND)
|
|
43
|
+
message(STATUS "GTest not found — fetching via FetchContent")
|
|
44
|
+
FetchContent_Declare(googletest
|
|
45
|
+
URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz
|
|
46
|
+
)
|
|
47
|
+
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
|
48
|
+
FetchContent_MakeAvailable(googletest)
|
|
49
|
+
endif()
|
|
50
|
+
endif()
|
|
51
|
+
|
|
52
|
+
# pybind11 (only when Python bindings are requested)
|
|
53
|
+
if(HOPTIMAL_BUILD_PYTHON)
|
|
54
|
+
find_package(pybind11 QUIET)
|
|
55
|
+
if(NOT pybind11_FOUND)
|
|
56
|
+
FetchContent_Declare(pybind11
|
|
57
|
+
URL https://github.com/pybind/pybind11/archive/refs/tags/v2.12.0.tar.gz
|
|
58
|
+
)
|
|
59
|
+
FetchContent_MakeAvailable(pybind11)
|
|
60
|
+
endif()
|
|
61
|
+
endif()
|
|
62
|
+
|
|
63
|
+
# ── hoptimal_core static library ──────────────────────────────────────────────────
|
|
64
|
+
add_library(hoptimal_core STATIC
|
|
65
|
+
# core
|
|
66
|
+
src/core/search_space.cpp
|
|
67
|
+
src/core/trial.cpp
|
|
68
|
+
src/core/study.cpp
|
|
69
|
+
src/storage/memory_storage.cpp
|
|
70
|
+
src/samplers/random_sampler.cpp
|
|
71
|
+
# Gaussian-process Bayesian optimization
|
|
72
|
+
src/utils/lbfgs.cpp
|
|
73
|
+
src/gp/kernel.cpp
|
|
74
|
+
src/gp/gaussian_process.cpp
|
|
75
|
+
src/gp/acquisition.cpp
|
|
76
|
+
src/samplers/gp_sampler.cpp
|
|
77
|
+
# pruners
|
|
78
|
+
src/pruners/median_pruner.cpp
|
|
79
|
+
src/pruners/hyperband_pruner.cpp
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
target_include_directories(hoptimal_core PUBLIC
|
|
83
|
+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
|
84
|
+
$<INSTALL_INTERFACE:include>
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
target_link_libraries(hoptimal_core PUBLIC Eigen3::Eigen)
|
|
88
|
+
|
|
89
|
+
# Build with position-independent code so the static hoptimal_core library can be
|
|
90
|
+
# linked into the _hoptimal shared module (pybind11). On Linux, linking a non-PIC
|
|
91
|
+
# static lib into a .so fails ("relocation ... can not be used when making a
|
|
92
|
+
# shared object; recompile with -fPIC"). Harmless for the test executables.
|
|
93
|
+
set_target_properties(hoptimal_core PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
|
94
|
+
|
|
95
|
+
target_compile_options(hoptimal_core PRIVATE
|
|
96
|
+
$<$<CXX_COMPILER_ID:MSVC>:/W4 /wd4100 /wd4127>
|
|
97
|
+
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Wpedantic>
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if(HOPTIMAL_USE_SANITIZERS AND NOT MSVC)
|
|
101
|
+
target_compile_options(hoptimal_core PRIVATE
|
|
102
|
+
-fsanitize=address,undefined -fno-omit-frame-pointer)
|
|
103
|
+
target_link_options(hoptimal_core PRIVATE
|
|
104
|
+
-fsanitize=address,undefined)
|
|
105
|
+
endif()
|
|
106
|
+
|
|
107
|
+
# ── Tests ─────────────────────────────────────────────────────────────────────
|
|
108
|
+
if(HOPTIMAL_BUILD_TESTS)
|
|
109
|
+
enable_testing()
|
|
110
|
+
add_subdirectory(tests/unit)
|
|
111
|
+
add_subdirectory(tests/integration)
|
|
112
|
+
endif()
|
|
113
|
+
|
|
114
|
+
# ── Python bindings ───────────────────────────────────────────────────────────
|
|
115
|
+
if(HOPTIMAL_BUILD_PYTHON)
|
|
116
|
+
pybind11_add_module(_hoptimal python/bindings/bindings.cpp)
|
|
117
|
+
target_link_libraries(_hoptimal PRIVATE hoptimal_core)
|
|
118
|
+
# scikit-build-core maps wheel.packages = ["python/hoptimal"] → the `hoptimal`
|
|
119
|
+
# package at the wheel root, so the compiled module installs into `hoptimal/`.
|
|
120
|
+
install(TARGETS _hoptimal DESTINATION hoptimal)
|
|
121
|
+
endif()
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
FROM python:3.11-slim AS core
|
|
2
|
+
|
|
3
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
4
|
+
build-essential \
|
|
5
|
+
cmake \
|
|
6
|
+
ninja-build \
|
|
7
|
+
git \
|
|
8
|
+
libeigen3-dev \
|
|
9
|
+
libgtest-dev \
|
|
10
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
11
|
+
|
|
12
|
+
WORKDIR /hoptimal
|
|
13
|
+
COPY . .
|
|
14
|
+
|
|
15
|
+
RUN cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DHOPTIMAL_BUILD_TESTS=ON \
|
|
16
|
+
&& cmake --build build -j"$(nproc)" \
|
|
17
|
+
&& ctest --test-dir build --output-on-failure
|
|
18
|
+
|
|
19
|
+
RUN pip install --no-cache-dir . && pip install --no-cache-dir pytest
|
|
20
|
+
|
|
21
|
+
RUN python -c "import hoptimal; \
|
|
22
|
+
s=hoptimal.create_study('minimize'); \
|
|
23
|
+
s.optimize(lambda t:(t.suggest_float('x',-5,5)-2)**2, 30); \
|
|
24
|
+
print('core OK — best:', s.best_value, s.best_params)"
|
|
25
|
+
|
|
26
|
+
FROM core AS integrations
|
|
27
|
+
|
|
28
|
+
RUN pip install --no-cache-dir \
|
|
29
|
+
scikit-learn \
|
|
30
|
+
xgboost \
|
|
31
|
+
lightgbm \
|
|
32
|
+
catboost \
|
|
33
|
+
optuna \
|
|
34
|
+
scikit-optimize
|
|
35
|
+
|
|
36
|
+
RUN python examples/test_sklearn.py \
|
|
37
|
+
&& python examples/test_xgboost.py \
|
|
38
|
+
&& python examples/test_lightgbm.py \
|
|
39
|
+
&& python examples/test_catboost.py
|
|
40
|
+
|
|
41
|
+
FROM integrations AS dl
|
|
42
|
+
|
|
43
|
+
RUN pip install --no-cache-dir torch --index-url https://download.pytorch.org/whl/cpu
|
|
44
|
+
RUN pip install --no-cache-dir "jax[cpu]" flax tensorflow-cpu
|
|
45
|
+
|
|
46
|
+
RUN python examples/test_pytorch.py \
|
|
47
|
+
&& python examples/test_jax.py \
|
|
48
|
+
&& python examples/test_tensorflow.py
|
hoptimal-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Daniel Dema
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
hoptimal-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hoptimal
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: High-performance hyperparameter optimisation — C++ core, Python bindings
|
|
5
|
+
Keywords: hyperparameter,optimization,bayesian-optimization,gaussian-process,tpe,cma-es,machine-learning,automl
|
|
6
|
+
Author-Email: Daniel Dema <danieldema42@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: C++
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Project-URL: Homepage, https://github.com/danieldema/hoptimal
|
|
17
|
+
Project-URL: Repository, https://github.com/danieldema/hoptimal
|
|
18
|
+
Project-URL: Issues, https://github.com/danieldema/hoptimal/issues
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Requires-Dist: numpy>=1.24
|
|
21
|
+
Provides-Extra: sklearn
|
|
22
|
+
Requires-Dist: scikit-learn>=1.3; extra == "sklearn"
|
|
23
|
+
Provides-Extra: torch
|
|
24
|
+
Requires-Dist: torch>=2.0; extra == "torch"
|
|
25
|
+
Provides-Extra: tensorflow
|
|
26
|
+
Requires-Dist: tensorflow>=2.13; extra == "tensorflow"
|
|
27
|
+
Provides-Extra: jax
|
|
28
|
+
Requires-Dist: jax>=0.4; extra == "jax"
|
|
29
|
+
Requires-Dist: flax>=0.7; extra == "jax"
|
|
30
|
+
Provides-Extra: xgboost
|
|
31
|
+
Requires-Dist: xgboost>=2.0; extra == "xgboost"
|
|
32
|
+
Provides-Extra: lightgbm
|
|
33
|
+
Requires-Dist: lightgbm>=4.0; extra == "lightgbm"
|
|
34
|
+
Provides-Extra: catboost
|
|
35
|
+
Requires-Dist: catboost>=1.2; extra == "catboost"
|
|
36
|
+
Provides-Extra: benchmark
|
|
37
|
+
Requires-Dist: optuna; extra == "benchmark"
|
|
38
|
+
Requires-Dist: hyperopt; extra == "benchmark"
|
|
39
|
+
Requires-Dist: botorch; extra == "benchmark"
|
|
40
|
+
Requires-Dist: scikit-optimize; extra == "benchmark"
|
|
41
|
+
Requires-Dist: ray[tune]; extra == "benchmark"
|
|
42
|
+
Requires-Dist: flaml; extra == "benchmark"
|
|
43
|
+
Requires-Dist: nevergrad; extra == "benchmark"
|
|
44
|
+
Requires-Dist: plotly; extra == "benchmark"
|
|
45
|
+
Requires-Dist: pandas; extra == "benchmark"
|
|
46
|
+
Requires-Dist: scipy; extra == "benchmark"
|
|
47
|
+
Provides-Extra: all
|
|
48
|
+
Requires-Dist: hoptimal[benchmark,catboost,jax,lightgbm,sklearn,tensorflow,torch,xgboost]; extra == "all"
|
|
49
|
+
Description-Content-Type: text/markdown
|
|
50
|
+
|
|
51
|
+
# hoptimal
|
|
52
|
+
|
|
53
|
+
[](https://github.com/danieldema/hoptimal/actions/workflows/ci.yml)
|
|
54
|
+

|
|
55
|
+

|
|
56
|
+

|
|
57
|
+
|
|
58
|
+
**A from-scratch C++ Gaussian-Process Bayesian optimizer with Python bindings — competitive with Optuna and scikit-optimize on sample efficiency.**
|
|
59
|
+
|
|
60
|
+
hoptimal implements Bayesian optimization (a Gaussian-Process surrogate + acquisition
|
|
61
|
+
functions) in modern C++20, exposed to Python through pybind11. The GP, the
|
|
62
|
+
Cholesky-based inference, the kernel-hyperparameter fitting, and the acquisition
|
|
63
|
+
optimization are all hand-written — no GPyTorch, no BoTorch, no scikit-learn under
|
|
64
|
+
the hood.
|
|
65
|
+
|
|
66
|
+
## Benchmark
|
|
67
|
+
|
|
68
|
+
Median regret (best-found − known optimum, lower is better) vs trial number on 8
|
|
69
|
+
standard optimization test functions, 8 seeds each — hoptimal against Optuna and
|
|
70
|
+
scikit-optimize:
|
|
71
|
+
|
|
72
|
+

|
|
73
|
+
|
|
74
|
+
hoptimal's GP methods are **best-in-class**: across the 8 functions they win 4
|
|
75
|
+
outright and place 2nd on the other 4, beating Optuna's TPE and CMA-ES and
|
|
76
|
+
trading wins with scikit-optimize's GP — especially strong on the harder
|
|
77
|
+
higher-dimensional problems (Hartmann-6, Ackley, Rosenbrock, Rastrigin).
|
|
78
|
+
|
|
79
|
+
| Function | hoptimal (best) | Optuna (best) | scikit-optimize |
|
|
80
|
+
|----------|------------:|--------------:|----------------:|
|
|
81
|
+
| branin | 0.0018 | 0.70 | **0.0011** |
|
|
82
|
+
| hartmann3 | 0.013 | 0.15 | **0.0002** |
|
|
83
|
+
| hartmann6 | **0.14** | 1.06 | 0.23 |
|
|
84
|
+
| ackley5 | **2.48** | 3.30 | 2.78 |
|
|
85
|
+
| rosenbrock4 | **5.29** | 10.4 | 9.28 |
|
|
86
|
+
| levy5 | 2.17 | **2.07** | 3.98 |
|
|
87
|
+
| rastrigin5 | **18.3** | 27.7 | 41.1 |
|
|
88
|
+
| styblinski5 | 48.8 | 55.5 | **27.7** |
|
|
89
|
+
|
|
90
|
+
Reproduce: `python benchmarks/python/competitors/bench_competitors.py && python benchmarks/python/visualize.py`
|
|
91
|
+
|
|
92
|
+
## Quickstart
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
import hoptimal
|
|
96
|
+
|
|
97
|
+
study = hoptimal.create_study("minimize") # GP + Expected Improvement by default
|
|
98
|
+
|
|
99
|
+
def objective(trial):
|
|
100
|
+
x = trial.suggest_float("x", -5.0, 5.0)
|
|
101
|
+
y = trial.suggest_float("y", -5.0, 5.0)
|
|
102
|
+
return (x - 2.0) ** 2 + (y + 1.0) ** 2
|
|
103
|
+
|
|
104
|
+
study.optimize(objective, n_trials=50)
|
|
105
|
+
print(study.best_value, study.best_params)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The `Trial` API mirrors Optuna's, so migration is mostly mechanical. For a
|
|
109
|
+
runnable tour (toy problem → Branin convergence plot → real sklearn model →
|
|
110
|
+
head-to-head vs Optuna) see [`examples/demo.ipynb`](examples/demo.ipynb).
|
|
111
|
+
|
|
112
|
+
## Features
|
|
113
|
+
|
|
114
|
+
| Capability | Status |
|
|
115
|
+
|------------|--------|
|
|
116
|
+
| GP Bayesian optimization — **EI**, **UCB**, **PI** acquisitions | ✅ |
|
|
117
|
+
| RBF and Matérn-5/2 kernels, MLE hyperparameter fitting (L-BFGS) | ✅ |
|
|
118
|
+
| Float / int / log-scale / categorical parameters | ✅ |
|
|
119
|
+
| Multi-objective optimization (Pareto front) | ✅ |
|
|
120
|
+
| Median and Hyperband pruners (early stopping) | ✅ |
|
|
121
|
+
| Parallel trials (`n_jobs`) with constant-liar | ✅ |
|
|
122
|
+
| ML integrations: scikit-learn, XGBoost, LightGBM, CatBoost, PyTorch, JAX/Flax, TensorFlow/Keras | ✅ |
|
|
123
|
+
|
|
124
|
+
All integrations are validated on clean Linux in CI (see the Dockerfile).
|
|
125
|
+
|
|
126
|
+
### scikit-learn example
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from sklearn.svm import SVC
|
|
130
|
+
from sklearn.pipeline import Pipeline
|
|
131
|
+
from sklearn.preprocessing import StandardScaler
|
|
132
|
+
from hoptimal.integrations.sklearn import HoptimalSearchCV
|
|
133
|
+
|
|
134
|
+
pipe = Pipeline([("scaler", StandardScaler()), ("svc", SVC())])
|
|
135
|
+
search = HoptimalSearchCV(
|
|
136
|
+
pipe,
|
|
137
|
+
{"svc__C": ("float", 1e-3, 1e3, {"log": True}),
|
|
138
|
+
"svc__gamma": ("float", 1e-4, 1e0, {"log": True}),
|
|
139
|
+
"svc__kernel":("categorical", ["rbf", "sigmoid"])},
|
|
140
|
+
n_trials=40, cv=5, scoring="accuracy",
|
|
141
|
+
)
|
|
142
|
+
search.fit(X, y)
|
|
143
|
+
print(search.best_score_, search.best_params_)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Build from source
|
|
147
|
+
|
|
148
|
+
**With Docker (recommended — builds + tests + validates integrations on clean Linux):**
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
docker build --target core -t hoptimal . # C++ core + tests + bindings
|
|
152
|
+
docker build --target integrations -t hoptimal . # + sklearn/xgboost/lightgbm/catboost
|
|
153
|
+
docker build --target dl -t hoptimal . # + pytorch/jax/tensorflow (heavy)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Locally** (needs a C++20 compiler, CMake, Eigen3; GoogleTest/pybind11 are
|
|
157
|
+
fetched automatically if absent):
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# C++ library + tests
|
|
161
|
+
cmake -B build -DHOPTIMAL_BUILD_TESTS=ON
|
|
162
|
+
cmake --build build --parallel
|
|
163
|
+
ctest --test-dir build --output-on-failure
|
|
164
|
+
|
|
165
|
+
# Python package
|
|
166
|
+
pip install .
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## How it works
|
|
170
|
+
|
|
171
|
+
Each trial, hoptimal fits a **Gaussian Process** to the observations so far, then
|
|
172
|
+
picks the next point by optimizing an **acquisition function** over the GP's
|
|
173
|
+
posterior. The implementation is hand-written C++:
|
|
174
|
+
|
|
175
|
+
- **GP regression** with RBF / Matérn-5/2 kernels; inference via a
|
|
176
|
+
**Cholesky factorization** of `K + σ²I` (with jitter for stability) rather
|
|
177
|
+
than an explicit inverse.
|
|
178
|
+
- **Kernel hyperparameters** (amplitude, length-scale) fitted by maximizing the
|
|
179
|
+
log marginal likelihood with L-BFGS and random restarts.
|
|
180
|
+
- **Acquisition** (EI / UCB / PI) optimized over the normalized `[0,1]ᵈ` space
|
|
181
|
+
via a quasi-random candidate set refined with L-BFGS.
|
|
182
|
+
|
|
183
|
+
The cost center — repeated Cholesky factorizations and acquisition
|
|
184
|
+
optimization — is exactly why the core is in C++.
|
|
185
|
+
|
|
186
|
+
## License
|
|
187
|
+
|
|
188
|
+
MIT — see [LICENSE](LICENSE).
|