fandhe-ai 0.13.0

Facade crate for a from-scratch Rust AI/ML library: tensors, autodiff, and CPU/CUDA/Metal backends behind one composition root
Documentation
//! numpy/Keras 慣習の互換 API 層(REQ-9・TASK-9.4・イシュー #411)。
//!
//! `autodiff` の自作コア(`Tape`/`Var`/`nn`)の上に薄いラッパーを被せ、
//! Python 慣習寄りの入口を提供する。テンソル生成は [`array`]
//! (numpy `np.array` 慣習)、レイヤー積み上げは `Sequential`
//! (Keras `Sequential` 慣習)。数値ロジック・shape 検査は一切持ち込まず
//! `tensor-core::Tensor::new`/`fandhe_ai_autodiff::nn::Module` へ委譲する(REQ-9
//! 「互換 API 層は自作コアの上の薄いラッパーに徹する」・`.claude/rules/
//! coding-rust.md`)。
//!
//! **配置の確定履歴**: TASK-9.2a(#95)は本モジュールを `fandhe_ai_autodiff::compat`
//! として確定していた(当時は 9 クレート構成で compat 専用クレートが
//! 存在せず、`autodiff` 配下以外に置く選択肢がなかったため)。10 クレート
//! 化(イシュー #52・`facade` の新設)を受け、TASK-9.3(#410)で
//! composition root(`Device` → 具体 `BackendOps` の結線)が `facade` へ
//! 一本化されたのに続き、TASK-9.4(本イシュー・#411)で compat 公開面も
//! `fandhe_ai_autodiff::compat` から本モジュール(`fandhe_ai::compat`)へ移設した。
//!
//! **サポート境界の明文化(TASK-9.4・REQ-9 の 2026-08-08 追記)**: `facade`
//! が唯一のサポートされる公開 API 面であり、`tensor-core`/`autodiff`/
//! `backend-*` は内部クレート(直接利用は非サポート)である。詳細・根拠は
//! `docs/compat-api-scope.md` の「サポート境界」節を参照(`docs/spec/
//! 04-requirements.md:209-210`・`05-tasks.md:322`)。
//!
//! **`predict_with_ops`(任意 `BackendOps` 注入経路)は本移設で公開面から
//! 撤去した**(破壊的変更。REQ-12「任意 `BackendOps` 実装を注入できる
//! 公開 API を設けない」・`crates/facade/tests/api_surface.rs` の機械検査と
//! 整合させるため)。`Sequential::predict` は本クレートの [`crate::tape`]
//! (既定 CPU・`CpuBackendOps`・融合有効)で `Tape` を構築して forward する
//! (`docs/public-api-design.md:431`「facade 経由なら既定バックエンドが
//! 透過的に効く」と整合)。ops を明示的に選びたい内部用途は
//! `Sequential::forward`(`&fandhe_ai_autodiff::Tape` を受け取るだけで `BackendOps`
//! は受け取らない)へ、呼び出し元が任意に構築した `Tape` を渡せば足りる。
//!
//! `lib.rs` クレート doc の「本クレート自体には互換レイヤ固有のロジック
//! を持ち込まない」は、「compat 層は本モジュールに隔離し、composition
//! root(`crate::tape`/`crate::tape_for`)へ互換慣習を漏らさない」という
//! 境界記述として引き継ぐ(依存方向は `compat` → `crate`(composition
//! root)/`autodiff`/`tensor_core` の一方向)。
//!
//! **対象範囲**(`docs/compat-api-scope.md` §1〜2): レイヤーは
//! `Sequential::add_*`(`sequential.rs` 冒頭・[`crate::compat::
//! Sequential`] 参照)が対応する種類に限る。当初の Linear・ReLU・
//! Sigmoid・Tanh の 3 種限定は、`Dropout`(#1603)・Conv2d/Conv1d
//! (#1770)・LayerNorm/RmsNorm/BatchNorm1d/BatchNorm2d/
//! Embedding/MultiheadAttention(#1760・親 #1618)を経て拡張済み。
//! `Sequential` 経由の学習
//! (勾配取得・パラメータ更新)は #294 で対応済み(`sequential.rs`
//! 冒頭 doc・`SequentialVars` 参照)。Keras 風 `compile()`/`fit()`/
//! `evaluate()` 最小版は [`crate::compat::Sequential::compile`]/
//! [`crate::compat::Sequential::fit`]/[`crate::compat::Sequential::evaluate`]
//! (イシュー #1761)で実装済み。callbacks
//! ([`crate::compat::EarlyStopping`]/[`crate::compat::ModelCheckpoint`]/
//! LR スケジューラ連携)・`validation_data` は
//! [`crate::compat::Sequential::fit_with_callbacks`](イシュー #1763・
//! 親 #1618・`callbacks` モジュール)で実装済み。分類 metrics
//! (accuracy・precision・recall・F1・confusion matrix)は
//! [`crate::compat::Sequential::fit_with_metrics`](イシュー #2072・
//! 親 #2059・`metrics` モジュール)で実装済み。`DataLoader` を直接
//! 受ける `fit` 入口は対象外のまま。層構成ごとのディレクトリ保存・復元
//! ([`crate::compat::save_model`]・[`crate::compat::load_model`]・[`crate::compat::ModelIoError`]。イシュー #2369・親 #2362)は
//! `add_*` 全 50 層(`add_module` の利用者定義層を除く。イシュー #2370)と、`compile`/
//! `compile_with_amp` 済みの状態(loss・optimizer 内部状態・AMP。#2372・#2373)に対応する
//! (BatchNorm の running stats も保存・復元する〈`num_batches_tracked` は非復元。#2371〉。
//! 旧世代ファイルの手動掃除・並行 save/load の非サポート・fsync 非保証の契約は
//! [`crate::compat::save_model`] の doc を参照)。

mod array;
mod callbacks;
// イシュー #2665 実装・#2679 公開: Functional API(#2667 で fit・evaluate〈`functional/train.rs`〉と
// 保存・復元〈`model_io/functional_io.rs`〉を追加)。型は下の `pub use` で公開する(詳細は `functional.rs` の
// モジュール doc と `docs/facade-functional-api-decision.md` §10・§16〜§18)。モジュール自体は非公開のまま。
mod functional;
mod logger_io;
mod metrics;
mod model_io;
mod sequential;
mod training;

pub use array::{ArrayData, array};
pub use callbacks::{
    Callback, CsvLogger, EarlyStopping, EmaCallback, JsonLogger, LambdaCallback, LrSchedule,
    ModelCheckpoint, Monitor, MonitorMode, SwaCallback,
};
/// `Sequential::add_multihead_attention_with_config` の引数型(イシュー #2530・ルート #2499 の
/// 承認形。再エクスポートはこの 1 行のみで `api_surface.rs` が完全一致で固定する)。
pub use fandhe_ai_autodiff::nn::MultiheadAttentionConfig;
pub use functional::{FunctionalBuilder, FunctionalModel, Node};
pub use metrics::{Metrics, MetricsResult};
pub use model_io::functional_io::{load_functional_model, save_functional_model};
pub use model_io::{ModelIoError, load_model, save_model};
pub use sequential::{Sequential, SequentialVars};
pub use training::{
    AmpConfig, AmpDType, FitConfig, FitTarget, FitWeights, History, Loss, Optimizer, TrainStepFn,
    TrainStepOptimizer, TrainStepOutput,
};

/// `TryReserveError`(`Vec::try_reserve_exact` 等の確保失敗)を
/// 非アロケーションなエラーへ写す共有ヘルパー(`training`・`metrics`
/// から `super::alloc_failed()` で参照する)。
///
/// 確保失敗を検出した直後に `format!` で新しい `String` を確保して
/// 報告すると、実メモリ枯渇時には報告経路そのものが
/// `handle_alloc_error` 経由で abort しうる(「確保失敗は panic/abort
/// せず `Err` で伝播する」契約を報告経路自身が破ることになる)。
/// `AutodiffError::Shape(ShapeError::ElementCountOverflow)` は unit
/// variant でヒープ確保を一切伴わないため、この経路の返り値として使う。
///
/// 意味論・パターンの出典: `fandhe_ai_autodiff::nn::init::alloc_failed`
/// (PR #2239・イシュー #2248)。autodiff 版は `pub(crate)` で facade
/// からは参照できないため、同じ意味論を facade 側にも複製する
/// (イシュー #2249)。
///
/// **適用範囲**: `Vec::try_reserve`/`try_reserve_exact` の失敗
/// (`TryReserveError`)経由のみ。`checked_mul` によるオーバーフロー
/// 検出や、長さ不一致・shape 不一致等の意味論エラーは診断メッセージを
/// 保持する価値が上回るため対象外とし、従来どおり
/// `AutodiffError::InvalidArgument(String)` を使い続ける。
fn alloc_failed() -> crate::AutodiffError {
    crate::AutodiffError::Shape(crate::ShapeError::ElementCountOverflow)
}

/// テスト直列化用ロック(`#[cfg(test)]` 限定・クレート内限定)。プロセス
/// グローバル RNG(`manual_seed` とその状態)を操作・観測するテストと、
/// グローバル RNG を暗黙に消費するテスト(`shuffle(true)` の `fit`・学習モード
/// の dropout 等)を、facade 単体テストバイナリ内の並列実行で排他する。
/// `tensor-core::rng::global_rng_test_lock` は `pub(crate)` のため別クレート
/// からは使えず、同型の静的ロックを facade 側に置く(公開 API は変えない)。
#[cfg(test)]
pub(crate) fn global_rng_test_lock() -> &'static std::sync::Mutex<()> {
    static LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
    &LOCK
}