diff options
Diffstat (limited to 'src/silx/math/test/test_histogramnd_error.py')
-rw-r--r-- | src/silx/math/test/test_histogramnd_error.py | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/src/silx/math/test/test_histogramnd_error.py b/src/silx/math/test/test_histogramnd_error.py new file mode 100644 index 0000000..c640b4a --- /dev/null +++ b/src/silx/math/test/test_histogramnd_error.py @@ -0,0 +1,487 @@ +# /*########################################################################## +# Copyright (C) 2016-2023 European Synchrotron Radiation Facility +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# ############################################################################*/ + +__authors__ = ["D. Naudet"] +__license__ = "MIT" +__date__ = "01/02/2016" + +""" +Tests of the histogramnd function, error cases. +""" +import unittest + +import numpy as np + +from silx.math.chistogramnd import chistogramnd as histogramnd +from silx.math import Histogramnd + + +# ============================================================== +# ============================================================== +# ============================================================== + + +class _Test_chistogramnd_errors(unittest.TestCase): + """ + Unit tests of the chistogramnd error cases. + """ + + __test__ = False # ignore abstract class + + def setUp(self): + self.skipTest("Abstract class") + + def test_weights_shape(self): + """ """ + + for err_w_shape in self.err_weights_shapes: + test_msg = "Testing invalid weights shape : {0}" "".format(err_w_shape) + + err_weights = np.random.randint(0, high=10, size=err_w_shape) + err_weights = err_weights.astype(np.double) + + ex_str = None + try: + histo, cumul = histogramnd( + self.sample, self.histo_range, self.n_bins, weights=err_weights + )[0:2] + except ValueError as ex: + ex_str = str(ex) + + self.assertIsNotNone(ex_str, msg=test_msg) + self.assertEqual( + ex_str, + "<weights> must be an array whose length " + "is equal to the number of samples.", + ) + + def test_histo_range_shape(self): + """ """ + n_dims = 1 if len(self.s_shape) == 1 else self.s_shape[1] + expected_txt_tpl = ( + "<histo_range> error : expected {n_dims} sets " + "of lower and upper bin edges, " + "got the following instead : {histo_range}. " + "(provided <sample> contains " + "{n_dims}D values)" + ) + + for err_histo_range in self.err_histo_range_shapes: + test_msg = "Testing invalid histo_range shape : {0}" "".format( + err_histo_range + ) + + expected_txt = expected_txt_tpl.format( + histo_range=err_histo_range, n_dims=n_dims + ) + + ex_str = None + try: + histo, cumul = histogramnd( + self.sample, err_histo_range, self.n_bins, weights=self.weights + )[0:2] + except ValueError as ex: + ex_str = str(ex) + + self.assertIsNotNone(ex_str, msg=test_msg) + self.assertEqual(ex_str, expected_txt, msg=test_msg) + + def test_nbins_shape(self): + """ """ + + expected_txt = ( + "n_bins must be either a scalar (same number " + "of bins for all dimensions) or " + "an array (number of bins for each " + "dimension)." + ) + + for err_n_bins in self.err_n_bins_shapes: + test_msg = "Testing invalid n_bins shape : {0}" "".format(err_n_bins) + + ex_str = None + try: + histo, cumul = histogramnd( + self.sample, self.histo_range, err_n_bins, weights=self.weights + )[0:2] + except ValueError as ex: + ex_str = str(ex) + + self.assertIsNotNone(ex_str, msg=test_msg) + self.assertEqual(ex_str, expected_txt, msg=test_msg) + + def test_nbins_values(self): + """ """ + expected_txt = "<n_bins> : only positive values allowed." + + for err_n_bins in self.err_n_bins_values: + test_msg = "Testing invalid n_bins value : {0}" "".format(err_n_bins) + + ex_str = None + try: + histo, cumul = histogramnd( + self.sample, self.histo_range, err_n_bins, weights=self.weights + )[0:2] + except ValueError as ex: + ex_str = str(ex) + + self.assertIsNotNone(ex_str, msg=test_msg) + self.assertEqual(ex_str, expected_txt, msg=test_msg) + + def test_histo_shape(self): + """ """ + for err_h_shape in self.err_histo_shapes: + test_msg = "Testing invalid histo shape : {0}" "".format(err_h_shape) + + expected_txt = ( + "Provided <histo> array doesn't have " + "a shape compatible with <n_bins> " + ": should be {0} instead of {1}." + "".format(self.h_shape, err_h_shape) + ) + + histo = np.zeros(shape=err_h_shape, dtype=np.uint32) + + ex_str = None + try: + histo, cumul = histogramnd( + self.sample, + self.histo_range, + self.n_bins, + weights=self.weights, + histo=histo, + )[0:2] + except ValueError as ex: + ex_str = str(ex) + + self.assertIsNotNone(ex_str, msg=test_msg) + self.assertEqual(ex_str, expected_txt, msg=test_msg) + + def test_histo_dtype(self): + """ """ + for err_h_dtype in self.err_histo_dtypes: + test_msg = "Testing invalid histo dtype : {0}" "".format(err_h_dtype) + + histo = np.zeros(shape=self.h_shape, dtype=err_h_dtype) + + expected_txt = ( + "Provided <histo> array doesn't have " + "the expected type " + ": should be {0} instead of {1}." + "".format(np.uint32, histo.dtype) + ) + + ex_str = None + try: + histo, cumul = histogramnd( + self.sample, + self.histo_range, + self.n_bins, + weights=self.weights, + histo=histo, + )[0:2] + except ValueError as ex: + ex_str = str(ex) + + self.assertIsNotNone(ex_str, msg=test_msg) + self.assertEqual(ex_str, expected_txt, msg=test_msg) + + def test_weighted_histo_shape(self): + """ """ + # using the same values as histo + for err_h_shape in self.err_histo_shapes: + test_msg = "Testing invalid weighted_histo shape : {0}" "".format( + err_h_shape + ) + + expected_txt = ( + "Provided <weighted_histo> array doesn't have " + "a shape compatible with <n_bins> " + ": should be {0} instead of {1}." + "".format(self.h_shape, err_h_shape) + ) + + cumul = np.zeros(shape=err_h_shape, dtype=np.double) + + ex_str = None + try: + histo, cumul = histogramnd( + self.sample, + self.histo_range, + self.n_bins, + weights=self.weights, + weighted_histo=cumul, + )[0:2] + except ValueError as ex: + ex_str = str(ex) + + self.assertIsNotNone(ex_str, msg=test_msg) + self.assertEqual(ex_str, expected_txt, msg=test_msg) + + def test_cumul_dtype(self): + """ """ + # using the same values as histo + for err_h_dtype in self.err_histo_dtypes: + test_msg = "Testing invalid weighted_histo dtype : {0}" "".format( + err_h_dtype + ) + + cumul = np.zeros(shape=self.h_shape, dtype=err_h_dtype) + + expected_txt = ( + "Provided <weighted_histo> array doesn't have " + "the expected type " + ": should be {0} or {1} instead of {2}." + "".format(np.float64, np.float32, cumul.dtype) + ) + + ex_str = None + try: + histo, cumul = histogramnd( + self.sample, + self.histo_range, + self.n_bins, + weights=self.weights, + weighted_histo=cumul, + )[0:2] + except ValueError as ex: + ex_str = str(ex) + + self.assertIsNotNone(ex_str, msg=test_msg) + self.assertEqual(ex_str, expected_txt, msg=test_msg) + + def test_wh_histo_dtype(self): + """ """ + # using the same values as histo + for err_h_dtype in self.err_histo_dtypes: + test_msg = "Testing invalid wh_dtype dtype : {0}" "".format(err_h_dtype) + + expected_txt = "<wh_dtype> type not supported : {0}." "".format(err_h_dtype) + + ex_str = None + try: + histo, cumul = histogramnd( + self.sample, + self.histo_range, + self.n_bins, + weights=self.weights, + wh_dtype=err_h_dtype, + )[0:2] + except ValueError as ex: + ex_str = str(ex) + + self.assertIsNotNone(ex_str, msg=test_msg) + self.assertEqual(ex_str, expected_txt, msg=test_msg) + + def test_unmanaged_dtypes(self): + """ """ + for err_unmanaged_dtype in self.err_unmanaged_dtypes: + test_msg = "Testing unmanaged dtypes : {0}" "".format(err_unmanaged_dtype) + + sample = self.sample.astype(err_unmanaged_dtype[0]) + weights = self.weights.astype(err_unmanaged_dtype[1]) + + expected_txt = ( + "Case not supported - sample:{0} " + "and weights:{1}." + "".format(sample.dtype, weights.dtype) + ) + + ex_str = None + try: + histogramnd(sample, self.histo_range, self.n_bins, weights=weights) + except TypeError as ex: + ex_str = str(ex) + + self.assertIsNotNone(ex_str, msg=test_msg) + self.assertEqual(ex_str, expected_txt, msg=test_msg) + + def test_uncontiguous_histo(self): + """ """ + # non contiguous array + shape = np.array(self.n_bins, ndmin=1) + shape[0] *= 2 + histo_tmp = np.zeros(shape) + histo = histo_tmp[::2, ...] + + expected_txt = "<histo> must be a C_CONTIGUOUS numpy array." + + ex_str = None + try: + histogramnd( + self.sample, + self.histo_range, + self.n_bins, + weights=self.weights, + histo=histo, + ) + except ValueError as ex: + ex_str = str(ex) + + self.assertIsNotNone(ex_str) + self.assertEqual(ex_str, expected_txt) + + def test_uncontiguous_weighted_histo(self): + """ """ + # non contiguous array + shape = np.array(self.n_bins, ndmin=1) + shape[0] *= 2 + cumul_tmp = np.zeros(shape) + cumul = cumul_tmp[::2, ...] + + expected_txt = "<weighted_histo> must be a C_CONTIGUOUS numpy array." + + ex_str = None + try: + histogramnd( + self.sample, + self.histo_range, + self.n_bins, + weights=self.weights, + weighted_histo=cumul, + ) + except ValueError as ex: + ex_str = str(ex) + + self.assertIsNotNone(ex_str) + self.assertEqual(ex_str, expected_txt) + + +class Test_chistogramnd_1D_errors(_Test_chistogramnd_errors): + """ + Unit tests of the 1D histogramnd error cases. + """ + + __test__ = True # because _Test_chistogramnd_errors is ignored + + def setUp(self): + # nominal values + self.n_elements = 1000 + self.s_shape = (self.n_elements,) + self.w_shape = (self.n_elements,) + + self.histo_range = [0.0, 100.0] + self.n_bins = 10 + + self.h_shape = (self.n_bins,) + + self.sample = np.random.randint(0, high=10, size=self.s_shape) + self.sample = self.sample.astype(np.double) + + self.weights = np.random.randint(0, high=10, size=self.w_shape) + self.weights = self.weights.astype(np.double) + + self.err_weights_shapes = ( + (self.n_elements + 1,), + (self.n_elements - 1,), + (self.n_elements - 1, 3), + ) + self.err_histo_range_shapes = ([0.0], [0.0, 1.0, 2.0], [[0.0], [1.0]]) + self.err_n_bins_shapes = ([10, 2], [[10], [2]]) + self.err_n_bins_values = (0, [-10], None) + self.err_histo_shapes = ( + (self.n_bins + 1,), + (self.n_bins - 1,), + (self.n_bins, self.n_bins), + ) + # these are used for testing the histo parameter as well + # as the weighted_histo parameter. + self.err_histo_dtypes = (np.uint16, np.float16) + + self.err_unmanaged_dtypes = ( + (np.double, np.uint16), + (np.uint16, np.double), + (np.uint16, np.uint16), + ) + + +class Test_chistogramnd_ND_range(unittest.TestCase): + """ """ + + def test_invalid_histo_range(self): + data = np.random.random((60, 60)) + nbins = 10 + + with self.assertRaises(ValueError): + histo_range = data.min(), np.inf + + Histogramnd(sample=data.ravel(), histo_range=histo_range, n_bins=nbins) + + histo_range = data.min(), np.nan + + Histogramnd(sample=data.ravel(), histo_range=histo_range, n_bins=nbins) + + +class Test_chistogramnd_ND_errors(_Test_chistogramnd_errors): + """ + Unit tests of the 3D histogramnd error cases. + """ + + __test__ = True # because _Test_chistogramnd_errors is ignored + + def setUp(self): + # nominal values + self.n_elements = 1000 + self.s_shape = (self.n_elements, 3) + self.w_shape = (self.n_elements,) + + self.histo_range = [[0.0, 100.0], [0.0, 100.0], [0.0, 100.0]] + self.n_bins = (10, 20, 30) + + self.h_shape = self.n_bins + + self.sample = np.random.randint(0, high=10, size=self.s_shape) + self.sample = self.sample.astype(np.double) + + self.weights = np.random.randint(0, high=10, size=self.w_shape) + self.weights = self.weights.astype(np.double) + + self.err_weights_shapes = ( + (self.n_elements + 1,), + (self.n_elements - 1,), + (self.n_elements - 1, 3), + ) + self.err_histo_range_shapes = ( + [0.0], + [0.0, 1.0], + [[0.0, 10.0], [0.0, 10.0]], + [0.0, 10.0, 0, 10.0, 0, 10.0], + ) + self.err_n_bins_shapes = ([10, 2], [[10], [20], [30]]) + self.err_n_bins_values = (0, [-10], [10, 20, -4], None, [10, None, 30]) + self.err_histo_shapes = ( + (self.n_bins[0] + 1, self.n_bins[1], self.n_bins[2]), + (self.n_bins[0], self.n_bins[1], self.n_bins[2] - 1), + (self.n_bins[0], self.n_bins[1]), + (self.n_bins[1], self.n_bins[0], self.n_bins[2]), + (self.n_bins[0], self.n_bins[1], self.n_bins[2], 10), + ) + # these are used for testing the histo parameter as well + # as the weighted_histo parameter. + self.err_histo_dtypes = (np.uint16, np.float16) + + self.err_unmanaged_dtypes = ( + (np.double, np.uint16), + (np.uint16, np.double), + (np.uint16, np.uint16), + ) |