burn_optim/optim/module/
base.rs1use alloc::sync::Arc;
2use core::any::Any;
3
4use crate::{RecordState, StateSink, StateSource};
5use burn_core as burn;
6use burn_core::tensor::kind::BridgeTensor;
7
8use crate::LearningRate;
9use burn::tensor::{Device, Tensor};
10
11pub trait Optimizer: Send + Sync + Clone + 'static {
17 type State<const D: usize>: Clone + RecordState;
22
23 fn step<const D: usize>(
28 &self,
29 lr: LearningRate,
30 tensor: Tensor<D>,
31 grad: Tensor<D>,
32 state: Option<Self::State<D>>,
33 ) -> (Tensor<D>, Option<Self::State<D>>);
34
35 fn to_device<const D: usize>(state: Self::State<D>, device: &Device) -> Self::State<D>;
40}
41
42#[derive(Clone)]
48pub struct DynState {
49 state: Arc<dyn Any + Send + Sync>,
50 rank: usize,
51}
52
53impl DynState {
54 pub fn create<T: Send + Sync + 'static>(state: T, rank: usize) -> Self {
56 Self {
57 state: Arc::new(state),
58 rank,
59 }
60 }
61
62 pub fn downcast<T: Clone + Send + Sync + 'static>(self) -> T {
66 let state = self
67 .state
68 .downcast::<T>()
69 .expect("The dynamic optimizer state should match the optimizer state type.");
70 Arc::try_unwrap(state).unwrap_or_else(|state| (*state).clone())
71 }
72
73 pub fn downcast_ref<T: 'static>(&self) -> &T {
75 self.state
76 .downcast_ref::<T>()
77 .expect("The dynamic optimizer state should match the optimizer state type.")
78 }
79
80 pub fn rank(&self) -> usize {
82 self.rank
83 }
84}
85
86macro_rules! dispatch_rank {
88 ($rank:expr, $d:ident => $body:block) => {
89 match $rank {
90 0 => {
91 const $d: usize = 0;
92 $body
93 }
94 1 => {
95 const $d: usize = 1;
96 $body
97 }
98 2 => {
99 const $d: usize = 2;
100 $body
101 }
102 3 => {
103 const $d: usize = 3;
104 $body
105 }
106 4 => {
107 const $d: usize = 4;
108 $body
109 }
110 5 => {
111 const $d: usize = 5;
112 $body
113 }
114 6 => {
115 const $d: usize = 6;
116 $body
117 }
118 7 => {
119 const $d: usize = 7;
120 $body
121 }
122 8 => {
123 const $d: usize = 8;
124 $body
125 }
126 other => panic!("Unsupported tensor rank for optimizer state: {other}"),
127 }
128 };
129}
130
131pub trait DynOptimizer: Send + Sync {
134 fn step_dyn(
136 &self,
137 rank: usize,
138 lr: LearningRate,
139 tensor: BridgeTensor,
140 grad: BridgeTensor,
141 state: Option<DynState>,
142 ) -> (BridgeTensor, Option<DynState>);
143
144 fn to_device_dyn(&self, state: DynState, device: &Device) -> DynState;
146
147 fn state_flatten(&self, prefix: &str, state: &DynState, out: &mut StateSink);
149
150 fn state_unflatten(
156 &self,
157 rank: usize,
158 prefix: &str,
159 src: &mut StateSource,
160 device: &Device,
161 ) -> Option<DynState>;
162}
163
164impl<O: Optimizer> DynOptimizer for O {
165 fn step_dyn(
166 &self,
167 rank: usize,
168 lr: LearningRate,
169 tensor: BridgeTensor,
170 grad: BridgeTensor,
171 state: Option<DynState>,
172 ) -> (BridgeTensor, Option<DynState>) {
173 dispatch_rank!(rank, D => {
174 let (tensor, state) = self.step(
175 lr,
176 Tensor::<D>::from_bridge(tensor),
177 Tensor::<D>::from_bridge(grad),
178 state.map(|state| state.downcast::<O::State<D>>()),
179 );
180
181 (tensor.into_bridge(), state.map(|state| DynState::create(state, D)))
182 })
183 }
184
185 fn to_device_dyn(&self, state: DynState, device: &Device) -> DynState {
186 dispatch_rank!(state.rank(), D => {
187 let state = O::to_device::<D>(state.downcast::<O::State<D>>(), device);
188 DynState::create(state, D)
189 })
190 }
191
192 fn state_flatten(&self, prefix: &str, state: &DynState, out: &mut StateSink) {
193 dispatch_rank!(state.rank(), D => {
194 RecordState::state_flatten(state.downcast_ref::<O::State<D>>(), prefix, out);
195 })
196 }
197
198 fn state_unflatten(
199 &self,
200 rank: usize,
201 prefix: &str,
202 src: &mut StateSource,
203 device: &Device,
204 ) -> Option<DynState> {
205 dispatch_rank!(rank, D => {
206 let state = <O::State<D> as RecordState>::state_unflatten(prefix, src, device)?;
207 Some(DynState::create(state, D))
208 })
209 }
210}