1# This file is part of h5py, a Python interface to the HDF5 library.
2#
3# http://www.h5py.org
4#
5# Copyright 2008-2013 Andrew Collette and contributors
6#
7# License:  Standard 3-clause BSD; see "license.txt" for full license terms
8#           and contributor agreement.
9
10import numpy as np
11
12import h5py
13from h5py import h5t
14
15from .common import TestCase, ut
16
17
18class TestCompound(ut.TestCase):
19
20    """
21        Feature: Compound types can be created from Python dtypes
22    """
23
24    def test_ref(self):
25        """ Reference types are correctly stored in compound types (issue 144)
26        """
27        dt = np.dtype([('a', h5py.ref_dtype), ('b', '<f4')])
28        tid = h5t.py_create(dt, logical=True)
29        t1, t2 = tid.get_member_type(0), tid.get_member_type(1)
30        self.assertEqual(t1, h5t.STD_REF_OBJ)
31        self.assertEqual(t2, h5t.IEEE_F32LE)
32        self.assertEqual(tid.get_member_offset(0), 0)
33        self.assertEqual(tid.get_member_offset(1), h5t.STD_REF_OBJ.get_size())
34
35    def test_out_of_order_offsets(self):
36        size = 20
37        type_dict = {
38            'names': ['f1', 'f2', 'f3'],
39            'formats': ['<f4', '<i4', '<f8'],
40            'offsets': [0, 16, 8]
41        }
42
43        expected_dtype = np.dtype(type_dict)
44
45        tid = h5t.create(h5t.COMPOUND, size)
46        for name, offset, dt in zip(
47                type_dict["names"], type_dict["offsets"], type_dict["formats"]
48        ):
49            tid.insert(
50                name.encode("utf8") if isinstance(name, str) else name,
51                offset,
52                h5t.py_create(dt)
53            )
54
55        self.assertEqual(tid.dtype, expected_dtype)
56        self.assertEqual(tid.dtype.itemsize, size)
57
58
59class TestTypeFloatID(TestCase):
60    """Test TypeFloatID."""
61
62    def test_custom_float_promotion(self):
63        """Custom floats are correctly promoted to standard floats on read."""
64
65        # This test uses the low-level API, so we need names as byte strings
66        test_filename = self.mktemp().encode()
67        dataset = b'DS1'
68        dataset2 = b'DS2'
69        dataset3 = b'DS3'
70        dataset4 = b'DS4'
71        dataset5 = b'DS5'
72
73        dims = (4, 7)
74
75        wdata = np.array([[-1.50066626e-09,   1.40062184e-09,   1.81216819e-10,
76                           4.01087163e-10,   4.27917257e-10,  -7.04858394e-11,
77                           5.74800652e-10],
78                          [-1.50066626e-09,   4.86579665e-10,   3.42879503e-10,
79                           5.12045517e-10,   5.10226528e-10,   2.24190444e-10,
80                           3.93356459e-10],
81                          [-1.50066626e-09,   5.24778443e-10,   8.19454726e-10,
82                           1.28966349e-09,   1.68483894e-10,   5.71276360e-11,
83                           -1.08684617e-10],
84                          [-1.50066626e-09,  -1.08343556e-10,  -1.58934199e-10,
85                           8.52196536e-10,   6.18456397e-10,   6.16637408e-10,
86                           1.31694833e-09]], dtype=np.float32)
87
88        wdata2 = np.array([[-1.50066626e-09,   5.63886715e-10,  -8.74251782e-11,
89                            1.32558853e-10,   1.59161573e-10,   2.29420039e-10,
90                            -7.24185156e-11],
91                           [-1.50066626e-09,   1.87810656e-10,   7.74889486e-10,
92                            3.95630195e-10,   9.42236511e-10,   8.38554115e-10,
93                            -8.71978045e-11],
94                           [-1.50066626e-09,   6.20275387e-10,   7.34871719e-10,
95                            6.64840627e-10,   2.64662958e-10,   1.05319486e-09,
96                            1.68256520e-10],
97                           [-1.50066626e-09,   1.67347025e-10,   5.12045517e-10,
98                            3.36513040e-10,   1.02545528e-10,   1.28784450e-09,
99                            4.06089384e-10]], dtype=np.float32)
100
101        # Create a new file using the default properties.
102        fid = h5py.h5f.create(test_filename)
103        # Create the dataspace.  No maximum size parameter needed.
104        space = h5py.h5s.create_simple(dims)
105
106        # create a custom type with larger bias
107        mytype = h5t.IEEE_F16LE.copy()
108        mytype.set_fields(14, 9, 5, 0, 9)
109        mytype.set_size(2)
110        mytype.set_ebias(53)
111        mytype.lock()
112
113        dset = h5py.h5d.create(fid, dataset, mytype, space)
114        dset.write(h5py.h5s.ALL, h5py.h5s.ALL, wdata)
115
116        del dset
117
118        # create a custom type with larger exponent
119        mytype2 = h5t.IEEE_F16LE.copy()
120        mytype2.set_fields(15, 9, 6, 0, 9)
121        mytype2.set_size(2)
122        mytype2.set_ebias(53)
123        mytype2.lock()
124
125        dset = h5py.h5d.create(fid, dataset2, mytype2, space)
126        dset.write(h5py.h5s.ALL, h5py.h5s.ALL, wdata2)
127
128        del dset
129
130        # create a custom type which reimplements 16-bit floats
131        mytype3 = h5t.IEEE_F16LE.copy()
132        mytype3.set_fields(15, 10, 5, 0, 10)
133        mytype3.set_size(2)
134        mytype3.set_ebias(15)
135        mytype3.lock()
136
137        dset = h5py.h5d.create(fid, dataset3, mytype3, space)
138        dset.write(h5py.h5s.ALL, h5py.h5s.ALL, wdata2)
139
140        del dset
141
142        # create a custom type with larger bias
143        mytype4 = h5t.IEEE_F16LE.copy()
144        mytype4.set_fields(15, 10, 5, 0, 10)
145        mytype4.set_size(2)
146        mytype4.set_ebias(258)
147        mytype4.lock()
148
149        dset = h5py.h5d.create(fid, dataset4, mytype4, space)
150        dset.write(h5py.h5s.ALL, h5py.h5s.ALL, wdata2)
151
152        del dset
153
154        # create a dataset with long doubles
155        dset = h5py.h5d.create(fid, dataset5, h5t.NATIVE_LDOUBLE, space)
156        dset.write(h5py.h5s.ALL, h5py.h5s.ALL, wdata2)
157
158        # Explicitly close and release resources.
159        del space
160        del dset
161        del fid
162
163        f = h5py.File(test_filename, 'r')
164
165        # ebias promotion to float32
166        values = f[dataset][:]
167        np.testing.assert_array_equal(values, wdata)
168        self.assertEqual(values.dtype, np.dtype('<f4'))
169
170        # esize promotion to float32
171        values = f[dataset2][:]
172        np.testing.assert_array_equal(values, wdata2)
173        self.assertEqual(values.dtype, np.dtype('<f4'))
174
175        # regular half floats
176        dset = f[dataset3]
177        try:
178            self.assertEqual(dset.dtype, np.dtype('<f2'))
179        except AttributeError:
180            self.assertEqual(dset.dtype, np.dtype('<f4'))
181
182        # ebias promotion to float64
183        dset = f[dataset4]
184        self.assertEqual(dset.dtype, np.dtype('<f8'))
185
186        # long double floats
187        dset = f[dataset5]
188        self.assertEqual(dset.dtype, np.longdouble)
189