1use std::collections::HashSet;
41
42use crate::nn::Module;
43use crate::nn::module::NodeKey;
44use crate::{AutodiffError, TapeRef, Tensor, Var};
45
46pub(crate) enum RequiresGradSnapshot {
55 Leaf(bool),
56 Nested {
57 own: bool,
58 children: Vec<(ChildIdentity, RequiresGradSnapshot)>,
63 },
64}
65
66type ChildIdentity = (String, &'static str, *const (), usize);
70
71pub(crate) fn snapshot_requires_grad<M: Module + ?Sized>(
76 module: &mut M,
77) -> Result<RequiresGradSnapshot, AutodiffError> {
78 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
111fn 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
151fn 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
161pub(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 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 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 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
272fn set_requires_grad_with_rollback(
278 owner: &str,
279 mut children: Vec<&mut dyn Module>,
280 requires_grad: bool,
281) -> Result<(), AutodiffError> {
282 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
310fn 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
325pub 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 pub fn new() -> Self {
343 ModuleList {
344 modules: Vec::new(),
345 training: true,
346 }
347 }
348
349 pub fn push(&mut self, module: Box<dyn Module>) {
351 self.modules.push(module);
352 }
353
354 pub fn len(&self) -> usize {
356 self.modules.len()
357 }
358
359 pub fn is_empty(&self) -> bool {
361 self.modules.is_empty()
362 }
363
364 pub fn get(&self, index: usize) -> Option<&dyn Module> {
366 self.modules.get(index).map(|m| m.as_ref())
367 }
368
369 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 pub fn iter(&self) -> impl Iterator<Item = &Box<dyn Module>> {
376 self.modules.iter()
377 }
378
379 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 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 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 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 fn requires_grad(&self) -> bool {
439 aggregate_requires_grad(self.modules.iter().map(|m| m.as_ref()))
440 }
441
442 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 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
484pub struct Sequential {
489 inner: ModuleList,
490}
491
492impl Default for Sequential {
493 fn default() -> Self {
494 Self::new()
495 }
496}
497
498impl Sequential {
499 pub fn new() -> Self {
501 Sequential {
502 inner: ModuleList::new(),
503 }
504 }
505
506 pub fn push(&mut self, module: Box<dyn Module>) {
508 self.inner.push(module);
509 }
510
511 #[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 pub fn len(&self) -> usize {
521 self.inner.len()
522 }
523
524 pub fn is_empty(&self) -> bool {
526 self.inner.is_empty()
527 }
528
529 pub fn layers(&self) -> &[Box<dyn Module>] {
531 &self.inner.modules
532 }
533
534 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, ¤t)?;
551 }
552 Ok(current)
553 }
554
555 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
589pub 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
606fn 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 pub fn new() -> Self {
627 ModuleDict {
628 modules: Vec::new(),
629 training: true,
630 }
631 }
632
633 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 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 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 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 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 pub fn contains_key(&self, key: &str) -> bool {
683 self.modules.iter().any(|(k, _)| k == key)
684 }
685
686 pub fn keys(&self) -> impl Iterator<Item = &str> {
688 self.modules.iter().map(|(k, _)| k.as_str())
689 }
690
691 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 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 pub fn len(&self) -> usize {
705 self.modules.len()
706 }
707
708 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 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 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 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 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 fn requires_grad(&self) -> bool {
780 aggregate_requires_grad(self.modules.iter().map(|(_, m)| m.as_ref()))
781 }
782
783 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
792fn 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
801pub 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
823fn 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 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 use std::cell::RefCell;
952 use std::rc::Rc;
953
954 type Calls = Rc<RefCell<Vec<bool>>>;
955
956 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 l.get_mut(0)
1028 .expect("0")
1029 .set_requires_grad(true)
1030 .expect("ok");
1031 assert!(l.requires_grad());
1032
1033 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 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 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 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 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 #[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 #[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 #[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 #[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 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 #[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 #[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 #[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 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 #[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 #[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 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 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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}