├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── LICENSE
├── README.rst
├── eigen3-hdf5-sparse.hpp
├── eigen3-hdf5.hpp
├── sparseh5.py
└── unittests
├── gtest-helpers.hpp
├── premake4.lua
├── test_Attribute.cpp
├── test_MatrixRoundTrip.cpp
├── test_Sparse.cpp
└── test_VectorRoundTrip.cpp
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | push:
4 | branches: [ main ]
5 | pull_request:
6 | branches: [ main ]
7 | jobs:
8 | test:
9 | runs-on: ubuntu-20.04
10 | steps:
11 | - uses: actions/checkout@v2
12 | - name: install dependencies
13 | run: sudo apt-get update && sudo apt-get install -y build-essential premake4 libeigen3-dev libhdf5-dev libgtest-dev valgrind
14 | - name: compile
15 | working-directory: unittests
16 | env:
17 | INCLUDES: "-I/usr/include/eigen3 -I/usr/include/hdf5/serial"
18 | CFLAGS: "-Werror"
19 | run: |
20 | sed -i -e 's/\"hdf5\"/\"hdf5_serial\"/g' premake4.lua
21 | premake4 --os=linux gmake
22 | make verbose=true
23 | - name: eigen3-hdf5-tests
24 | run: ./unittests/eigen3-hdf5-tests
25 | - name: valgrind
26 | run: valgrind ./unittests/eigen3-hdf5-tests
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.[oa]
2 |
3 | /unittests/Makefile
4 | /unittests/*.make
5 | /unittests/eigen3-hdf5-tests
6 | /unittests/obj/
7 | /unittests/test_*.h5
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 James R. Garrison
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | eigen3-hdf5
2 | ===========
3 |
4 | .. image:: https://github.com/garrison/eigen3-hdf5/actions/workflows/main.yml/badge.svg
5 | :target: https://github.com/garrison/eigen3-hdf5/actions
6 |
7 | Easy serialization of C++ `Eigen `_
8 | matrices using `HDF5 `_.
9 |
10 | The library is meant to be bare-bones (at least for now). It gets me
11 | 90% of what I want/need in a few hundred lines of code. It may also
12 | get you 90% (or even 100%) of what you need.
13 |
14 | Requirements
15 | ------------
16 |
17 | * Eigen3 (tested on 3.1 and 3.2 branches)
18 | * HDF5 C++ wrapper library >= 1.8.12 (yes, this is a very recent
19 | version)
20 |
21 | Because ``eigen3-hdf5`` is a template library, there is nothing to link
22 | against (besides the HDF5 libraries).
23 |
24 | API
25 | ---
26 |
27 | Supports saving and restoring Eigen matrices and vectors of ``float``,
28 | ``double``, ``long double``, ``int``, ``unsigned int``, and
29 | ``std::complex<>``.
30 |
31 | .. code:: c++
32 |
33 | #include
34 |
35 | void save_matrix()
36 | {
37 | Eigen::Matrix3d mat;
38 | mat << 1, 2, 3, 4, 5, 6, 7, 8, 9;
39 | H5::H5File file("filename1.h5", H5F_ACC_TRUNC);
40 | EigenHDF5::save(file, "MatrixDataSetName", mat);
41 | }
42 |
43 | void load_vector()
44 | {
45 | Eigen::Vector4i vec;
46 | H5::H5File file("filename2.h5", H5F_ACC_RDONLY);
47 | EigenHDF5::load(file, "VectorDataSetName", vec);
48 | }
49 |
50 | See the `unittests `_ directory for more examples.
51 |
52 | Unit tests
53 | ----------
54 |
55 | I am using `premake4 `_ and
56 | `googletest `_ because I am
57 | familiar with them.
58 |
59 | The unit tests currently write to specific files in the current
60 | directory. This could change, eventually.
61 |
62 | The GitHub Action can be approximated locally by installing `act
63 | `_ and running::
64 |
65 | act -P ubuntu-20.04=ghcr.io/catthehacker/ubuntu:act-20.04
66 |
67 | License
68 | -------
69 |
70 | MIT license.
71 |
72 | Next steps
73 | ----------
74 |
75 | * Support more fundamental data types
76 |
77 | Thoughts/notes
78 | --------------
79 |
80 | * Using the HDF5 C++ wrapper library supposedly means it `won't work
81 | with parallel hdf5
82 | `_. If I were to
83 | do it again, I would write ``eigen3-hdf5`` using the regular C HDF5
84 | API, not the C++ wrapper. Patches are welcome. :)
85 |
--------------------------------------------------------------------------------
/eigen3-hdf5-sparse.hpp:
--------------------------------------------------------------------------------
1 | #ifndef _EIGEN3_HDF5_SPARSE_HPP
2 | #define _EIGEN3_HDF5_SPARSE_HPP
3 |
4 | #include
5 |
6 | #include
7 | #include
8 |
9 | #include
10 |
11 | #if H5_VERSION_LE(1,10,0)
12 | #define Eigen3Hdf5_H5Location H5::H5Location
13 | #define Eigen3Hdf5_H5CommonFG H5::CommonFG
14 | #else
15 | #define Eigen3Hdf5_H5Location H5::H5Object
16 | #define Eigen3Hdf5_H5CommonFG H5::H5Location
17 | #endif
18 |
19 | namespace EigenHDF5
20 | {
21 |
22 | template
23 | class MyTriplet : public Eigen::Triplet
24 | {
25 | public:
26 | MyTriplet (void)
27 | : Eigen::Triplet()
28 | {
29 | }
30 |
31 | MyTriplet (const unsigned int &i, const unsigned int &j, const Scalar &v = Scalar(0))
32 | : Eigen::Triplet(i, j, v)
33 | {
34 | }
35 |
36 | static std::size_t offsetof_row (void) { return offsetof(MyTriplet, m_row); }
37 | static std::size_t offsetof_col (void) { return offsetof(MyTriplet, m_col); }
38 | static std::size_t offsetof_value (void) { return offsetof(MyTriplet, m_value); }
39 | };
40 |
41 | template
42 | class SparseH5Type : public H5::CompType
43 | {
44 | public:
45 | SparseH5Type (void)
46 | : CompType(sizeof(MyTriplet))
47 | {
48 | const H5::DataType * const datatypei = DatatypeSpecialization::get();
49 | const H5::DataType * const datatype = DatatypeSpecialization::get();
50 | assert(datatype->getSize() == sizeof(T));
51 | this->insertMember(std::string("r"), MyTriplet::offsetof_row(), *datatypei);
52 | this->insertMember(std::string("c"), MyTriplet::offsetof_col(), *datatypei);
53 | this->insertMember(std::string("v"), MyTriplet::offsetof_value(), *datatype);
54 | this->pack();
55 | }
56 |
57 | static const SparseH5Type * get_singleton (void)
58 | {
59 | // NOTE: constructing this could be a race condition
60 | static SparseH5Type singleton;
61 | return &singleton;
62 | }
63 | };
64 |
65 | template
66 | void save_sparse (Eigen3Hdf5_H5CommonFG &h5group, const std::string &name, const SparseMatrixType &mat, const H5::DSetCreatPropList &plist=H5::DSetCreatPropList::DEFAULT)
67 | {
68 | typedef typename SparseMatrixType::Scalar Scalar;
69 | // save the actual sparse matrix
70 | std::vector > data;
71 | data.reserve(mat.nonZeros());
72 | for (int k = 0; k < mat.outerSize(); ++k) {
73 | for (typename SparseMatrixType::InnerIterator it(mat, k); it; ++it) {
74 | if (it.value() != Scalar(0))
75 | data.push_back(MyTriplet(it.row(), it.col(), it.value()));
76 | }
77 | }
78 | const hsize_t nnz = data.size();
79 | const H5::DataSpace dataspace(1, &nnz);
80 | const H5::DataType * const datatype = SparseH5Type::get_singleton();
81 | H5::DataSet dataset = h5group.createDataSet(name, *datatype, dataspace, plist);
82 | dataset.write(data.data(), *datatype);
83 | // save the matrix's shape as an attribute
84 | Eigen::Matrix shape;
85 | shape(0) = mat.rows();
86 | shape(1) = mat.cols();
87 | save_attribute(dataset, "shape", shape);
88 | }
89 |
90 | template
91 | void load_sparse (const Eigen3Hdf5_H5CommonFG &h5group, const std::string &name, SparseMatrixType &mat)
92 | {
93 | typedef typename SparseMatrixType::Scalar Scalar;
94 | const H5::DataSet dataset = h5group.openDataSet(name);
95 | const H5::DataSpace dataspace = dataset.getSpace();
96 | const std::size_t ndims = dataspace.getSimpleExtentNdims();
97 | if (ndims != 1) {
98 | throw std::runtime_error("HDF5 array has incorrect number of dimensions to represent a sparse matrix.");
99 | }
100 | Eigen::Matrix shape;
101 | load_attribute(dataset, "shape", shape);
102 | hsize_t nnz;
103 | dataspace.getSimpleExtentDims(&nnz); // assumes ndims == 1 in the data representation
104 | const H5::DataType * const datatype = SparseH5Type::get_singleton();
105 | std::vector > data(nnz);
106 | dataset.read(data.data(), *datatype, dataspace);
107 | mat.resize(shape(0), shape(1)); // NOTE: this also clears all existing values
108 | mat.setFromTriplets(data.begin(), data.end());
109 | }
110 |
111 | } // namespace EigenHDF5
112 |
113 | #endif
114 |
--------------------------------------------------------------------------------
/eigen3-hdf5.hpp:
--------------------------------------------------------------------------------
1 | #ifndef _EIGEN3_HDF5_HPP
2 | #define _EIGEN3_HDF5_HPP
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #include
12 | #include
13 | #include
14 |
15 | #if H5_VERSION_LE(1,10,0)
16 | #define Eigen3Hdf5_H5Location H5::H5Location
17 | #define Eigen3Hdf5_H5CommonFG H5::CommonFG
18 | #else
19 | #define Eigen3Hdf5_H5Location H5::H5Object
20 | #define Eigen3Hdf5_H5CommonFG H5::H5Location
21 | #endif
22 |
23 | namespace EigenHDF5
24 | {
25 |
26 | template
27 | struct DatatypeSpecialization;
28 |
29 | // floating-point types
30 |
31 | template <>
32 | struct DatatypeSpecialization
33 | {
34 | static inline const H5::DataType * get (void)
35 | {
36 | return &H5::PredType::NATIVE_FLOAT;
37 | }
38 | };
39 |
40 | template <>
41 | struct DatatypeSpecialization
42 | {
43 | static inline const H5::DataType * get (void)
44 | {
45 | return &H5::PredType::NATIVE_DOUBLE;
46 | }
47 | };
48 |
49 | template <>
50 | struct DatatypeSpecialization
51 | {
52 | static inline const H5::DataType * get (void)
53 | {
54 | return &H5::PredType::NATIVE_LDOUBLE;
55 | }
56 | };
57 |
58 | // integer types
59 |
60 | template <>
61 | struct DatatypeSpecialization
62 | {
63 | static inline const H5::DataType * get (void)
64 | {
65 | return &H5::PredType::NATIVE_SHORT;
66 | }
67 | };
68 |
69 | template <>
70 | struct DatatypeSpecialization
71 | {
72 | static inline const H5::DataType * get (void)
73 | {
74 | return &H5::PredType::NATIVE_USHORT;
75 | }
76 | };
77 |
78 | template <>
79 | struct DatatypeSpecialization
80 | {
81 | static inline const H5::DataType * get (void)
82 | {
83 | return &H5::PredType::NATIVE_INT;
84 | }
85 | };
86 |
87 | template <>
88 | struct DatatypeSpecialization
89 | {
90 | static inline const H5::DataType * get (void)
91 | {
92 | return &H5::PredType::NATIVE_UINT;
93 | }
94 | };
95 |
96 | template <>
97 | struct DatatypeSpecialization
98 | {
99 | static inline const H5::DataType * get (void)
100 | {
101 | return &H5::PredType::NATIVE_LONG;
102 | }
103 | };
104 |
105 | template <>
106 | struct DatatypeSpecialization
107 | {
108 | static inline const H5::DataType * get (void)
109 | {
110 | return &H5::PredType::NATIVE_ULONG;
111 | }
112 | };
113 |
114 | template <>
115 | struct DatatypeSpecialization
116 | {
117 | static inline const H5::DataType * get (void)
118 | {
119 | return &H5::PredType::NATIVE_LLONG;
120 | }
121 | };
122 |
123 | template <>
124 | struct DatatypeSpecialization
125 | {
126 | static inline const H5::DataType * get (void)
127 | {
128 | return &H5::PredType::NATIVE_ULLONG;
129 | }
130 | };
131 |
132 | // complex types
133 | //
134 | // inspired by http://www.mail-archive.com/hdf-forum@hdfgroup.org/msg00759.html
135 |
136 | template
137 | class ComplexH5Type : public H5::CompType
138 | {
139 | public:
140 | ComplexH5Type (void)
141 | : CompType(sizeof(std::complex))
142 | {
143 | const H5::DataType * const datatype = DatatypeSpecialization::get();
144 | assert(datatype->getSize() == sizeof(T));
145 | // If we call the members "r" and "i", h5py interprets the
146 | // structure correctly as complex numbers.
147 | this->insertMember(std::string("r"), 0, *datatype);
148 | this->insertMember(std::string("i"), sizeof(T), *datatype);
149 | this->pack();
150 | }
151 |
152 | static const ComplexH5Type * get_singleton (void)
153 | {
154 | // NOTE: constructing this could be a race condition
155 | static ComplexH5Type singleton;
156 | return &singleton;
157 | }
158 | };
159 |
160 | template
161 | struct DatatypeSpecialization >
162 | {
163 | static inline const H5::DataType * get (void)
164 | {
165 | return ComplexH5Type::get_singleton();
166 | }
167 | };
168 |
169 | // string types, to be used mainly for attributes
170 |
171 | template <>
172 | struct DatatypeSpecialization
173 | {
174 | static inline const H5::DataType * get (void)
175 | {
176 | static const H5::StrType strtype(0, H5T_VARIABLE);
177 | return &strtype;
178 | }
179 | };
180 |
181 | template <>
182 | struct DatatypeSpecialization
183 | {
184 | static inline const H5::DataType * get (void)
185 | {
186 | static const H5::StrType strtype(0, H5T_VARIABLE);
187 | return &strtype;
188 | }
189 | };
190 |
191 | // XXX: for some unknown reason the following two functions segfault if
192 | // H5T_VARIABLE is used. The passed strings should still be null-terminated,
193 | // so this is a bit worrisome.
194 |
195 | template
196 | struct DatatypeSpecialization
197 | {
198 | static inline const H5::DataType * get (void)
199 | {
200 | static const H5::StrType strtype(0, N);
201 | return &strtype;
202 | }
203 | };
204 |
205 | template
206 | struct DatatypeSpecialization
207 | {
208 | static inline const H5::DataType * get (void)
209 | {
210 | static const H5::StrType strtype(0, N);
211 | return &strtype;
212 | }
213 | };
214 |
215 | namespace internal
216 | {
217 | template
218 | H5::DataSpace create_dataspace (const Eigen::EigenBase &mat)
219 | {
220 | const std::size_t dimensions_size = 2;
221 | const hsize_t dimensions[dimensions_size] = {
222 | static_cast(mat.rows()),
223 | static_cast(mat.cols())
224 | };
225 | return H5::DataSpace(dimensions_size, dimensions);
226 | }
227 |
228 | template
229 | bool write_rowmat(const Eigen::EigenBase &mat,
230 | const H5::DataType * const datatype,
231 | H5::DataSet *dataset,
232 | const H5::DataSpace* dspace)
233 | {
234 | if (mat.derived().innerStride() != 1)
235 | {
236 | // inner stride != 1 is an edge case this function does not (yet) handle. (I think it
237 | // could by using the inner stride as the first element of mstride below. But I do
238 | // not have a test case to try it out, so just return false for now.)
239 | return false;
240 | }
241 |
242 | assert(mat.rows() >= 0);
243 | assert(mat.cols() >= 0);
244 | assert(mat.derived().outerStride() >= 0);
245 | hsize_t rows = hsize_t(mat.rows());
246 | hsize_t cols = hsize_t(mat.cols());
247 | hsize_t stride = hsize_t(mat.derived().outerStride());
248 |
249 | // slab params for the file data
250 | hsize_t fstride[2] = { 1, cols };
251 |
252 | // slab params for the memory data
253 | hsize_t mstride[2] = { 1, stride };
254 |
255 | // slab params for both file and memory data
256 | hsize_t count[2] = { 1, 1 };
257 | hsize_t block[2] = { rows, cols };
258 | hsize_t start[2] = { 0, 0 };
259 |
260 | // memory dataspace
261 | hsize_t mdim[2] = { rows, stride };
262 | H5::DataSpace mspace(2, mdim);
263 |
264 | dspace->selectHyperslab(H5S_SELECT_SET, count, start, fstride, block);
265 | mspace.selectHyperslab(H5S_SELECT_SET, count, start, mstride, block);
266 | dataset->write(mat.derived().data(), *datatype, mspace, *dspace);
267 |
268 | return true;
269 | }
270 |
271 | template
272 | bool write_colmat(const Eigen::EigenBase &mat,
273 | const H5::DataType * const datatype,
274 | H5::DataSet *dataset,
275 | const H5::DataSpace* dspace)
276 | {
277 | if (mat.derived().innerStride() != 1)
278 | {
279 | // inner stride != 1 is an edge case this function does not (yet) handle. (I think it
280 | // could by using the inner stride as the first element of mstride below. But I do
281 | // not have a test case to try it out, so just return false for now.)
282 | return false;
283 | }
284 |
285 | assert(mat.rows() >= 0);
286 | assert(mat.cols() >= 0);
287 | assert(mat.derived().outerStride() >= 0);
288 | hsize_t rows = hsize_t(mat.rows());
289 | hsize_t cols = hsize_t(mat.cols());
290 | hsize_t stride = hsize_t(mat.derived().outerStride());
291 |
292 | // slab params for the file data
293 | hsize_t fstride[2] = { 1, cols };
294 | hsize_t fcount[2] = { 1, 1 };
295 | hsize_t fblock[2] = { 1, cols };
296 |
297 | // slab params for the memory data
298 | hsize_t mstride[2] = { stride, 1 };
299 | hsize_t mcount[2] = { 1, 1 };
300 | hsize_t mblock[2] = { cols, 1 };
301 |
302 | // memory dataspace
303 | hsize_t mdim[2] = { cols, stride };
304 | H5::DataSpace mspace(2, mdim);
305 |
306 | // transpose the column major data in memory to the row major data in the file by
307 | // writing one row slab at a time.
308 | for (hsize_t i = 0; i < rows; i++)
309 | {
310 | hsize_t fstart[2] = { i, 0 };
311 | hsize_t mstart[2] = { 0, i };
312 | dspace->selectHyperslab(H5S_SELECT_SET, fcount, fstart, fstride, fblock);
313 | mspace.selectHyperslab(H5S_SELECT_SET, mcount, mstart, mstride, mblock);
314 | dataset->write(mat.derived().data(), *datatype, mspace, *dspace);
315 | }
316 | return true;
317 | }
318 | }
319 |
320 | template
321 | void save_scalar_attribute (const Eigen3Hdf5_H5Location &h5obj, const std::string &name, const T &value)
322 | {
323 | const H5::DataType * const datatype = DatatypeSpecialization::get();
324 | H5::DataSpace dataspace(H5S_SCALAR);
325 | H5::Attribute att = h5obj.createAttribute(name, *datatype, dataspace);
326 | att.write(*datatype, &value);
327 | }
328 |
329 | template <>
330 | inline void save_scalar_attribute (const Eigen3Hdf5_H5Location &h5obj, const std::string &name, const std::string &value)
331 | {
332 | save_scalar_attribute(h5obj, name, value.c_str());
333 | }
334 |
335 | // see http://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenTypes.html
336 |
337 | template
338 | void save (Eigen3Hdf5_H5CommonFG &h5group, const std::string &name, const Eigen::EigenBase &mat, const H5::DSetCreatPropList &plist=H5::DSetCreatPropList::DEFAULT)
339 | {
340 | typedef typename Derived::Scalar Scalar;
341 | const H5::DataType * const datatype = DatatypeSpecialization::get();
342 | const H5::DataSpace dataspace = internal::create_dataspace(mat);
343 | H5::DataSet dataset = h5group.createDataSet(name, *datatype, dataspace, plist);
344 |
345 | bool written = false; // flag will be true when the data has been written
346 | if (mat.derived().Flags & Eigen::RowMajor)
347 | {
348 | written = internal::write_rowmat(mat, datatype, &dataset, &dataspace);
349 | }
350 | else
351 | {
352 | written = internal::write_colmat(mat, datatype, &dataset, &dataspace);
353 | }
354 |
355 | if (!written)
356 | {
357 | // data has not yet been written, so there is nothing else to try but copy the input
358 | // matrix to a row major matrix and write it.
359 | const Eigen::Matrix row_major_mat(mat);
360 | dataset.write(row_major_mat.data(), *datatype);
361 | }
362 | }
363 |
364 | template
365 | void save_attribute (const Eigen3Hdf5_H5Location &h5obj, const std::string &name, const Eigen::EigenBase &mat)
366 | {
367 | typedef typename Derived::Scalar Scalar;
368 | const Eigen::Matrix row_major_mat(mat);
369 | const H5::DataSpace dataspace = internal::create_dataspace(mat);
370 | const H5::DataType * const datatype = DatatypeSpecialization::get();
371 | H5::Attribute dataset = h5obj.createAttribute(name, *datatype, dataspace);
372 | dataset.write(*datatype, row_major_mat.data());
373 | }
374 |
375 | namespace internal
376 | {
377 | // H5::Attribute and H5::DataSet both have similar API's, and although they
378 | // share a common base class, the relevant methods are not virtual. Worst
379 | // of all, they take their arguments in different orders!
380 |
381 | template
382 | inline void read_data (const H5::DataSet &dataset, Scalar *data, const H5::DataType &datatype)
383 | {
384 | dataset.read(data, datatype);
385 | }
386 |
387 | template
388 | inline void read_data (const H5::Attribute &dataset, Scalar *data, const H5::DataType &datatype)
389 | {
390 | dataset.read(datatype, data);
391 | }
392 |
393 | // read a column major attribute; I do not know if there is an hdf routine to read an
394 | // attribute hyperslab, so I take the lazy way out: just read the conventional hdf
395 | // row major data and let eigen copy it into mat.
396 | template
397 | bool read_colmat(const Eigen::DenseBase &mat,
398 | const H5::DataType * const datatype,
399 | const H5::Attribute &dataset)
400 | {
401 | typename Derived::Index rows = mat.rows();
402 | typename Derived::Index cols = mat.cols();
403 | typename Eigen::Matrix temp(rows, cols);
404 | internal::read_data(dataset, temp.data(), *datatype);
405 | const_cast &>(mat) = temp;
406 | return true;
407 | }
408 |
409 | template
410 | bool read_colmat(const Eigen::DenseBase &mat,
411 | const H5::DataType * const datatype,
412 | const H5::DataSet &dataset)
413 | {
414 | if (mat.derived().innerStride() != 1)
415 | {
416 | // inner stride != 1 is an edge case this function does not (yet) handle. (I think it
417 | // could by using the inner stride as the first element of mstride below. But I do
418 | // not have a test case to try it out, so just return false for now.)
419 | return false;
420 | }
421 |
422 | assert(mat.rows() >= 0);
423 | assert(mat.cols() >= 0);
424 | assert(mat.derived().outerStride() >= 0);
425 | hsize_t rows = hsize_t(mat.rows());
426 | hsize_t cols = hsize_t(mat.cols());
427 | hsize_t stride = hsize_t(mat.derived().outerStride());
428 |
429 | if (stride != rows)
430 | {
431 | // this function does not (yet) read into a mat that has a different stride than the
432 | // dataset.
433 | return false;
434 | }
435 |
436 | // slab params for the file data
437 | hsize_t fstride[2] = { 1, cols };
438 | hsize_t fcount[2] = { 1, 1 };
439 | hsize_t fblock[2] = { 1, cols };
440 |
441 | // file dataspace
442 | hsize_t fdim[2] = { rows, cols };
443 | H5::DataSpace fspace(2, fdim);
444 |
445 | // slab params for the memory data
446 | hsize_t mstride[2] = { stride, 1 };
447 | hsize_t mcount[2] = { 1, 1 };
448 | hsize_t mblock[2] = { cols, 1 };
449 |
450 | // memory dataspace
451 | hsize_t mdim[2] = { cols, stride };
452 | H5::DataSpace mspace(2, mdim);
453 |
454 | // transpose the column major data in memory to the row major data in the file by
455 | // writing one row slab at a time.
456 | for (hsize_t i = 0; i < rows; i++)
457 | {
458 | hsize_t fstart[2] = { i, 0 };
459 | hsize_t mstart[2] = { 0, i };
460 | fspace.selectHyperslab(H5S_SELECT_SET, fcount, fstart, fstride, fblock);
461 | mspace.selectHyperslab(H5S_SELECT_SET, mcount, mstart, mstride, mblock);
462 | dataset.read(const_cast &>(mat).derived().data(), *datatype, mspace, fspace);
463 | }
464 | return true;
465 | }
466 |
467 | template
468 | void _load (const DataSet &dataset, const Eigen::DenseBase &mat)
469 | {
470 | typedef typename Derived::Scalar Scalar;
471 | const H5::DataSpace dataspace = dataset.getSpace();
472 | const std::size_t ndims = dataspace.getSimpleExtentNdims();
473 | assert(ndims > 0);
474 | const std::size_t dimensions_size = 2;
475 | hsize_t dimensions[dimensions_size];
476 | dimensions[1] = 1; // in case it's 1D
477 | if (ndims > dimensions_size) {
478 | throw std::runtime_error("HDF5 array has too many dimensions.");
479 | }
480 | dataspace.getSimpleExtentDims(dimensions);
481 | const hsize_t rows = dimensions[0], cols = dimensions[1];
482 | const H5::DataType * const datatype = DatatypeSpecialization::get();
483 | Eigen::DenseBase &mat_ = const_cast &>(mat);
484 | mat_.derived().resize(rows, cols);
485 | bool written = false;
486 | bool isRowMajor = mat.Flags & Eigen::RowMajor;
487 | if (isRowMajor || dimensions[0] == 1 || dimensions[1] == 1)
488 | {
489 | // mat is already row major
490 | typename Derived::Index istride = mat_.derived().outerStride();
491 | assert(istride >= 0);
492 | hsize_t stride = istride >= 0 ? istride : 0;
493 | if (stride == cols || (stride == rows && cols == 1))
494 | {
495 | // mat has natural stride, so read directly into its data block
496 | read_data(dataset, mat_.derived().data(), *datatype);
497 | written = true;
498 | }
499 | }
500 | else
501 | {
502 | // colmajor flag is 0 so the assert needs to check that mat is not rowmajor.
503 | assert(!(mat.Flags & Eigen::RowMajor));
504 |
505 | written = read_colmat(mat_, datatype, dataset);
506 | }
507 |
508 | if (!written)
509 | {
510 | // dataset has not been loaded directly into mat_, so as a last resort read it into a
511 | // temp and copy it to mat_. (Should only need to do this when the mat_ to be loaded
512 | // into has an unnatural stride.)
513 | Eigen::Matrix temp(rows, cols);
514 | internal::read_data(dataset, temp.data(), *datatype);
515 | mat_ = temp;
516 | written = true;
517 | }
518 | }
519 | }
520 |
521 | template
522 | void load (const Eigen3Hdf5_H5CommonFG &h5group, const std::string &name, const Eigen::DenseBase &mat)
523 | {
524 | const H5::DataSet dataset = h5group.openDataSet(name);
525 | internal::_load(dataset, mat);
526 | }
527 |
528 | template
529 | void load_attribute (const Eigen3Hdf5_H5Location &h5obj, const std::string &name, const Eigen::DenseBase &mat)
530 | {
531 | const H5::Attribute dataset = h5obj.openAttribute(name);
532 | internal::_load(dataset, mat);
533 | }
534 |
535 | } // namespace EigenHDF5
536 |
537 | #endif
538 |
--------------------------------------------------------------------------------
/sparseh5.py:
--------------------------------------------------------------------------------
1 | # to be used with h5py
2 |
3 | import numpy as np
4 | from scipy.sparse import coo_matrix
5 |
6 | def to_sparse_repr(mat):
7 | mat = coo_matrix(mat)
8 | dtype = [('r', int), ('c', int), ('v', mat.dtype)]
9 | m = np.zeros(shape=(mat.nnz,), dtype=dtype)
10 | m["r"] = mat.row
11 | m["c"] = mat.col
12 | m["v"] = mat.data
13 | return m
14 |
15 | def from_sparse_repr(m, shape=None):
16 | return coo_matrix((m["v"], (m["r"], m["c"])), shape=shape).tocsr()
17 |
18 | def save_sparse_h5(grp, name, data):
19 | grp[name] = to_sparse_repr(data)
20 | grp[name].attrs["shape"] = data.shape
21 |
22 | def load_sparse_h5(grp, name):
23 | shape = grp[name].attrs["shape"]
24 | return from_sparse_repr(grp[name], shape=shape)
25 |
--------------------------------------------------------------------------------
/unittests/gtest-helpers.hpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | namespace Eigen {
5 | // C++ and/or gtest require that these two methods, which are used by calling
6 | // ASSERT_PRED_FORMAT2, be in the namespace of its argument.
7 |
8 | // utility function to print an eigen object to an ostream; gtest will use this when
9 | // it outputs a matrix used in a failed assertion. Without this function, gtest seems
10 | // to dump some kind of byte representation of an eigen matrix, which is not very
11 | // helpful.
12 | template
13 | void PrintTo(const Eigen::EigenBase& mat, ::std::ostream* os)
14 | {
15 | (*os) << mat.derived() << "\n";
16 | }
17 |
18 | // utility function for gtest to use to check if two eigen objects are identical.
19 | // returns assertion success when they are identical; returns assertion failure along
20 | // with a nicely formatted message with the matrix contents when they are not
21 | // identical.
22 | //
23 | // I put this function in this matrix test cpp file for a few reasons: 1) there is
24 | // not already a header file to put common test code for eigen3-hdf5, and 2) because
25 | // I needed it to help me debug test failures as I implemented the no copy read and
26 | // write functions. I really think that providing a header for common test code
27 | // should be addressed at some point, and then this function (and its companion
28 | // PrintTo) should be moved there.
29 | //
30 | // Usage:
31 | //
32 | // ASSERT_PRED_FORMAT2(assert_same, mat, mat2);
33 | template
34 | ::testing::AssertionResult assert_same(const char* exp_expr,
35 | const char* act_expr,
36 | const Eigen::EigenBase& exp,
37 | const Eigen::EigenBase& act)
38 | {
39 | if (exp.rows() == act.rows() &&
40 | exp.cols() == act.cols() &&
41 | exp.derived() == act.derived())
42 | {
43 | return ::testing::AssertionSuccess();
44 | }
45 |
46 | // if eigen did not define the == operator, you could use
47 | // exp.derived().cwiseEqual(act.derived()).all();
48 |
49 | ::testing::AssertionResult result = ::testing::AssertionFailure()
50 | << "Eigen objects are not the same: ("
51 | << exp_expr << ", " << act_expr << ")\n"
52 | << exp_expr << ":\n"
53 | << ::testing::PrintToString(exp)
54 | << "\n---and\n" << act_expr << ":\n"
55 | << ::testing::PrintToString(act)
56 | << "\n---are not equal!\n";
57 |
58 | return result;
59 | }
60 | } // namespace Eigen
61 |
--------------------------------------------------------------------------------
/unittests/premake4.lua:
--------------------------------------------------------------------------------
1 | newoption {
2 | trigger = "gtest",
3 | description = "googletest source root directory (default /usr/src/gtest)",
4 | value = "path",
5 | }
6 |
7 | local gtest_root = _OPTIONS["gtest"] or "/usr/src/gtest"
8 |
9 | solution "eigen3-hdf5-tests"
10 | configurations { "Local" }
11 |
12 | buildoptions { "-g", "-Wall", "-Wextra" }
13 | includedirs { gtest_root .. "/include" }
14 | defines { "GTEST_HAS_PTHREAD=0" }
15 |
16 | configuration "Local"
17 | flags { "Optimize" }
18 |
19 | project "gtest_main"
20 | language "C++"
21 | kind "StaticLib"
22 |
23 | includedirs { gtest_root }
24 | files { gtest_root .. "/src/gtest-all.cc", gtest_root .. "/src/gtest_main.cc" }
25 |
26 | project "eigen3-hdf5-tests"
27 | language "C++"
28 | kind "ConsoleApp"
29 |
30 | files { "*.cpp" }
31 | includedirs { ".." }
32 |
33 | links { "hdf5", "hdf5_cpp", "gtest_main" }
34 |
--------------------------------------------------------------------------------
/unittests/test_Attribute.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "eigen3-hdf5.hpp"
5 | #include "gtest-helpers.hpp"
6 |
7 | TEST(Attribute, Matrix) {
8 | Eigen::Matrix rmat1, rmat2;
9 | Eigen::Matrix cmat1, cmat2;
10 | rmat1 << 1, 2, 3, 4, 5, 6;
11 | cmat1 << 1, 2, 3, 4, 5, 6;
12 | {
13 | H5::H5File file("test_Attribute_Matrix.h5", H5F_ACC_TRUNC);
14 | EigenHDF5::save_attribute(file, "rowmat", rmat1);
15 | EigenHDF5::save_attribute(file, "colmat", cmat1);
16 | }
17 | {
18 | H5::H5File file("test_Attribute_Matrix.h5", H5F_ACC_RDONLY);
19 | EigenHDF5::load_attribute(file, "rowmat", rmat2);
20 | EigenHDF5::load_attribute(file, "colmat", cmat2);
21 | }
22 | ASSERT_PRED_FORMAT2(assert_same, rmat1, rmat2);
23 | ASSERT_PRED_FORMAT2(assert_same, cmat1, cmat2);
24 | ASSERT_PRED_FORMAT2(assert_same, rmat2, cmat2);
25 | }
26 |
27 | TEST(Attribute, Integer) {
28 | H5::H5File file("test_Attribute_Integer.h5", H5F_ACC_TRUNC);
29 | EigenHDF5::save_scalar_attribute(file, "integer", 23);
30 | }
31 |
32 | TEST(Attribute, Double) {
33 | H5::H5File file("test_Attribute_Double.h5", H5F_ACC_TRUNC);
34 | EigenHDF5::save_scalar_attribute(file, "double", 23.7);
35 | }
36 |
37 | TEST(Attribute, String) {
38 | H5::H5File file("test_Attribute_String.h5", H5F_ACC_TRUNC);
39 | EigenHDF5::save_scalar_attribute(file, "str1", std::string("hello"));
40 | EigenHDF5::save_scalar_attribute(file, "str2", "goodbye");
41 | const char *s = "again";
42 | EigenHDF5::save_scalar_attribute(file, "str3", s);
43 | }
44 |
--------------------------------------------------------------------------------
/unittests/test_MatrixRoundTrip.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include
5 | #include
6 |
7 | #include "eigen3-hdf5.hpp"
8 | #include "gtest-helpers.hpp"
9 |
10 | TEST(MatrixRoundTrip, Double) {
11 | Eigen::MatrixXd mat(3, 4), mat2;
12 | mat << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12;
13 | #ifdef LOGGING
14 | std::cout << mat << std::endl;
15 | #endif
16 | {
17 | H5::H5File file("test_MatrixRoundTrip_Double.h5", H5F_ACC_TRUNC);
18 | EigenHDF5::save(file, "double_matrix", mat);
19 | }
20 | {
21 | H5::H5File file("test_MatrixRoundTrip_Double.h5", H5F_ACC_RDONLY);
22 | EigenHDF5::load(file, "double_matrix", mat2);
23 | }
24 | ASSERT_PRED_FORMAT2(assert_same, mat, mat2);
25 | }
26 |
27 | TEST(MatrixRoundTrip, LongDouble) {
28 | Eigen::Matrix mat(3, 4), mat2;
29 | mat << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12;
30 | #ifdef LOGGING
31 | std::cout << mat << std::endl;
32 | #endif
33 | {
34 | H5::H5File file("test_MatrixRoundTrip_LongDouble.h5", H5F_ACC_TRUNC);
35 | EigenHDF5::save(file, "longdouble_matrix", mat);
36 | }
37 | {
38 | H5::H5File file("test_MatrixRoundTrip_LongDouble.h5", H5F_ACC_RDONLY);
39 | EigenHDF5::load(file, "longdouble_matrix", mat2);
40 | }
41 | ASSERT_EQ(mat, mat2);
42 | }
43 |
44 | TEST(MatrixRoundTrip, Int) {
45 | Eigen::MatrixXi mat(3, 4), mat2;
46 | mat << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12;
47 | #ifdef LOGGING
48 | std::cout << mat << std::endl;
49 | #endif
50 | {
51 | H5::H5File file("test_MatrixRoundTrip_Int.h5", H5F_ACC_TRUNC);
52 | EigenHDF5::save(file, "int_matrix", mat);
53 | }
54 | {
55 | H5::H5File file("test_MatrixRoundTrip_Int.h5", H5F_ACC_RDONLY);
56 | EigenHDF5::load(file, "int_matrix", mat2);
57 | }
58 | ASSERT_EQ(mat, mat2);
59 | }
60 |
61 | TEST(MatrixRoundTrip, ULongLong) {
62 | Eigen::Matrix mat(3, 4), mat2;
63 | mat << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12;
64 | #ifdef LOGGING
65 | std::cout << mat << std::endl;
66 | #endif
67 | {
68 | H5::H5File file("test_MatrixRoundTrip_ULongLong.h5", H5F_ACC_TRUNC);
69 | EigenHDF5::save(file, "ull_matrix", mat);
70 | }
71 | {
72 | H5::H5File file("test_MatrixRoundTrip_ULongLong.h5", H5F_ACC_RDONLY);
73 | EigenHDF5::load(file, "ull_matrix", mat2);
74 | }
75 | ASSERT_EQ(mat, mat2);
76 | }
77 |
78 | TEST(MatrixRoundTrip, ComplexDouble) {
79 | Eigen::MatrixXcd mat(3, 4), mat2;
80 | mat << 1, std::complex(0, 2), 3, 4, 5, 6, 7, 8, 9, 10, 11, 12;
81 | #ifdef LOGGING
82 | std::cout << mat << std::endl;
83 | #endif
84 | {
85 | H5::H5File file("test_MatrixRoundTrip_ComplexDouble.h5", H5F_ACC_TRUNC);
86 | EigenHDF5::save(file, "complex_matrix", mat);
87 | }
88 | {
89 | H5::H5File file("test_MatrixRoundTrip_ComplexDouble.h5", H5F_ACC_RDONLY);
90 | EigenHDF5::load(file, "complex_matrix", mat2);
91 | }
92 | ASSERT_EQ(mat, mat2);
93 | }
94 |
95 | TEST(MatrixRoundTrip, IntBlock) {
96 | Eigen::Matrix4i mat(Eigen::Matrix4i::Zero());
97 | Eigen::Matrix4i mat2(Eigen::Matrix4i::Zero());
98 | mat(0, 0) = 1;
99 | mat(0, 1) = 2;
100 | mat(1, 0) = 3;
101 | mat(1, 1) = 4;
102 | mat(2, 2) = 5;
103 | mat2(2, 2) = 5;
104 | #ifdef LOGGING
105 | std::cout << mat << std::endl;
106 | #endif
107 | {
108 | H5::H5File file("test_MatrixRoundTrip_IntBlock.h5", H5F_ACC_TRUNC);
109 | EigenHDF5::save(file, "int_block", mat.block(0, 0, 2, 2));
110 | }
111 | {
112 | H5::H5File file("test_MatrixRoundTrip_IntBlock.h5", H5F_ACC_RDONLY);
113 | EigenHDF5::load(file, "int_block", mat2.block(0, 0, 2, 2));
114 | }
115 | ASSERT_PRED_FORMAT2(assert_same, mat, mat2);
116 | }
117 |
118 | TEST(MatrixRoundTrip, IntBlockRowMajor) {
119 | typedef Eigen::Matrix Matrix4RowMajor;
120 | Matrix4RowMajor mat(Eigen::Matrix4i::Zero());
121 | Matrix4RowMajor mat2(Eigen::Matrix4i::Zero());
122 | mat <<
123 | 1, 2, 3, 4,
124 | 5, 6, 7, 8,
125 | 9, 10, 11, 12,
126 | 13, 14, 15, 16;
127 |
128 | mat2(0, 2) = 3; mat2(0, 3) = 4;
129 | mat2(1, 2) = 7; mat2(1, 3) = 8;
130 | mat2(2, 0) = 9; mat2(2, 1) = 10; mat2(2, 2) = 11; mat2(2, 3) = 12;
131 | mat2(3, 0) = 13; mat2(3, 1) = 14; mat2(3, 2) = 15; mat2(3, 3) = 16;
132 | #ifdef LOGGING
133 | std::cout << mat << std::endl;
134 | #endif
135 | {
136 | H5::H5File file("test_MatrixRoundTrip_IntBlockRowMajor.h5", H5F_ACC_TRUNC);
137 | EigenHDF5::save(file, "int_block", mat.block(0, 0, 2, 2));
138 | }
139 | {
140 | H5::H5File file("test_MatrixRoundTrip_IntBlockRowMajor.h5", H5F_ACC_RDONLY);
141 | EigenHDF5::load(file, "int_block", mat2.block(0, 0, 2, 2));
142 | }
143 | ASSERT_PRED_FORMAT2(assert_same, mat, mat2);
144 | }
145 |
146 | TEST(MatrixRoundTrip, DoubleSkipInternalCopy) {
147 | Eigen::Matrix mat(3, 4); // , mat2;
148 | Eigen::MatrixXd mat2;
149 | mat << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12;
150 | #ifdef LOGGING
151 | std::cout << mat << std::endl;
152 | #endif
153 | {
154 | H5::H5File file("test_MatrixRoundTrip_DoubleSkipInternalCopy.h5", H5F_ACC_TRUNC);
155 | EigenHDF5::save(file, "double_matrix", mat);
156 | }
157 | {
158 | H5::H5File file("test_MatrixRoundTrip_DoubleSkipInternalCopy.h5", H5F_ACC_RDONLY);
159 | EigenHDF5::load(file, "double_matrix", mat2);
160 | }
161 | ASSERT_PRED_FORMAT2(assert_same, mat, mat2);
162 | }
163 |
164 | TEST(MatrixRoundTrip, DoubleSkipInternalCopyBlock) {
165 | typedef Eigen::Matrix MyMatrixXdRowMajor;
166 | MyMatrixXdRowMajor mat(3, 4);
167 | Eigen::Block matblock = mat.block(1, 1, 2, 3);
168 | Eigen::MatrixXd mat2;
169 | mat << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12;
170 | #ifdef LOGGING
171 | std::cout << mat << std::endl;
172 | #endif
173 | {
174 | H5::H5File file("test_MatrixRoundTrip_DoubleSkipInternalCopy.h5", H5F_ACC_TRUNC);
175 | EigenHDF5::save(file, "double_matrix", matblock);
176 | }
177 | {
178 | H5::H5File file("test_MatrixRoundTrip_DoubleSkipInternalCopy.h5", H5F_ACC_RDONLY);
179 | EigenHDF5::load(file, "double_matrix", mat2);
180 | }
181 | ASSERT_PRED_FORMAT2(assert_same, matblock, mat2);
182 | }
183 |
184 | TEST(MatrixRoundTrip, DoubleFixedRow) {
185 | typedef Eigen::Matrix MyMatrix46RowMajor;
186 | MyMatrix46RowMajor mat;
187 | Eigen::Block matblock = mat.block(1, 2, 2, 3);
188 | MyMatrix46RowMajor fmat2;
189 | Eigen::Matrix fmatblock2;
190 | Eigen::Matrix dmat2, dmatblock2;
191 |
192 | mat << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24;
193 | #ifdef LOGGING
194 | std::cout << mat << std::endl;
195 | #endif
196 | {
197 | H5::H5File file("test_MatrixRoundTrip_DoubleFixedRow.h5", H5F_ACC_TRUNC);
198 | EigenHDF5::save(file, "double_matrix", mat);
199 | EigenHDF5::save(file, "matrix_block", matblock);
200 | }
201 | {
202 | H5::H5File file("test_MatrixRoundTrip_DoubleFixedRow.h5", H5F_ACC_RDONLY);
203 | // read into a dynamic sized matrix and then copy into fixed size
204 | EigenHDF5::load(file, "double_matrix", dmat2);
205 | EigenHDF5::load(file, "matrix_block", dmatblock2);
206 | fmat2 = dmat2;
207 | fmatblock2 = dmatblock2;
208 | }
209 | ASSERT_PRED_FORMAT2(assert_same, mat, fmat2);
210 | ASSERT_PRED_FORMAT2(assert_same, matblock, fmatblock2);
211 | }
212 |
213 | TEST(MatrixRoundTrip, DoubleFixedCol) {
214 | typedef Eigen::Matrix MyMatrix46ColMajor;
215 | MyMatrix46ColMajor mat;
216 | Eigen::Block matblock = mat.block(1, 2, 2, 3);
217 | MyMatrix46ColMajor fmat2;
218 | Eigen::Matrix fmatblock2;
219 | Eigen::Matrix dmat2, dmatblock2;
220 |
221 | mat << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24;
222 | #ifdef LOGGING
223 | std::cout << mat << std::endl;
224 | #endif
225 | {
226 | H5::H5File file("test_MatrixRoundTrip_DoubleFixedRow.h5", H5F_ACC_TRUNC);
227 | EigenHDF5::save(file, "double_matrix", mat);
228 | EigenHDF5::save(file, "matrix_block", matblock);
229 | }
230 | {
231 | H5::H5File file("test_MatrixRoundTrip_DoubleFixedRow.h5", H5F_ACC_RDONLY);
232 | EigenHDF5::load(file, "double_matrix", fmat2);
233 | EigenHDF5::load(file, "matrix_block", fmatblock2);
234 | }
235 | ASSERT_PRED_FORMAT2(assert_same, mat, fmat2);
236 | ASSERT_PRED_FORMAT2(assert_same, matblock, fmatblock2);
237 | }
238 |
--------------------------------------------------------------------------------
/unittests/test_Sparse.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | #include "eigen3-hdf5-sparse.hpp"
9 |
10 | #include
11 |
12 | TEST(SparseMatrix, Double) {
13 | Eigen::SparseMatrix mat(3, 3), mat2;
14 | mat.insert(0, 1) = 2.7;
15 | mat.insert(2, 0) = 82;
16 | {
17 | H5::H5File file("test_SparseMatrix_Double.h5", H5F_ACC_TRUNC);
18 | EigenHDF5::save_sparse(file, "mat", mat);
19 | }
20 | {
21 | H5::H5File file("test_SparseMatrix_Double.h5", H5F_ACC_RDONLY);
22 | EigenHDF5::load_sparse(file, "mat", mat2);
23 | }
24 | #ifdef LOGGING
25 | std::cout << mat2 << std::endl;
26 | #endif
27 | ASSERT_EQ(Eigen::MatrixXd(mat), Eigen::MatrixXd(mat2));
28 | }
29 |
30 | TEST(SparseMatrix, Complex) {
31 | Eigen::SparseMatrix > mat(4, 4), mat2;
32 | mat.insert(0, 1) = std::complex(2, 4.5);
33 | mat.insert(1, 2) = std::complex(82, 1);
34 | {
35 | H5::H5File file("test_SparseMatrix_Complex.h5", H5F_ACC_TRUNC);
36 | EigenHDF5::save_sparse(file, "mat", mat);
37 | }
38 | {
39 | H5::H5File file("test_SparseMatrix_Complex.h5", H5F_ACC_RDONLY);
40 | EigenHDF5::load_sparse(file, "mat", mat2);
41 | }
42 | #ifdef LOGGING
43 | std::cout << mat2 << std::endl;
44 | #endif
45 | ASSERT_EQ(Eigen::MatrixXcd(mat), Eigen::MatrixXcd(mat2));
46 | }
47 |
--------------------------------------------------------------------------------
/unittests/test_VectorRoundTrip.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include
4 | #include
5 |
6 | #include "eigen3-hdf5.hpp"
7 |
8 | #include
9 |
10 | TEST(VectorRoundTrip, Double) {
11 | Eigen::Vector4d mat, mat2;
12 | mat << 1, 2, 3, 4;
13 | #ifdef LOGGING
14 | std::cout << mat << std::endl;
15 | #endif
16 | {
17 | H5::H5File file("test_VectorRoundTrip_Double.h5", H5F_ACC_TRUNC);
18 | EigenHDF5::save(file, "double_vector", mat);
19 | }
20 | {
21 | H5::H5File file("test_VectorRoundTrip_Double.h5", H5F_ACC_RDONLY);
22 | EigenHDF5::load(file, "double_vector", mat2);
23 | }
24 | ASSERT_EQ(mat, mat2);
25 | }
26 |
27 | TEST(VectorRoundTrip, Int) {
28 | Eigen::VectorXi mat(12), mat2;
29 | mat << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12;
30 | #ifdef LOGGING
31 | std::cout << mat << std::endl;
32 | #endif
33 | {
34 | H5::H5File file("test_VectorRoundTrip_Int.h5", H5F_ACC_TRUNC);
35 | EigenHDF5::save(file, "int_vector", mat);
36 | }
37 | {
38 | H5::H5File file("test_VectorRoundTrip_Int.h5", H5F_ACC_RDONLY);
39 | EigenHDF5::load(file, "int_vector", mat2);
40 | }
41 | ASSERT_EQ(mat, mat2);
42 | }
43 |
--------------------------------------------------------------------------------