1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
//! composition root(TASK-9.3・イシュー #410・spec 確定は
//! fandhe-ai-spec#52/spec PR #53。`docs/spec/05-tasks.md:315`)。
//!
//! `facade` は 2 つの責務を担う(spec 確定内容)。
//!
//! 1. **composition root(本クレート・本イシューで実装)**: [`Device`]
//! 識別子を受け取り、対応する具体 `BackendOps` 実装
//! (`fandhe_ai_backend_cpu::CpuBackendOps`/`fandhe_ai_backend_cuda::CudaBackendOps`/
//! `fandhe_ai_backend_metal::MetalBackendOps`)を構築して `fandhe_ai_autodiff::Tape` へ
//! 結線する。この結線ロジックを持つのは本クレートのみであり、
//! `tensor-core`/`autodiff`/`backend-*` は互いに他バックエンドを
//! 直接参照しない構造的境界(REQ-9・`docs/fusion-graph-design.md`
//! §3.4「`autodiff` は具体クレートへの依存を一切持たない」)を、
//! 上位でここに一本化する。
//! 2. **compat 公開面**([`compat::array`]/[`compat::Sequential`]):
//! TASK-9.4(イシュー #411)で `fandhe_ai_autodiff::compat` から本クレートへ
//! 移設し実装済み。サポート境界の明文化(`facade` が唯一のサポート
//! 対象公開 API 面であり `tensor-core`/`autodiff`/`backend-*` は
//! 内部クレート)は `docs/compat-api-scope.md` を参照。
//!
//! 3. **optim 公開面**([`optim`]。イシュー #961・親 #960): SGD・AdamW・
//! gradient clipping・LR スケジューラを `fandhe_ai::optim` の単一入口
//! へ再エクスポートする。値型・純関数のみのため REQ-12 と矛盾しない
//! (詳細は [`optim`] モジュール doc)。
//!
//! # 公開面の設計(REQ-12: 任意 `BackendOps` 注入の公開 API を設けない)
//!
//! 利用者向けに公開するのは [`Device`] 識別子を受け取る 2 関数
//! ([`tape`]・[`tape_for`])と、それに必要な最小限の型再エクスポート
//! ([`Device`]・[`BackendError`])のみである。`fandhe_ai_autodiff::Tape`・
//! `fandhe_ai_tensor_core::BackendOps` は本クレートから再エクスポートしない
//! (`fandhe_ai::Tape::new_with_ops(ops)` という経路がサポート面に露出すると
//! 「任意 `BackendOps` 実装を注入できる公開 API を設けない」(REQ-12)と
//! 矛盾するため)。この制約は `tests/api_surface.rs` がソース走査で
//! 機械的に固定する。
//!
//! **`Tape`(composition root が構築する値)の扱い(codex-review PR #424
//! P1 是正)**: [`tape`]・[`tape_for`] の戻り値は `fandhe_ai_autodiff::Tape` を
//! そのまま返すのではなく、本クレート所有の newtype [`Tape`] でラップする。
//! `fandhe_ai_autodiff::Tape` を素通しすると `Tape::new_with_ops` という
//! `BackendOps` 注入経路が facade 型として到達可能になり REQ-12 と矛盾する
//! ため、[`Tape`] は [`Tape::var`]/[`Tape::backward`] の 2 メソッドのみを
//! 再委譲する(構築は [`tape`]/[`tape_for`] 経由のみで、`fandhe_ai_autodiff::Tape`
//! の任意構築経路は到達不能なまま)。[`compat::Sequential::forward`]/
//! [`compat::Sequential::bind`] もこの [`Tape`] 型を引数に取る(旧実装は
//! `fandhe_ai_autodiff::Tape` を直に引数へ取っており、内部クレートの型が facade の
//! 公開シグネチャへ直接露出していた。codex-review 指摘)。
//!
//! **`Var`/`Gradients`/`AutodiffError`/`LinearVars`(`autodiff` 由来)・
//! `Tensor`(`tensor_core` 由来)の扱い**: これらは `BackendOps` 注入の
//! 迂回経路を持たない値型・エラー型であるため、`facade` の正式な公開契約
//! として本クレートから再エクスポートする(下記 `pub use`)。
//! `compat::{array, Sequential}` の公開シグネチャはこの再エクスポート
//! パス(`crate::{AutodiffError, Tensor}` 等)を使う。
//!
//! # `Device::Cuda(_)`/`Device::Metal` の構築規則
//!
//! spec は本規則を「TASK-9.3 実装時にユーザー承認を得て確定」とする
//! 未決事項として残しているため、以下の最小・自明な規則を採用した
//! (イシュー #410 実装計画 §2-2。PR 本文でユーザー確認を仰ぐ)。
//!
//! - `Device::Cpu` → `CpuBackendOps::new()`(常に利用可能なため検証不要)。
//! - `Device::Cuda(ordinal)` → `CudaDeviceProvider::select` で存在検証
//! (driver 不在・範囲外 ordinal は [`BackendError`] を返す fail-fast)
//! したうえで `CudaBackendOps::new(ordinal)` を構築する。イシュー #929:
//! `CudaBackendOps` の各演算メソッドは `backend-cuda` 側のプロセス内
//! キャッシュ(`crate::context_cache`。`ordinal` キー)を経由するため、
//! 同一プロセス内で 2 回目以降に `tape_for(Device::Cuda(_))` を呼んでも
//! `CudaContext` 生成・NVRTC コンパイルは再実行されない。`resolve_ops`
//! 自体(この関数)は毎回新しい `CudaBackendOps`/`Tape` を構築する
//! 軽量な値であり、重い初期化コストはバックエンド側キャッシュが吸収する。
//! - `Device::Metal`(`cfg(target_os = "macos")`)→ `MetalDeviceProvider::select`
//! で検証したうえで `MetalBackendOps::new()` を構築する。
//!
//! 既定デバイスの自動選択(GPU フォールバック等)・デバイス列挙の集約入口
//! (`Device::available()` 相当の facade 結線)は本イシューのスコープ外
//! (`docs/public-api-design.md` §4.1 の未決事項を尊重。
//! out-of-scope-tracking.md 対象)。
use select_from;
use ;
/// numpy/Keras 慣習の互換 API 層(compat 公開面。TASK-9.4・#411)。
/// [`compat::array`]・[`compat::Sequential`] を提供する(詳細はモジュール
/// doc・`docs/compat-api-scope.md` 参照)。
/// optimizer 公開面(イシュー #961・親 #960)。SGD・AdamW・gradient
/// clipping・LR スケジューラを再エクスポートする(詳細・適用順序契約は
/// モジュール doc 参照)。
// 公開面として再エクスポートする型(モジュール冒頭「公開面の設計」参照)。
// `fandhe_ai_autodiff::Tape`(生の型)・`fandhe_ai_tensor_core::BackendOps` は意図的に含めない
// (`Tape::new_with_ops` という BackendOps 注入経路が到達可能になるため。
// REQ-12)。`Var`/`Gradients`/`AutodiffError`/`LinearVars`・`Tensor` は
// 迂回経路を持たない値型・エラー型のため facade の正式な公開契約として
// 再エクスポートする(codex-review PR #424 P1 是正)。
// `tests/api_surface.rs::facade_does_not_reexport_tape_or_backend_ops` は
// `pub use` を行単位(`trimmed.starts_with("pub use")`)で走査するため、
// 複数行に折り返す `pub use fandhe_ai_autodiff::{ ... };` ブロックは
// 開き括弧の行しか検査対象に入らず、ブロック内部に `Tape` が紛れ込んでも
// 検出できない(レビュー指摘対応)。1 文 1 行を維持する。
//
// `SgdConfig` はここ(クレート root)と `crate::optim::SgdConfig`
// (イシュー #961)の 2 経路から再エクスポートされるが、いずれも
// `fandhe_ai_autodiff::optim::SgdConfig` を指す同一型である(再エクスポート
// 経路が 2 つあるだけで型は 1 つ。`Tape::step_device_param_store` の
// 引数型として本経路が既に使われているため、`optim` モジュール新設に
// 伴いこちら側を除去・付け替えることはしない)。
//
// `DeviceParamStore`/`Tape::step_device_param_store`(デバイス常駐更新
// 経路)は REQ-9 の 2026-08-29 追記(正本 spec
// `docs/spec/04-requirements.md:213`。実装リポ #984/#986)で `tape()`系・
// `compat`/`optim` と並ぶ確定入口となった(`docs/compat-api-scope.md` §0)。
pub use ;
pub use ;
pub use ;
/// composition root([`tape`]/[`tape_for`])が構築する `Tape` の
/// newtype ラッパー(codex-review PR #424 P1 是正)。
///
/// `fandhe_ai_autodiff::Tape` をそのまま公開すると `Tape::new_with_ops(ops)`
/// (任意 `BackendOps` 注入経路)が facade の型として到達可能になり
/// REQ-12「任意 `BackendOps` 実装を注入できる公開 API を設けない」と
/// 矛盾する。本型は内部の `fandhe_ai_autodiff::Tape`(フィールド `0`。`pub(crate)`
/// のためクレート外から直接構築・分解できない)が持つメソッドのうち
/// [`Tape::var`]/[`Tape::backward`] のみを再委譲し、それ以外(特に
/// `new_with_ops`)を到達不能に保つ。構築できるのは [`tape`]/[`tape_for`]
/// のみ(`pub(crate)` タプルフィールドのため crate 外からのフィールド
/// アクセス・構築は不能。`tests/api_surface.rs` が機械的に固定する)。
Tape);
/// 既定バックエンド(CPU・TASK-2.5 ユーザー承認済み。
/// `docs/public-api-design.md:429`)で [`Tape`] を構築する。
///
/// CPU は常に利用可能であるため非 fallible。`CpuBackendOps::new()`
/// (`run_fused` を `run_fused_elementwise` へオーバーライド済み=融合
/// 有効。`crates/backend-cpu/src/ops.rs`)を結線する唯一の入口。
/// 指定した [`Device`] へ明示的に結線した [`Tape`] を構築する。
///
/// 存在しないデバイス・範囲外 ordinal・driver 不在は [`BackendError`]
/// を返す(fail-fast。本番経路で `panic!`/`unwrap()` しない。
/// `.claude/rules/coding-rust.md`)。構築規則はモジュール冒頭コメント
/// 参照。
/// `device` に対応する具体 `BackendOps` を解決する(非公開)。
///
/// composition root の中核: `Device` → 具体バックエンドクレートの
/// `BackendOps` 実装への唯一の変換点。呼び出し元は [`tape_for`] のみ。
/// REQ-14 の明示解放 API(イシュー #1018 ツリー・#1020 CUDA・#1021
/// Metal)。`device` に対応するバックエンドのデバイスメモリプール
/// (`backend-cuda`/`backend-metal` のサイズクラス別プール実装)が
/// アイドル保持している分を即座に実解放する。プールを持たない
/// バックエンド(CPU 等)は何もせず `Ok(())` を返す(`BackendOps::
/// release_cached_device_memory` の既定契約。`docs/device-memory-pool-
/// design.md` §3.1「facade からの再公開」)。
///
/// `fandhe_ai_tensor_core::BackendOps::release_cached_device_memory` への
/// 薄い委譲(composition root。`docs/compat-api-scope.md` §0 の確定
/// 公開面)。`Device` は `tensor-core` 由来の外部型(識別子 enum)の
/// ため facade は inherent メソッドを追加できず(orphan rule。
/// `docs/facade-device-handle-design.md` の「案 B のみ採用」方針)、
/// [`tape_for`] と同型の自由関数として公開する。存在しないデバイス・
/// 範囲外 ordinal・driver 不在は [`BackendError`] を返す(`tape_for` と
/// 同じ fail-fast。`resolve_ops` を経由するため検証規則も同一)。
/// `device` のデバイスメモリプールの統計スナップショット(診断用。
/// イシュー #1020・#1021)。POD [`PoolStats`] のみを返し、内部ハンドル
/// 表現は含まない(`docs/device-memory-pool-design.md` §3.1「facade
/// からの再公開」)。プールを持たないバックエンドは `Ok(None)`
/// (`BackendOps::device_memory_pool_stats` の既定実装)。存在しない
/// デバイス・範囲外 ordinal・driver 不在は [`release_cached_memory`]
/// と同じく [`BackendError`] を返す。
/// CUDA GEMM(`fandhe_ai::tape().var(a).matmul(b)` 等が最終的に到達する
/// `CudaBackendOps::gemm`)の TF32 Tensor Core 経路を opt-in で有効化・
/// 無効化する(イシュー #1042。親ツリー #1029 Phase 2)。
///
/// `fandhe_ai_backend_cuda::precision::set_tf32_gemm_enabled` への薄い
/// 委譲(composition root。`docs/compat-api-scope.md` §0 の確定公開面)。
/// **既定は無効(FP32 厳密)**。有効化すると以降の全スレッド・全 CUDA
/// device の `gemm` 呼び出しがプロセスワイドに TF32 Tensor Core 経路へ
/// 切り替わる(`Device` 単位ではない。`fandhe_ai_backend_cuda::precision`
/// モジュール冒頭コメントの契約参照)。有効時に TF32 カーネルが使用不能
/// (cc<8.0・NVRTC コンパイル失敗等)な環境では `gemm` 呼び出しが
/// [`BackendError`] を返す(fail-closed。FP32 への黙示フォールバックは
/// しない)。数値一致許容誤差(相対 1e-3 未満 または 絶対 1e-5 未満)・
/// 適用範囲(`gemm_bias_act`・`gemm_resident_*`・学習経路は対象外)は
/// 変更しない(`docs/cuda-tf32-optin-api-decision.md`)。
/// [`set_cuda_tf32_gemm_enabled`] で設定した現在の opt-in 状態を返す
/// (既定 `false`)。