├── .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 | --------------------------------------------------------------------------------