@@ -3,15 +3,23 @@ use serde::{Deserialize, Serialize};
33use tsify:: Tsify ;
44
55use crate :: { error:: SceneError , step:: Step , targets:: TargetsMap , shape:: InputSpec } ;
6+ use crate :: tiered:: TieredConfig ;
67use super :: adam:: { AdamState , AdamConfig } ;
78use super :: robust:: { self , OptimConfig } ;
89
910#[ derive( Debug , Clone , Tsify , Serialize , Deserialize ) ]
1011pub struct Model {
1112 pub steps : Vec < Step > ,
13+ /// Original step indices when steps are filtered (e.g. tiered keyframes).
14+ /// `None` means steps[i] corresponds to step i (no filtering).
15+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
16+ pub step_indices : Option < Vec < usize > > ,
1217 pub repeat_idx : Option < usize > ,
1318 pub min_idx : usize ,
1419 pub min_error : f64 ,
20+ /// Actual number of training iterations (may differ from `steps.len()`
21+ /// when step filtering is active).
22+ pub total_steps : usize ,
1523}
1624
1725impl Model {
@@ -20,7 +28,7 @@ impl Model {
2028 let min_error = step. error . re ;
2129 let steps = vec ! [ step] ;
2230 let repeat_idx: Option < usize > = None ;
23- Ok ( Model { steps, min_idx : 0 , repeat_idx, min_error } )
31+ Ok ( Model { steps, step_indices : None , min_idx : 0 , repeat_idx, min_error, total_steps : 1 } )
2432 }
2533 pub fn train ( & mut self , max_step_error_ratio : f64 , max_steps : usize ) -> Result < ( ) , SceneError > {
2634 let num_steps = self . steps . len ( ) ;
@@ -66,6 +74,7 @@ impl Model {
6674 }
6775 step = nxt;
6876 }
77+ self . total_steps = self . steps . len ( ) ;
6978 Ok ( ( ) )
7079 }
7180 pub fn grad_size ( & self ) -> usize {
@@ -128,6 +137,7 @@ impl Model {
128137 }
129138 step = nxt;
130139 }
140+ self . total_steps = self . steps . len ( ) ;
131141 Ok ( ( ) )
132142 }
133143
@@ -139,27 +149,49 @@ impl Model {
139149 /// - Learning rate warmup for stability
140150 /// - Step rejection when error increases significantly
141151 pub fn train_robust ( & mut self , max_steps : usize ) -> Result < ( ) , SceneError > {
142- self . train_robust_with_config ( OptimConfig :: default ( ) , max_steps)
152+ self . train_robust_with_config ( OptimConfig :: default ( ) , max_steps, None )
143153 }
144154
145155 /// Train using robust optimization with custom configuration.
146- pub fn train_robust_with_config ( & mut self , config : OptimConfig , max_steps : usize ) -> Result < ( ) , SceneError > {
147- let num_steps = self . steps . len ( ) ;
148- let initial_step = & self . steps [ num_steps - 1 ] ;
149-
150- let new_steps = robust:: train_robust ( initial_step, config, max_steps) ?;
151-
152- // Add new steps to history, tracking min error
153- for ( idx, step) in new_steps. into_iter ( ) . skip ( 1 ) . enumerate ( ) {
154- let step_idx = num_steps + idx;
155- let err = step. error . re ;
156+ ///
157+ /// When `tiered` is `Some`, only tiered keyframe steps (plus best and final)
158+ /// are retained in memory, preventing OOM on long training runs.
159+ pub fn train_robust_with_config (
160+ & mut self ,
161+ config : OptimConfig ,
162+ max_steps : usize ,
163+ tiered : Option < & TieredConfig > ,
164+ ) -> Result < ( ) , SceneError > {
165+ let step_offset = self . steps . len ( ) - 1 ;
166+ let initial_step = & self . steps [ step_offset] ;
167+
168+ let retain: Option < Box < dyn Fn ( usize ) -> bool > > = tiered. map ( |tc| {
169+ let tc = tc. clone ( ) ;
170+ Box :: new ( move |idx : usize | tc. is_keyframe ( idx) ) as Box < dyn Fn ( usize ) -> bool >
171+ } ) ;
172+
173+ let result = robust:: train_robust_filtered (
174+ initial_step,
175+ config,
176+ max_steps,
177+ step_offset,
178+ retain. as_deref ( ) ,
179+ ) ?;
180+
181+ // result.steps[0] is the initial step (already in self.steps), skip it
182+ for step in result. steps . into_iter ( ) . skip ( 1 ) {
183+ self . steps . push ( step) ;
184+ }
156185
157- if err < self . min_error {
158- self . min_idx = step_idx;
159- self . min_error = err;
160- }
186+ self . min_idx = result. min_idx ;
187+ self . min_error = result. min_error ;
188+ self . total_steps = result. total_steps ;
161189
162- self . steps . push ( step) ;
190+ // Store step indices when filtering is active
191+ if tiered. is_some ( ) {
192+ let mut indices = vec ! [ 0usize ] ; // initial step
193+ indices. extend ( result. step_indices . iter ( ) . skip ( 1 ) ) ;
194+ self . step_indices = Some ( indices) ;
163195 }
164196
165197 Ok ( ( ) )
0 commit comments