# tkbo マニュアル

## 1. 役割

`tkbo` は、**離散的な候補点集合から、次に評価する点を選ぶ**ためのベイズ最適化・適応学習パッケージです。回帰器そのものを比較・利用する `tkmlr` と責務を分け、`tkbo` は `initialize → ask → tell` の探索ループを担当します。

内部は次の3層に分かれます。

| 層 | 責務 |
|---|---|
| surrogate | 観測データから候補点の予測平均・標準偏差を返す |
| acquisition | 予測平均・標準偏差から候補点のスコアを計算する |
| backend / optimizer | 候補集合、観測済み点、`ask/tell` ループを管理する |

## 2. 推奨import

```python
from tkbo import create_optimizer
```

独自バックエンド等を登録するときだけ、次を追加します。

```python
from tkbo import register_backend, register_surrogate, register_acquisition
```

## 3. 最小例

```python
import numpy as np
from tkbo import create_optimizer

X = np.linspace(0.0, 10.0, 101).reshape(-1, 1)
y = np.full(len(X), np.nan)

initial = np.array([0, 30, 80])
y[initial] = np.sin(X[initial, 0])

optimizer = create_optimizer(
    model="sklearn_gpr",
    acquisition="ei",
    surrogate__alpha=1.0e-8,
    surrogate__n_restarts_optimizer=10,
)
optimizer.initialize(X, y=y)

suggestion = optimizer.ask(n_points=1)
index = int(suggestion.indices[0])
value = float(np.sin(X[index, 0]))  # 実際には実験・計算を実行
optimizer.tell(index, value)
```

`y` を候補数と同じ長さにし、未観測点を `np.nan` とする形式が最も単純です。

## 4. バックエンド

| `model` / `backend` | 意味 |
|---|---|
| `sklearn_gpr` | `CustomOptimizer` + sklearn GPR surrogate |
| `physbo_gp` | `CustomOptimizer` + PHYSBO surrogate |
| `custom` | surrogate と acquisition を明示指定 |
| `physbo` | PHYSBO native の離散探索 |
| `random` | 未観測候補からランダム選択 |
| `grid` | 元の順序で未観測候補を返す |

`model="sklearn_gpr"` と `model="physbo_gp"` は、内部で `backend="custom"` に変換されます。

## 5. acquisition関数

| 名前 | クラス | 主なパラメータ | 用途 |
|---|---|---|---|
| `ei` | `ExpectedImprovement` | `xi` | 改善量と不確かさの両方を考慮 |
| `pi` | `ProbabilityImprovement` | `xi` | 改善確率を最大化 |
| `ucb` | `UpperConfidenceBound` | `kappa` | 平均と不確かさの加重和 |
| `lcb` | `LowerConfidenceBound` | `kappa` | 最小化向けの下側信頼限界 |
| `stein` | `SteinLiteAcquisition` | `eps`, `std_weight`, `grad_weight` | 不確かさと平均勾配を使う探索heuristic |
| `entropy` | `EntropySearchLite` | なし | `log(std)` による不確かさ探索 |

`stein` は厳密なSVGDではなく、有限差分した予測平均の勾配を使う実務的な探索スコアです。

## 6. データ契約

- `X_candidates`: shape `(n_candidates, n_features)` の数値配列。1次元配列は `(n, 1)` に変換されます。
- `y`: 候補数と同じ長さで、未観測を `NaN` にするか、`observed_indices` と同じ長さにします。
- `observed_mask`: 候補数と同じ長さのbool配列。
- `ask()` の戻り値: `BOResult(indices, scores, X)`。
- `tell(indices, y_new)`: 候補インデックスと新しい目的値を登録します。
- `maximize=False`: 最小化問題として扱います。

観測点が1点もない状態では、`CustomOptimizer.ask()` は未観測点を先頭から返し、予測平均は `NaN`、標準偏差は `inf` になります。初期点をランダムにしたい場合は `random` backendを使うか、初期観測を明示してください。

## 7. パラメータの渡し方

`create_optimizer()` では、surrogate用とacquisition用のパラメータを接頭辞で分離します。

```python
optimizer = create_optimizer(
    model="sklearn_gpr",
    acquisition="ucb",
    maximize=True,
    surrogate__alpha=1.0e-8,
    surrogate__n_restarts_optimizer=20,
    acquisition__kappa=2.5,
)
```

接頭辞なしの引数はoptimizer backendへ渡されます。

## 8. 独自acquisitionの登録

```python
import numpy as np
from tkbo.base import BaseAcquisition
from tkbo import register_acquisition

class MeanPlusUncertainty(BaseAcquisition):
    name = "mean_plus_uncertainty"

    def __init__(self, weight=2.0):
        self.weight = float(weight)

    def __call__(self, X, mean, std, y_observed=None,
                 observed_mask=None, maximize=True, **kwargs):
        mean = np.asarray(mean, dtype=float)
        std = np.asarray(std, dtype=float)
        signed_mean = mean if maximize else -mean
        return signed_mean + self.weight * std

register_acquisition("mean_plus_uncertainty", MeanPlusUncertainty)
```

## 9. CLI

```bash
python -m tkbo.cli \
  --mode ask \
  --infile data.xlsx \
  --target "max:y" \
  --features x1,x2 \
  --model sklearn_gpr \
  --acquisition ei
```

CLIはpandasでCSV/Excelを読み、候補・予測・提案フラグを付加した表を出力します。正確なオプションは `python -m tkbo.cli --help` で確認してください。

## 10. 依存関係

- 必須コア: `numpy`
- analytic acquisition: `scipy`
- `sklearn_gpr`: `scikit-learn`
- `physbo` / `physbo_gp`: `physbo`
- CLI表入出力: `pandas`, Excelでは `openpyxl`

## 11. 生成AI向けコード生成ルール

1. 通常は `create_optimizer()` を使い、backendクラスを直接生成しない。
2. 離散候補集合を必ず先に用意し、`initialize()` を1回呼ぶ。
3. 未観測目的値は `NaN` とし、0や空文字で代用しない。
4. `ask()` が返すのは候補の**インデックス**であり、目的値ではない。
5. 実験・計算後に同じインデックスを `tell()` へ返す。
6. surrogateパラメータは `surrogate__`、acquisitionパラメータは `acquisition__` を付ける。
7. 連続変数最適化、制約最適化、多目的Pareto最適化を、このAPIだけで実装済みと仮定しない。
8. PHYSBO固有スコアとtkbo側acquisitionを混同しない。独自acquisitionには `model="physbo_gp"` または `model="sklearn_gpr"` を使う。
