@@ -183,3 +183,227 @@ def test_invalid_layernorm_pattern():
183
183
184
184
# Verify no LayerNorm transformation occurred
185
185
assert not any (node .op_type == "LayerNormalization" for node in sanitizer .model .graph .node )
186
+
187
+
188
+ def test_convert_fp64_initializers ():
189
+ """Test conversion of FP64 initializers to FP32."""
190
+ # Create a model with FP64 initializers
191
+ x = helper .make_tensor_value_info ("X" , TensorProto .FLOAT , [2 , 3 ])
192
+ y = helper .make_tensor_value_info ("Y" , TensorProto .FLOAT , [2 , 3 ])
193
+
194
+ # Create FP64 initializers
195
+ fp64_weights = np .array ([[1.5 , 2.5 , 3.5 ], [4.5 , 5.5 , 6.5 ]], dtype = np .float64 )
196
+ fp64_bias = np .array ([0.1 , 0.2 , 0.3 ], dtype = np .float64 )
197
+ fp32_weights = np .array ([[1.0 , 2.0 ], [3.0 , 4.0 ]], dtype = np .float32 )
198
+
199
+ initializers = [
200
+ numpy_helper .from_array (fp64_weights , name = "fp64_weights" ),
201
+ numpy_helper .from_array (fp64_bias , name = "fp64_bias" ),
202
+ numpy_helper .from_array (fp32_weights , name = "fp32_weights" ),
203
+ ]
204
+
205
+ # Verify the FP64 initializers have correct data type
206
+ assert initializers [0 ].data_type == TensorProto .DOUBLE
207
+ assert initializers [1 ].data_type == TensorProto .DOUBLE
208
+ assert initializers [2 ].data_type == TensorProto .FLOAT
209
+
210
+ add_node = helper .make_node ("Add" , ["X" , "fp64_weights" ], ["Y" ])
211
+
212
+ graph = helper .make_graph (
213
+ nodes = [add_node ], name = "fp64_test" , inputs = [x ], outputs = [y ], initializer = initializers
214
+ )
215
+
216
+ model = helper .make_model (graph )
217
+ sanitizer = GraphSanitizer (model )
218
+
219
+ # Test the conversion
220
+ result = sanitizer ._convert_fp64_initializers ()
221
+ assert result is True
222
+
223
+ # Verify all initializers are now FP32
224
+ for init in sanitizer .model .graph .initializer :
225
+ if init .name in ["fp64_weights" , "fp64_bias" ]:
226
+ assert init .data_type == TensorProto .FLOAT
227
+ # Verify data integrity
228
+ converted_data = numpy_helper .to_array (init )
229
+ assert converted_data .dtype == np .float32
230
+ elif init .name == "fp32_weights" :
231
+ assert init .data_type == TensorProto .FLOAT
232
+
233
+
234
+ def test_convert_fp64_io_types ():
235
+ """Test conversion of FP64 input/output types to FP32."""
236
+ # Create inputs and outputs with FP64 types
237
+ x_fp64 = helper .make_tensor_value_info ("X_fp64" , TensorProto .DOUBLE , [2 , 3 ])
238
+ y_fp64 = helper .make_tensor_value_info ("Y_fp64" , TensorProto .DOUBLE , [2 , 3 ])
239
+ x_fp32 = helper .make_tensor_value_info ("X_fp32" , TensorProto .FLOAT , [2 , 3 ])
240
+
241
+ # Create value_info with FP64 type
242
+ value_info_fp64 = helper .make_tensor_value_info ("intermediate" , TensorProto .DOUBLE , [2 , 3 ])
243
+ value_info_fp32 = helper .make_tensor_value_info ("intermediate2" , TensorProto .FLOAT , [2 , 3 ])
244
+
245
+ add_node = helper .make_node ("Add" , ["X_fp64" , "X_fp32" ], ["Y_fp64" ])
246
+
247
+ graph = helper .make_graph (
248
+ nodes = [add_node ],
249
+ name = "fp64_io_test" ,
250
+ inputs = [x_fp64 , x_fp32 ],
251
+ outputs = [y_fp64 ],
252
+ value_info = [value_info_fp64 , value_info_fp32 ],
253
+ )
254
+
255
+ model = helper .make_model (graph )
256
+ sanitizer = GraphSanitizer (model )
257
+
258
+ # Test the conversion
259
+ result = sanitizer ._convert_fp64_io_types ()
260
+ assert result is True
261
+
262
+ # Verify inputs are converted
263
+ assert sanitizer .model .graph .input [0 ].type .tensor_type .elem_type == TensorProto .FLOAT
264
+ assert sanitizer .model .graph .input [1 ].type .tensor_type .elem_type == TensorProto .FLOAT
265
+
266
+ # Verify outputs are converted
267
+ assert sanitizer .model .graph .output [0 ].type .tensor_type .elem_type == TensorProto .FLOAT
268
+
269
+ # Verify value_info are converted
270
+ for vi in sanitizer .model .graph .value_info :
271
+ assert vi .type .tensor_type .elem_type == TensorProto .FLOAT
272
+
273
+
274
+ def test_convert_fp64_nodes ():
275
+ """Test conversion of specific node types from FP64 to FP32."""
276
+ x = helper .make_tensor_value_info ("X" , TensorProto .FLOAT , [2 , 3 ])
277
+ y = helper .make_tensor_value_info ("Y" , TensorProto .FLOAT , [2 , 3 ])
278
+
279
+ # Create FP64 constant tensor for ConstantOfShape and Constant nodes
280
+ fp64_value = numpy_helper .from_array (np .array ([1.5 ], dtype = np .float64 ))
281
+ fp64_shape_value = numpy_helper .from_array (np .array ([2.5 ], dtype = np .float64 ))
282
+
283
+ # Create nodes that use FP64
284
+ cast_node = helper .make_node ("Cast" , ["X" ], ["cast_out" ], to = TensorProto .DOUBLE )
285
+ constant_node = helper .make_node ("Constant" , [], ["const_out" ], value = fp64_value )
286
+ constant_shape_node = helper .make_node (
287
+ "ConstantOfShape" , ["shape" ], ["shape_out" ], value = fp64_shape_value
288
+ )
289
+ add_node = helper .make_node ("Add" , ["cast_out" , "const_out" ], ["Y" ])
290
+
291
+ # Shape input for ConstantOfShape
292
+ shape_init = numpy_helper .from_array (np .array ([2 , 3 ], dtype = np .int64 ), name = "shape" )
293
+
294
+ graph = helper .make_graph (
295
+ nodes = [cast_node , constant_node , constant_shape_node , add_node ],
296
+ name = "fp64_nodes_test" ,
297
+ inputs = [x ],
298
+ outputs = [y ],
299
+ initializer = [shape_init ],
300
+ )
301
+
302
+ model = helper .make_model (graph )
303
+ sanitizer = GraphSanitizer (model )
304
+
305
+ # Test the conversion
306
+ result = sanitizer ._convert_fp64_nodes ()
307
+ assert result is True
308
+
309
+ # Verify Cast node is converted
310
+ cast_nodes = [n for n in sanitizer .model .graph .node if n .op_type == "Cast" ]
311
+ assert len (cast_nodes ) == 1
312
+ cast_attr = next (attr for attr in cast_nodes [0 ].attribute if attr .name == "to" )
313
+ assert cast_attr .i == TensorProto .FLOAT
314
+
315
+ # Verify Constant node is converted
316
+ constant_nodes = [n for n in sanitizer .model .graph .node if n .op_type == "Constant" ]
317
+ assert len (constant_nodes ) == 1
318
+ const_attr = next (attr for attr in constant_nodes [0 ].attribute if attr .name == "value" )
319
+ assert const_attr .t .data_type == TensorProto .FLOAT
320
+
321
+ # Verify ConstantOfShape node is converted
322
+ const_shape_nodes = [n for n in sanitizer .model .graph .node if n .op_type == "ConstantOfShape" ]
323
+ assert len (const_shape_nodes ) == 1
324
+ shape_attr = next (attr for attr in const_shape_nodes [0 ].attribute if attr .name == "value" )
325
+ assert shape_attr .t .data_type == TensorProto .FLOAT
326
+
327
+
328
+ def test_convert_fp64_to_fp32_integration ():
329
+ """Test the main convert_fp64_to_fp32 method with mixed FP64/FP32 content."""
330
+ # Create a model with mixed FP64 and FP32 content
331
+ x_fp64 = helper .make_tensor_value_info ("X" , TensorProto .DOUBLE , [2 , 3 ])
332
+ y_fp32 = helper .make_tensor_value_info ("Y" , TensorProto .FLOAT , [2 , 3 ])
333
+
334
+ # FP64 initializer
335
+ fp64_weights = numpy_helper .from_array (
336
+ np .array ([[1.5 , 2.5 ], [3.5 , 4.5 ]], dtype = np .float64 ), name = "weights"
337
+ )
338
+
339
+ # FP64 constant value
340
+ fp64_const_value = numpy_helper .from_array (np .array ([0.5 ], dtype = np .float64 ))
341
+
342
+ # Create nodes
343
+ cast_node = helper .make_node ("Cast" , ["X" ], ["cast_out" ], to = TensorProto .DOUBLE )
344
+ constant_node = helper .make_node ("Constant" , [], ["const_out" ], value = fp64_const_value )
345
+ add_node = helper .make_node ("Add" , ["cast_out" , "const_out" ], ["Y" ])
346
+
347
+ graph = helper .make_graph (
348
+ nodes = [cast_node , constant_node , add_node ],
349
+ name = "mixed_fp64_test" ,
350
+ inputs = [x_fp64 ],
351
+ outputs = [y_fp32 ],
352
+ initializer = [fp64_weights ],
353
+ )
354
+
355
+ model = helper .make_model (graph )
356
+ sanitizer = GraphSanitizer (model )
357
+
358
+ # Test the main conversion method
359
+ sanitizer .convert_fp64_to_fp32 ()
360
+
361
+ # Verify all FP64 content has been converted
362
+ # Check input types
363
+ assert sanitizer .model .graph .input [0 ].type .tensor_type .elem_type == TensorProto .FLOAT
364
+
365
+ # Check initializers
366
+ for init in sanitizer .model .graph .initializer :
367
+ assert init .data_type == TensorProto .FLOAT
368
+
369
+ # Check Cast node
370
+ cast_nodes = [n for n in sanitizer .model .graph .node if n .op_type == "Cast" ]
371
+ cast_attr = next (attr for attr in cast_nodes [0 ].attribute if attr .name == "to" )
372
+ assert cast_attr .i == TensorProto .FLOAT
373
+
374
+ # Check Constant node
375
+ constant_nodes = [n for n in sanitizer .model .graph .node if n .op_type == "Constant" ]
376
+ const_attr = next (attr for attr in constant_nodes [0 ].attribute if attr .name == "value" )
377
+ assert const_attr .t .data_type == TensorProto .FLOAT
378
+
379
+
380
+ def test_convert_fp64_no_changes_needed ():
381
+ """Test that conversion methods return False when no FP64 content exists."""
382
+ # Create a model with only FP32 content
383
+ x = helper .make_tensor_value_info ("X" , TensorProto .FLOAT , [2 , 3 ])
384
+ y = helper .make_tensor_value_info ("Y" , TensorProto .FLOAT , [2 , 3 ])
385
+
386
+ fp32_weights = numpy_helper .from_array (
387
+ np .array ([[1.0 , 2.0 ], [3.0 , 4.0 ]], dtype = np .float32 ), name = "weights"
388
+ )
389
+ fp32_const_value = numpy_helper .from_array (np .array ([0.5 ], dtype = np .float32 ))
390
+
391
+ cast_node = helper .make_node ("Cast" , ["X" ], ["cast_out" ], to = TensorProto .FLOAT )
392
+ constant_node = helper .make_node ("Constant" , [], ["const_out" ], value = fp32_const_value )
393
+ add_node = helper .make_node ("Add" , ["cast_out" , "const_out" ], ["Y" ])
394
+
395
+ graph = helper .make_graph (
396
+ nodes = [cast_node , constant_node , add_node ],
397
+ name = "fp32_only_test" ,
398
+ inputs = [x ],
399
+ outputs = [y ],
400
+ initializer = [fp32_weights ],
401
+ )
402
+
403
+ model = helper .make_model (graph )
404
+ sanitizer = GraphSanitizer (model )
405
+
406
+ # Test that no conversions are needed
407
+ assert sanitizer ._convert_fp64_initializers () is False
408
+ assert sanitizer ._convert_fp64_io_types () is False
409
+ assert sanitizer ._convert_fp64_nodes () is False
0 commit comments