diff options
Diffstat (limited to 'silx/utils/test/test_array_like.py')
-rw-r--r-- | silx/utils/test/test_array_like.py | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/silx/utils/test/test_array_like.py b/silx/utils/test/test_array_like.py new file mode 100644 index 0000000..7cd004c --- /dev/null +++ b/silx/utils/test/test_array_like.py @@ -0,0 +1,453 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2016-2017 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. +# +# ###########################################################################*/ +"""Tests for array_like module""" + +__authors__ = ["P. Knobel"] +__license__ = "MIT" +__date__ = "09/01/2017" + +try: + import h5py +except ImportError: + h5py = None + +import numpy +import os +import tempfile +import unittest + +from ..array_like import DatasetView, ListOfImages +from ..array_like import get_dtype, get_concatenated_dtype, get_shape,\ + is_array, is_nested_sequence, is_list_of_arrays + + +@unittest.skipIf(h5py is None, + "h5py is needed to test DatasetView") +class TestTransposedDatasetView(unittest.TestCase): + + def setUp(self): + # dataset attributes + self.ndim = 3 + self.original_shape = (5, 10, 20) + self.size = 1 + for dim in self.original_shape: + self.size *= dim + + self.volume = numpy.arange(self.size).reshape(self.original_shape) + + self.tempdir = tempfile.mkdtemp() + self.h5_fname = os.path.join(self.tempdir, "tempfile.h5") + with h5py.File(self.h5_fname, "w") as f: + f["volume"] = self.volume + + self.h5f = h5py.File(self.h5_fname, "r") + + self.all_permutations = [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), + (2, 0, 1), (2, 1, 0)] + + def tearDown(self): + self.h5f.close() + os.unlink(self.h5_fname) + os.rmdir(self.tempdir) + + def _testSize(self, obj): + """These assertions apply to all following test cases""" + self.assertEqual(obj.ndim, self.ndim) + self.assertEqual(obj.size, self.size) + size_from_shape = 1 + for dim in obj.shape: + size_from_shape *= dim + self.assertEqual(size_from_shape, self.size) + + for dim in self.original_shape: + self.assertIn(dim, obj.shape) + + def testNoTransposition(self): + """no transposition (transposition = (0, 1, 2))""" + a = DatasetView(self.h5f["volume"]) + + self.assertEqual(a.shape, self.original_shape) + self._testSize(a) + + # reversing the dimensions twice results in no change + rtrans = list(reversed(range(self.ndim))) + self.assertTrue(numpy.array_equal( + a, + a.transpose(rtrans).transpose(rtrans))) + + for i in range(a.shape[0]): + for j in range(a.shape[1]): + for k in range(a.shape[2]): + self.assertEqual(self.h5f["volume"][i, j, k], + a[i, j, k]) + + def _testTransposition(self, transposition): + """test transposed dataset + + :param tuple transposition: List of dimensions (0... n-1) sorted + in the desired order + """ + a = DatasetView(self.h5f["volume"], + transposition=transposition) + self._testSize(a) + + # sort shape of transposed object, to hopefully find the original shape + sorted_shape = tuple(dim_size for (_, dim_size) in + sorted(zip(transposition, a.shape))) + self.assertEqual(sorted_shape, self.original_shape) + + a_as_array = numpy.array(self.h5f["volume"]).transpose(transposition) + + # test the __array__ method + self.assertTrue(numpy.array_equal( + numpy.array(a), + a_as_array)) + + # test slicing + for selection in [(2, slice(None), slice(None)), + (slice(None), 1, slice(0, 8)), + (slice(0, 3), slice(None), 3), + (1, 3, slice(None)), + (slice(None), 2, 1), + (4, slice(1, 9, 2), 2)]: + self.assertIsInstance(a[selection], numpy.ndarray) + self.assertTrue(numpy.array_equal( + a[selection], + a_as_array[selection])) + + # test the DatasetView.__getitem__ for single values + # (step adjusted to test at least 3 indices in each dimension) + for i in range(0, a.shape[0], a.shape[0] // 3): + for j in range(0, a.shape[1], a.shape[1] // 3): + for k in range(0, a.shape[2], a.shape[2] // 3): + sorted_indices = tuple(idx for (_, idx) in + sorted(zip(transposition, [i, j, k]))) + viewed_value = a[i, j, k] + corresponding_original_value = self.h5f["volume"][sorted_indices] + self.assertEqual(viewed_value, + corresponding_original_value) + + # reversing the dimensions twice results in no change + rtrans = list(reversed(range(self.ndim))) + self.assertTrue(numpy.array_equal( + a, + a.transpose(rtrans).transpose(rtrans))) + + # test .T property + self.assertTrue(numpy.array_equal( + a.T, + a.transpose(rtrans))) + + def testTransposition012(self): + """transposition = (0, 1, 2) + (should be the same as testNoTransposition)""" + self._testTransposition((0, 1, 2)) + + def testTransposition021(self): + """transposition = (0, 2, 1)""" + self._testTransposition((0, 2, 1)) + + def testTransposition102(self): + """transposition = (1, 0, 2)""" + self._testTransposition((1, 0, 2)) + + def testTransposition120(self): + """transposition = (1, 2, 0)""" + self._testTransposition((1, 2, 0)) + + def testTransposition201(self): + """transposition = (2, 0, 1)""" + self._testTransposition((2, 0, 1)) + + def testTransposition210(self): + """transposition = (2, 1, 0)""" + self._testTransposition((2, 1, 0)) + + def testAllDoubleTranspositions(self): + for trans1 in self.all_permutations: + for trans2 in self.all_permutations: + self._testDoubleTransposition(trans1, trans2) + + def _testDoubleTransposition(self, transposition1, transposition2): + a = DatasetView(self.h5f["volume"], + transposition=transposition1).transpose(transposition2) + + b = self.volume.transpose(transposition1).transpose(transposition2) + + self.assertTrue(numpy.array_equal(a, b), + "failed with double transposition %s %s" % (transposition1, transposition2)) + + def test1DIndex(self): + a = DatasetView(self.h5f["volume"]) + self.assertTrue(numpy.array_equal(self.volume[1], + a[1])) + + b = DatasetView(self.h5f["volume"], transposition=(1, 0, 2)) + self.assertTrue(numpy.array_equal(self.volume[:, 1, :], + b[1])) + + +class TestTransposedListOfImages(unittest.TestCase): + def setUp(self): + # images attributes + self.ndim = 3 + self.original_shape = (5, 10, 20) + self.size = 1 + for dim in self.original_shape: + self.size *= dim + + volume = numpy.arange(self.size).reshape(self.original_shape) + + self.images = [] + for i in range(self.original_shape[0]): + self.images.append( + volume[i]) + + self.images_as_3D_array = numpy.array(self.images) + + self.all_permutations = [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), + (2, 0, 1), (2, 1, 0)] + + def tearDown(self): + pass + + def _testSize(self, obj): + """These assertions apply to all following test cases""" + self.assertEqual(obj.ndim, self.ndim) + self.assertEqual(obj.size, self.size) + size_from_shape = 1 + for dim in obj.shape: + size_from_shape *= dim + self.assertEqual(size_from_shape, self.size) + + for dim in self.original_shape: + self.assertIn(dim, obj.shape) + + def testNoTransposition(self): + """no transposition (transposition = (0, 1, 2))""" + a = ListOfImages(self.images) + + self.assertEqual(a.shape, self.original_shape) + self._testSize(a) + + for i in range(a.shape[0]): + for j in range(a.shape[1]): + for k in range(a.shape[2]): + self.assertEqual(self.images[i][j, k], + a[i, j, k]) + + # reversing the dimensions twice results in no change + rtrans = list(reversed(range(self.ndim))) + self.assertTrue(numpy.array_equal( + a, + a.transpose(rtrans).transpose(rtrans))) + + # test .T property + self.assertTrue(numpy.array_equal( + a.T, + a.transpose(rtrans))) + + def _testTransposition(self, transposition): + """test transposed dataset + + :param tuple transposition: List of dimensions (0... n-1) sorted + in the desired order + """ + a = ListOfImages(self.images, + transposition=transposition) + self._testSize(a) + + # sort shape of transposed object, to hopefully find the original shape + sorted_shape = tuple(dim_size for (_, dim_size) in + sorted(zip(transposition, a.shape))) + self.assertEqual(sorted_shape, self.original_shape) + + a_as_array = numpy.array(self.images).transpose(transposition) + + # test the DatasetView.__array__ method + self.assertTrue(numpy.array_equal( + numpy.array(a), + a_as_array)) + + # test slicing + for selection in [(2, slice(None), slice(None)), + (slice(None), 1, slice(0, 8)), + (slice(0, 3), slice(None), 3), + (1, 3, slice(None)), + (slice(None), 2, 1), + (4, slice(1, 9, 2), 2)]: + self.assertIsInstance(a[selection], numpy.ndarray) + self.assertTrue(numpy.array_equal( + a[selection], + a_as_array[selection])) + + # test the DatasetView.__getitem__ for single values + # (step adjusted to test at least 3 indices in each dimension) + for i in range(0, a.shape[0], a.shape[0] // 3): + for j in range(0, a.shape[1], a.shape[1] // 3): + for k in range(0, a.shape[2], a.shape[2] // 3): + viewed_value = a[i, j, k] + sorted_indices = tuple(idx for (_, idx) in + sorted(zip(transposition, [i, j, k]))) + corresponding_original_value = self.images[sorted_indices[0]][sorted_indices[1:]] + self.assertEqual(viewed_value, + corresponding_original_value) + + # reversing the dimensions twice results in no change + rtrans = list(reversed(range(self.ndim))) + self.assertTrue(numpy.array_equal( + a, + a.transpose(rtrans).transpose(rtrans))) + + # test .T property + self.assertTrue(numpy.array_equal( + a.T, + a.transpose(rtrans))) + + def _testDoubleTransposition(self, transposition1, transposition2): + a = ListOfImages(self.images, + transposition=transposition1).transpose(transposition2) + + b = self.images_as_3D_array.transpose(transposition1).transpose(transposition2) + + self.assertTrue(numpy.array_equal(a, b), + "failed with double transposition %s %s" % (transposition1, transposition2)) + + def testTransposition012(self): + """transposition = (0, 1, 2) + (should be the same as testNoTransposition)""" + self._testTransposition((0, 1, 2)) + + def testTransposition021(self): + """transposition = (0, 2, 1)""" + self._testTransposition((0, 2, 1)) + + def testTransposition102(self): + """transposition = (1, 0, 2)""" + self._testTransposition((1, 0, 2)) + + def testTransposition120(self): + """transposition = (1, 2, 0)""" + self._testTransposition((1, 2, 0)) + + def testTransposition201(self): + """transposition = (2, 0, 1)""" + self._testTransposition((2, 0, 1)) + + def testTransposition210(self): + """transposition = (2, 1, 0)""" + self._testTransposition((2, 1, 0)) + + def testAllDoubleTranspositions(self): + for trans1 in self.all_permutations: + for trans2 in self.all_permutations: + self._testDoubleTransposition(trans1, trans2) + + def test1DIndex(self): + a = ListOfImages(self.images) + self.assertTrue(numpy.array_equal(self.images[1], + a[1])) + + b = ListOfImages(self.images, transposition=(1, 0, 2)) + self.assertTrue(numpy.array_equal(self.images_as_3D_array[:, 1, :], + b[1])) + + +class TestFunctions(unittest.TestCase): + """Test functions to guess the dtype and shape of an array_like + object""" + def testListOfLists(self): + l = [[0, 1, 2], [2, 3, 4]] + self.assertEqual(get_dtype(l), + numpy.dtype(int)) + self.assertEqual(get_shape(l), + (2, 3)) + self.assertTrue(is_nested_sequence(l)) + self.assertFalse(is_array(l)) + self.assertFalse(is_list_of_arrays(l)) + + l = [[0., 1.], [2., 3.]] + self.assertEqual(get_dtype(l), + numpy.dtype(float)) + self.assertEqual(get_shape(l), + (2, 2)) + self.assertTrue(is_nested_sequence(l)) + self.assertFalse(is_array(l)) + self.assertFalse(is_list_of_arrays(l)) + + # concatenated dtype of int and float + l = [numpy.array([[0, 1, 2], [2, 3, 4]]), + numpy.array([[0., 1., 2.], [2., 3., 4.]])] + + self.assertEqual(get_concatenated_dtype(l), + numpy.array(l).dtype) + self.assertEqual(get_shape(l), + (2, 2, 3)) + self.assertFalse(is_nested_sequence(l)) + self.assertFalse(is_array(l)) + self.assertTrue(is_list_of_arrays(l)) + + def testNumpyArray(self): + a = numpy.array([[0, 1], [2, 3]]) + self.assertEqual(get_dtype(a), + a.dtype) + self.assertFalse(is_nested_sequence(a)) + self.assertTrue(is_array(a)) + self.assertFalse(is_list_of_arrays(a)) + + @unittest.skipIf(h5py is None, + "h5py is needed for this test") + def testH5pyDataset(self): + a = numpy.array([[0, 1], [2, 3]]) + + tempdir = tempfile.mkdtemp() + h5_fname = os.path.join(tempdir, "tempfile.h5") + with h5py.File(h5_fname, "w") as h5f: + h5f["dataset"] = a + d = h5f["dataset"] + + self.assertEqual(get_dtype(d), + numpy.dtype(int)) + self.assertFalse(is_nested_sequence(d)) + self.assertTrue(is_array(d)) + self.assertFalse(is_list_of_arrays(d)) + + os.unlink(h5_fname) + os.rmdir(tempdir) + + +def suite(): + test_suite = unittest.TestSuite() + test_suite.addTest( + unittest.defaultTestLoader.loadTestsFromTestCase(TestTransposedDatasetView)) + test_suite.addTest( + unittest.defaultTestLoader.loadTestsFromTestCase(TestTransposedListOfImages)) + test_suite.addTest( + unittest.defaultTestLoader.loadTestsFromTestCase(TestFunctions)) + return test_suite + + +if __name__ == '__main__': + unittest.main(defaultTest='suite') |