arrowspace-tuner 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.
- arrowspace_tuner-0.1.0/.coverage +0 -0
- arrowspace_tuner-0.1.0/.github/workflows/ci.yml +38 -0
- arrowspace_tuner-0.1.0/.gitignore +14 -0
- arrowspace_tuner-0.1.0/.python-version +1 -0
- arrowspace_tuner-0.1.0/CHANGELOG.md +26 -0
- arrowspace_tuner-0.1.0/LICENSE +13 -0
- arrowspace_tuner-0.1.0/PKG-INFO +154 -0
- arrowspace_tuner-0.1.0/README.md +119 -0
- arrowspace_tuner-0.1.0/notebooks/quickstart.ipynb +0 -0
- arrowspace_tuner-0.1.0/pyproject.toml +91 -0
- arrowspace_tuner-0.1.0/src/arrowspace_tuner/__init__.py +40 -0
- arrowspace_tuner-0.1.0/src/arrowspace_tuner/api.py +132 -0
- arrowspace_tuner-0.1.0/src/arrowspace_tuner/core/__init__.py +25 -0
- arrowspace_tuner-0.1.0/src/arrowspace_tuner/core/config.py +121 -0
- arrowspace_tuner-0.1.0/src/arrowspace_tuner/core/graph.py +100 -0
- arrowspace_tuner-0.1.0/src/arrowspace_tuner/core/objective.py +285 -0
- arrowspace_tuner-0.1.0/src/arrowspace_tuner/py.typed +0 -0
- arrowspace_tuner-0.1.0/src/arrowspace_tuner/reporting/__init__.py +13 -0
- arrowspace_tuner-0.1.0/src/arrowspace_tuner/reporting/reporter.py +183 -0
- arrowspace_tuner-0.1.0/src/arrowspace_tuner/tuner.py +298 -0
- arrowspace_tuner-0.1.0/tests/conftest.py +116 -0
- arrowspace_tuner-0.1.0/tests/test_objective.py +203 -0
- arrowspace_tuner-0.1.0/tests/test_tuner.py +283 -0
- arrowspace_tuner-0.1.0/uv.lock +2849 -0
|
Binary file
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: pytest + ruff + mypy
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.12", "3.13"]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Install uv
|
|
21
|
+
uses: astral-sh/setup-uv@v3
|
|
22
|
+
with:
|
|
23
|
+
version: "latest"
|
|
24
|
+
|
|
25
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
26
|
+
run: uv python install ${{ matrix.python-version }}
|
|
27
|
+
|
|
28
|
+
- name: Install dependencies
|
|
29
|
+
run: uv sync --extra dev
|
|
30
|
+
|
|
31
|
+
- name: Lint (ruff)
|
|
32
|
+
run: uv run ruff check src tests
|
|
33
|
+
|
|
34
|
+
- name: Type check (mypy)
|
|
35
|
+
run: uv run mypy src
|
|
36
|
+
|
|
37
|
+
- name: Test (pytest)
|
|
38
|
+
run: uv run pytest --tb=short
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `arrowspace_tuner` are documented here.
|
|
4
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
5
|
+
Versioning follows [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## [0.1.0] — 2026-04-29
|
|
10
|
+
|
|
11
|
+
Initial release.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `EpsTuner` — main public class for hyperparameter discovery over `eps`, `k`, `tau`
|
|
16
|
+
- `arrowspace_tuner.optuna()` — one-liner convenience API: `aspace, gl = arrowspace.optuna(embeddings)`
|
|
17
|
+
- `StudyConfig` / `BuildParams` — typed dataclasses for power-user configuration
|
|
18
|
+
- Query-free spectral objective: weighted composite of MRR-Top0 proxy, Fiedler value, and lambda variance
|
|
19
|
+
- Optuna TPE sampler with pruning on degenerate graphs (NNZ ≤ N, disconnected, flat spectrum)
|
|
20
|
+
- `sample_n` subsampling: 33x speedup on 50k corpus with identical best params (validated)
|
|
21
|
+
- `storage` parameter for SQLite-backed persistence and parallel/resumed runs
|
|
22
|
+
- `tuner.save_report()` — saves `trials.csv`, `best_params.json`, and Plotly HTML plots
|
|
23
|
+
- `[report]` optional extra (pandas + plotly) — kept out of hard dependencies
|
|
24
|
+
- `py.typed` marker — PEP 561 compliant, full mypy strict mode
|
|
25
|
+
- Comprehensive test suite: `test_objective.py`, `test_tuner.py`, `conftest.py`
|
|
26
|
+
- CI workflow: pytest + ruff + mypy on every push and pull request
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright [2026] Tommaso Moriondo
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: arrowspace_tuner
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Hyperparameter discovery (eps auto-tuning) for ArrowSpace via Optuna.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Genefold/arrowspace_tuner
|
|
6
|
+
Project-URL: Repository, https://github.com/Genefold/arrowspace_tuner.git
|
|
7
|
+
Author-email: Tommaso Moriondo <moriondotommaso@gmail.com>
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: arrowspace,graph-laplacian,hyperparameter-tuning,optuna,spectral-analysis,vector-search
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.12
|
|
20
|
+
Requires-Dist: arrowspace>=0.26.0
|
|
21
|
+
Requires-Dist: numpy>=2.4.4
|
|
22
|
+
Requires-Dist: optuna>=4.8.0
|
|
23
|
+
Requires-Dist: scipy>=1.17.1
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: mypy>=1.15; extra == 'dev'
|
|
26
|
+
Requires-Dist: pandas>=3.0.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: plotly>=6.7.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: ruff>=0.9; extra == 'dev'
|
|
31
|
+
Provides-Extra: report
|
|
32
|
+
Requires-Dist: pandas>=3.0.0; extra == 'report'
|
|
33
|
+
Requires-Dist: plotly>=6.7.0; extra == 'report'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# arrowspace_tuner
|
|
37
|
+
|
|
38
|
+
[](https://github.com/Genefold/arrowspace_tuner/actions/workflows/ci.yml)
|
|
39
|
+
[](https://pypi.org/project/arrowspace-tuner/)
|
|
40
|
+
[](https://pypi.org/project/arrowspace-tuner/)
|
|
41
|
+
[](LICENSE)
|
|
42
|
+
|
|
43
|
+
Hyperparameter discovery for [ArrowSpace](https://github.com/tuned-org-uk/arrowspace-rs) — automatically finds the best `eps`, `k`, and `tau` for your corpus using a query-free spectral objective.
|
|
44
|
+
|
|
45
|
+
## Why
|
|
46
|
+
|
|
47
|
+
ArrowSpace's retrieval quality depends on three graph-construction parameters:
|
|
48
|
+
|
|
49
|
+
| Parameter | What it controls |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `eps` | Neighbourhood radius for graph edges |
|
|
52
|
+
| `k` | Number of nearest neighbours per node |
|
|
53
|
+
| `tau` | Search temperature (exploration vs. exploitation) |
|
|
54
|
+
|
|
55
|
+
Setting these by hand is tedious and corpus-dependent. `arrowspace_tuner` uses [Optuna](https://optuna.org/) and a label-free spectral MRR proxy to find them automatically in minutes.
|
|
56
|
+
|
|
57
|
+
## Install
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Core (no pandas/plotly)
|
|
61
|
+
pip install arrowspace-tuner
|
|
62
|
+
|
|
63
|
+
# With HTML/CSV reporting
|
|
64
|
+
pip install arrowspace-tuner[report]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Quickstart
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
import numpy as np
|
|
71
|
+
import arrowspace_tuner as arrowspace
|
|
72
|
+
|
|
73
|
+
embeddings = np.load("corpus.npy") # shape (N, D) float64
|
|
74
|
+
|
|
75
|
+
# One-liner: auto-discover eps, k, tau — runs in ~15 min on 50k corpus
|
|
76
|
+
aspace, gl = arrowspace.optuna(embeddings)
|
|
77
|
+
|
|
78
|
+
# Search as normal
|
|
79
|
+
results = aspace.search(query_embedding, gl, tau=0.8)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Power-user API
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from arrowspace_tuner import EpsTuner
|
|
86
|
+
|
|
87
|
+
tuner = EpsTuner(
|
|
88
|
+
n_trials = 15,
|
|
89
|
+
sample_n = 5_000, # 33x faster: explore on 5k, final build on full corpus
|
|
90
|
+
eps_low = 0.8, # narrow bounds if you know your corpus geometry
|
|
91
|
+
eps_high = 2.5,
|
|
92
|
+
k_low = 15,
|
|
93
|
+
k_high = 40,
|
|
94
|
+
tau_low = 0.05,
|
|
95
|
+
tau_high = 0.5,
|
|
96
|
+
n_probe = 50,
|
|
97
|
+
storage = "sqlite:///tune.db", # resume interrupted runs
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
aspace, gl = tuner.fit(embeddings)
|
|
101
|
+
|
|
102
|
+
print(tuner.best_params) # {"eps": 1.615, "k": 38, "tau": 0.114}
|
|
103
|
+
print(tuner.best_score) # 2.138
|
|
104
|
+
print(tuner.best_fiedler) # 0.718 — graph connectivity health
|
|
105
|
+
print(tuner.best_mrr_proxy) # 2.896 — retrieval coherence proxy
|
|
106
|
+
|
|
107
|
+
# Save CSV + HTML plots (requires [report] extra)
|
|
108
|
+
tuner.save_report(out_dir="results")
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Speed
|
|
112
|
+
|
|
113
|
+
The dominant cost is building the ArrowSpace graph on N vectors. With `sample_n`:
|
|
114
|
+
|
|
115
|
+
| Setting | Per trial | 15 trials | Notes |
|
|
116
|
+
|---|---|---|---|
|
|
117
|
+
| Full corpus (50k) | ~23 min | ~5.8h | baseline |
|
|
118
|
+
| `sample_n=5_000` | ~1.5 min | **~27 min** | **33x faster, same best params** |
|
|
119
|
+
|
|
120
|
+
The final build after the study always uses the full corpus.
|
|
121
|
+
|
|
122
|
+
## Objective
|
|
123
|
+
|
|
124
|
+
The objective is a weighted composite of three spectral signals — no ground-truth labels required:
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
score = 0.70 * mrr_top0_spectral # retrieval coherence
|
|
128
|
+
+ 0.20 * log1p(fiedler) # graph connectivity health
|
|
129
|
+
+ 0.10 * log1p(var_lambda) # spectral richness
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Parallel runs
|
|
133
|
+
|
|
134
|
+
Optuna + SQLite lets you run multiple workers simultaneously:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# Terminal 1
|
|
138
|
+
python -m arrowspace_tuner --storage sqlite:///tune.db --trials 15
|
|
139
|
+
|
|
140
|
+
# Terminal 2 (simultaneously)
|
|
141
|
+
python -m arrowspace_tuner --storage sqlite:///tune.db --trials 15
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Requirements
|
|
145
|
+
|
|
146
|
+
- Python ≥ 3.12
|
|
147
|
+
- `arrowspace >= 0.26.0`
|
|
148
|
+
- `optuna >= 4.8.0`
|
|
149
|
+
- `scipy >= 1.17.1`
|
|
150
|
+
- `numpy >= 2.4.4`
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
Apache-2.0 — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# arrowspace_tuner
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Genefold/arrowspace_tuner/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/arrowspace-tuner/)
|
|
5
|
+
[](https://pypi.org/project/arrowspace-tuner/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
Hyperparameter discovery for [ArrowSpace](https://github.com/tuned-org-uk/arrowspace-rs) — automatically finds the best `eps`, `k`, and `tau` for your corpus using a query-free spectral objective.
|
|
9
|
+
|
|
10
|
+
## Why
|
|
11
|
+
|
|
12
|
+
ArrowSpace's retrieval quality depends on three graph-construction parameters:
|
|
13
|
+
|
|
14
|
+
| Parameter | What it controls |
|
|
15
|
+
|---|---|
|
|
16
|
+
| `eps` | Neighbourhood radius for graph edges |
|
|
17
|
+
| `k` | Number of nearest neighbours per node |
|
|
18
|
+
| `tau` | Search temperature (exploration vs. exploitation) |
|
|
19
|
+
|
|
20
|
+
Setting these by hand is tedious and corpus-dependent. `arrowspace_tuner` uses [Optuna](https://optuna.org/) and a label-free spectral MRR proxy to find them automatically in minutes.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Core (no pandas/plotly)
|
|
26
|
+
pip install arrowspace-tuner
|
|
27
|
+
|
|
28
|
+
# With HTML/CSV reporting
|
|
29
|
+
pip install arrowspace-tuner[report]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quickstart
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
import numpy as np
|
|
36
|
+
import arrowspace_tuner as arrowspace
|
|
37
|
+
|
|
38
|
+
embeddings = np.load("corpus.npy") # shape (N, D) float64
|
|
39
|
+
|
|
40
|
+
# One-liner: auto-discover eps, k, tau — runs in ~15 min on 50k corpus
|
|
41
|
+
aspace, gl = arrowspace.optuna(embeddings)
|
|
42
|
+
|
|
43
|
+
# Search as normal
|
|
44
|
+
results = aspace.search(query_embedding, gl, tau=0.8)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Power-user API
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from arrowspace_tuner import EpsTuner
|
|
51
|
+
|
|
52
|
+
tuner = EpsTuner(
|
|
53
|
+
n_trials = 15,
|
|
54
|
+
sample_n = 5_000, # 33x faster: explore on 5k, final build on full corpus
|
|
55
|
+
eps_low = 0.8, # narrow bounds if you know your corpus geometry
|
|
56
|
+
eps_high = 2.5,
|
|
57
|
+
k_low = 15,
|
|
58
|
+
k_high = 40,
|
|
59
|
+
tau_low = 0.05,
|
|
60
|
+
tau_high = 0.5,
|
|
61
|
+
n_probe = 50,
|
|
62
|
+
storage = "sqlite:///tune.db", # resume interrupted runs
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
aspace, gl = tuner.fit(embeddings)
|
|
66
|
+
|
|
67
|
+
print(tuner.best_params) # {"eps": 1.615, "k": 38, "tau": 0.114}
|
|
68
|
+
print(tuner.best_score) # 2.138
|
|
69
|
+
print(tuner.best_fiedler) # 0.718 — graph connectivity health
|
|
70
|
+
print(tuner.best_mrr_proxy) # 2.896 — retrieval coherence proxy
|
|
71
|
+
|
|
72
|
+
# Save CSV + HTML plots (requires [report] extra)
|
|
73
|
+
tuner.save_report(out_dir="results")
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Speed
|
|
77
|
+
|
|
78
|
+
The dominant cost is building the ArrowSpace graph on N vectors. With `sample_n`:
|
|
79
|
+
|
|
80
|
+
| Setting | Per trial | 15 trials | Notes |
|
|
81
|
+
|---|---|---|---|
|
|
82
|
+
| Full corpus (50k) | ~23 min | ~5.8h | baseline |
|
|
83
|
+
| `sample_n=5_000` | ~1.5 min | **~27 min** | **33x faster, same best params** |
|
|
84
|
+
|
|
85
|
+
The final build after the study always uses the full corpus.
|
|
86
|
+
|
|
87
|
+
## Objective
|
|
88
|
+
|
|
89
|
+
The objective is a weighted composite of three spectral signals — no ground-truth labels required:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
score = 0.70 * mrr_top0_spectral # retrieval coherence
|
|
93
|
+
+ 0.20 * log1p(fiedler) # graph connectivity health
|
|
94
|
+
+ 0.10 * log1p(var_lambda) # spectral richness
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Parallel runs
|
|
98
|
+
|
|
99
|
+
Optuna + SQLite lets you run multiple workers simultaneously:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# Terminal 1
|
|
103
|
+
python -m arrowspace_tuner --storage sqlite:///tune.db --trials 15
|
|
104
|
+
|
|
105
|
+
# Terminal 2 (simultaneously)
|
|
106
|
+
python -m arrowspace_tuner --storage sqlite:///tune.db --trials 15
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Requirements
|
|
110
|
+
|
|
111
|
+
- Python ≥ 3.12
|
|
112
|
+
- `arrowspace >= 0.26.0`
|
|
113
|
+
- `optuna >= 4.8.0`
|
|
114
|
+
- `scipy >= 1.17.1`
|
|
115
|
+
- `numpy >= 2.4.4`
|
|
116
|
+
|
|
117
|
+
## License
|
|
118
|
+
|
|
119
|
+
Apache-2.0 — see [LICENSE](LICENSE).
|
|
File without changes
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "arrowspace_tuner"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Hyperparameter discovery (eps auto-tuning) for ArrowSpace via Optuna."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "Apache-2.0" }
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Tommaso Moriondo", email = "moriondotommaso@gmail.com" },
|
|
13
|
+
]
|
|
14
|
+
requires-python = ">=3.12"
|
|
15
|
+
keywords = [
|
|
16
|
+
"vector-search",
|
|
17
|
+
"spectral-analysis",
|
|
18
|
+
"hyperparameter-tuning",
|
|
19
|
+
"optuna",
|
|
20
|
+
"arrowspace",
|
|
21
|
+
"graph-laplacian",
|
|
22
|
+
]
|
|
23
|
+
classifiers = [
|
|
24
|
+
"Development Status :: 3 - Alpha",
|
|
25
|
+
"Intended Audience :: Developers",
|
|
26
|
+
"Intended Audience :: Science/Research",
|
|
27
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
28
|
+
"Programming Language :: Python :: 3",
|
|
29
|
+
"Programming Language :: Python :: 3.12",
|
|
30
|
+
"Programming Language :: Python :: 3.13",
|
|
31
|
+
"Typing :: Typed",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
# ── Hard dependencies ──────────────────────────────────────────────────────
|
|
35
|
+
# These are always required: the Rust wheel, Optuna, SciPy for Fiedler, NumPy.
|
|
36
|
+
# plotly and pandas are NOT here — they are opt-in via [report].
|
|
37
|
+
dependencies = [
|
|
38
|
+
"arrowspace>=0.26.0",
|
|
39
|
+
"numpy>=2.4.4",
|
|
40
|
+
"optuna>=4.8.0",
|
|
41
|
+
"scipy>=1.17.1",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
# ── Optional extras ────────────────────────────────────────────────────────
|
|
45
|
+
[project.optional-dependencies]
|
|
46
|
+
|
|
47
|
+
# pip install arrowspace_tuner[report]
|
|
48
|
+
# Needed for tuner.save_report() and all HTML/CSV output from reporter.py
|
|
49
|
+
report = [
|
|
50
|
+
"plotly>=6.7.0",
|
|
51
|
+
"pandas>=3.0.0",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
# pip install arrowspace_tuner[dev]
|
|
55
|
+
# Full dev environment: testing + linting + type checking
|
|
56
|
+
dev = [
|
|
57
|
+
"pytest>=8.0",
|
|
58
|
+
"pytest-cov>=5.0",
|
|
59
|
+
"ruff>=0.9",
|
|
60
|
+
"mypy>=1.15",
|
|
61
|
+
"plotly>=6.7.0", # needed to test reporter.py
|
|
62
|
+
"pandas>=3.0.0",
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
[project.urls]
|
|
66
|
+
Homepage = "https://github.com/Genefold/arrowspace_tuner"
|
|
67
|
+
Repository = "https://github.com/Genefold/arrowspace_tuner.git"
|
|
68
|
+
|
|
69
|
+
# ── Hatchling config ───────────────────────────────────────────────────────
|
|
70
|
+
[tool.hatch.build.targets.wheel]
|
|
71
|
+
packages = ["src/arrowspace_tuner"]
|
|
72
|
+
|
|
73
|
+
# ── Ruff ──────────────────────────────────────────────────────────────────
|
|
74
|
+
[tool.ruff]
|
|
75
|
+
line-length = 100
|
|
76
|
+
target-version = "py312"
|
|
77
|
+
|
|
78
|
+
[tool.ruff.lint]
|
|
79
|
+
select = ["E", "F", "I", "UP", "ANN"]
|
|
80
|
+
ignore = ["ANN101"]
|
|
81
|
+
|
|
82
|
+
# ── Mypy ──────────────────────────────────────────────────────────────────
|
|
83
|
+
[tool.mypy]
|
|
84
|
+
python_version = "3.12"
|
|
85
|
+
strict = true
|
|
86
|
+
ignore_missing_imports = true # arrowspace has no stubs
|
|
87
|
+
|
|
88
|
+
# ── Pytest ────────────────────────────────────────────────────────────────
|
|
89
|
+
[tool.pytest.ini_options]
|
|
90
|
+
testpaths = ["tests"]
|
|
91
|
+
addopts = "--cov=arrowspace_tuner --cov-report=term-missing"
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
arrowspace_tuner — hyperparameter discovery for ArrowSpace.
|
|
3
|
+
|
|
4
|
+
Quickstart
|
|
5
|
+
----------
|
|
6
|
+
import numpy as np
|
|
7
|
+
import arrowspace_tuner as arrowspace
|
|
8
|
+
|
|
9
|
+
embeddings = np.load("corpus.npy")
|
|
10
|
+
|
|
11
|
+
# one-liner: auto-discover eps, k, tau
|
|
12
|
+
aspace, gl = arrowspace.optuna(embeddings)
|
|
13
|
+
|
|
14
|
+
# power-user: full control + post-run inspection
|
|
15
|
+
from arrowspace_tuner import EpsTuner
|
|
16
|
+
|
|
17
|
+
tuner = EpsTuner(n_trials=100, sample_n=10_000, eps_low=0.5, eps_high=3.0)
|
|
18
|
+
aspace, gl = tuner.fit(embeddings)
|
|
19
|
+
print(tuner.best_params) # {"eps": 1.2, "k": 14, "tau": 0.8}
|
|
20
|
+
print(tuner.best_score)
|
|
21
|
+
tuner.save_report() # requires pip install arrowspace-tuner[report]
|
|
22
|
+
"""
|
|
23
|
+
from .api import optuna
|
|
24
|
+
from .tuner import EpsTuner
|
|
25
|
+
|
|
26
|
+
# Power-user exports: config dataclasses for advanced customisation
|
|
27
|
+
from .core import BuildParams, StudyConfig
|
|
28
|
+
|
|
29
|
+
__version__ = "0.1.0"
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
# primary public API
|
|
33
|
+
"optuna",
|
|
34
|
+
"EpsTuner",
|
|
35
|
+
# config — for power users
|
|
36
|
+
"BuildParams",
|
|
37
|
+
"StudyConfig",
|
|
38
|
+
# version
|
|
39
|
+
"__version__",
|
|
40
|
+
]
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""
|
|
2
|
+
api.py — one-liner convenience function for hyperparameter discovery.
|
|
3
|
+
|
|
4
|
+
This module exists solely to satisfy the acceptance criteria:
|
|
5
|
+
|
|
6
|
+
aspace, gl = arrowspace.optuna(embeddings)
|
|
7
|
+
|
|
8
|
+
It is a thin shim over EpsTuner with sensible defaults.
|
|
9
|
+
For any non-trivial use case, instantiate EpsTuner directly.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
from .core.config import _DEFAULT_N_TRIALS
|
|
16
|
+
from .tuner import EpsTuner
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def optuna(
|
|
20
|
+
embeddings: np.ndarray,
|
|
21
|
+
*,
|
|
22
|
+
n_trials: int = _DEFAULT_N_TRIALS,
|
|
23
|
+
sample_n: int | None = 5_000,
|
|
24
|
+
seed: int = 54,
|
|
25
|
+
study_name: str = "arrowspace_fstar",
|
|
26
|
+
storage: str | None = None,
|
|
27
|
+
eps_low: float = 0.3,
|
|
28
|
+
eps_high: float = 4.0,
|
|
29
|
+
k_low: int = 3,
|
|
30
|
+
k_high: int = 40,
|
|
31
|
+
tau_low: float = 0.1,
|
|
32
|
+
tau_high: float = 1.0,
|
|
33
|
+
n_probe: int = 50,
|
|
34
|
+
) -> tuple[object, object]:
|
|
35
|
+
"""
|
|
36
|
+
Auto-discover eps, k, and tau and return a ready-to-use (aspace, gl) pair.
|
|
37
|
+
|
|
38
|
+
This is the simplest entry point to arrowspace_tuner. It runs an Optuna
|
|
39
|
+
study with default settings and returns the ArrowSpace index built with
|
|
40
|
+
the best hyperparameters found.
|
|
41
|
+
|
|
42
|
+
Defaults are tuned for speed on large corpora (> 50k items):
|
|
43
|
+
- sample_n=5_000 gives a 33x speedup over full-corpus trials with
|
|
44
|
+
identical best params found (validated on a 50k CVE corpus).
|
|
45
|
+
- n_probe=50 is sufficient to rank parameter regions reliably.
|
|
46
|
+
- The final build after the study always uses the full corpus.
|
|
47
|
+
|
|
48
|
+
Parameters
|
|
49
|
+
----------
|
|
50
|
+
embeddings : np.ndarray
|
|
51
|
+
Shape (N, D) float64 corpus embeddings.
|
|
52
|
+
n_trials : int
|
|
53
|
+
Number of Optuna trials. Default 15.
|
|
54
|
+
sample_n : int | None
|
|
55
|
+
Subsample size per trial. Default 5_000. None = full corpus.
|
|
56
|
+
Recommended for large corpora (> 50k items).
|
|
57
|
+
seed : int
|
|
58
|
+
Random seed for reproducibility.
|
|
59
|
+
study_name : str
|
|
60
|
+
Optuna study identifier.
|
|
61
|
+
storage : str | None
|
|
62
|
+
Optuna storage URI for persistence. None = in-memory.
|
|
63
|
+
Use "sqlite:///tune.db" to resume interrupted runs.
|
|
64
|
+
eps_low, eps_high : float
|
|
65
|
+
Log-scale search bounds for eps.
|
|
66
|
+
k_low, k_high : int
|
|
67
|
+
Search bounds for k.
|
|
68
|
+
tau_low, tau_high : float
|
|
69
|
+
Search bounds for tau.
|
|
70
|
+
n_probe : int
|
|
71
|
+
Number of anchor queries per trial for the MRR proxy. Default 50.
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
aspace : ArrowSpace
|
|
76
|
+
ArrowSpace index built with the best hyperparameters found.
|
|
77
|
+
gl : GraphLaplacian
|
|
78
|
+
Corresponding graph Laplacian.
|
|
79
|
+
|
|
80
|
+
Examples
|
|
81
|
+
--------
|
|
82
|
+
Minimal usage — matches the acceptance criteria exactly:
|
|
83
|
+
|
|
84
|
+
import numpy as np
|
|
85
|
+
import arrowspace_tuner as arrowspace
|
|
86
|
+
|
|
87
|
+
embeddings = np.load("corpus.npy")
|
|
88
|
+
aspace, gl = arrowspace.optuna(embeddings)
|
|
89
|
+
|
|
90
|
+
results = aspace.search(query_embedding, gl, tau=0.8)
|
|
91
|
+
|
|
92
|
+
With a custom search range:
|
|
93
|
+
|
|
94
|
+
aspace, gl = arrowspace.optuna(
|
|
95
|
+
embeddings,
|
|
96
|
+
n_trials=30,
|
|
97
|
+
sample_n=10_000,
|
|
98
|
+
eps_low=0.5,
|
|
99
|
+
eps_high=3.0,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
Inspecting the study after the fact:
|
|
103
|
+
|
|
104
|
+
from arrowspace_tuner import EpsTuner
|
|
105
|
+
|
|
106
|
+
tuner = EpsTuner(n_trials=15, sample_n=5_000)
|
|
107
|
+
aspace, gl = tuner.fit(embeddings)
|
|
108
|
+
print(tuner.best_params)
|
|
109
|
+
tuner.save_report()
|
|
110
|
+
|
|
111
|
+
Resuming an interrupted run:
|
|
112
|
+
|
|
113
|
+
aspace, gl = arrowspace.optuna(
|
|
114
|
+
embeddings,
|
|
115
|
+
storage="sqlite:///tune.db",
|
|
116
|
+
)
|
|
117
|
+
"""
|
|
118
|
+
tuner = EpsTuner(
|
|
119
|
+
n_trials = n_trials,
|
|
120
|
+
sample_n = sample_n,
|
|
121
|
+
seed = seed,
|
|
122
|
+
study_name = study_name,
|
|
123
|
+
storage = storage,
|
|
124
|
+
eps_low = eps_low,
|
|
125
|
+
eps_high = eps_high,
|
|
126
|
+
k_low = k_low,
|
|
127
|
+
k_high = k_high,
|
|
128
|
+
tau_low = tau_low,
|
|
129
|
+
tau_high = tau_high,
|
|
130
|
+
n_probe = n_probe,
|
|
131
|
+
)
|
|
132
|
+
return tuner.fit(embeddings)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
arrowspace_tuner.core — internal building blocks.
|
|
3
|
+
|
|
4
|
+
This subpackage is not part of the public API.
|
|
5
|
+
Import from arrowspace_tuner directly:
|
|
6
|
+
|
|
7
|
+
from arrowspace_tuner import EpsTuner, optuna
|
|
8
|
+
from arrowspace_tuner import StudyConfig, BuildParams # for power users
|
|
9
|
+
"""
|
|
10
|
+
from .config import BuildParams, StudyConfig
|
|
11
|
+
from .graph import fiedler_normalized, gl_to_scipy
|
|
12
|
+
from .objective import build_and_score, make_objective
|
|
13
|
+
__version__ = "0.1.0"
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
# config
|
|
17
|
+
"BuildParams",
|
|
18
|
+
"StudyConfig",
|
|
19
|
+
# graph
|
|
20
|
+
"fiedler_normalized",
|
|
21
|
+
"gl_to_scipy",
|
|
22
|
+
# objective
|
|
23
|
+
"build_and_score",
|
|
24
|
+
"make_objective",
|
|
25
|
+
]
|