burn_core/module/param/
flag.rs1use alloc::format;
2use core::fmt::{Display, Formatter, Result as FmtResult};
3
4use burn_tensor::Device;
5
6use crate::module::{
7 Content, Devices, Module, ModuleDisplay, ModuleDisplayDefault, ModuleMapper, ModuleVisitor,
8 Param, ParamId, ParameterValue,
9};
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
30pub struct Flag(bool);
31
32impl Flag {
33 pub const fn new(value: bool) -> Self {
35 Self(value)
36 }
37
38 pub const fn is_enabled(&self) -> bool {
40 self.0
41 }
42
43 pub const fn with(self, value: bool) -> Self {
45 Self(value)
46 }
47}
48
49impl Display for Flag {
50 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
51 match self.0 {
52 true => f.write_str("enabled"),
53 false => f.write_str("disabled"),
54 }
55 }
56}
57
58impl super::sealed::Sealed for Flag {
59 fn is_active(&self) -> bool {
60 self.is_enabled()
61 }
62}
63
64impl ParameterValue for Flag {}
65
66impl Param<Flag> {
67 pub fn from_bool(value: bool) -> Self {
69 Self::initialized(ParamId::new(), Flag::new(value))
70 }
71
72 pub fn with_value(self, value: bool) -> Self {
74 let mut flag = self.map(|flag| flag.with(value));
75 flag.is_active = value;
76 flag
77 }
78}
79
80impl Module for Param<Flag> {
81 fn map<M: ModuleMapper>(self, mapper: &mut M) -> Self {
82 mapper.map_flag(self)
83 }
84
85 fn visit<V: ModuleVisitor>(&self, visitor: &mut V) {
86 visitor.visit_flag(self);
87 }
88
89 fn to_device(self, _device: &Device) -> Self {
90 self
91 }
92
93 fn fork(self, _device: &Device) -> Self {
94 self
95 }
96
97 fn collect_devices(&self, devices: Devices) -> Devices {
98 devices
99 }
100
101 fn valid(&self) -> Self {
102 let enabled = self.is_active;
103 let mut flag = Self::initialized(self.id, Flag::new(false));
104 flag.is_active = enabled;
105 flag
106 }
107
108 fn train(self) -> Self {
109 let enabled = self.is_active;
110 let mut flag = Self::initialized(self.id, Flag::new(enabled));
111 flag.is_active = enabled;
112 flag
113 }
114}
115
116impl ModuleDisplayDefault for Param<Flag> {
117 fn content(&self, content: Content) -> Option<Content> {
118 content.add_formatted(&format!("{}", self.val())).optional()
119 }
120}
121
122impl ModuleDisplay for Param<Flag> {}
123
124#[cfg(test)]
125mod tests {
126 use crate::test_device;
127
128 use super::*;
129
130 #[test]
131 fn inference_temporarily_disables_an_enabled_flag() {
132 let flag = Param::<Flag>::from_bool(true);
133 let valid = flag.valid();
134
135 assert!(!valid.is_enabled());
136 assert!(valid.train().is_enabled());
137 }
138
139 #[test]
140 fn a_disabled_flag_stays_disabled_across_an_inference_round_trip() {
141 let flag = Param::<Flag>::from_bool(true).with_value(false);
142 let valid = flag.valid();
143
144 assert!(!valid.is_enabled());
145 assert!(!valid.train().is_enabled());
146 }
147
148 #[test]
149 fn enabling_a_flag_during_validation_is_preserved_by_train() {
150 let flag = Param::<Flag>::from_bool(false).valid().with_value(true);
151
152 assert!(flag.is_enabled());
153 assert!(flag.is_active);
154
155 let flag = flag.train();
156
157 assert!(flag.is_enabled());
158 assert!(flag.is_active);
159 }
160
161 #[test]
162 fn mapping_a_validation_flag_preserves_what_train_restores() {
163 let flag = Param::<Flag>::from_bool(true)
164 .valid()
165 .map(|flag| flag.with(false));
166
167 assert!(!flag.is_enabled());
168 assert!(flag.is_active);
169
170 let flag = flag.train();
171
172 assert!(flag.is_enabled());
173 assert!(flag.is_active);
174 }
175
176 #[test]
177 fn flag_transitions_preserve_identity() {
178 let flag = Param::<Flag>::from_bool(true);
179 let id = flag.id;
180
181 assert_eq!(flag.clone().with_value(false).id, id);
182 assert_eq!(flag.valid().id, id);
183 assert_eq!(flag.train().id, id);
184 }
185
186 #[test]
187 fn flags_are_device_independent() {
188 let flag = Param::<Flag>::from_bool(true);
189 let id = flag.id;
190 let device = test_device();
191
192 let moved = flag.clone().to_device(&device);
193 assert!(moved.is_enabled());
194 assert_eq!(moved.id, id);
195 assert!(moved.devices().is_empty());
196
197 let forked = flag.fork(&device);
198 assert!(forked.is_enabled());
199 assert_eq!(forked.id, id);
200 assert!(forked.devices().is_empty());
201 }
202
203 #[test]
204 fn no_grad_does_not_disable_a_flag() {
205 let flag = Param::<Flag>::from_bool(true).no_grad();
206
207 assert!(flag.is_enabled());
208 }
209
210 #[cfg(feature = "autodiff")]
211 #[test]
212 fn validation_state_is_restored_only_by_train() {
213 let device = test_device().autodiff();
214 let valid = Param::<Flag>::from_bool(true).valid();
215
216 assert!(!valid.is_enabled());
217 assert!(!valid.clone().to_device(&device).is_enabled());
218 assert!(!valid.clone().fork(&device).is_enabled());
219 assert!(valid.train().is_enabled());
220 }
221}