@@ -46,6 +46,56 @@ def _abstract_nnx_state():
4646class TestLoadStateIfPossibleNNX (unittest .TestCase ):
4747 """Cover the NNX branches in load_state_if_possible."""
4848
49+ def test_emergency_linen_restore_converts_back_to_nnx (self ):
50+ kernel_abstract = jax .ShapeDtypeStruct ((2 , 1 ), jnp .float32 )
51+ step_abstract = jax .ShapeDtypeStruct ((), jnp .uint32 )
52+ abstract_nnx_pure = {
53+ "model" : {
54+ "linear" : {"kernel" : kernel_abstract },
55+ "dropout" : {"rngs" : {"params" : {"count" : step_abstract }}},
56+ },
57+ "optimizer" : {"step" : step_abstract },
58+ }
59+ abstract_nnx_state = mock .Mock ()
60+ abstract_nnx_state .to_pure_dict .return_value = abstract_nnx_pure
61+ restored_linen = {
62+ "params" : {"params" : {"linear" : {"kernel" : jnp .ones ((2 , 1 ))}}},
63+ "step" : jnp .asarray (7 , dtype = jnp .int32 ),
64+ }
65+ checkpoint_manager = mock .Mock ()
66+ checkpoint_manager .restore .return_value = mock .Mock (state = restored_linen )
67+
68+ restored = checkpointing ._restore_emergency_linen_checkpoint_into_nnx ( # pylint: disable=protected-access
69+ checkpoint_manager ,
70+ 14 ,
71+ abstract_nnx_state ,
72+ lambda leaf : ocp .type_handlers .ArrayRestoreArgs (
73+ global_shape = leaf .shape ,
74+ dtype = leaf .dtype ,
75+ ),
76+ )
77+
78+ checkpoint_manager .restore .assert_called_once ()
79+ restore_args = checkpoint_manager .restore .call_args .kwargs ["args" ].state
80+ self .assertEqual (
81+ set (restore_args .item .keys ()),
82+ {"params" , "step" },
83+ )
84+ self .assertNotIn ("model" , restore_args .item )
85+ self .assertNotIn ("optimizer" , restore_args .item )
86+ self .assertTrue (restore_args .partial_restore )
87+ self .assertIn ("model" , restored )
88+ self .assertIn ("optimizer" , restored )
89+ self .assertNotIn ("params" , restored )
90+ self .assertNotIn ("opt_state" , restored )
91+ self .assertTrue (bool (jnp .array_equal (restored ["model" ]["linear" ]["kernel" ], jnp .ones ((2 , 1 )))))
92+ self .assertEqual (restored ["optimizer" ]["step" ].dtype , jnp .uint32 )
93+ self .assertEqual (int (restored ["optimizer" ]["step" ]), 7 )
94+ self .assertEqual (
95+ restored ["model" ]["dropout" ]["rngs" ]["params" ]["count" ].shape ,
96+ (),
97+ )
98+
4999 def test_load_parameters_from_path_splits_nnx_state_for_param_view (self ):
50100 """When abstract_unboxed_pre_state is an nnx.State, the function must call
51101 nnx.split(model, nnx.Param, ...) to get the params and forward them to load_params_from_path."""
@@ -126,8 +176,7 @@ def test_load_state_if_possible_wraps_load_params_mismatch_exception(self):
126176 str (ctx .exception ),
127177 )
128178 self .assertIn (
129- "This is often caused by a mismatch in the 'scan_layers'"
130- " configuration" ,
179+ "This is often caused by a mismatch in the 'scan_layers'" " configuration" ,
131180 str (ctx .exception ),
132181 )
133182
@@ -156,69 +205,33 @@ class TestCheckpointMismatchHandling(unittest.TestCase):
156205 def test_is_structural_or_shape_mismatch (self ):
157206 """Verifies that is_structural_or_shape_mismatch matches only shape/tree mismatches in ValueError/TypeError."""
158207 # Matches
159- self .assertTrue (
160- checkpointing .is_structural_or_shape_mismatch (
161- ValueError ("PyTree structure mismatch" )
162- )
163- )
164- self .assertTrue (
165- checkpointing .is_structural_or_shape_mismatch (
166- TypeError ("shape mismatch in leaf" )
167- )
168- )
169- self .assertTrue (
170- checkpointing .is_structural_or_shape_mismatch (
171- ValueError ("tree paths matched 143/145" )
172- )
173- )
174- self .assertTrue (
175- checkpointing .is_structural_or_shape_mismatch (
176- ValueError ("invalid type shapedtypestruct" )
177- )
178- )
208+ self .assertTrue (checkpointing .is_structural_or_shape_mismatch (ValueError ("PyTree structure mismatch" )))
209+ self .assertTrue (checkpointing .is_structural_or_shape_mismatch (TypeError ("shape mismatch in leaf" )))
210+ self .assertTrue (checkpointing .is_structural_or_shape_mismatch (ValueError ("tree paths matched 143/145" )))
211+ self .assertTrue (checkpointing .is_structural_or_shape_mismatch (ValueError ("invalid type shapedtypestruct" )))
179212
180213 # Does not match
181- self .assertFalse (
182- checkpointing .is_structural_or_shape_mismatch (
183- ValueError ("checkpoint directory does not exist" )
184- )
185- )
186- self .assertFalse (
187- checkpointing .is_structural_or_shape_mismatch (
188- FileNotFoundError ("file not found: checkpoint" )
189- )
190- )
191- self .assertFalse (
192- checkpointing .is_structural_or_shape_mismatch (
193- RuntimeError ("something went wrong" )
194- )
195- )
214+ self .assertFalse (checkpointing .is_structural_or_shape_mismatch (ValueError ("checkpoint directory does not exist" )))
215+ self .assertFalse (checkpointing .is_structural_or_shape_mismatch (FileNotFoundError ("file not found: checkpoint" )))
216+ self .assertFalse (checkpointing .is_structural_or_shape_mismatch (RuntimeError ("something went wrong" )))
196217
197218 def test_handle_checkpoint_mismatch_intercepts_matching_exceptions (self ):
198219 """Verifies that handle_checkpoint_mismatch intercepts and wraps structural errors."""
199220 with self .assertRaises (ValueError ) as ctx :
200- with checkpointing .handle_checkpoint_mismatch (
201- "load parameters" , "gs://bucket/params"
202- ):
221+ with checkpointing .handle_checkpoint_mismatch ("load parameters" , "gs://bucket/params" ):
203222 raise ValueError ("PyTree structure mismatch" )
204223
205- self .assertIn (
206- "Failed to load parameters from gs://bucket/params." , str (ctx .exception )
207- )
224+ self .assertIn ("Failed to load parameters from gs://bucket/params." , str (ctx .exception ))
208225 self .assertIn (
209226 "This is often caused by a mismatch in the 'scan_layers' configuration" ,
210227 str (ctx .exception ),
211228 )
212- self .assertIn (
213- "Original error: PyTree structure mismatch" , str (ctx .exception )
214- )
229+ self .assertIn ("Original error: PyTree structure mismatch" , str (ctx .exception ))
215230
216231 def test_handle_checkpoint_mismatch_re_raises_non_matching_exceptions (self ):
217232 """Verifies that handle_checkpoint_mismatch does not intercept non-structural errors."""
218233 with self .assertRaises (FileNotFoundError ):
219- with checkpointing .handle_checkpoint_mismatch (
220- "load parameters" , "gs://bucket/params"
221- ):
234+ with checkpointing .handle_checkpoint_mismatch ("load parameters" , "gs://bucket/params" ):
222235 raise FileNotFoundError ("file not found: checkpoint" )
223236
224237
@@ -248,12 +261,8 @@ def test_linen_layout_params_restore_into_nnx_state(self):
248261
249262 self .assertIsInstance (restored , nnx .State )
250263 pure = restored .to_pure_dict ()
251- self .assertTrue (
252- jnp .array_equal (pure ["linear" ]["kernel" ], weights ["linear" ]["kernel" ])
253- )
254- self .assertTrue (
255- jnp .array_equal (pure ["linear" ]["bias" ], weights ["linear" ]["bias" ])
256- )
264+ self .assertTrue (jnp .array_equal (pure ["linear" ]["kernel" ], weights ["linear" ]["kernel" ]))
265+ self .assertTrue (jnp .array_equal (pure ["linear" ]["bias" ], weights ["linear" ]["bias" ]))
257266
258267
259268if __name__ == "__main__" :
0 commit comments