TRust-BO 第2段階用の 小型・高性能 Gaussian Process Optimizer(pure Rust)。
BLAS / LAPACK / OpenBLAS に依存せず、rand / rand_distr / thiserror のみで動作します。
行列計算はすべて自前の Cholesky 分解 + 前進/後退代入で行い、逆行列は一切作りません。
TRust-BO は2段構成の Bayesian Optimization エンジンです。
| 段階 | 担当 | 空間 |
|---|---|---|
| 第1段階 | MLP アンサンブル + Trust Region + PyO3 Rust core | 100D〜200D+ の全空間 |
| 第2段階 | 本クレート(GP valley refiner) | 第1段階が発見した谷の内部(2D〜50D) |
第1段階は 100D 問題で真の最適値 0.0 に対し y ≈ 8 前後の谷まで到達できます。
本クレートの役割は、その谷の中で局所構造を精密に学習し、y ≈ 8 から y ≈ 0 近傍まで詰めることです。
ベンチマークの quadratic-basin-20d / quadratic-basin-50d はまさにこのハンドオフ
(初期点 y ≈ 8 をシードし、そこから 0 へ精密化)を再現したものです。
- 厳密GPは学習点数 n に対して O(n³)、予測 O(n²)。高次元で必要になるサンプル数では計算が破綻する。
- 距離ベースのカーネルは高次元で距離が集中(concentration of measure)し、ARD lengthscale の 推定が不可能になる。100D を直接張る GP は「どこも等しく遠い」事前に退化する。
- 一方、谷の内部は実効次元が低く滑らかなので、低次元GPの得意領域。大域探索は スケールする第1段階(MLP + TR)に任せ、GP は局所精密化に専念する方が両者とも性能が出る。
2D〜20D は、数十〜数百評価で ARD lengthscale・outputscale・noise を周辺尤度から安定推定でき、 BoTorch の SingleTaskGP + LogEI / TuRBO と同等の挙動を狙える領域です。本クレートの 性能目標(BoTorch級)はこの範囲で設定しています。
動作可能・実用可能だが性能保証は弱いという位置づけです。具体的には:
- ハイパーパラメータ最適化は 12D 超で勾配スタート数と反復数を自動縮退 (有限差分勾配のコストが次元に比例するため)。
- 獲得関数最適化は coordinate search + CEM の組合せで 50D でも確実に動作。
- GP の学習点数は
max_gp_points(デフォルト256)で打ち切り、Trust Region 中心の 最近傍点のみでモデルを張る。 - 設計上限は 50D。それ以上は第1段階の担当。
use trustbo_gp::{GpConfig, GpOptimizer};
// 入力は [0,1]^d を想定(実領域への写像は呼び出し側が所有)。目的は最小化。
let mut opt = GpOptimizer::new(6, GpConfig::default(), 42);
// 第1段階からのハンドオフ: 既知の評価点を事前に tell してよい
// (init_design_size() 個以上 tell すると初期サンプリングをスキップ)。
for _ in 0..100 {
let x = opt.ask()?; // 次に評価すべき点
let y = objective(&x); // ユーザー側で評価
opt.tell(x, y)?; // 観測を登録
}
let (best_x, best_y) = opt.best().unwrap();
// バッチ提案(constant-liar fantasizing)
let batch = opt.ask_batch(4)?;
// GP 単体での利用も可能
use trustbo_gp::GpModel;
let model = GpModel::fit(&xs, &ys, &GpConfig::default())?;
let (mu, var) = model.predict(&x)?;
let cov = model.posterior_covariance(&queries)?;
let lml = model.log_marginal_likelihood();デフォルト設定: Matern 5/2 ARD カーネル + LogEI + TuRBO 型 Trust Region
(L_init=0.8, L_min=1e-3, L_max=1.6, success_tol=3, failure_tol=max(4,dim))。
カーネル(Matern52/Matern32/RBF)と獲得関数(EI/LogEI/PI/LCB/UCB/Thompson)は
GpConfig で切り替えられます。
negative log marginal likelihood を、log空間 bounds (lengthscale [1e-3,10] / outputscale [1e-6,1e6] / noise [1e-10,1e-1])内で最小化します。 単純ランダム探索ではなく:
- multi-start(16スタート、推奨初期値 + warm start)を NLL でランク付け
- 上位スタートを projected L-BFGS(中央差分勾配 + Armijo line search) で最適化
- random search fallback(勾配が全滅した場合はフル予算)
- 座標方向の local refinement で仕上げ
勾配計算は nll_grad 相当の1関数に隔離してあり、将来解析勾配へ差し替え可能です。
- Cholesky は jitter 1e-10 から10倍ずつ 1e-2 まで escalation、失敗時は
GpError::CholeskyFailed - 入力の NaN/inf は全公開APIで検出して
GpError::NonFiniteInput - 予測分散は 1e-12 で clamp
- LogEI は z « 0 の領域で漸近展開に切替え、EI がアンダーフローする領域でも有限値を維持
cargo run --release # 全ベンチマーク
cargo run --release -- branin # 名前でフィルタ
cargo run --release -- --seed 7 -v # シード変更 + 進捗表示
cargo test # テストスイートスイート: Branin 2D / Hartmann 3D / Hartmann 6D / Rosenbrock 4D / Ackley 5D / Levy 5D / Sphere 10D / shifted Sphere 10D / quadratic basin 20D / quadratic basin 50D。 出力には best value, regret, best x, 経過時間, カーネル, 獲得関数, TR length, ハイパーパラメータ, restart 回数が含まれます。
参考結果(デフォルト設定 Matern52 + LogEI、seed 42、シングルスレッド):
| benchmark | dim | evals | best | regret | 備考 |
|---|---|---|---|---|---|
| branin-2d | 2 | 80 | 0.397887 | 0.000000 | 真の最適値に6桁一致 |
| hartmann-3d | 3 | 80 | -3.862780 | 0.000000 | |
| hartmann-6d | 6 | 120 | -3.322368 | 0.000002 | |
| rosenbrock-4d | 4 | 120 | 0.001898 | 0.001898 | |
| ackley-5d | 5 | 150 | 0.009725 | 0.009725 | 領域 [-5,10]^5(TuRBO論文準拠) |
| levy-5d | 5 | 150 | 2.356455 | 2.356455 | 多峰性、restart 2回 |
| sphere-10d | 10 | 120 | 0.000025 | 0.000025 | |
| shifted-sphere-10d | 10 | 120 | 0.000007 | 0.000007 | |
| quadratic-basin-20d | 20 | 120 | 0.019307 | 0.019307 | 初期 y≈5.8 → 0.019 |
| quadratic-basin-50d | 50 | 200 | 1.435275 | 1.435275 | 初期 y≈6.2 → 1.44(設計上限域) |
basin 問題は第1段階ハンドオフの再現で、f≈8 のシェル上の点 40 個を tell でシードしてから
最適化を開始します(40点の最小値が初期 best)。全スイートの実行時間は約3.5分です。
ask/tell インターフェースは PyO3 バインディングを想定して、引数・戻り値を
Vec<f64> / f64 / Result のみで構成しています。接続予定の表面:
GpOptimizer::new(dim: usize, config: GpConfig, seed: u64) -> Self
GpOptimizer::ask(&mut self) -> Result<Vec<f64>, GpError>
GpOptimizer::ask_batch(&mut self, batch_size: usize) -> Result<Vec<Vec<f64>>, GpError>
GpOptimizer::tell(&mut self, x: Vec<f64>, y: f64) -> Result<(), GpError>
GpOptimizer::best(&self) -> Option<(Vec<f64>, f64)>
GpOptimizer::fit_model(&mut self) -> Result<(), GpError>
GpModel::fit / predict / predict_batch / posterior_covariance / log_marginal_likelihood
GpError は thiserror 製で Display を実装済みのため、PyErr への変換は文字列経由で機械的に行えます。
--features serde で全 Config が serde 対応になり、Python 側から JSON で設定を渡せます。
src/
lib.rs 公開API・クレートドキュメント
config.rs GpConfig / KernelType / AcquisitionType / 各サブ設定
error.rs GpError(thiserror)
linalg.rs Cholesky(+adaptive jitter)/前進後退代入/統計ヘルパ
kernel.rs Matern52 / Matern32 / RBF(すべてARD、log空間パラメータ)
gp.rs GpModel(fit / predict / posterior_covariance / LML)
hyperopt.rs multi-start + projected L-BFGS + fallback + local refinement
acquisition.rs EI / LogEI(安定版)/ PI / LCB / UCB / Thompson近似
candidate.rs LHS / uniform / CEM / coordinate search / multi-start局所探索
trust_region.rs TuRBO型 Trust Region(成功・失敗カウンタ、restart)
optimizer.rs GpOptimizer(ask / tell / ask_batch / restart制御)
benchmarks.rs ベンチマーク問題とランナー(GP本体とは ask/tell 経由で疎結合)
main.rs cargo run --release エントリポイント
tests/
gp_tests.rs / kernel_tests.rs / optimizer_tests.rs