Skip to main content

fandhe_ai/nn/
container.rs

1//! facade 側コンテナ `nn::ModuleList`/`nn::Sequential`(イシュー #2396・親 #2338)と
2//! `nn::ModuleDict`/`nn::summary`(イシュー #2402。autodiff #2134 の鏡写し)。
3//!
4//! 役割: facade 利用者が [`crate::nn::Module`] を実装した独自層を、facade だけの依存で
5//! 「積んで forward し、state_dict を取る」ための PyTorch `nn.ModuleList`/`nn.Sequential`
6//! 相当の薄いコンテナである(#2338 承認事項 3。`docs/facade-nn-module-exposure-decision.md`
7//! §6・§10・§14)。保持するのは `Box<dyn crate::nn::Module>` で、公開シグネチャに
8//! `fandhe_ai_autodiff` の型は出ない(REQ-12)。
9//!
10//! 意味論は `crates/autodiff/src/nn/container.rs` の同名コンテナと一致させる
11//! (`forward` の fail-closed、`"{index}.{name}"` 命名、`set_parameter` の接頭辞振り分けと
12//! 未知キーの `Err`、`set_training`/`training` の伝播、`children` の `"{index}"` 命名。#2401)。`state_dict`/`load_state_dict` は
13//! autodiff 側と同じく override せず、[`crate::nn::Module`] の既定実装(two-pass 検証・
14//! キー昇順適用・逆順ロールバック)に任せる。凍結 API(#2400)は `set_requires_grad` の
15//! 子への伝播と失敗時ロールバック(`0..=index` を逆順)、`requires_grad` の集約
16//! (パラメータを持つ子の `any`。該当なしは `true`)を autodiff 側と一致させる。
17//! ロールバックは子孫を葉(`children` が空の層)単位で復元する(公開の
18//! `Module::children`/`children_mut` 経由のため、利用者定義の複合層も対象。PR #2426
19//! レビュー指摘・2026-09-29 ユーザー承認)。
20//!
21//! 子の照合と検出範囲(PR #2426 第 7・8 回レビュー P1。`Module` 参照から真の同一性は利用者の
22//! 協力なしに得られないため、検出できる範囲を次のとおり定義する):
23//! - (a) `children`/`children_mut` は `set_requires_grad` の間、同じ子を返し続けることが
24//!   実装側の契約である。
25//! - (b) ライブラリは、件数・名前・型(`Module::type_name`)・データアドレス・`size_of_val` で
26//!   検出できる契約違反を、状態変更前の `InvalidArgument`(事前検査)または復元時の部分適用
27//!   エラーとして報告する。復元は自身の setter より前に子を照合し、不一致なら setter を
28//!   呼ばず部分適用エラーにする。
29//! - (c) 同じ型・同じ格納位置での値の置き換え(同じアドレスでの再確保を含む)は検出できず、
30//!   ロールバックは位置(格納スロット)単位で、そのスロットを凍結前の値へ戻す。
31//! - (d) setter の実行中の伝播先は利用者の実装の責任で、ライブラリは防げない(事後照合で
32//!   検出できる範囲のみ報告する)。`type_name` を偽って override する実装は信頼境界の外。
33//!
34//! autodiff 側との差: 子は facade `Module` の不透明な trait object のため、
35//! `Sequential::forward` は autodiff 側の Linear→ReLU 融合を行わず子を順に適用するだけである。
36//! また `push` した子のモードはコンテナの現在モードへ同期されない(autodiff 側と同じ。
37//! 次の `set_training` で伝わる)。`compat::Sequential`(`crate::compat::sequential`)とは
38//! 別パスの別型である。
39
40use std::collections::HashSet;
41
42use crate::nn::Module;
43use crate::nn::module::NodeKey;
44use crate::{AutodiffError, TapeRef, Tensor, Var};
45
46/// `set_requires_grad` ロールバック用の再帰スナップショット(葉単位。イシュー #2400・
47/// PR #2426 レビュー指摘)。
48///
49/// [`Module::children`] が非空の層は子ごと(名前付き)に再帰し、葉(`children` が空)は
50/// `requires_grad()` の値 1 つを保持する。集約値 1 つで潰すと、凍結済みと追跡中を併せ持つ
51/// 複合層が失敗時に均一化されてしまうため。複合層自身の `requires_grad` は復元対象外
52/// (子の葉が正)。`FacadeModuleAdapter` が autodiff の `Module::requires_grad_snapshot` へ
53/// `Opaque` として包んで渡す際にも使う(`pub(crate)`)。
54pub(crate) enum RequiresGradSnapshot {
55    Leaf(bool),
56    Nested {
57        own: bool,
58        /// 取得時点の子の照合キー(名前・型・データアドレス・サイズ)と葉単位スナップショット。
59        /// 復元時は自身の setter より前に照合し、キーで検出できる不一致の位置へは復元せず
60        /// (不一致なら自身の setter も呼ばない)部分適用エラーにする。同じ型・同じ位置の
61        /// 置き換えは検出できず位置単位で復元する(PR #2426 第 6〜8 回レビュー P1)。
62        children: Vec<(ChildIdentity, RequiresGradSnapshot)>,
63    },
64}
65
66/// `children` が返す 1 つの子の照合キー(名前・`Module::type_name`・データアドレス・サイズ)。
67/// `children_mut` 側の同位置の子と比べ、検出可能な範囲の食い違いを見つけるのに使う。真の
68/// 同一性ではない(同じ型・同じ位置の置き換えは区別できない)。
69type ChildIdentity = (String, &'static str, *const (), usize);
70
71/// `module` の凍結状態を葉単位で記録する。状態は変更しない。辿った各ノードで
72/// [`Module::children`] と [`Module::children_mut`] の件数・名前・順序・照合キー(型・
73/// アドレス・サイズ)が食い違う場合は `InvalidArgument`(`children_mut` 未実装の複合層・別の子を返す実装など。
74/// 呼び出し側が状態変更前に呼ぶことで、キーで検出できる契約違反の事前検査を兼ねる)。
75pub(crate) fn snapshot_requires_grad<M: Module + ?Sized>(
76    module: &mut M,
77) -> Result<RequiresGradSnapshot, AutodiffError> {
78    // 共有借用の参照は `children_mut` を呼ぶ前にここで drop し、照合キーだけ持ち越す。
79    let identities: Vec<ChildIdentity> = module
80        .children()
81        .into_iter()
82        .map(|(n, c)| {
83            (
84                n,
85                c.type_name(),
86                c as *const dyn Module as *const (),
87                std::mem::size_of_val(c),
88            )
89        })
90        .collect();
91    let type_name = module.type_name();
92    let nested = {
93        let children = module.children_mut();
94        check_children_consistent(type_name, &identities, &children)?;
95        children
96            .into_iter()
97            .zip(identities)
98            .map(|((_, child), identity)| Ok((identity, snapshot_requires_grad(child)?)))
99            .collect::<Result<Vec<_>, AutodiffError>>()?
100    };
101    if nested.is_empty() {
102        Ok(RequiresGradSnapshot::Leaf(module.requires_grad()))
103    } else {
104        Ok(RequiresGradSnapshot::Nested {
105            own: module.requires_grad(),
106            children: nested,
107        })
108    }
109}
110
111/// `children` の照合キー列と `children_mut` の結果が位置ごとに一致することを検査する
112/// (名前・`type_name`・データアドレス・`size_of_val` が同じ。不一致は `InvalidArgument`)。
113/// 一致は「同じ子」の証明ではない(同じ型・同じ位置の置き換えは検出できない)。vtable 比較は codegen unit をまたぐと値がずれ偽陰性になりうるため
114/// 行わない。サイズも比べるのは、ZST が後続フィールドとアドレスを共有しうるため
115/// (サイズまで同じ ZST 同士は状態を持たず区別不要)。
116fn check_children_consistent(
117    type_name: &str,
118    expected: &[ChildIdentity],
119    children_mut: &[(String, &mut dyn Module)],
120) -> Result<(), AutodiffError> {
121    let consistent = expected.len() == children_mut.len()
122        && expected
123            .iter()
124            .zip(children_mut)
125            .all(|((n, ty, addr, size), (m, c))| {
126                n == m
127                    && *ty == c.type_name()
128                    && *addr == (&**c as *const dyn Module as *const ())
129                    && *size == std::mem::size_of_val(&**c)
130            });
131    if consistent {
132        Ok(())
133    } else {
134        Err(AutodiffError::InvalidArgument(format!(
135            "set_requires_grad: `{type_name}` exposes children {:?} (types {:?}) via `children` \
136             but {:?} (types {:?}) via `children_mut` (a count, name, type, address or size \
137             mismatch); implement `children_mut` consistently with `children` (same count, \
138             names, order, types and child references) so that freezing can be rolled back \
139             per leaf",
140            expected.iter().map(|(n, _, _, _)| n).collect::<Vec<_>>(),
141            expected.iter().map(|(_, t, _, _)| t).collect::<Vec<_>>(),
142            children_mut.iter().map(|(n, _)| n).collect::<Vec<_>>(),
143            children_mut
144                .iter()
145                .map(|(_, c)| c.type_name())
146                .collect::<Vec<_>>()
147        )))
148    }
149}
150
151/// 位置 `i` の子が取得時の照合キー `expected` と一致するか(名前・`type_name`・
152/// データアドレス・`size_of_val`)。同じ型・同じ位置の置き換えは一致とみなす(位置単位の復元)。
153fn child_matches(expected: &ChildIdentity, name: &str, child: &dyn Module) -> bool {
154    let (exp_name, exp_type, exp_addr, exp_size) = expected;
155    exp_name == name
156        && *exp_type == child.type_name()
157        && *exp_addr == (child as *const dyn Module as *const ())
158        && *exp_size == std::mem::size_of_val(child)
159}
160
161/// スナップショットへ葉単位で復元する。
162///
163/// 葉は `requires_grad()` の一致で早期 `Ok` にせず必ず `set_requires_grad` を呼ぶ(getter が
164/// 既定 `true` のままの外部実装対策)が、呼ぶ**前に** `children()` が空のままであることを
165/// 確かめる。子を持つようになっていれば setter を呼ばず(未知の子へ伝播させない)、部分適用を
166/// 明示した `InvalidArgument` を返す(PR #2426 第 7 回レビュー P1)。
167///
168/// 入れ子は次の順で復元する(同第 7 回 P1 是正)。
169/// 1. **setter より前に照合する**: `children_mut` の結果を取得時の照合キー(名前・型・
170///    アドレス・`size_of_val`)と件数を含めて照合し、照合用の参照は照合後に drop する。
171///    1 つでも不一致なら自身への `set_requires_grad(own)` は**呼ばない**(自身の setter が
172///    キーの食い違う別の子へ伝播して書き換えるのを避ける)。
173/// 2. 全一致のときだけ自身へ `set_requires_grad(own)` を呼び、その後 `children_mut` を
174///    取り直して再照合しつつ、一致した位置の子を個別に再帰復元する。キーが食い違う位置の子
175///    には復元値を適用しない。
176/// 3. 自身の未復元・キーが食い違う子の未復元・各失敗は集約し、部分適用を明示した
177///    `InvalidArgument` を返す。`Ok` は偽装しない。
178///
179/// 検出範囲と限界(第 8 回 P1 で意味論として定義):
180/// - キーで検出できるのは件数・名前・型・アドレス・サイズの食い違いだけ。**同じ型・同じ
181///   格納位置での値の置き換え(同じアドレスでの再確保を含む)は検出できず、位置単位で復元
182///   する**(そのスロットを凍結前の値へ戻す。正当な意味論)。
183/// - 自身の setter の**実行中**の伝播先は利用者の実装の責任で、ライブラリは防げない。事後
184///   照合(手順 2)で検出できる範囲だけ部分適用エラーとして報告する。
185/// - `children`/`children_mut` は `set_requires_grad` の間、同じ子を返し続けることが実装側の
186///   契約で、`type_name` を偽って override する実装は信頼境界の外。
187pub(crate) fn restore_requires_grad<M: Module + ?Sized>(
188    module: &mut M,
189    snapshot: &RequiresGradSnapshot,
190) -> Result<(), AutodiffError> {
191    match snapshot {
192        RequiresGradSnapshot::Leaf(value) => {
193            // 取得時は葉だった層が子を持つようになっていれば、setter が未知の子へ伝播しうる。
194            let now = module.children().len();
195            if now != 0 {
196                return Err(AutodiffError::InvalidArgument(format!(
197                    "restore_requires_grad: a leaf at snapshot time now exposes {now} \
198                     child(ren); `set_requires_grad` was not called to avoid propagating to \
199                     unknown children, so the module may be left in a partially applied state"
200                )));
201            }
202            module.set_requires_grad(*value)
203        }
204        RequiresGradSnapshot::Nested {
205            own,
206            children: expected,
207        } => {
208            let mut failures: Vec<String> = Vec::new();
209            // 手順 1: setter の前に参照先を照合する(参照は照合後にここで drop)。
210            let references_intact = {
211                let children = module.children_mut();
212                children.len() == expected.len()
213                    && children
214                        .iter()
215                        .zip(expected)
216                        .all(|((name, child), (exp, _))| child_matches(exp, name, &**child))
217            };
218            if references_intact {
219                if let Err(e) = module.set_requires_grad(*own) {
220                    failures.push(format!("self ({e})"));
221                }
222            } else {
223                failures.push(
224                    "self (not restored: the children no longer match the snapshot in count, \
225                     name, type, address or size, so `set_requires_grad` was skipped to avoid \
226                     propagating to a different child)"
227                        .to_string(),
228                );
229            }
230            // 手順 2: 取り直して再照合し、一致した位置の子だけを個別に復元する。
231            let children = module.children_mut();
232            if children.len() != expected.len() {
233                failures.push(format!(
234                    "child count changed (snapshot {}, actual {}); only matching positions \
235                     were restored",
236                    expected.len(),
237                    children.len()
238                ));
239            }
240            for (pos, (name, child)) in children.into_iter().enumerate() {
241                let Some((exp, snap)) = expected.get(pos) else {
242                    break;
243                };
244                if !child_matches(exp, &name, &*child) {
245                    failures.push(format!(
246                        "child `{}` (its name, type, address or size no longer matches the \
247                         snapshot [expected type `{}`, found `{}` at `{name}`], so it was not \
248                         restored)",
249                        exp.0,
250                        exp.1,
251                        child.type_name()
252                    ));
253                    continue;
254                }
255                if let Err(e) = restore_requires_grad(child, snap) {
256                    failures.push(format!("child `{name}` ({e})"));
257                }
258            }
259            if failures.is_empty() {
260                Ok(())
261            } else {
262                Err(AutodiffError::InvalidArgument(format!(
263                    "restore_requires_grad: rollback incomplete, the module may be left in a \
264                     partially applied state: {}",
265                    failures.join(", ")
266                )))
267            }
268        }
269    }
270}
271
272/// `ModuleList`/`ModuleDict` 共通の `set_requires_grad` 本体(伝播+失敗時ロールバック)。
273///
274/// 適用前に各子を [`snapshot_requires_grad`] で葉単位に記録し、失敗したら失敗した子自身を
275/// 含む `0..=index` を逆順に葉単位で復元する(部分適用を残しうる外部実装対策)。復元失敗は
276/// 打ち切らず集約して部分適用を明示した `Err` を返す。`owner` はエラー文言の型名。
277fn set_requires_grad_with_rollback(
278    owner: &str,
279    mut children: Vec<&mut dyn Module>,
280    requires_grad: bool,
281) -> Result<(), AutodiffError> {
282    // 状態変更前の事前検査を兼ねる: `children`/`children_mut` が不整合な子孫が 1 つでも
283    // あれば、ここで `InvalidArgument` を返し何も変更しない。
284    let previous: Vec<RequiresGradSnapshot> = children
285        .iter_mut()
286        .map(|m| snapshot_requires_grad(&mut **m))
287        .collect::<Result<_, _>>()?;
288    for index in 0..children.len() {
289        if let Err(err) = children[index].set_requires_grad(requires_grad) {
290            let mut failures: Vec<String> = Vec::new();
291            for i in (0..=index).rev() {
292                if let Err(e) = restore_requires_grad(&mut *children[i], &previous[i]) {
293                    failures.push(format!("module {i} ({e})"));
294                }
295            }
296            if !failures.is_empty() {
297                return Err(AutodiffError::InvalidArgument(format!(
298                    "{owner}::set_requires_grad: failed to apply to module {index} \
299                     ({err}), and rollback also failed for: {}; the {owner} may now be \
300                     left in a partially applied state",
301                    failures.join(", ")
302                )));
303            }
304            return Err(err);
305        }
306    }
307    Ok(())
308}
309
310/// 子の `requires_grad` 集約(パラメータを持つ子の `any`。該当なしは `true`)。
311fn aggregate_requires_grad<'a>(children: impl Iterator<Item = &'a dyn Module>) -> bool {
312    let mut saw = false;
313    for m in children {
314        if m.parameter_count() == 0 {
315            continue;
316        }
317        saw = true;
318        if m.requires_grad() {
319            return true;
320        }
321    }
322    !saw
323}
324
325/// PyTorch `nn.ModuleList` 相当。forward を持たない子 `Module` の保持器。
326///
327/// `forward` は常に `AutodiffError::InvalidArgument` を返す。モードの正は自身のフラグ
328/// (既定 `true`)で、`set_training` は全子へ伝播する。
329pub struct ModuleList {
330    modules: Vec<Box<dyn Module>>,
331    training: bool,
332}
333
334impl Default for ModuleList {
335    fn default() -> Self {
336        Self::new()
337    }
338}
339
340impl ModuleList {
341    /// 空の `ModuleList` を作る。
342    pub fn new() -> Self {
343        ModuleList {
344            modules: Vec::new(),
345            training: true,
346        }
347    }
348
349    /// 子 `Module` を末尾へ追加する(子のモードは同期しない)。
350    pub fn push(&mut self, module: Box<dyn Module>) {
351        self.modules.push(module);
352    }
353
354    /// 保持している子 `Module` の数。
355    pub fn len(&self) -> usize {
356        self.modules.len()
357    }
358
359    /// 子 `Module` を 1 つも保持していないか。
360    pub fn is_empty(&self) -> bool {
361        self.modules.is_empty()
362    }
363
364    /// `index` の子への参照(範囲外は `None`。panic しない)。
365    pub fn get(&self, index: usize) -> Option<&dyn Module> {
366        self.modules.get(index).map(|m| m.as_ref())
367    }
368
369    /// [`Self::get`] の可変版。
370    pub fn get_mut(&mut self, index: usize) -> Option<&mut (dyn Module + 'static)> {
371        self.modules.get_mut(index).map(|m| m.as_mut())
372    }
373
374    /// 子の走査(層順)。
375    pub fn iter(&self) -> impl Iterator<Item = &Box<dyn Module>> {
376        self.modules.iter()
377    }
378
379    /// [`Self::iter`] の可変版。
380    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Box<dyn Module>> {
381        self.modules.iter_mut()
382    }
383}
384
385impl FromIterator<Box<dyn Module>> for ModuleList {
386    fn from_iter<I: IntoIterator<Item = Box<dyn Module>>>(iter: I) -> Self {
387        ModuleList {
388            modules: iter.into_iter().collect(),
389            training: true,
390        }
391    }
392}
393
394impl Module for ModuleList {
395    /// `nn.ModuleList` は forward を持たない。誤用を型付きエラーで検出する。
396    fn forward<'t>(&self, _tape: TapeRef<'t>, _input: &Var<'t>) -> Result<Var<'t>, AutodiffError> {
397        Err(AutodiffError::InvalidArgument(
398            "ModuleList has no forward (nn.ModuleList is a holder, not a callable module)"
399                .to_string(),
400        ))
401    }
402
403    /// 子は `"{index}"`(`named_parameters` の接頭辞と一致。#2401)。
404    fn children(&self) -> Vec<(String, &dyn Module)> {
405        self.modules
406            .iter()
407            .enumerate()
408            .map(|(i, m)| (i.to_string(), m.as_ref()))
409            .collect()
410    }
411
412    fn set_training(&mut self, training: bool) {
413        self.training = training;
414        for module in &mut self.modules {
415            module.set_training(training);
416        }
417    }
418
419    fn training(&self) -> bool {
420        self.training
421    }
422
423    /// 全子へ伝播する。失敗したら適用済みの子と失敗した子自身(`0..=index`)を逆順に
424    /// 適用前の値へ必ず戻し(`requires_grad()` が一致して見えても `set_requires_grad` を
425    /// 呼ぶ)、復元失敗は打ち切らず集約して `Err` に含める。子孫は `children`/`children_mut`
426    /// 経由で葉単位に復元するため、凍結済みと追跡中が混在する内側の複合層も
427    /// 呼び出し前と完全一致に戻る(`children_mut` 不整合な構成は変更前に `InvalidArgument`)(autodiff 側と同じ契約。PR #2426 レビュー指摘)。
428    fn set_requires_grad(&mut self, requires_grad: bool) -> Result<(), AutodiffError> {
429        let children: Vec<&mut dyn Module> = self
430            .modules
431            .iter_mut()
432            .map(|m| -> &mut dyn Module { m.as_mut() })
433            .collect();
434        set_requires_grad_with_rollback("ModuleList", children, requires_grad)
435    }
436
437    /// パラメータを持つ子のどれかが `true` なら `true`。該当する子がいなければ `true`。
438    fn requires_grad(&self) -> bool {
439        aggregate_requires_grad(self.modules.iter().map(|m| m.as_ref()))
440    }
441
442    /// `children` の可変版(名前・順序は同一。#2400)。
443    fn children_mut(&mut self) -> Vec<(String, &mut dyn Module)> {
444        self.modules
445            .iter_mut()
446            .enumerate()
447            .map(|(i, m)| -> (String, &mut dyn Module) { (i.to_string(), m.as_mut()) })
448            .collect()
449    }
450
451    fn named_parameters(&self) -> Vec<(String, &Tensor<f32>)> {
452        let mut out = Vec::new();
453        for (index, module) in self.modules.iter().enumerate() {
454            for (name, tensor) in module.named_parameters() {
455                out.push((format!("{index}.{name}"), tensor));
456            }
457        }
458        out
459    }
460
461    /// `"{index}.{name}"` を分解して子へ委譲する。区切りなし・index 非数値・範囲外は
462    /// すべて `InvalidArgument`(fail-closed。panic しない)。
463    fn set_parameter(&mut self, name: &str, value: Tensor<f32>) -> Result<(), AutodiffError> {
464        let (index_str, rest) = name.split_once('.').ok_or_else(|| {
465            AutodiffError::InvalidArgument(format!(
466                "ModuleList::set_parameter: no parameter named `{name}` (expected `{{index}}.{{name}}`)"
467            ))
468        })?;
469        let index: usize = index_str.parse().map_err(|_| {
470            AutodiffError::InvalidArgument(format!(
471                "ModuleList::set_parameter: no parameter named `{name}` (`{index_str}` is not a valid module index)"
472            ))
473        })?;
474        let len = self.modules.len();
475        match self.modules.get_mut(index) {
476            Some(module) => module.set_parameter(rest, value),
477            None => Err(AutodiffError::InvalidArgument(format!(
478                "ModuleList::set_parameter: no parameter named `{name}` (index {index} out of range; ModuleList has {len} modules)"
479            ))),
480        }
481    }
482}
483
484/// PyTorch `nn.Sequential` 相当。子 `Module` を層順に適用する facade 側コンテナ。
485///
486/// 空の `Sequential` は恒等写像。内部は [`ModuleList`] へ委譲する。Linear→ReLU 融合は
487/// 行わない(子が不透明な facade `Module` のため)。
488pub struct Sequential {
489    inner: ModuleList,
490}
491
492impl Default for Sequential {
493    fn default() -> Self {
494        Self::new()
495    }
496}
497
498impl Sequential {
499    /// 空の `Sequential` を作る。
500    pub fn new() -> Self {
501        Sequential {
502            inner: ModuleList::new(),
503        }
504    }
505
506    /// 子 `Module` を末尾へ追加する(子のモードは同期しない)。
507    pub fn push(&mut self, module: Box<dyn Module>) {
508        self.inner.push(module);
509    }
510
511    /// 所有権を消費するビルダー形式の追加(メソッドチェーン用)。
512    // `std::ops::Add::add` ではなく PyTorch 風ビルダーの慣用名のため trait 実装にしない。
513    #[allow(clippy::should_implement_trait)]
514    pub fn add<M: Module + 'static>(mut self, module: M) -> Self {
515        self.inner.push(Box::new(module));
516        self
517    }
518
519    /// 保持している層の数。
520    pub fn len(&self) -> usize {
521        self.inner.len()
522    }
523
524    /// 層を 1 つも保持していないか。
525    pub fn is_empty(&self) -> bool {
526        self.inner.is_empty()
527    }
528
529    /// 層列へのスライス参照。
530    pub fn layers(&self) -> &[Box<dyn Module>] {
531        &self.inner.modules
532    }
533
534    /// [`Self::layers`] の可変版。
535    pub fn layers_mut(&mut self) -> &mut [Box<dyn Module>] {
536        &mut self.inner.modules
537    }
538}
539
540impl From<ModuleList> for Sequential {
541    fn from(inner: ModuleList) -> Self {
542        Sequential { inner }
543    }
544}
545
546impl Module for Sequential {
547    fn forward<'t>(&self, tape: TapeRef<'t>, input: &Var<'t>) -> Result<Var<'t>, AutodiffError> {
548        let mut current = *input;
549        for layer in &self.inner.modules {
550            current = layer.forward(tape, &current)?;
551        }
552        Ok(current)
553    }
554
555    /// 内側 `ModuleList` へ委譲(`inner` 自体はノードとして出さない。#2401)。
556    fn children(&self) -> Vec<(String, &dyn Module)> {
557        self.inner.children()
558    }
559
560    fn set_training(&mut self, training: bool) {
561        self.inner.set_training(training);
562    }
563
564    fn training(&self) -> bool {
565        self.inner.training()
566    }
567
568    fn named_parameters(&self) -> Vec<(String, &Tensor<f32>)> {
569        self.inner.named_parameters()
570    }
571
572    fn set_parameter(&mut self, name: &str, value: Tensor<f32>) -> Result<(), AutodiffError> {
573        self.inner.set_parameter(name, value)
574    }
575
576    fn set_requires_grad(&mut self, requires_grad: bool) -> Result<(), AutodiffError> {
577        self.inner.set_requires_grad(requires_grad)
578    }
579
580    fn requires_grad(&self) -> bool {
581        self.inner.requires_grad()
582    }
583
584    fn children_mut(&mut self) -> Vec<(String, &mut dyn Module)> {
585        self.inner.children_mut()
586    }
587}
588
589/// PyTorch `nn.ModuleDict` 相当。キー付きの子 `Module` 保持器(イシュー #2402。
590/// autodiff `ModuleDict`〈#2134〉の鏡写し)。
591///
592/// 内部は挿入順を保つ `Vec<(String, Box<dyn Module>)>`(列挙順が「登録順」契約のため
593/// `HashMap` にしない。検索は O(n) だが層数は高々数十)。`forward` は常に
594/// `AutodiffError::InvalidArgument`(保持器のみ)。キーは空文字列と `'.'` 含みを拒否する。
595pub struct ModuleDict {
596    modules: Vec<(String, Box<dyn Module>)>,
597    training: bool,
598}
599
600impl Default for ModuleDict {
601    fn default() -> Self {
602        Self::new()
603    }
604}
605
606/// `ModuleDict` のキー検証(fail-closed。`.claude/rules/security.md` A03)。空キーは
607/// `named_modules` のパス連結で空セグメントを生み、`'.'` 含みは `set_parameter` の
608/// `split_once('.')` 解釈を壊すため拒否する。文言は autodiff 側とバイト一致させる。
609fn validate_module_dict_key(key: &str) -> Result<(), AutodiffError> {
610    if key.is_empty() {
611        return Err(AutodiffError::InvalidArgument(
612            "ModuleDict: key must not be empty".to_string(),
613        ));
614    }
615    if key.contains('.') {
616        return Err(AutodiffError::InvalidArgument(format!(
617            "ModuleDict: key `{key}` must not contain `.` (reserved for named_modules/\
618             set_parameter path separation)"
619        )));
620    }
621    Ok(())
622}
623
624impl ModuleDict {
625    /// 空の `ModuleDict` を作る。
626    pub fn new() -> Self {
627        ModuleDict {
628            modules: Vec::new(),
629            training: true,
630        }
631    }
632
633    /// `(key, module)` 列から構築する(挿入順を保持)。不正キーがあれば `Err`(panic しない)。
634    pub fn from_pairs(pairs: Vec<(String, Box<dyn Module>)>) -> Result<Self, AutodiffError> {
635        let mut dict = ModuleDict::new();
636        for (key, module) in pairs {
637            dict.insert(key, module)?;
638        }
639        Ok(dict)
640    }
641
642    /// `key` の子を挿入する。同名キーがあれば置換して旧値を返す(位置は保つ)。
643    /// 不正キー(空・`'.'` 含み)は `Err`。
644    pub fn insert(
645        &mut self,
646        key: impl Into<String>,
647        module: Box<dyn Module>,
648    ) -> Result<Option<Box<dyn Module>>, AutodiffError> {
649        let key = key.into();
650        validate_module_dict_key(&key)?;
651        if let Some(slot) = self.modules.iter_mut().find(|(k, _)| *k == key) {
652            Ok(Some(std::mem::replace(&mut slot.1, module)))
653        } else {
654            self.modules.push((key, module));
655            Ok(None)
656        }
657    }
658
659    /// `key` の子を削除して返す(無ければ `None`)。
660    pub fn remove(&mut self, key: &str) -> Option<Box<dyn Module>> {
661        let index = self.modules.iter().position(|(k, _)| k == key)?;
662        Some(self.modules.remove(index).1)
663    }
664
665    /// `key` の子への参照(無ければ `None`)。
666    pub fn get(&self, key: &str) -> Option<&dyn Module> {
667        self.modules
668            .iter()
669            .find(|(k, _)| k == key)
670            .map(|(_, m)| m.as_ref())
671    }
672
673    /// [`Self::get`] の可変版。
674    pub fn get_mut(&mut self, key: &str) -> Option<&mut (dyn Module + 'static)> {
675        self.modules
676            .iter_mut()
677            .find(|(k, _)| k == key)
678            .map(|(_, m)| m.as_mut())
679    }
680
681    /// `key` を保持しているか。
682    pub fn contains_key(&self, key: &str) -> bool {
683        self.modules.iter().any(|(k, _)| k == key)
684    }
685
686    /// キー列の走査(挿入順)。
687    pub fn keys(&self) -> impl Iterator<Item = &str> {
688        self.modules.iter().map(|(k, _)| k.as_str())
689    }
690
691    /// `(key, &dyn Module)` 列の走査(挿入順)。
692    pub fn iter(&self) -> impl Iterator<Item = (&str, &dyn Module)> {
693        self.modules.iter().map(|(k, m)| (k.as_str(), m.as_ref()))
694    }
695
696    /// [`Self::iter`] の可変版。
697    pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut (dyn Module + 'static))> {
698        self.modules
699            .iter_mut()
700            .map(|(k, m)| (k.as_str(), m.as_mut()))
701    }
702
703    /// 保持している子の数。
704    pub fn len(&self) -> usize {
705        self.modules.len()
706    }
707
708    /// 子を 1 つも保持していないか。
709    pub fn is_empty(&self) -> bool {
710        self.modules.is_empty()
711    }
712}
713
714impl Module for ModuleDict {
715    fn forward<'t>(&self, _tape: TapeRef<'t>, _input: &Var<'t>) -> Result<Var<'t>, AutodiffError> {
716        Err(AutodiffError::InvalidArgument(
717            "ModuleDict has no forward (nn.ModuleDict is a holder, not a callable module)"
718                .to_string(),
719        ))
720    }
721
722    fn set_training(&mut self, training: bool) {
723        self.training = training;
724        for (_, module) in &mut self.modules {
725            module.set_training(training);
726        }
727    }
728
729    fn training(&self) -> bool {
730        self.training
731    }
732
733    /// `"{key}.{name}"` を挿入順で連結する。
734    fn named_parameters(&self) -> Vec<(String, &Tensor<f32>)> {
735        let mut out = Vec::new();
736        for (key, module) in &self.modules {
737            for (name, tensor) in module.named_parameters() {
738                out.push((format!("{key}.{name}"), tensor));
739            }
740        }
741        out
742    }
743
744    /// 先頭の `'.'` までをキーとして子へ委譲する。区切りなし・未知キーは `InvalidArgument`。
745    fn set_parameter(&mut self, name: &str, value: Tensor<f32>) -> Result<(), AutodiffError> {
746        let (key, rest) = name.split_once('.').ok_or_else(|| {
747            AutodiffError::InvalidArgument(format!(
748                "ModuleDict::set_parameter: no parameter named `{name}` (expected `{{key}}.{{name}}`)"
749            ))
750        })?;
751        match self.get_mut(key) {
752            Some(module) => module.set_parameter(rest, value),
753            None => Err(AutodiffError::InvalidArgument(format!(
754                "ModuleDict::set_parameter: no parameter named `{name}` (no child module with key `{key}`)"
755            ))),
756        }
757    }
758
759    /// 名前はキー、順序は挿入順。
760    fn children(&self) -> Vec<(String, &dyn Module)> {
761        self.modules
762            .iter()
763            .map(|(key, module)| (key.clone(), module.as_ref()))
764            .collect()
765    }
766
767    /// 挿入順に全子へ伝播する。ロールバック・集約は [`ModuleList`] と同一
768    /// (`set_requires_grad_with_rollback`。#2402 の申し送りを #2400 で実施)。
769    fn set_requires_grad(&mut self, requires_grad: bool) -> Result<(), AutodiffError> {
770        let children: Vec<&mut dyn Module> = self
771            .modules
772            .iter_mut()
773            .map(|(_, m)| -> &mut dyn Module { m.as_mut() })
774            .collect();
775        set_requires_grad_with_rollback("ModuleDict", children, requires_grad)
776    }
777
778    /// パラメータを持つ子のどれかが `true` なら `true`。該当する子がいなければ `true`。
779    fn requires_grad(&self) -> bool {
780        aggregate_requires_grad(self.modules.iter().map(|(_, m)| m.as_ref()))
781    }
782
783    /// `children` の可変版(キー・挿入順は同一。#2400)。
784    fn children_mut(&mut self) -> Vec<(String, &mut dyn Module)> {
785        self.modules
786            .iter_mut()
787            .map(|(key, module)| -> (String, &mut dyn Module) { (key.clone(), module.as_mut()) })
788            .collect()
789    }
790}
791
792/// `type_name` の表示用短縮(`<` の手前で切り、最後の `::` 以降を取る)。
793fn short_type_name(full: &str) -> &str {
794    let without_generics = full.split('<').next().unwrap_or(full);
795    without_generics
796        .rsplit("::")
797        .next()
798        .unwrap_or(without_generics)
799}
800
801/// `module` の構造を人間可読な文字列へ整形する(PyTorch `print(model)` 相当。
802/// autodiff `summary`〈#2134〉の鏡写し。イシュー #2402)。
803///
804/// 子を持つノードは `"{Type}(\n"` の後に子を 2 スペースずつインデントして並べ
805/// `"{indent}) [params: N]\n"` で閉じる。葉は `"{indent}({name}): {Type} [params: N]\n"`
806/// (ルートが葉なら `"{Type} [params: N]\n"`)。末尾に `"Submodules: {named_modules 数}\n"`・
807/// `"Total parameters: {parameter_count}\n"` を付ける。shape 推定・`extra_repr` は対象外。
808///
809/// 循環は祖先スタックで打ち切り、ゼロサイズ型を除く共有子は `(データポインタ, type_name)`
810/// で dedup する(`named_modules` と同じ同一性キー)。型名は `std::any::type_name` 由来で
811/// 安定が保証されないため表示用途に限る。
812pub fn summary(module: &dyn Module) -> String {
813    let mut out = String::new();
814    let mut ancestors: Vec<NodeKey> =
815        vec![(module as *const dyn Module as *const (), module.type_name())];
816    let mut visited: HashSet<NodeKey> = HashSet::new();
817    write_module(&mut out, None, module, 0, &mut ancestors, &mut visited);
818    out.push_str(&format!("Submodules: {}\n", module.named_modules().len()));
819    out.push_str(&format!("Total parameters: {}\n", module.parameter_count()));
820    out
821}
822
823/// [`summary`] の再帰本体(`name` は子としての名前。ルートは `None`)。
824fn write_module(
825    out: &mut String,
826    name: Option<&str>,
827    module: &dyn Module,
828    depth: usize,
829    ancestors: &mut Vec<NodeKey>,
830    visited: &mut HashSet<NodeKey>,
831) {
832    let indent = "  ".repeat(depth);
833    let type_name = short_type_name(module.type_name());
834    let children = module.children();
835
836    if children.is_empty() {
837        match name {
838            Some(name) => out.push_str(&format!(
839                "{indent}({name}): {type_name} [params: {}]\n",
840                module.parameter_count()
841            )),
842            None => out.push_str(&format!(
843                "{type_name} [params: {}]\n",
844                module.parameter_count()
845            )),
846        }
847        return;
848    }
849
850    match name {
851        Some(name) => out.push_str(&format!("{indent}({name}): {type_name}(\n")),
852        None => out.push_str(&format!("{type_name}(\n")),
853    }
854    for (child_name, child) in &children {
855        let key: NodeKey = (*child as *const dyn Module as *const (), child.type_name());
856        if ancestors.contains(&key) {
857            continue;
858        }
859        let is_zst = std::mem::size_of_val(*child) == 0;
860        if !is_zst && !visited.insert(key) {
861            continue;
862        }
863        ancestors.push(key);
864        write_module(out, Some(child_name), *child, depth + 1, ancestors, visited);
865        ancestors.pop();
866    }
867    out.push_str(&format!(
868        "{indent}) [params: {}]\n",
869        module.parameter_count()
870    ));
871}
872#[cfg(test)]
873mod tests {
874    use super::*;
875    use fandhe_ai_autodiff::nn::Module as AutodiffModule;
876
877    /// テスト専用: autodiff 組み込み層を facade `Module` として積むための包み型
878    /// (#2397 の公開・本番アダプタとは別物)。autodiff 側コンテナとの文言一致検証に使う。
879    struct BuiltinLayer<L: AutodiffModule>(L);
880
881    impl<L: AutodiffModule> Module for BuiltinLayer<L> {
882        fn forward<'t>(
883            &self,
884            tape: TapeRef<'t>,
885            input: &Var<'t>,
886        ) -> Result<Var<'t>, AutodiffError> {
887            self.0.forward(tape.0, input)
888        }
889        fn named_parameters(&self) -> Vec<(String, &Tensor<f32>)> {
890            self.0.named_parameters()
891        }
892        fn set_parameter(&mut self, name: &str, value: Tensor<f32>) -> Result<(), AutodiffError> {
893            self.0.set_parameter(name, value)
894        }
895        fn type_name(&self) -> &'static str {
896            self.0.type_name()
897        }
898    }
899
900    fn linear() -> fandhe_ai_autodiff::nn::Linear {
901        fandhe_ai_autodiff::nn::Linear::new(3, 4, true, 7).expect("linear")
902    }
903
904    fn pair() -> (ModuleList, fandhe_ai_autodiff::nn::ModuleList) {
905        let mut mine = ModuleList::new();
906        mine.push(Box::new(BuiltinLayer(linear())));
907        let mut theirs = fandhe_ai_autodiff::nn::ModuleList::new();
908        theirs.push(Box::new(linear()));
909        (mine, theirs)
910    }
911
912    #[test]
913    fn names_and_errors_match_autodiff_container() {
914        let (mut mine, mut theirs) = pair();
915        let n1: Vec<String> = mine
916            .named_parameters()
917            .into_iter()
918            .map(|(n, _)| n)
919            .collect();
920        let n2: Vec<String> = theirs
921            .named_parameters()
922            .into_iter()
923            .map(|(n, _)| n)
924            .collect();
925        assert_eq!(n1, n2);
926
927        let t = crate::tape();
928        let x = t.var(&Tensor::from_slice(&[0.0f32; 3], &[1, 3]).expect("x"));
929        let e1 = mine
930            .forward(TapeRef::from(&t), &x)
931            .expect_err("Err")
932            .to_string();
933        let e2 = theirs.forward(&t.0, &x).expect_err("Err").to_string();
934        assert_eq!(e1, e2);
935
936        let v = Tensor::from_slice(&[0.0f32], &[1]).expect("v");
937        for key in ["weight", "x.weight", "9.weight", "0.nope"] {
938            let a = mine
939                .set_parameter(key, v.clone())
940                .expect_err("Err")
941                .to_string();
942            let b = theirs
943                .set_parameter(key, v.clone())
944                .expect_err("Err")
945                .to_string();
946            assert_eq!(a, b, "key={key}");
947        }
948    }
949    // ---- #2400: 凍結 API の伝播・集約・ロールバック ----
950
951    use std::cell::RefCell;
952    use std::rc::Rc;
953
954    type Calls = Rc<RefCell<Vec<bool>>>;
955
956    /// パラメータ持ちで `requires_grad` を保持する子。`fail_apply`/`fail_restore` が真だと
957    /// 対応する呼び出しで部分適用状態を残してから `Err` を返す。
958    struct Fz {
959        p: Tensor<f32>,
960        rg: bool,
961        fail_apply: bool,
962        fail_restore: bool,
963    }
964    impl Fz {
965        fn new() -> Self {
966            Fz {
967                p: Tensor::from_slice(&[1.0f32, 2.0], &[2]).expect("p"),
968                rg: true,
969                fail_apply: false,
970                fail_restore: false,
971            }
972        }
973    }
974    impl Module for Fz {
975        fn forward<'t>(
976            &self,
977            _tape: TapeRef<'t>,
978            input: &Var<'t>,
979        ) -> Result<Var<'t>, AutodiffError> {
980            Ok(*input)
981        }
982        fn named_parameters(&self) -> Vec<(String, &Tensor<f32>)> {
983            vec![("p".into(), &self.p)]
984        }
985        fn set_requires_grad(&mut self, v: bool) -> Result<(), AutodiffError> {
986            if (!v && self.fail_apply) || (v && self.fail_restore) {
987                self.rg = false;
988                return Err(AutodiffError::InvalidArgument("child failed".into()));
989            }
990            self.rg = v;
991            Ok(())
992        }
993        fn requires_grad(&self) -> bool {
994            self.rg
995        }
996    }
997
998    struct Ident;
999    impl Module for Ident {
1000        fn forward<'t>(
1001            &self,
1002            _tape: TapeRef<'t>,
1003            input: &Var<'t>,
1004        ) -> Result<Var<'t>, AutodiffError> {
1005            Ok(*input)
1006        }
1007    }
1008
1009    fn list_of(children: Vec<Box<dyn Module>>) -> ModuleList {
1010        children.into_iter().collect()
1011    }
1012
1013    #[test]
1014    fn freeze_propagates_and_aggregates() {
1015        let mut l = list_of(vec![
1016            Box::new(Fz::new()),
1017            Box::new(Ident),
1018            Box::new(Fz::new()),
1019        ]);
1020        assert!(l.requires_grad());
1021        l.freeze().expect("freeze");
1022        assert!(!l.requires_grad(), "無状態の子は集約から除外");
1023        assert!(!l.get(0).expect("0").requires_grad());
1024        assert!(!l.get(2).expect("2").requires_grad());
1025
1026        // any: 1 つでも true なら true。
1027        l.get_mut(0)
1028            .expect("0")
1029            .set_requires_grad(true)
1030            .expect("ok");
1031        assert!(l.requires_grad());
1032
1033        // 空・無状態のみは true。
1034        assert!(ModuleList::new().requires_grad());
1035        assert!(list_of(vec![Box::new(Ident)]).requires_grad());
1036    }
1037
1038    #[test]
1039    fn rollback_restores_failed_child_itself() {
1040        let mut failing = Fz::new();
1041        failing.fail_apply = true;
1042        let mut l = list_of(vec![Box::new(Fz::new()), Box::new(failing)]);
1043        let e = l.freeze().expect_err("2 番目が失敗");
1044        assert!(e.to_string().contains("child failed"), "{e}");
1045        assert!(l.get(0).expect("0").requires_grad());
1046        assert!(l.get(1).expect("1").requires_grad(), "失敗した子自身も戻る");
1047    }
1048
1049    #[test]
1050    fn rollback_calls_setter_even_when_trait_getter_looks_unchanged() {
1051        // requires_grad() が常に true の子でも、復元時に set_requires_grad が呼ばれる。
1052        struct Sticky(Calls, Tensor<f32>, bool);
1053        impl Module for Sticky {
1054            fn forward<'t>(
1055                &self,
1056                _tape: TapeRef<'t>,
1057                input: &Var<'t>,
1058            ) -> Result<Var<'t>, AutodiffError> {
1059                Ok(*input)
1060            }
1061            fn named_parameters(&self) -> Vec<(String, &Tensor<f32>)> {
1062                vec![("p".into(), &self.1)]
1063            }
1064            fn set_requires_grad(&mut self, v: bool) -> Result<(), AutodiffError> {
1065                self.0.borrow_mut().push(v);
1066                if self.2 && !v {
1067                    return Err(AutodiffError::InvalidArgument("fail".into()));
1068                }
1069                Ok(())
1070            }
1071        }
1072        let c: Calls = Rc::new(RefCell::new(Vec::new()));
1073        let p = Tensor::from_slice(&[1.0f32], &[1]).expect("p");
1074        let mut l = list_of(vec![
1075            Box::new(Sticky(c.clone(), p.clone(), false)),
1076            Box::new(Sticky(c.clone(), p, true)),
1077        ]);
1078        l.freeze().expect_err("fail");
1079        // 適用: 0(false)・1(false→Err)。復元: 1(true)・0(true)。
1080        assert_eq!(*c.borrow(), vec![false, false, true, true]);
1081    }
1082
1083    #[test]
1084    fn rollback_failures_are_aggregated_and_continue() {
1085        let mut a = Fz::new();
1086        a.fail_restore = true;
1087        let mut b = Fz::new();
1088        b.fail_apply = true;
1089        let mut l = list_of(vec![Box::new(a), Box::new(Fz::new()), Box::new(b)]);
1090        let e = l.freeze().expect_err("3 番目が失敗").to_string();
1091        assert!(e.contains("rollback also failed for"), "{e}");
1092        assert!(e.contains("partially applied state"), "{e}");
1093        assert!(e.contains("module 0"), "{e}");
1094        assert!(
1095            l.get(1).expect("1").requires_grad(),
1096            "他の子の復元は続行される"
1097        );
1098    }
1099
1100    #[test]
1101    fn sequential_delegates_and_nested_uniform_rollback() {
1102        let mut s = Sequential::new();
1103        s.push(Box::new(Fz::new()));
1104        s.freeze().expect("freeze");
1105        assert!(!s.requires_grad());
1106
1107        // ネスト(均一状態): 後続の子が失敗すると内側は true へ戻る。
1108        let mut failing = Fz::new();
1109        failing.fail_apply = true;
1110        let inner = list_of(vec![Box::new(Fz::new()), Box::new(Fz::new())]);
1111        let mut outer = Sequential::new();
1112        outer.push(Box::new(inner));
1113        outer.push(Box::new(failing));
1114        outer.freeze().expect_err("後続が失敗");
1115        assert!(outer.requires_grad());
1116        assert!(outer.layers()[0].requires_grad());
1117    }
1118
1119    /// 葉ごとの `requires_grad` を層順に平坦化して返す(入れ子は `children` で辿る)。
1120    fn leaf_states(m: &dyn Module) -> Vec<bool> {
1121        let children = m.children();
1122        if children.is_empty() {
1123            vec![m.requires_grad()]
1124        } else {
1125            children
1126                .into_iter()
1127                .flat_map(|(_, c)| leaf_states(c))
1128                .collect()
1129        }
1130    }
1131
1132    fn fz(rg: bool) -> Fz {
1133        let mut f = Fz::new();
1134        f.rg = rg;
1135        f
1136    }
1137
1138    fn failing_fz() -> Fz {
1139        let mut f = Fz::new();
1140        f.fail_apply = true;
1141        f
1142    }
1143
1144    /// PR #2426 P1: 内側が「凍結済み+追跡中」の混在でも、外側の後続子が失敗したとき
1145    /// 各葉が呼び出し前と完全一致する(ModuleList ネスト)。
1146    #[test]
1147    fn nested_module_list_mixed_state_restored_per_leaf_on_failure() {
1148        let inner = list_of(vec![Box::new(fz(false)), Box::new(fz(true))]);
1149        let mut outer = list_of(vec![Box::new(inner), Box::new(failing_fz())]);
1150        let before = leaf_states(&outer);
1151        assert_eq!(before, vec![false, true, true]);
1152        outer.freeze().expect_err("後続が失敗");
1153        assert_eq!(leaf_states(&outer), before);
1154    }
1155
1156    /// 同上(Sequential ネスト・2 段ネスト)。
1157    #[test]
1158    fn nested_sequential_mixed_state_restored_per_leaf_on_failure() {
1159        let mut deep = Sequential::new();
1160        deep.push(Box::new(fz(true)));
1161        deep.push(Box::new(fz(false)));
1162        let inner = list_of(vec![Box::new(fz(false)), Box::new(deep)]);
1163        let mut outer = Sequential::new();
1164        outer.push(Box::new(inner));
1165        outer.push(Box::new(failing_fz()));
1166        let before = leaf_states(&outer);
1167        assert_eq!(before, vec![false, true, false, true]);
1168        outer.freeze().expect_err("後続が失敗");
1169        assert_eq!(leaf_states(&outer), before);
1170    }
1171
1172    /// 同上(ModuleDict ネスト。外側が ModuleDict・内側が ModuleDict の両方)。
1173    #[test]
1174    fn nested_module_dict_mixed_state_restored_per_leaf_on_failure() {
1175        let mut inner = ModuleDict::new();
1176        inner.insert("a", Box::new(fz(false))).expect("k");
1177        inner.insert("b", Box::new(fz(true))).expect("k");
1178        let mut outer = ModuleDict::new();
1179        outer.insert("in", Box::new(inner)).expect("k");
1180        outer.insert("bad", Box::new(failing_fz())).expect("k");
1181        let before = leaf_states(&outer);
1182        assert_eq!(before, vec![false, true, true]);
1183        outer.freeze().expect_err("後続が失敗");
1184        assert_eq!(leaf_states(&outer), before);
1185    }
1186
1187    /// ModuleDict の伝播・集約(#2402 申し送り)と、成功時は入れ子も全葉へ伝播する。
1188    #[test]
1189    fn module_dict_freeze_propagates_and_aggregates() {
1190        let mut d = ModuleDict::new();
1191        d.insert("a", Box::new(Fz::new())).expect("k");
1192        d.insert("n", Box::new(Ident)).expect("k");
1193        d.insert("l", Box::new(list_of(vec![Box::new(fz(true))])))
1194            .expect("k");
1195        assert!(d.requires_grad());
1196        d.freeze().expect("freeze");
1197        assert_eq!(leaf_states(&d), vec![false, true, false]);
1198        assert!(!d.requires_grad());
1199        d.set_requires_grad(true).expect("unfreeze");
1200        assert!(d.requires_grad());
1201        assert!(ModuleDict::new().requires_grad());
1202    }
1203
1204    /// アダプタが返す `Opaque` スナップショットから facade 側の葉状態を平坦化して読む。
1205    fn adapter_leaf_states(m: &mut dyn fandhe_ai_autodiff::nn::Module) -> Vec<bool> {
1206        fn flat(s: &super::RequiresGradSnapshot, out: &mut Vec<bool>) {
1207            match s {
1208                super::RequiresGradSnapshot::Leaf(v) => out.push(*v),
1209                super::RequiresGradSnapshot::Nested { children: c, .. } => {
1210                    c.iter().for_each(|(_, x)| flat(x, out))
1211                }
1212            }
1213        }
1214        let snap = m.requires_grad_snapshot().expect("snapshot");
1215        let fandhe_ai_autodiff::nn::RequiresGradSnapshot::Opaque(any) = snap else {
1216            panic!("アダプタは Opaque を返すはず");
1217        };
1218        let inner = any
1219            .downcast_ref::<super::RequiresGradSnapshot>()
1220            .expect("facade スナップショット");
1221        let mut out = Vec::new();
1222        flat(inner, &mut out);
1223        out
1224    }
1225
1226    fn adapted(
1227        m: impl Module + 'static,
1228    ) -> crate::nn::module::FacadeModuleAdapter<Box<dyn Module>> {
1229        crate::nn::module::FacadeModuleAdapter(Box::new(m) as Box<dyn Module>)
1230    }
1231
1232    fn mixed_facade_container() -> ModuleList {
1233        let inner = list_of(vec![Box::new(fz(false)), Box::new(fz(true))]);
1234        list_of(vec![Box::new(fz(false)), Box::new(inner)])
1235    }
1236
1237    /// PR #2426 P1(アダプタ境界): 混在状態の facade コンテナをアダプタで包んで autodiff の
1238    /// `ModuleList` に積み、後続の子が失敗しても全葉が呼び出し前と完全一致する。
1239    #[test]
1240    fn adapter_in_autodiff_module_list_restores_mixed_facade_container_per_leaf() {
1241        let mut outer = fandhe_ai_autodiff::nn::ModuleList::new();
1242        outer.push(Box::new(adapted(mixed_facade_container())));
1243        outer.push(Box::new(adapted(failing_fz())));
1244        let before = adapter_leaf_states(outer.get_mut(0).expect("0"));
1245        assert_eq!(before, vec![false, false, true]);
1246        fandhe_ai_autodiff::nn::Module::set_requires_grad(&mut outer, false).expect_err("失敗");
1247        assert_eq!(adapter_leaf_states(outer.get_mut(0).expect("0")), before);
1248    }
1249
1250    /// 同上(autodiff `Sequential`・入れ子 autodiff `ModuleList` 経由)。
1251    #[test]
1252    fn adapter_in_autodiff_sequential_restores_mixed_facade_container_per_leaf() {
1253        let mut inner = fandhe_ai_autodiff::nn::ModuleList::new();
1254        inner.push(Box::new(adapted(mixed_facade_container())));
1255        let mut outer = fandhe_ai_autodiff::nn::Sequential::new();
1256        outer.push(Box::new(inner));
1257        outer.push(Box::new(adapted(failing_fz())));
1258        let list = outer.layers_mut()[0]
1259            .as_module_list_mut()
1260            .expect("ModuleList");
1261        let before = adapter_leaf_states(list.get_mut(0).expect("0"));
1262        fandhe_ai_autodiff::nn::Module::set_requires_grad(&mut outer, false).expect_err("失敗");
1263        let list = outer.layers_mut()[0]
1264            .as_module_list_mut()
1265            .expect("ModuleList");
1266        assert_eq!(adapter_leaf_states(list.get_mut(0).expect("0")), before);
1267    }
1268
1269    /// 同上(autodiff `ModuleDict`)。
1270    #[test]
1271    fn adapter_in_autodiff_module_dict_restores_mixed_facade_container_per_leaf() {
1272        let mut outer = fandhe_ai_autodiff::nn::ModuleDict::new();
1273        outer
1274            .insert("m", Box::new(adapted(mixed_facade_container())))
1275            .expect("k");
1276        outer
1277            .insert("bad", Box::new(adapted(failing_fz())))
1278            .expect("k");
1279        let before = adapter_leaf_states(outer.get_mut("m").expect("m"));
1280        fandhe_ai_autodiff::nn::Module::set_requires_grad(&mut outer, false).expect_err("失敗");
1281        assert_eq!(adapter_leaf_states(outer.get_mut("m").expect("m")), before);
1282    }
1283
1284    /// 自身の `requires_grad` フラグも持つ利用者定義の複合層(PR #2426 第 4 の P1)。
1285    /// `own` は外部から観測するため共有セルで保持する。
1286    struct SelfFlagComp {
1287        own: Rc<std::cell::Cell<bool>>,
1288        a: Fz,
1289        b: Fz,
1290    }
1291    impl Module for SelfFlagComp {
1292        fn forward<'t>(
1293            &self,
1294            _tape: TapeRef<'t>,
1295            input: &Var<'t>,
1296        ) -> Result<Var<'t>, AutodiffError> {
1297            Ok(*input)
1298        }
1299        fn named_parameters(&self) -> Vec<(String, &Tensor<f32>)> {
1300            vec![("a.p".into(), &self.a.p), ("b.p".into(), &self.b.p)]
1301        }
1302        fn set_requires_grad(&mut self, v: bool) -> Result<(), AutodiffError> {
1303            self.own.set(v);
1304            self.a.set_requires_grad(v)?;
1305            self.b.set_requires_grad(v)
1306        }
1307        fn requires_grad(&self) -> bool {
1308            self.own.get()
1309        }
1310        fn children(&self) -> Vec<(String, &dyn Module)> {
1311            vec![("a".into(), &self.a), ("b".into(), &self.b)]
1312        }
1313        fn children_mut(&mut self) -> Vec<(String, &mut dyn Module)> {
1314            vec![("a".into(), &mut self.a), ("b".into(), &mut self.b)]
1315        }
1316    }
1317
1318    /// PR #2426 第 4 の P1(アダプタ境界): 自身のフラグを持つ複合層をアダプタで包んで autodiff の
1319    /// `ModuleList` に積み、後続の子が失敗しても自身のフラグと全葉が呼び出し前へ戻る。
1320    #[test]
1321    fn adapter_restores_composite_own_flag_after_later_failure() {
1322        let own = Rc::new(std::cell::Cell::new(true));
1323        let mut a = Fz::new();
1324        a.rg = false;
1325        let comp = SelfFlagComp {
1326            own: Rc::clone(&own),
1327            a,
1328            b: Fz::new(),
1329        };
1330        let mut outer = fandhe_ai_autodiff::nn::ModuleList::new();
1331        outer.push(Box::new(adapted(comp)));
1332        outer.push(Box::new(adapted(failing_fz())));
1333        let before = adapter_leaf_states(outer.get_mut(0).expect("0"));
1334        assert_eq!(before, vec![false, true]);
1335        fandhe_ai_autodiff::nn::Module::set_requires_grad(&mut outer, false).expect_err("失敗");
1336        assert!(own.get(), "自身のフラグが呼び出し前へ戻る");
1337        assert_eq!(adapter_leaf_states(outer.get_mut(0).expect("0")), before);
1338    }
1339
1340    /// facade コンテナ経路: 複合層自身のフラグと全葉が失敗後に戻る(子の失敗が複合層内部でも)。
1341    #[test]
1342    fn facade_list_restores_composite_own_flag_after_failure() {
1343        let own = Rc::new(std::cell::Cell::new(true));
1344        let mut a = Fz::new();
1345        a.rg = false;
1346        let mut bad = Fz::new();
1347        bad.fail_apply = true;
1348        let comp = SelfFlagComp {
1349            own: Rc::clone(&own),
1350            a,
1351            b: bad,
1352        };
1353        let mut l = list_of(vec![Box::new(comp)]);
1354        l.freeze().expect_err("複合層内部の失敗");
1355        assert!(own.get(), "自身のフラグが呼び出し前へ戻る");
1356        assert_eq!(leaf_states_of(&l), vec![false, true]);
1357    }
1358
1359    fn leaf_states_of(m: &dyn Module) -> Vec<bool> {
1360        let c = m.children();
1361        if c.is_empty() {
1362            vec![m.requires_grad()]
1363        } else {
1364            c.into_iter().flat_map(|(_, x)| leaf_states_of(x)).collect()
1365        }
1366    }
1367
1368    /// 利用者定義の複合層(`children`/`children_mut` 実装あり。PR #2426 第 3 の P1)。
1369    struct Comp {
1370        a: Fz,
1371        b: Fz,
1372    }
1373    impl Module for Comp {
1374        fn forward<'t>(
1375            &self,
1376            _tape: TapeRef<'t>,
1377            input: &Var<'t>,
1378        ) -> Result<Var<'t>, AutodiffError> {
1379            Ok(*input)
1380        }
1381        fn named_parameters(&self) -> Vec<(String, &Tensor<f32>)> {
1382            vec![("a.p".into(), &self.a.p), ("b.p".into(), &self.b.p)]
1383        }
1384        fn set_requires_grad(&mut self, v: bool) -> Result<(), AutodiffError> {
1385            self.a.set_requires_grad(v)?;
1386            self.b.set_requires_grad(v)
1387        }
1388        fn requires_grad(&self) -> bool {
1389            self.a.requires_grad() || self.b.requires_grad()
1390        }
1391        fn children(&self) -> Vec<(String, &dyn Module)> {
1392            vec![("a".into(), &self.a), ("b".into(), &self.b)]
1393        }
1394        fn children_mut(&mut self) -> Vec<(String, &mut dyn Module)> {
1395            vec![("a".into(), &mut self.a), ("b".into(), &mut self.b)]
1396        }
1397    }
1398
1399    /// `children` だけ実装し `children_mut` を実装しない複合層(拒否対象)。
1400    struct ChildrenOnly(Comp);
1401    impl Module for ChildrenOnly {
1402        fn forward<'t>(
1403            &self,
1404            tape: TapeRef<'t>,
1405            input: &Var<'t>,
1406        ) -> Result<Var<'t>, AutodiffError> {
1407            self.0.forward(tape, input)
1408        }
1409        fn named_parameters(&self) -> Vec<(String, &Tensor<f32>)> {
1410            self.0.named_parameters()
1411        }
1412        fn set_requires_grad(&mut self, v: bool) -> Result<(), AutodiffError> {
1413            self.0.set_requires_grad(v)
1414        }
1415        fn requires_grad(&self) -> bool {
1416            self.0.requires_grad()
1417        }
1418        fn children(&self) -> Vec<(String, &dyn Module)> {
1419            self.0.children()
1420        }
1421    }
1422
1423    fn mixed_comp() -> Comp {
1424        Comp {
1425            a: fz(false),
1426            b: fz(true),
1427        }
1428    }
1429
1430    /// PR #2426 第 3 の P1: 混在状態の利用者定義複合層を autodiff `ModuleList` へアダプタ経由で
1431    /// 積み、後続の失敗後も全葉が呼び出し前と完全一致する。
1432    #[test]
1433    fn adapter_in_autodiff_module_list_restores_user_composite_per_leaf() {
1434        let mut outer = fandhe_ai_autodiff::nn::ModuleList::new();
1435        outer.push(Box::new(adapted(mixed_comp())));
1436        outer.push(Box::new(adapted(failing_fz())));
1437        let before = adapter_leaf_states(outer.get_mut(0).expect("0"));
1438        assert_eq!(before, vec![false, true]);
1439        fandhe_ai_autodiff::nn::Module::set_requires_grad(&mut outer, false).expect_err("失敗");
1440        assert_eq!(adapter_leaf_states(outer.get_mut(0).expect("0")), before);
1441    }
1442
1443    /// `children_mut` 未実装の複合層をアダプタ経由で積むと、状態を変更する前に
1444    /// `InvalidArgument` で拒否され、全葉が不変(autodiff `ModuleList`)。
1445    #[test]
1446    fn adapter_in_autodiff_module_list_rejects_children_only_composite_before_mutation() {
1447        let mut outer = fandhe_ai_autodiff::nn::ModuleList::new();
1448        outer.push(Box::new(adapted(fz(true))));
1449        outer.push(Box::new(adapted(ChildrenOnly(mixed_comp()))));
1450        let e = fandhe_ai_autodiff::nn::Module::set_requires_grad(&mut outer, false)
1451            .expect_err("事前検査で拒否");
1452        assert!(matches!(e, AutodiffError::InvalidArgument(_)), "{e}");
1453        assert!(e.to_string().contains("children_mut"), "{e}");
1454        assert_eq!(
1455            adapter_leaf_states(outer.get_mut(0).expect("0")),
1456            vec![true]
1457        );
1458    }
1459
1460    /// 凍結処理中に `children_mut` の参照先が別の子へ切り替わる複合層をアダプタ経由で autodiff
1461    /// `ModuleList` に積んだ経路(PR #2426 第 6 回レビュー P1)。部分適用を示す `Err` が返り、
1462    /// 元エラーも含まれ、別の子 `b` へスナップショットの値は適用されない。
1463    #[test]
1464    fn adapter_in_autodiff_module_list_detects_child_swapped_during_freeze() {
1465        use std::cell::Cell;
1466        use std::rc::Rc;
1467        struct Sl {
1468            p: Tensor<f32>,
1469            rg: Rc<Cell<bool>>,
1470        }
1471        impl Module for Sl {
1472            fn forward<'t>(
1473                &self,
1474                _tape: TapeRef<'t>,
1475                input: &Var<'t>,
1476            ) -> Result<Var<'t>, AutodiffError> {
1477                Ok(*input)
1478            }
1479            fn named_parameters(&self) -> Vec<(String, &Tensor<f32>)> {
1480                vec![("p".into(), &self.p)]
1481            }
1482            fn set_requires_grad(&mut self, v: bool) -> Result<(), AutodiffError> {
1483                self.rg.set(v);
1484                Ok(())
1485            }
1486            fn requires_grad(&self) -> bool {
1487                self.rg.get()
1488            }
1489        }
1490        struct Sw {
1491            a: Sl,
1492            b: Sl,
1493            switched: bool,
1494        }
1495        impl Module for Sw {
1496            fn forward<'t>(
1497                &self,
1498                _tape: TapeRef<'t>,
1499                input: &Var<'t>,
1500            ) -> Result<Var<'t>, AutodiffError> {
1501                Ok(*input)
1502            }
1503            fn named_parameters(&self) -> Vec<(String, &Tensor<f32>)> {
1504                self.a.named_parameters()
1505            }
1506            fn set_requires_grad(&mut self, v: bool) -> Result<(), AutodiffError> {
1507                self.switched = true;
1508                if !v {
1509                    self.b.set_requires_grad(false)?;
1510                    self.a.set_requires_grad(false)?;
1511                }
1512                Ok(())
1513            }
1514            fn requires_grad(&self) -> bool {
1515                self.a.requires_grad() || self.b.requires_grad()
1516            }
1517            fn children(&self) -> Vec<(String, &dyn Module)> {
1518                let c = if self.switched { &self.b } else { &self.a };
1519                vec![("x".into(), c)]
1520            }
1521            fn children_mut(&mut self) -> Vec<(String, &mut dyn Module)> {
1522                let c = if self.switched {
1523                    &mut self.b
1524                } else {
1525                    &mut self.a
1526                };
1527                vec![("x".into(), c)]
1528            }
1529        }
1530        let mk = |v: bool| Sl {
1531            p: Tensor::from_slice(&[1.0f32, 2.0], &[2]).expect("p"),
1532            rg: Rc::new(Cell::new(v)),
1533        };
1534        let b = mk(false);
1535        let b_rg = Rc::clone(&b.rg);
1536        let mut outer = fandhe_ai_autodiff::nn::ModuleList::new();
1537        outer.push(Box::new(adapted(Sw {
1538            a: mk(true),
1539            b,
1540            switched: false,
1541        })));
1542        outer.push(Box::new(adapted(failing_fz())));
1543        let e = fandhe_ai_autodiff::nn::Module::set_requires_grad(&mut outer, false)
1544            .expect_err("後続子が失敗しロールバックも不完全");
1545        let msg = e.to_string();
1546        assert!(msg.contains("partially applied"), "{msg}");
1547        assert!(msg.contains("child failed"), "元エラーも含む: {msg}");
1548        assert!(!b_rg.get(), "別の子 b へスナップショットの値を適用しない");
1549    }
1550
1551    /// 凍結の本処理で参照先が切り替わり、自身の setter が両方の子へ伝播する複合層をアダプタ経由で
1552    /// autodiff `ModuleList` に積んだ経路(PR #2426 第 7 回レビュー P1)。復元は自身の setter を
1553    /// 呼ばず(呼び出し回数 1 のまま)部分適用の `Err` を返し、新しい子 `b` を復元値で書き換えない。
1554    #[test]
1555    fn adapter_in_autodiff_module_list_skips_own_setter_when_child_swapped() {
1556        use std::cell::Cell;
1557        use std::rc::Rc;
1558        struct Sl {
1559            p: Tensor<f32>,
1560            rg: Rc<Cell<bool>>,
1561        }
1562        impl Module for Sl {
1563            fn forward<'t>(
1564                &self,
1565                _tape: TapeRef<'t>,
1566                input: &Var<'t>,
1567            ) -> Result<Var<'t>, AutodiffError> {
1568                Ok(*input)
1569            }
1570            fn named_parameters(&self) -> Vec<(String, &Tensor<f32>)> {
1571                vec![("p".into(), &self.p)]
1572            }
1573            fn set_requires_grad(&mut self, v: bool) -> Result<(), AutodiffError> {
1574                self.rg.set(v);
1575                Ok(())
1576            }
1577            fn requires_grad(&self) -> bool {
1578                self.rg.get()
1579            }
1580        }
1581        struct Sw {
1582            a: Sl,
1583            b: Sl,
1584            switched: bool,
1585            setter_calls: Rc<Cell<usize>>,
1586        }
1587        impl Module for Sw {
1588            fn forward<'t>(
1589                &self,
1590                _tape: TapeRef<'t>,
1591                input: &Var<'t>,
1592            ) -> Result<Var<'t>, AutodiffError> {
1593                Ok(*input)
1594            }
1595            fn named_parameters(&self) -> Vec<(String, &Tensor<f32>)> {
1596                self.a.named_parameters()
1597            }
1598            fn set_requires_grad(&mut self, v: bool) -> Result<(), AutodiffError> {
1599                self.setter_calls.set(self.setter_calls.get() + 1);
1600                self.switched = true;
1601                self.a.set_requires_grad(v)?;
1602                self.b.set_requires_grad(v)
1603            }
1604            fn requires_grad(&self) -> bool {
1605                self.a.requires_grad() || self.b.requires_grad()
1606            }
1607            fn children(&self) -> Vec<(String, &dyn Module)> {
1608                let c = if self.switched { &self.b } else { &self.a };
1609                vec![("x".into(), c)]
1610            }
1611            fn children_mut(&mut self) -> Vec<(String, &mut dyn Module)> {
1612                let c = if self.switched {
1613                    &mut self.b
1614                } else {
1615                    &mut self.a
1616                };
1617                vec![("x".into(), c)]
1618            }
1619        }
1620        let mk = |v: bool| Sl {
1621            p: Tensor::from_slice(&[1.0f32, 2.0], &[2]).expect("p"),
1622            rg: Rc::new(Cell::new(v)),
1623        };
1624        let b = mk(true);
1625        let b_rg = Rc::clone(&b.rg);
1626        let setter_calls = Rc::new(Cell::new(0usize));
1627        let mut outer = fandhe_ai_autodiff::nn::ModuleList::new();
1628        outer.push(Box::new(adapted(Sw {
1629            a: mk(true),
1630            b,
1631            switched: false,
1632            setter_calls: Rc::clone(&setter_calls),
1633        })));
1634        outer.push(Box::new(adapted(failing_fz())));
1635        let e = fandhe_ai_autodiff::nn::Module::set_requires_grad(&mut outer, false)
1636            .expect_err("後続子が失敗しロールバックも不完全");
1637        let msg = e.to_string();
1638        assert!(msg.contains("partially applied"), "{msg}");
1639        assert!(msg.contains("child failed"), "元エラーも含む: {msg}");
1640        assert_eq!(setter_calls.get(), 1, "復元では自身の setter を呼ばない");
1641        assert!(!b_rg.get(), "b は復元値 true で書き換えられない");
1642    }
1643
1644    /// 同名で別の子を `children_mut` から返す複合層をアダプタ経由で積むと、状態変更前に
1645    /// `InvalidArgument` で拒否され全葉が不変(PR #2426 第 5 回レビュー P1。autodiff `ModuleList`)。
1646    #[test]
1647    fn adapter_in_autodiff_module_list_rejects_swapped_children_mut_before_mutation() {
1648        struct Swapped(Comp);
1649        impl Module for Swapped {
1650            fn forward<'t>(
1651                &self,
1652                tape: TapeRef<'t>,
1653                input: &Var<'t>,
1654            ) -> Result<Var<'t>, AutodiffError> {
1655                self.0.forward(tape, input)
1656            }
1657            fn named_parameters(&self) -> Vec<(String, &Tensor<f32>)> {
1658                self.0.named_parameters()
1659            }
1660            fn set_requires_grad(&mut self, v: bool) -> Result<(), AutodiffError> {
1661                self.0.set_requires_grad(v)
1662            }
1663            fn requires_grad(&self) -> bool {
1664                self.0.requires_grad()
1665            }
1666            fn children(&self) -> Vec<(String, &dyn Module)> {
1667                vec![("x".into(), &self.0.a)]
1668            }
1669            fn children_mut(&mut self) -> Vec<(String, &mut dyn Module)> {
1670                vec![("x".into(), &mut self.0.b)]
1671            }
1672        }
1673        let mut outer = fandhe_ai_autodiff::nn::ModuleList::new();
1674        outer.push(Box::new(adapted(fz(true))));
1675        outer.push(Box::new(adapted(Swapped(mixed_comp()))));
1676        let e = fandhe_ai_autodiff::nn::Module::set_requires_grad(&mut outer, false)
1677            .expect_err("事前検査で拒否");
1678        assert!(matches!(e, AutodiffError::InvalidArgument(_)), "{e}");
1679        assert!(e.to_string().contains("children_mut"), "{e}");
1680        assert_eq!(
1681            adapter_leaf_states(outer.get_mut(0).expect("0")),
1682            vec![true]
1683        );
1684    }
1685
1686    /// アダプタの復元は `Opaque` 以外・型違いを fail-closed で拒否する。
1687    #[test]
1688    fn adapter_restore_rejects_foreign_snapshot() {
1689        use fandhe_ai_autodiff::nn::{Module as Ad, RequiresGradSnapshot as Snap};
1690        let mut a = adapted(fz(true));
1691        assert!(a.restore_requires_grad_snapshot(&Snap::Leaf(true)).is_err());
1692        let foreign = Snap::Opaque(Box::new(7u8));
1693        let e = a
1694            .restore_requires_grad_snapshot(&foreign)
1695            .expect_err("型違い");
1696        assert!(e.to_string().contains("not produced by"), "{e}");
1697        let _ = Ad::requires_grad(&a);
1698    }
1699
1700    /// 復元中の入れ子が構造変化(子数不一致)した場合は fail-closed の `Err`。
1701    #[test]
1702    fn restore_shape_mismatch_is_fail_closed() {
1703        let mut l = list_of(vec![Box::new(fz(true))]);
1704        let snap = RequiresGradSnapshot::Nested {
1705            own: true,
1706            children: vec![
1707                (
1708                    ("0".to_string(), "", std::ptr::null(), 0),
1709                    RequiresGradSnapshot::Leaf(true),
1710                ),
1711                (
1712                    ("1".to_string(), "", std::ptr::null(), 0),
1713                    RequiresGradSnapshot::Leaf(true),
1714                ),
1715            ],
1716        };
1717        let e = restore_requires_grad(&mut l, &snap).expect_err("不一致");
1718        assert!(e.to_string().contains("child count changed"), "{e}");
1719        let mut leaf = fz(true);
1720        let e = restore_requires_grad(&mut leaf, &snap).expect_err("葉");
1721        assert!(e.to_string().contains("child count changed"), "{e}");
1722    }
1723
1724    fn relu_box() -> Box<dyn Module> {
1725        Box::new(BuiltinLayer(fandhe_ai_autodiff::nn::activation::Relu))
1726    }
1727
1728    fn dict_pair() -> (ModuleDict, fandhe_ai_autodiff::nn::ModuleDict) {
1729        let mut mine = ModuleDict::new();
1730        mine.insert("enc", Box::new(BuiltinLayer(linear())))
1731            .expect("k");
1732        mine.insert("act", relu_box()).expect("k");
1733        let mut theirs = fandhe_ai_autodiff::nn::ModuleDict::new();
1734        theirs.insert("enc", Box::new(linear())).expect("k");
1735        theirs
1736            .insert("act", Box::new(fandhe_ai_autodiff::nn::activation::Relu))
1737            .expect("k");
1738        (mine, theirs)
1739    }
1740
1741    #[test]
1742    fn module_dict_matches_autodiff_module_dict() {
1743        let (mut mine, mut theirs) = dict_pair();
1744        let names = |v: Vec<(String, &Tensor<f32>)>| -> Vec<String> {
1745            v.into_iter().map(|(n, _)| n).collect()
1746        };
1747        assert_eq!(
1748            names(mine.named_parameters()),
1749            names(theirs.named_parameters())
1750        );
1751
1752        let t = crate::tape();
1753        let x = t.var(&Tensor::from_slice(&[0.0f32; 3], &[1, 3]).expect("x"));
1754        let e1 = mine
1755            .forward(TapeRef::from(&t), &x)
1756            .expect_err("Err")
1757            .to_string();
1758        let e2 = theirs.forward(&t.0, &x).expect_err("Err").to_string();
1759        assert_eq!(e1, e2);
1760
1761        let v = Tensor::from_slice(&[0.0f32], &[1]).expect("v");
1762        for key in ["weight", "x.weight", "enc.nope"] {
1763            let a = mine
1764                .set_parameter(key, v.clone())
1765                .expect_err("Err")
1766                .to_string();
1767            let b = theirs
1768                .set_parameter(key, v.clone())
1769                .expect_err("Err")
1770                .to_string();
1771            assert_eq!(a, b, "key={key}");
1772        }
1773        for key in ["", "a.b"] {
1774            let a = mine.insert(key, relu_box()).err().expect("Err").to_string();
1775            let b = theirs
1776                .insert(key, Box::new(fandhe_ai_autodiff::nn::activation::Relu))
1777                .err()
1778                .expect("Err")
1779                .to_string();
1780            assert_eq!(a, b, "key={key:?}");
1781        }
1782    }
1783
1784    #[test]
1785    fn summary_matches_autodiff_summary() {
1786        let (mine, theirs) = dict_pair();
1787        assert_eq!(summary(&mine), fandhe_ai_autodiff::nn::summary(&theirs));
1788
1789        let mut seq = Sequential::new();
1790        seq.push(Box::new(BuiltinLayer(linear())));
1791        seq.push(relu_box());
1792        seq.push(Box::new(mine));
1793        let mut aseq = fandhe_ai_autodiff::nn::Sequential::new();
1794        aseq.push(Box::new(linear()));
1795        aseq.push(Box::new(fandhe_ai_autodiff::nn::activation::Relu));
1796        aseq.push(Box::new(theirs));
1797        assert_eq!(summary(&seq), fandhe_ai_autodiff::nn::summary(&aseq));
1798    }
1799}