1313
1414BATCH = 100
1515
16+ def _s (label , dtype = None ):
17+ """Append [f64] or [f32] to label for ULP report clarity."""
18+ return label if dtype is None else f"{ label } [{ np .dtype (dtype ).name } ]"
19+
1620# ============================================================================
1721# ULP computation & reporting
1822# ============================================================================
@@ -149,16 +153,16 @@ def dtype(self, request): return request.param
149153
150154 def test_batch_default (self , cpp , dtype ):
151155 a = random_batch ((BATCH ,), dtype = dtype , seed = 1001 )
152- assert_ulp_close (cpp .stats .norm .pdf (a ), sp_norm .pdf (a ), f"pdf batch={ BATCH } " )
156+ assert_ulp_close (cpp .stats .norm .pdf (a ), sp_norm .pdf (a ), _s ( f"pdf batch={ BATCH } " , dtype ) )
153157
154158 @pytest .mark .parametrize ("v" , [0.0 , 1.0 , - 1.0 , 2.0 , - 2.0 , 3.0 , - 3.0 , 5.0 , - 5.0 ])
155159 def test_canonical (self , cpp , dtype , v ):
156160 a = np .array ([v ], dtype = dtype )
157- assert_ulp_close (cpp .stats .norm .pdf (a ), sp_norm .pdf (a ), f"pdf({ v } )" )
161+ assert_ulp_close (cpp .stats .norm .pdf (a ), sp_norm .pdf (a ), _s ( f"pdf({ v } )" , dtype ) )
158162
159163 def test_extreme (self , cpp , dtype ):
160164 a = np .array ([6.0 , 8.0 , 10.0 , - 6.0 , - 8.0 , - 10.0 , 20.0 , - 20.0 ], dtype = dtype )
161- assert_ulp_close (cpp .stats .norm .pdf (a ), sp_norm .pdf (a ), "pdf extreme" )
165+ assert_ulp_close (cpp .stats .norm .pdf (a ), sp_norm .pdf (a ), _s ( "pdf extreme" , dtype ) )
162166
163167 @pytest .mark .parametrize ("loc,scale" , [
164168 (0.0 ,1.0 ),(1.0 ,1.0 ),(- 2.0 ,1.0 ),(0.0 ,2.0 ),(0.0 ,0.5 ),(3.0 ,4.0 ),
@@ -168,14 +172,14 @@ def test_loc_scale(self, cpp, dtype, loc, scale):
168172 a = random_batch ((BATCH ,), dtype = dtype , seed = 1002 )
169173 assert_ulp_close (cpp .stats .norm .pdf (a , dtype (loc ), dtype (scale )),
170174 sp_norm .pdf (a , loc = dtype (loc ), scale = dtype (scale )),
171- f"pdf(loc={ loc } ,scale={ scale } )" )
175+ _s ( f"pdf(loc={ loc } ,scale={ scale } )" , dtype ) )
172176
173177 @pytest .mark .parametrize ("loc,scale" , [(- 10.0 ,0.01 ), (10.0 ,0.01 )])
174178 def test_tiny_scale (self , cpp , dtype , loc , scale ):
175179 a = random_batch ((BATCH ,), dtype = dtype , seed = 1003 )
176180 assert_ulp_close (cpp .stats .norm .pdf (a , dtype (loc ), dtype (scale )),
177181 sp_norm .pdf (a , loc = dtype (loc ), scale = dtype (scale )),
178- f"pdf(loc={ loc } ,scale={ scale } )" )
182+ _s ( f"pdf(loc={ loc } ,scale={ scale } )" , dtype ) )
179183
180184
181185class TestNormCdf :
@@ -184,12 +188,12 @@ def dtype(self, request): return request.param
184188
185189 def test_batch_default (self , cpp , dtype ):
186190 a = random_batch ((BATCH ,), dtype = dtype , seed = 1004 )
187- assert_bit_aligned (cpp .stats .norm .cdf (a ), sp_norm .cdf (a ), f"cdf batch={ BATCH } " )
191+ assert_bit_aligned (cpp .stats .norm .cdf (a ), sp_norm .cdf (a ), _s ( f"cdf batch={ BATCH } " , dtype ) )
188192
189193 @pytest .mark .parametrize ("v" , [0.0 , 1.0 , - 1.0 , 2.0 , - 2.0 , 3.0 , - 3.0 ])
190194 def test_canonical (self , cpp , dtype , v ):
191195 a = np .array ([v ], dtype = dtype )
192- assert_bit_aligned (cpp .stats .norm .cdf (a ), sp_norm .cdf (a ), f"cdf({ v } )" )
196+ assert_bit_aligned (cpp .stats .norm .cdf (a ), sp_norm .cdf (a ), _s ( f"cdf({ v } )" , dtype ) )
193197
194198 @pytest .mark .parametrize ("loc,scale" , [
195199 (0.0 ,1.0 ),(1.0 ,1.0 ),(- 2.0 ,1.0 ),(0.0 ,2.0 ),(0.0 ,0.5 ),(3.0 ,4.0 ),
@@ -198,7 +202,7 @@ def test_loc_scale(self, cpp, dtype, loc, scale):
198202 a = random_batch ((BATCH ,), dtype = dtype , seed = 1005 )
199203 assert_bit_aligned (cpp .stats .norm .cdf (a , dtype (loc ), dtype (scale )),
200204 sp_norm .cdf (a , loc = dtype (loc ), scale = dtype (scale )),
201- f"cdf(loc={ loc } ,scale={ scale } )" )
205+ _s ( f"cdf(loc={ loc } ,scale={ scale } )" , dtype ) )
202206
203207
204208class TestNormPpf :
@@ -208,12 +212,12 @@ def dtype(self, request): return request.param
208212
209213 def test_batch_default (self , cpp , dtype ):
210214 a = random_uniform ((BATCH ,), 0.001 , 0.999 , dtype = dtype , seed = 1006 )
211- assert_bit_aligned (cpp .stats .norm .ppf (a ), sp_norm .ppf (a ), f"ppf batch={ BATCH } " )
215+ assert_bit_aligned (cpp .stats .norm .ppf (a ), sp_norm .ppf (a ), _s ( f"ppf batch={ BATCH } " , dtype ) )
212216
213217 @pytest .mark .parametrize ("p" , [0.5 , 0.025 , 0.975 , 0.001 , 0.999 ])
214218 def test_canonical (self , cpp , dtype , p ):
215219 a = np .array ([p ], dtype = dtype )
216- assert_bit_aligned (cpp .stats .norm .ppf (a ), sp_norm .ppf (a ), f"ppf({ p } )" )
220+ assert_bit_aligned (cpp .stats .norm .ppf (a ), sp_norm .ppf (a ), _s ( f"ppf({ p } )" , dtype ) )
217221
218222 @pytest .mark .parametrize ("loc,scale" , [
219223 (0.0 ,1.0 ),(1.0 ,1.0 ),(- 2.0 ,1.0 ),(0.0 ,2.0 ),(0.0 ,0.5 ),
@@ -222,7 +226,7 @@ def test_loc_scale(self, cpp, dtype, loc, scale):
222226 a = random_uniform ((BATCH ,), 0.001 , 0.999 , dtype = dtype , seed = 1007 )
223227 assert_bit_aligned (cpp .stats .norm .ppf (a , dtype (loc ), dtype (scale )),
224228 sp_norm .ppf (a , loc = dtype (loc ), scale = dtype (scale )),
225- f"ppf(loc={ loc } ,scale={ scale } )" )
229+ _s ( f"ppf(loc={ loc } ,scale={ scale } )" , dtype ) )
226230
227231
228232# ============================================================================
@@ -237,19 +241,19 @@ def test_trapezoid_batch(self, cpp, dtype):
237241 y = random_batch ((BATCH ,), dtype = dtype , seed = 1008 )
238242 assert_bit_aligned (
239243 np .float64 (cpp .trapezoid (y )), np .float64 (sp_integrate .trapezoid (y )),
240- f"trapezoid batch={ BATCH } " )
244+ _s ( f"trapezoid batch={ BATCH } " , dtype ) )
241245
242246 def test_simpson_batch (self , cpp , dtype ):
243247 y = random_batch ((101 ,), dtype = dtype , seed = 1009 )
244248 assert_bit_aligned (
245249 np .float64 (cpp .simpson (y )), np .float64 (sp_integrate .simpson (y )),
246- f"simpson batch=101" )
250+ _s ( f"simpson batch=101" , dtype ) )
247251
248252 def test_trapezoid_known (self , cpp , dtype ):
249253 y = np .array ([0.0 , 1.0 , 4.0 , 9.0 , 16.0 ], dtype = dtype )
250254 assert_bit_aligned (
251255 np .float64 (cpp .trapezoid (y )), np .float64 (sp_integrate .trapezoid (y )),
252- "trapezoid known" )
256+ _s ( "trapezoid known" , dtype ) )
253257
254258 def test_simpson_known (self , cpp ):
255259 y = np .array ([0.0 , 1.0 , 4.0 , 9.0 , 16.0 ], dtype = np .float64 )
@@ -302,14 +306,14 @@ def test_solve_2x2(self, cpp, dtype):
302306 b = np .array ([5.0 , 6.0 ], dtype = dtype )
303307 assert_bit_aligned (
304308 np .asarray (cpp .linalg .solve (A , b )), self ._np_solve (A , b ),
305- "solve 2x2" )
309+ _s ( "solve 2x2" , dtype ) )
306310
307311 def test_solve_identity (self , cpp , dtype ):
308312 A = np .eye (3 , dtype = dtype )
309313 b = np .array ([1.0 , 2.0 , 3.0 ], dtype = dtype )
310314 assert_bit_aligned (
311315 np .asarray (cpp .linalg .solve (A , b )), self ._np_solve (A , b ),
312- "solve identity" )
316+ _s ( "solve identity" , dtype ) )
313317
314318 def test_solve_batch (self , cpp , dtype ):
315319 """100 random matrices (n=4..8) + random RHS vectors."""
@@ -319,7 +323,7 @@ def test_solve_batch(self, cpp, dtype):
319323 A = (rng .randn (n , n ) * 2.0 + 3.0 * np .eye (n )).astype (dtype )
320324 b = rng .randn (n ).astype (dtype )
321325 cpp_r = np .asarray (cpp .linalg .solve (A , b ), dtype = np .float64 )
322- assert_linalg_close (cpp_r , self ._np_solve (A , b ), f"solve batch[{ i } ] n={ n } " )
326+ assert_linalg_close (cpp_r , self ._np_solve (A , b ), _s ( f"solve batch[{ i } ] n={ n } " , dtype ) )
323327
324328 @pytest .mark .parametrize ("n" , [10 , 20 ])
325329 def test_solve_large (self , cpp , dtype , n ):
@@ -328,7 +332,7 @@ def test_solve_large(self, cpp, dtype, n):
328332 A = (rng .randn (n , n ) * 1.5 + 4.0 * np .eye (n )).astype (dtype )
329333 b = rng .randn (n ).astype (dtype )
330334 cpp_r = np .asarray (cpp .linalg .solve (A , b ), dtype = np .float64 )
331- assert_linalg_close (cpp_r , self ._np_solve (A , b ), f"solve large n={ n } " )
335+ assert_linalg_close (cpp_r , self ._np_solve (A , b ), _s ( f"solve large n={ n } " , dtype ) )
332336
333337 @pytest .mark .parametrize ("seed" , [5555 , 6666 , 7777 ])
334338 def test_solve_ill_conditioned (self , cpp , dtype , seed ):
@@ -342,7 +346,7 @@ def test_solve_ill_conditioned(self, cpp, dtype, seed):
342346 b = rng .randn (n ).astype (dtype )
343347 cpp_r = np .asarray (cpp .linalg .solve (A , b ), dtype = np .float64 )
344348 # For ill-conditioned matrices, relax tolerance (float32 promotion + LU)
345- assert_linalg_close (cpp_r , self ._np_solve (A , b ), f"solve ill-cond seed={ seed } " , atol = 1e-10 )
349+ assert_linalg_close (cpp_r , self ._np_solve (A , b ), _s ( f"solve ill-cond seed={ seed } " , dtype ) , atol = 1e-10 )
346350
347351
348352# ============================================================================
@@ -360,15 +364,15 @@ def test_batch(self, cpp, dtype, metric):
360364 assert_bit_aligned (
361365 np .asarray (cpp .spatial .distance .cdist (XA , XB , metric )),
362366 sp_distance .cdist (XA , XB , metric ),
363- f"cdist { metric } " )
367+ _s ( f"cdist { metric } " , dtype ) )
364368
365369 def test_small (self , cpp , dtype ):
366370 XA = np .array ([[0. , 0. ], [1. , 1. ]], dtype = dtype )
367371 XB = np .array ([[0. , 1. ], [1. , 0. ], [2. , 2. ]], dtype = dtype )
368372 assert_bit_aligned (
369373 np .asarray (cpp .spatial .distance .cdist (XA , XB , "euclidean" )),
370374 sp_distance .cdist (XA , XB , "euclidean" ),
371- "cdist small" )
375+ _s ( "cdist small" , dtype ) )
372376
373377
374378# ============================================================================
@@ -390,15 +394,15 @@ def test_query_batch(self, cpp, dtype):
390394 q = random_batch ((3 ,), dtype = dtype , seed = 1016 )
391395 d_cpp , i_cpp = self ._tk (cpp , dtype )(pts ).query (q , k = 1 )
392396 d_py , i_py = sp_cKDTree (pts ).query (q , k = 1 )
393- assert_bit_aligned (np .asarray (d_cpp ), np .asarray (d_py ), "KDTree dist" )
397+ assert_bit_aligned (np .asarray (d_cpp ), np .asarray (d_py ), _s ( "KDTree dist" , dtype ) )
394398 np .testing .assert_array_equal (np .asarray (i_cpp ), np .asarray (i_py ))
395399
396400 def test_query_k3_batch (self , cpp , dtype ):
397401 pts = random_batch ((BATCH , 3 ), dtype = dtype , seed = 1017 )
398402 q = random_batch ((3 ,), dtype = dtype , seed = 1018 )
399403 d_cpp , i_cpp = self ._tk (cpp , dtype )(pts ).query (q , k = 3 )
400404 d_py , i_py = sp_cKDTree (pts ).query (q , k = 3 )
401- assert_bit_aligned (np .asarray (d_cpp ), np .asarray (d_py ), "KDTree k=3 dist" )
405+ assert_bit_aligned (np .asarray (d_cpp ), np .asarray (d_py ), _s ( "KDTree k=3 dist" , dtype ) )
402406 np .testing .assert_array_equal (np .asarray (i_cpp ), np .asarray (i_py ))
403407
404408
@@ -416,15 +420,15 @@ def test_batch(self, cpp, dtype, sigma):
416420 assert_bit_aligned (
417421 np .asarray (cpp .ndimage .gaussian_filter1d (a , sigma = sigma )),
418422 sp_ndimage .gaussian_filter1d (a , sigma = sigma ),
419- f"gaussian_filter1d sigma={ sigma } " )
423+ _s ( f"gaussian_filter1d sigma={ sigma } " , dtype ) )
420424
421425 @pytest .mark .parametrize ("mode" , ["reflect" , "constant" , "nearest" , "mirror" , "wrap" ])
422426 def test_modes (self , cpp , dtype , mode ):
423427 a = random_batch ((BATCH ,), dtype = dtype , seed = 1020 )
424428 assert_bit_aligned (
425429 np .asarray (cpp .ndimage .gaussian_filter1d (a , sigma = 1.5 , mode = mode )),
426430 np .asarray (sp_ndimage .gaussian_filter1d (a , sigma = 1.5 , mode = mode ), dtype = np .float64 ),
427- f"gaussian_filter1d mode={ mode } " )
431+ _s ( f"gaussian_filter1d mode={ mode } " , dtype ) )
428432
429433
430434# ============================================================================
@@ -441,7 +445,7 @@ def test_batch(self, cpp, dtype, k):
441445 assert_bit_aligned (
442446 np .asarray (cpp .signal .medfilt (a , kernel_size = k ), dtype = np .float64 ),
443447 np .asarray (sp_signal .medfilt (a , kernel_size = k ), dtype = np .float64 ),
444- f"medfilt k={ k } " )
448+ _s ( f"medfilt k={ k } " , dtype ) )
445449
446450
447451# ============================================================================
@@ -476,27 +480,27 @@ def _check(self, cpp, dtype, R, seq, label):
476480 # --- identity / canonical angles ---
477481
478482 def test_identity (self , cpp , dtype ):
479- self ._check (cpp , dtype , np .eye (3 , dtype = dtype ), "xyz" , "Rotation identity" )
483+ self ._check (cpp , dtype , np .eye (3 , dtype = dtype ), "xyz" , _s ( "Rotation identity" , dtype ) )
480484
481485 def test_x_rotation (self , cpp , dtype ):
482486 R = sp_Rotation .from_euler ("xyz" , [np .pi / 4 , 0 , 0 ]).as_matrix ()
483- self ._check (cpp , dtype , R .astype (dtype ), "xyz" , "Rotation x-45deg" )
487+ self ._check (cpp , dtype , R .astype (dtype ), "xyz" , _s ( "Rotation x-45deg" , dtype ) )
484488
485489 def test_y_rotation (self , cpp , dtype ):
486490 R = sp_Rotation .from_euler ("xyz" , [0 , np .pi / 6 , 0 ]).as_matrix ()
487- self ._check (cpp , dtype , R .astype (dtype ), "xyz" , "Rotation y-30deg" )
491+ self ._check (cpp , dtype , R .astype (dtype ), "xyz" , _s ( "Rotation y-30deg" , dtype ) )
488492
489493 def test_z_rotation (self , cpp , dtype ):
490494 R = sp_Rotation .from_euler ("xyz" , [0 , 0 , np .pi / 3 ]).as_matrix ()
491- self ._check (cpp , dtype , R .astype (dtype ), "xyz" , "Rotation z-60deg" )
495+ self ._check (cpp , dtype , R .astype (dtype ), "xyz" , _s ( "Rotation z-60deg" , dtype ) )
492496
493497 def test_xyz_sequence (self , cpp , dtype ):
494498 R = sp_Rotation .from_euler ("xyz" , np .deg2rad ([20. , 30. , 45. ])).as_matrix ()
495- self ._check (cpp , dtype , R .astype (dtype ), "xyz" , "Rotation xyz(20,30,45)" )
499+ self ._check (cpp , dtype , R .astype (dtype ), "xyz" , _s ( "Rotation xyz(20,30,45)" , dtype ) )
496500
497501 def test_zyx_sequence (self , cpp , dtype ):
498502 R = sp_Rotation .from_euler ("zyx" , np .deg2rad ([10. , - 20. , 40. ])).as_matrix ()
499- self ._check (cpp , dtype , R .astype (dtype ), "zyx" , "Rotation zyx(10,-20,40)" )
503+ self ._check (cpp , dtype , R .astype (dtype ), "zyx" , _s ( "Rotation zyx(10,-20,40)" , dtype ) )
500504
501505 # --- 100 random batches for each Tait-Bryan sequence (§5 requirement) ---
502506
@@ -510,7 +514,7 @@ def test_random_batch(self, cpp, dtype, seq):
510514 for i in range (BATCH ):
511515 a = rng .uniform (- np .pi / 2 + 0.1 , np .pi / 2 - 0.1 , 3 )
512516 R = sp_Rotation .from_euler (seq , a ).as_matrix ().astype (dtype )
513- self ._check (cpp , dtype , R , seq , f"Rotation { seq } random[{ i } ]" )
517+ self ._check (cpp , dtype , R , seq , _s ( f"Rotation { seq } random[{ i } ]" , dtype ) )
514518
515519 # --- gimbal lock boundary tests ---
516520
@@ -524,7 +528,7 @@ def test_gimbal_lock_near(self, cpp, dtype, beta):
524528 alpha = rng .uniform (- np .pi , np .pi )
525529 gamma = rng .uniform (- np .pi , np .pi )
526530 R = sp_Rotation .from_euler ("xyz" , [alpha , beta , gamma ]).as_matrix ().astype (dtype )
527- self ._check (cpp , dtype , R , "xyz" , f"Rotation gimbal beta={ beta :.4f} [{ i } ]" )
531+ self ._check (cpp , dtype , R , "xyz" , _s ( f"Rotation gimbal beta={ beta :.4f} [{ i } ]" , dtype ) )
528532
529533 # --- random rotation matrix test ---
530534
@@ -534,7 +538,7 @@ def test_random_matrices(self, cpp, dtype):
534538 rng = np .random .RandomState (31415 )
535539 for i in range (BATCH ):
536540 R = sp_Rotation .random (random_state = rng ).as_matrix ().astype (dtype )
537- self ._check (cpp , dtype , R , "xyz" , f"Rotation random_matrix[{ i } ]" )
541+ self ._check (cpp , dtype , R , "xyz" , _s ( f"Rotation random_matrix[{ i } ]" , dtype ) )
538542
539543 # --- intrinsic 'XYZ' sequence ---
540544
@@ -545,7 +549,7 @@ def test_XYZ_intrinsic(self, cpp, dtype):
545549 for i in range (BATCH ):
546550 a = rng .uniform (- np .pi / 2 + 0.1 , np .pi / 2 - 0.1 , 3 )
547551 R = sp_Rotation .from_euler ("XYZ" , a ).as_matrix ().astype (dtype )
548- self ._check (cpp , dtype , R , "XYZ" , f"Rotation XYZ intrinsic[{ i } ]" )
552+ self ._check (cpp , dtype , R , "XYZ" , _s ( f"Rotation XYZ intrinsic[{ i } ]" , dtype ) )
549553
550554
551555if __name__ == "__main__" :
0 commit comments