@@ -467,6 +467,55 @@ fn test_gemm_bias_activation_bwd_batched_3d_parity() {
467467 }
468468}
469469
470+ // ============================================================================
471+ // Helpers
472+ // ============================================================================
473+
474+ /// Assert all elements of a tensor are finite, reading as the correct native dtype.
475+ fn assert_finite < R : numr:: runtime:: Runtime > (
476+ tensor : & numr:: tensor:: Tensor < R > ,
477+ dtype : numr:: dtype:: DType ,
478+ label : & str ,
479+ ) {
480+ use numr:: dtype:: DType ;
481+ macro_rules! check {
482+ ( $T: ty) => {
483+ for ( i, val) in tensor. to_vec:: <$T>( ) . iter( ) . enumerate( ) {
484+ let v = * val as f64 ;
485+ assert!(
486+ v. is_finite( ) ,
487+ "non-finite {label} [{dtype:?}] at index {i}: {v}"
488+ ) ;
489+ }
490+ } ;
491+ }
492+ match dtype {
493+ DType :: F64 => check ! ( f64 ) ,
494+ DType :: F32 => check ! ( f32 ) ,
495+ #[ cfg( feature = "f16" ) ]
496+ DType :: F16 => {
497+ for ( i, val) in tensor. to_vec :: < half:: f16 > ( ) . iter ( ) . enumerate ( ) {
498+ let v = f32:: from ( * val) as f64 ;
499+ assert ! (
500+ v. is_finite( ) ,
501+ "non-finite {label} [{dtype:?}] at index {i}: {v}"
502+ ) ;
503+ }
504+ }
505+ #[ cfg( feature = "f16" ) ]
506+ DType :: BF16 => {
507+ for ( i, val) in tensor. to_vec :: < half:: bf16 > ( ) . iter ( ) . enumerate ( ) {
508+ let v = f32:: from ( * val) as f64 ;
509+ assert ! (
510+ v. is_finite( ) ,
511+ "non-finite {label} [{dtype:?}] at index {i}: {v}"
512+ ) ;
513+ }
514+ }
515+ _ => { } // integer/bool dtypes are always "finite"
516+ }
517+ }
518+
470519// ============================================================================
471520// matmul_bias_activation_bwd: negative values / edge cases
472521// ============================================================================
@@ -496,25 +545,11 @@ fn test_gemm_bias_activation_bwd_negative_values_parity() {
496545 . matmul_bias_activation_bwd ( & grad_t, & a_t, & b_t, & bias_t, activation)
497546 . unwrap ( ) ;
498547
499- // Verify finiteness on CPU reference
500- for val in cpu_da. to_vec :: < f64 > ( ) . iter ( ) {
501- assert ! (
502- val. is_finite( ) ,
503- "non-finite d_a for {activation:?} [{dtype:?}]"
504- ) ;
505- }
506- for val in cpu_db. to_vec :: < f64 > ( ) . iter ( ) {
507- assert ! (
508- val. is_finite( ) ,
509- "non-finite d_b for {activation:?} [{dtype:?}]"
510- ) ;
511- }
512- for val in cpu_dbias. to_vec :: < f64 > ( ) . iter ( ) {
513- assert ! (
514- val. is_finite( ) ,
515- "non-finite d_bias for {activation:?} [{dtype:?}]"
516- ) ;
517- }
548+ // Verify finiteness on CPU reference (must read as native dtype,
549+ // not f64, because to_vec is a raw byte copy with no conversion)
550+ assert_finite ( & cpu_da, dtype, & format ! ( "d_a for {activation:?}" ) ) ;
551+ assert_finite ( & cpu_db, dtype, & format ! ( "d_b for {activation:?}" ) ) ;
552+ assert_finite ( & cpu_dbias, dtype, & format ! ( "d_bias for {activation:?}" ) ) ;
518553
519554 #[ cfg( feature = "cuda" ) ]
520555 if is_dtype_supported ( "cuda" , dtype) {
0 commit comments