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 materialize(self) -> Self {
102 self
103 }
104
105 fn valid(&self) -> Self {
106 let enabled = self.is_active;
107 let mut flag = Self::initialized(self.id, Flag::new(false));
108 flag.is_active = enabled;
109 flag
110 }
111
112 fn train(self) -> Self {
113 let enabled = self.is_active;
114 let mut flag = Self::initialized(self.id, Flag::new(enabled));
115 flag.is_active = enabled;
116 flag
117 }
118}
119
120impl ModuleDisplayDefault for Param<Flag> {
121 fn content(&self, content: Content) -> Option<Content> {
122 content.add_formatted(&format!("{}", self.val())).optional()
123 }
124}
125
126impl ModuleDisplay for Param<Flag> {}
127
128#[cfg(test)]
129mod tests {
130 use crate::test_device;
131
132 use super::*;
133
134 #[test]
135 fn inference_temporarily_disables_an_enabled_flag() {
136 let flag = Param::<Flag>::from_bool(true);
137 let valid = flag.valid();
138
139 assert!(!valid.is_enabled());
140 assert!(valid.train().is_enabled());
141 }
142
143 #[test]
144 fn a_disabled_flag_stays_disabled_across_an_inference_round_trip() {
145 let flag = Param::<Flag>::from_bool(true).with_value(false);
146 let valid = flag.valid();
147
148 assert!(!valid.is_enabled());
149 assert!(!valid.train().is_enabled());
150 }
151
152 #[test]
153 fn enabling_a_flag_during_validation_is_preserved_by_train() {
154 let flag = Param::<Flag>::from_bool(false).valid().with_value(true);
155
156 assert!(flag.is_enabled());
157 assert!(flag.is_active);
158
159 let flag = flag.train();
160
161 assert!(flag.is_enabled());
162 assert!(flag.is_active);
163 }
164
165 #[test]
166 fn mapping_a_validation_flag_preserves_what_train_restores() {
167 let flag = Param::<Flag>::from_bool(true)
168 .valid()
169 .map(|flag| flag.with(false));
170
171 assert!(!flag.is_enabled());
172 assert!(flag.is_active);
173
174 let flag = flag.train();
175
176 assert!(flag.is_enabled());
177 assert!(flag.is_active);
178 }
179
180 #[test]
181 fn flag_transitions_preserve_identity() {
182 let flag = Param::<Flag>::from_bool(true);
183 let id = flag.id;
184
185 assert_eq!(flag.clone().with_value(false).id, id);
186 assert_eq!(flag.valid().id, id);
187 assert_eq!(flag.train().id, id);
188 }
189
190 #[test]
191 fn flags_are_device_independent() {
192 let flag = Param::<Flag>::from_bool(true);
193 let id = flag.id;
194 let device = test_device();
195
196 let moved = flag.clone().to_device(&device);
197 assert!(moved.is_enabled());
198 assert_eq!(moved.id, id);
199 assert!(moved.devices().is_empty());
200
201 let forked = flag.fork(&device);
202 assert!(forked.is_enabled());
203 assert_eq!(forked.id, id);
204 assert!(forked.devices().is_empty());
205 }
206
207 #[test]
208 fn no_grad_does_not_disable_a_flag() {
209 let flag = Param::<Flag>::from_bool(true).no_grad();
210
211 assert!(flag.is_enabled());
212 }
213
214 #[cfg(feature = "autodiff")]
215 #[test]
216 fn validation_state_is_restored_only_by_train() {
217 let device = test_device().autodiff();
218 let valid = Param::<Flag>::from_bool(true).valid();
219
220 assert!(!valid.is_enabled());
221 assert!(!valid.clone().to_device(&device).is_enabled());
222 assert!(!valid.clone().fork(&device).is_enabled());
223 assert!(valid.train().is_enabled());
224 }
225}