From 18c406cc84ee92a6697024ed196563a7b6b4627c Mon Sep 17 00:00:00 2001 From: Kenya Otsuka Date: Tue, 26 May 2026 10:51:25 +0000 Subject: [PATCH 1/2] fix: replace strict float assertions with assert_allclose in tests Floating-point computation results (corrcoef, mean, detrend, normalize, SGD optimizer step) were compared with assert_array_equal / assertEqual, causing environment-dependent failures due to rounding differences. Replace with np.testing.assert_allclose (rtol=1e-12, atol=1e-12 for float64 NumPy paths; rtol=1e-6, atol=1e-8 for PyTorch tensors) and assertAlmostEqual for the scalar learning-rate check. Integer/index/mask comparisons are left as assert_array_equal. Addresses KamitaniLab#129 Co-authored-by: Claude --- tests/feature/test_feature.py | 24 ++++++++++----------- tests/preproc/test_interface.py | 8 +++---- tests/recon/torch/modules/test_optimizer.py | 5 +++-- tests/test_stats.py | 10 ++++----- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/tests/feature/test_feature.py b/tests/feature/test_feature.py index 720e8aba..026714eb 100644 --- a/tests/feature/test_feature.py +++ b/tests/feature/test_feature.py @@ -26,7 +26,7 @@ def test_normalize_feature_1d(self): shift=feat_mean0, scale=feat_std0, std_ddof=1) - np.testing.assert_array_equal(feat_test, feat_valid) + np.testing.assert_allclose(feat_test, feat_valid, rtol=1e-12, atol=1e-12) # Mean (channel-wise) + SD (all) feat_valid = ((feat - feat_mean_ch) / feat_std_all) * feat_std0 + feat_mean0 @@ -35,7 +35,7 @@ def test_normalize_feature_1d(self): shift=feat_mean0, scale=feat_std0, std_ddof=1) - np.testing.assert_array_equal(feat_test, feat_valid) + np.testing.assert_allclose(feat_test, feat_valid, rtol=1e-12, atol=1e-12) # Mean (all) + SD (channel-wise) feat_valid = ((feat - feat_mean_all) / feat_std_ch) * feat_std0 + feat_mean0 @@ -44,7 +44,7 @@ def test_normalize_feature_1d(self): shift=feat_mean0, scale=feat_std0, std_ddof=1) - np.testing.assert_array_equal(feat_test, feat_valid) + np.testing.assert_allclose(feat_test, feat_valid, rtol=1e-12, atol=1e-12) # Mean (all) + SD (all) feat_valid = ((feat - feat_mean_all) / feat_std_all) * feat_std0 + feat_mean0 @@ -53,7 +53,7 @@ def test_normalize_feature_1d(self): shift=feat_mean0, scale=feat_std0, std_ddof=1) - np.testing.assert_array_equal(feat_test, feat_valid) + np.testing.assert_allclose(feat_test, feat_valid, rtol=1e-12, atol=1e-12) # Mean (channel-wise) + SD (channel-wise), self-mean shift feat_valid = ((feat - feat_mean_ch) / feat_std_ch) * feat_std0 + feat_mean_ch @@ -62,7 +62,7 @@ def test_normalize_feature_1d(self): shift='self', scale=feat_std0, std_ddof=1) - np.testing.assert_array_equal(feat_test, feat_valid) + np.testing.assert_allclose(feat_test, feat_valid, rtol=1e-12, atol=1e-12) # Mean (channel-wise) + SD (channel-wise), self-mean shift and self-SD scale feat_valid = ((feat - feat_mean_ch) / feat_std_ch) * feat_std_ch + feat_mean_ch @@ -71,7 +71,7 @@ def test_normalize_feature_1d(self): shift='self', scale='self', std_ddof=1) - np.testing.assert_array_equal(feat_test, feat_valid) + np.testing.assert_allclose(feat_test, feat_valid, rtol=1e-12, atol=1e-12) def test_normalize_feature_3d(self): feat = np.random.rand(64, 16, 16) @@ -94,7 +94,7 @@ def test_normalize_feature_3d(self): shift=feat_mean0, scale=feat_std0, std_ddof=1) - np.testing.assert_array_equal(feat_test, feat_valid) + np.testing.assert_allclose(feat_test, feat_valid, rtol=1e-12, atol=1e-12) # Mean (channel-wise) + SD (all) feat_valid = ((feat - feat_mean_ch) / feat_std_all) * feat_std0 + feat_mean0 @@ -103,7 +103,7 @@ def test_normalize_feature_3d(self): shift=feat_mean0, scale=feat_std0, std_ddof=1) - np.testing.assert_array_equal(feat_test, feat_valid) + np.testing.assert_allclose(feat_test, feat_valid, rtol=1e-12, atol=1e-12) # Mean (all) + SD (channel-wise) feat_valid = ((feat - feat_mean_all) / feat_std_ch) * feat_std0 + feat_mean0 @@ -112,7 +112,7 @@ def test_normalize_feature_3d(self): shift=feat_mean0, scale=feat_std0, std_ddof=1) - np.testing.assert_array_equal(feat_test, feat_valid) + np.testing.assert_allclose(feat_test, feat_valid, rtol=1e-12, atol=1e-12) # Mean (all) + SD (all) feat_valid = ((feat - feat_mean_all) / feat_std_all) * feat_std0 + feat_mean0 @@ -121,7 +121,7 @@ def test_normalize_feature_3d(self): shift=feat_mean0, scale=feat_std0, std_ddof=1) - np.testing.assert_array_equal(feat_test, feat_valid) + np.testing.assert_allclose(feat_test, feat_valid, rtol=1e-12, atol=1e-12) # Mean (channel-wise) + SD (channel-wise), self-mean shift feat_valid = ((feat - feat_mean_ch) / feat_std_ch) * feat_std0 + feat_mean_ch @@ -130,7 +130,7 @@ def test_normalize_feature_3d(self): shift='self', scale=feat_std0, std_ddof=1) - np.testing.assert_array_equal(feat_test, feat_valid) + np.testing.assert_allclose(feat_test, feat_valid, rtol=1e-12, atol=1e-12) # Mean (channel-wise) + SD (channel-wise), self-mean shift and self-SD scale feat_valid = ((feat - feat_mean_ch) / feat_std_ch) * feat_std_ch + feat_mean_ch @@ -146,7 +146,7 @@ def test_normalize_feature_3d(self): channel_wise_std=False, scale=feat_std0, std_ddof=1) - np.testing.assert_array_equal(feat_test, feat_valid) + np.testing.assert_allclose(feat_test, feat_valid, rtol=1e-12, atol=1e-12) if __name__ == '__main__': diff --git a/tests/preproc/test_interface.py b/tests/preproc/test_interface.py index 1e4922ac..7460808a 100644 --- a/tests/preproc/test_interface.py +++ b/tests/preproc/test_interface.py @@ -25,7 +25,7 @@ def test_average_sample(cls): test_output_x, test_output_ind = interface.average_sample( x, group, verbose=True) - np.testing.assert_array_equal(test_output_x, exp_output_x) + np.testing.assert_allclose(test_output_x, exp_output_x, rtol=1e-12, atol=1e-12) np.testing.assert_array_equal(test_output_ind, exp_output_ind) @classmethod @@ -43,7 +43,7 @@ def test_detrend_sample_default(cls): test_output = interface.detrend_sample(x, group, verbose=True) - np.testing.assert_array_equal(test_output, exp_output) + np.testing.assert_allclose(test_output, exp_output, rtol=1e-12, atol=1e-12) @classmethod def test_detrend_sample_nokeepmean(cls): @@ -59,7 +59,7 @@ def test_detrend_sample_nokeepmean(cls): test_output = interface.detrend_sample( x, group, keep_mean=False, verbose=True) - np.testing.assert_array_equal(test_output, exp_output) + np.testing.assert_allclose(test_output, exp_output, rtol=1e-12, atol=1e-12) @classmethod def test_normalize_sample(cls): @@ -77,7 +77,7 @@ def test_normalize_sample(cls): test_output = interface.normalize_sample(x, group, verbose=True) - np.testing.assert_array_equal(test_output, exp_output) + np.testing.assert_allclose(test_output, exp_output, rtol=1e-12, atol=1e-12) @classmethod def test_shift_sample_singlegroup(cls): diff --git a/tests/recon/torch/modules/test_optimizer.py b/tests/recon/torch/modules/test_optimizer.py index 8831a90a..4017ef47 100644 --- a/tests/recon/torch/modules/test_optimizer.py +++ b/tests/recon/torch/modules/test_optimizer.py @@ -53,6 +53,7 @@ def test_build_optimizer_factory(self): latent_next, latent_next_expected, rtol=1e-6, + atol=1e-8, err_msg="Optimizer does not update the latent variable correctly.", ) @@ -92,10 +93,10 @@ def test_build_scheduler_factory(self): loss.backward() optimizer.step() scheduler.step() - self.assertEqual( + self.assertAlmostEqual( optimizer.param_groups[0]["lr"], 0.1 * 0.1, - "Scheduler does not update the learning rate correctly.", + msg="Scheduler does not update the learning rate correctly.", ) # check if reference to the optimizer is kept during re-initialization diff --git a/tests/test_stats.py b/tests/test_stats.py index 9c99a829..ba5480b2 100644 --- a/tests/test_stats.py +++ b/tests/test_stats.py @@ -20,7 +20,7 @@ def test_corrcoef_matrix_matrix_default(self): test_output = bdst.corrcoef(x, y) - np.testing.assert_array_equal(test_output, exp_output) + np.testing.assert_allclose(test_output, exp_output, rtol=1e-12, atol=1e-12) def test_corrcoef_matrix_matrix_varcol(self): '''Test for corrcoef (matrix and matrix, var=col)''' @@ -33,7 +33,7 @@ def test_corrcoef_matrix_matrix_varcol(self): test_output = bdst.corrcoef(x, y, var='col') - np.testing.assert_array_equal(test_output, exp_output) + np.testing.assert_allclose(test_output, exp_output, rtol=1e-12, atol=1e-12) def test_corrcoef_vector_vector(self): '''Test for corrcoef (vector and vector)''' @@ -45,7 +45,7 @@ def test_corrcoef_vector_vector(self): test_output = bdst.corrcoef(x, y) - np.testing.assert_array_equal(test_output, exp_output) + np.testing.assert_allclose(test_output, exp_output, rtol=1e-12, atol=1e-12) def test_corrcoef_hvector_hvector(self): '''Test for corrcoef (horizontal vector and horizontal vector)''' @@ -57,7 +57,7 @@ def test_corrcoef_hvector_hvector(self): test_output = bdst.corrcoef(x, y) - np.testing.assert_array_equal(test_output, exp_output) + np.testing.assert_allclose(test_output, exp_output, rtol=1e-12, atol=1e-12) def test_corrcoef_vvector_vvector(self): '''Test for corrcoef (vertical vector and vertical vector)''' @@ -69,7 +69,7 @@ def test_corrcoef_vvector_vvector(self): test_output = bdst.corrcoef(x, y) - np.testing.assert_array_equal(test_output, exp_output) + np.testing.assert_allclose(test_output, exp_output, rtol=1e-12, atol=1e-12) def test_corrcoef_matrix_vector_varrow(self): '''Test for corrcoef (matrix and vector, var=row)''' From 79709197439e766c187e6b7b537457ef2a937c68 Mon Sep 17 00:00:00 2001 From: Kenya Otsuka Date: Thu, 28 May 2026 10:02:25 +0000 Subject: [PATCH 2/2] fix: replace assertTrue(np.allclose) with assert_allclose in test_metrics Explicit rtol=1e-12, atol=1e-12 for float64 NumPy correlation computations, consistent with the rest of the test suite changes. Co-authored-by: Claude --- tests/evals/test_metrics.py | 57 ++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/tests/evals/test_metrics.py b/tests/evals/test_metrics.py index 3ef9053c..1d3db0cf 100644 --- a/tests/evals/test_metrics.py +++ b/tests/evals/test_metrics.py @@ -18,9 +18,9 @@ def test_profile_correlation(self): for i in range(n) ]]) - self.assertTrue(np.allclose( - profile_correlation(x, y), r - )) + np.testing.assert_allclose( + profile_correlation(x, y), r, rtol=1e-12, atol=1e-12 + ) self.assertEqual(profile_correlation(x, y).shape, (1, n)) # Multi-d array @@ -34,9 +34,9 @@ def test_profile_correlation(self): ]]) r = r.reshape(1, 4, 3, 2) - self.assertTrue(np.allclose( - profile_correlation(x, y), r - )) + np.testing.assert_allclose( + profile_correlation(x, y), r, rtol=1e-12, atol=1e-12 + ) self.assertEqual(profile_correlation(x, y).shape, (1, 4, 3, 2)) def test_pattern_correlation(self): @@ -48,9 +48,9 @@ def test_pattern_correlation(self): for i in range(10) ]) - self.assertTrue(np.allclose( - pattern_correlation(x, y), r - )) + np.testing.assert_allclose( + pattern_correlation(x, y), r, rtol=1e-12, atol=1e-12 + ) self.assertEqual(pattern_correlation(x, y).shape, (10,)) # Multi-d array @@ -63,26 +63,23 @@ def test_pattern_correlation(self): for i in range(10) ]) - self.assertTrue(np.allclose( - pattern_correlation(x, y), r - )) + np.testing.assert_allclose( + pattern_correlation(x, y), r, rtol=1e-12, atol=1e-12 + ) self.assertEqual(pattern_correlation(x, y).shape, (10,)) def test_2d(self): with open('tests/data/testdata-2d.pkl.gz', 'rb') as f: d = pickle.load(f) - self.assertTrue(np.allclose( - profile_correlation(d['x'], d['y']), - d['r_prof'] - )) - self.assertTrue(np.allclose( - pattern_correlation(d['x'], d['y']), - d['r_patt'] - )) - self.assertTrue(np.allclose( - pairwise_identification(d['x'], d['y']), - d['ident_acc'] - )) + np.testing.assert_allclose( + profile_correlation(d['x'], d['y']), d['r_prof'], rtol=1e-12, atol=1e-12 + ) + np.testing.assert_allclose( + pattern_correlation(d['x'], d['y']), d['r_patt'], rtol=1e-12, atol=1e-12 + ) + np.testing.assert_allclose( + pairwise_identification(d['x'], d['y']), d['ident_acc'], rtol=1e-12, atol=1e-12 + ) def test_2d_nan(self): with open('tests/data/testdata-2d-nan.pkl.gz', 'rb') as f: @@ -91,14 +88,14 @@ def test_2d_nan(self): # profile_correlation(d['x'], d['y']), # d['r_prof'] # )) - self.assertTrue(np.allclose( + np.testing.assert_allclose( pattern_correlation(d['x'], d['y'], remove_nan=True), - d['r_patt'], - )) - self.assertTrue(np.allclose( + d['r_patt'], rtol=1e-12, atol=1e-12 + ) + np.testing.assert_allclose( pairwise_identification(d['x'], d['y'], remove_nan=True), - d['ident_acc'], - )) + d['ident_acc'], rtol=1e-12, atol=1e-12 + ) if __name__ == '__main__': unittest.main() \ No newline at end of file