1#ifndef TATAMI_WRITE_SPARSE_MATRIX_TO_HDF5_HPP
2#define TATAMI_WRITE_SPARSE_MATRIX_TO_HDF5_HPP
114inline H5::DataSet create_1d_compressed_hdf5_dataset(H5::Group& location,
WriteStorageType type,
const std::string& name, hsize_t length,
int deflate_level, hsize_t chunk,
bool shuffle) {
115 H5::DataSpace dspace(1, &length);
116 H5::DSetCreatPropList plist;
118 if (deflate_level >= 0 && length) {
119 plist.setDeflate(deflate_level);
120 if (chunk > length) {
121 plist.setChunk(1, &length);
123 plist.setChunk(1, &chunk);
130 const auto dtype = choose_pred_type(type);
131 return location.createDataSet(name, *dtype, dspace, plist);
134template<
typename Type_>
135bool does_non_negative_integer_fit(
const WriteStorageType type,
const Type_ x) {
136 static_assert(std::is_integral<Type_>::value);
140 case WriteStorageType::INT8:
141 okay = fits_upper_limit<std::int8_t>(x);
143 case WriteStorageType::UINT8:
144 okay = fits_upper_limit<std::uint8_t>(x);
146 case WriteStorageType::INT16:
147 okay = fits_upper_limit<std::int16_t >(x);
149 case WriteStorageType::UINT16:
150 okay = fits_upper_limit<std::uint16_t>(x);
152 case WriteStorageType::INT32:
153 okay = fits_upper_limit<std::int32_t >(x);
155 case WriteStorageType::UINT32:
156 okay = fits_upper_limit<std::uint32_t>(x);
158 case WriteStorageType::INT64:
159 okay = fits_upper_limit<std::int64_t >(x);
161 case WriteStorageType::UINT64:
162 okay = fits_upper_limit<std::uint64_t>(x);
171template<
typename Index_>
172WriteStorageType choose_index_type(
const std::optional<WriteStorageType>& index_type, Index_ upper_index) {
173 static_assert(std::is_integral<Index_>::value);
175 if (!index_type.has_value()) {
176 if (fits_upper_limit<std::uint8_t>(upper_index)) {
177 return WriteStorageType::UINT8;
178 }
else if (fits_upper_limit<std::uint16_t>(upper_index)) {
179 return WriteStorageType::UINT16;
180 }
else if (fits_upper_limit<std::uint32_t>(upper_index)) {
181 return WriteStorageType::UINT32;
182 }
else if (fits_upper_limit<std::uint64_t>(upper_index)) {
183 return WriteStorageType::UINT64;
185 throw std::runtime_error(
"no type can store the largest index");
188 const auto itype = *index_type;
189 if (!does_non_negative_integer_fit(itype, upper_index)) {
190 throw std::runtime_error(
"specified type cannot store the largest index");
196inline WriteStorageType choose_ptr_type(
const std::optional<WriteStorageType>& ptr_type, hsize_t nnzero) {
197 if (!ptr_type.has_value()) {
198 if (fits_upper_limit<std::uint32_t>(nnzero)) {
199 return WriteStorageType::UINT32;
200 }
else if (fits_upper_limit<std::uint64_t>(nnzero)) {
201 return WriteStorageType::UINT64;
204 throw std::runtime_error(
"no type can store the number of non-zero elements");
207 const auto ptype = *ptr_type;
208 if (!does_non_negative_integer_fit(ptype, nnzero)) {
209 throw std::runtime_error(
"specified type cannot store the number of non-zero elements");
215template<
typename Value_,
typename Index_>
216struct WriteSparseHdf5Statistics {
217 Value_ lower_data = 0;
218 Value_ upper_data = 0;
219 Index_ upper_index = 0;
220 hsize_t non_zeros = 0;
221 bool has_decimal =
false;
222 bool has_nonfinite =
false;
224 void add_value(Value_ val) {
225 if constexpr(!std::is_integral<Value_>::value) {
226 if (std::trunc(val) != val) {
229 if (!std::isfinite(val)) {
230 has_nonfinite =
true;
234 if (val < lower_data) {
236 }
else if (val > upper_data) {
241 void add_index(Index_ idx) {
242 if (idx > upper_index) {
248template<
typename Value_,
typename Index_>
251 output.non_zeros = sanisizer::sum<hsize_t>(output.non_zeros, extracted.
number);
253 for (Index_ i = 0; i < extracted.
number; ++i) {
254 output.add_value(extracted.
value[i]);
257 for (Index_ i = 0; i < extracted.
number; ++i) {
258 output.add_index(extracted.
index[i]);
262template<
typename Value_,
typename Index_>
263void update_hdf5_stats(
const Value_* extracted, Index_ n, WriteSparseHdf5Statistics<Value_, Index_>& output) {
264 Index_ local_nonzero = 0;
265 for (Index_ i = 0; i < n; ++i) {
266 auto val = extracted[i];
271 output.add_value(val);
276 output.non_zeros = sanisizer::sum<hsize_t>(output.non_zeros, local_nonzero);
279template<
typename Value_,
typename Index_>
281 const auto NR = mat.
nrow(), NC = mat.
ncol();
283 WriteSparseHdf5Statistics<Value_, Index_> output;
284 auto collected = sanisizer::create<std::vector<WriteSparseHdf5Statistics<Value_, Index_> > >(nthreads - 1);
290 WriteSparseHdf5Statistics<Value_, Index_> current_output;
293 std::vector<Value_> xbuffer(NC);
294 std::vector<Index_> ibuffer(NC);
295 for (Index_ r = start, end = start + len; r < end; ++r) {
296 auto extracted = wrk->fetch(r, xbuffer.data(), ibuffer.data());
297 update_hdf5_stats(extracted, current_output);
301 (t ? collected[t - 1] : output) = std::move(current_output);
306 WriteSparseHdf5Statistics<Value_, Index_> current_output;
309 std::vector<Value_> xbuffer(NR);
310 std::vector<Index_> ibuffer(NR);
311 for (Index_ c = start, end = start + len; c < end; ++c) {
312 auto extracted = wrk->fetch(c, xbuffer.data(), ibuffer.data());
313 update_hdf5_stats(extracted, current_output);
317 (t ? collected[t - 1] : output) = std::move(current_output);
324 WriteSparseHdf5Statistics<Value_, Index_> current_output;
327 std::vector<Value_> xbuffer(NC);
328 for (Index_ r = start, end = start + len; r < end; ++r) {
329 auto extracted = wrk->fetch(r, xbuffer.data());
330 update_hdf5_stats(extracted, NC, current_output);
334 (t ? collected[t - 1] : output) = std::move(current_output);
339 WriteSparseHdf5Statistics<Value_, Index_> current_output;
342 std::vector<Value_> xbuffer(NR);
343 for (Index_ c = start, end = start + len; c < end; ++c) {
344 auto extracted = wrk->fetch(c, xbuffer.data());
345 update_hdf5_stats(extracted, NR, current_output);
349 (t ? collected[t - 1] : output) = std::move(current_output);
354 for (
int i = 1; i < num_used; ++i) {
355 auto& current = collected[i - 1];
356 output.lower_data = std::min(output.lower_data, current.lower_data);
357 output.upper_data = std::max(output.upper_data, current.upper_data);
358 output.upper_index = std::max(output.upper_index, current.upper_index);
359 output.non_zeros = sanisizer::sum<hsize_t>(output.non_zeros, current.non_zeros);
360 output.has_decimal = output.has_decimal || current.has_decimal;
361 output.has_nonfinite = output.has_nonfinite || current.has_nonfinite;
367template<
typename Value_,
typename Index_>
368void write_compressed_sparse_matrix_two_pass(
372 const std::string& data_name,
373 const std::string& index_name,
374 const std::string& ptr_name,
375 const WriteCompressedSparseMatrixOptions& params
377 auto stats = write_sparse_hdf5_statistics(mat, params.num_threads);
378 const auto data_type = choose_data_type(params.data_type, stats.lower_data, stats.upper_data, stats.has_decimal, params.force_integer, stats.has_nonfinite);
379 const auto index_type = choose_index_type(params.index_type, stats.upper_index);
382 const auto non_zeros = stats.non_zeros;
383 H5::DataSet data_ds = create_1d_compressed_hdf5_dataset(location, data_type, data_name, non_zeros, params.deflate_level, params.chunk_size, params.shuffle);
384 H5::DataSet index_ds = create_1d_compressed_hdf5_dataset(location, index_type, index_name, non_zeros, params.deflate_level, params.chunk_size, params.shuffle);
386 H5::DataSpace inspace(1, &non_zeros);
387 H5::DataSpace outspace(1, &non_zeros);
388 const auto& dstype = define_mem_type<Value_>();
389 const auto& ixtype = define_mem_type<Index_>();
391 const Index_ NR = mat.
nrow(), NC = mat.
ncol();
392 std::vector<hsize_t> ptrs;
394 auto fill_datasets = [&](
const Value_* vptr,
const Index_* iptr, hsize_t count) ->
void {
396 inspace.setExtentSimple(1, &count);
397 outspace.selectHyperslab(H5S_SELECT_SET, &count, &offset);
398 data_ds.write(vptr, dstype, inspace, outspace);
399 index_ds.write(iptr, ixtype, inspace, outspace);
405 if (layout == WriteStorageLayout::ROW) {
406 ptrs.resize(sanisizer::sum<
decltype(ptrs.size())>(NR, 1));
411 for (Index_ r = 0; r < NR; ++r) {
412 auto extracted = wrk->fetch(r, xbuffer.data(), ibuffer.data());
413 fill_datasets(extracted.value, extracted.index, extracted.number);
414 ptrs[r + 1] = offset;
418 ptrs.resize(sanisizer::sum<
decltype(ptrs.size())>(NC, 1));
423 for (Index_ c = 0; c < NC; ++c) {
424 auto extracted = wrk->fetch(c, xbuffer.data(), ibuffer.data());
425 fill_datasets(extracted.value, extracted.index, extracted.number);
426 ptrs[c + 1] = offset;
431 std::vector<Value_> sparse_xbuffer;
432 std::vector<Index_> sparse_ibuffer;
433 auto fill_datasets_from_dense = [&](
const Value_* extracted, Index_ n) ->
void {
434 sparse_xbuffer.clear();
435 sparse_ibuffer.clear();
436 for (Index_ i = 0; i < n; ++i) {
438 sparse_xbuffer.push_back(extracted[i]);
439 sparse_ibuffer.push_back(i);
443 hsize_t count = sparse_xbuffer.size();
444 fill_datasets(sparse_xbuffer.data(), sparse_ibuffer.data(), count);
447 if (layout == WriteStorageLayout::ROW) {
448 ptrs.resize(sanisizer::sum<
decltype(ptrs.size())>(NR, 1));
451 for (Index_ r = 0; r < NR; ++r) {
452 auto extracted = wrk->fetch(r, dbuffer.data());
453 fill_datasets_from_dense(extracted, NC);
454 ptrs[r + 1] = offset;
458 ptrs.resize(sanisizer::sum<
decltype(ptrs.size())>(NC, 1));
461 for (Index_ c = 0; c < NC; ++c) {
462 auto extracted = wrk->fetch(c, dbuffer.data());
463 fill_datasets_from_dense(extracted, NR);
464 ptrs[c + 1] = offset;
470 auto ptr_len = sanisizer::cast<hsize_t>(ptrs.size());
471 H5::DataSet ptr_ds = create_1d_compressed_hdf5_dataset(
473 choose_ptr_type(params.ptr_type, ptrs.back()),
476 params.deflate_level,
480 H5::DataSpace ptr_space(1, &ptr_len);
481 ptr_ds.write(ptrs.data(), H5::PredType::NATIVE_HSIZE, ptr_space);
486inline H5::DataSet create_1d_compressed_hdf5_dataset(H5::Group& location,
WriteStorageType type,
const std::string& name,
int deflate_level, hsize_t chunk,
bool shuffle) {
487 const hsize_t length = 0;
488 constexpr auto copy = H5S_UNLIMITED;
489 H5::DataSpace dspace(1, &length, ©);
490 H5::DSetCreatPropList plist;
493 if (deflate_level == 0) {
494 throw std::runtime_error(
"'deflate_level' must be positive if 'two_pass = false'");
497 plist.setDeflate(deflate_level);
498 plist.setChunk(1, &chunk);
503 const auto dtype = choose_pred_type(type);
504 return location.createDataSet(name, *dtype, dspace, plist);
507template<
typename Value_,
typename Index_>
508void write_compressed_sparse_matrix_one_pass(
512 const std::string& data_name,
513 const std::string& index_name,
514 const std::string& ptr_name,
515 const WriteCompressedSparseMatrixOptions& params
517 const auto requested_dtype = *(params.data_type);
518 const auto requested_itype = *(params.index_type);
519 H5::DataSet data_ds = create_1d_compressed_hdf5_dataset(location, requested_dtype, data_name, params.deflate_level, params.chunk_size, params.shuffle);
520 H5::DataSet index_ds = create_1d_compressed_hdf5_dataset(location, requested_itype, index_name, params.deflate_level, params.chunk_size, params.shuffle);
523 H5::DataSpace outspace;
524 const auto& dstype = define_mem_type<Value_>();
525 const auto& ixtype = define_mem_type<Index_>();
527 const Index_ NR = mat.
nrow(), NC = mat.
ncol();
528 std::vector<hsize_t> ptrs;
530 auto fill_datasets = [&](
const Value_* vptr,
const Index_* iptr, hsize_t count, H5::DataSpace& inspace) ->
void {
533 const hsize_t new_size = sanisizer::sum<hsize_t>(offset, count);
534 data_ds.extend(&new_size);
535 index_ds.extend(&new_size);
537 constexpr hsize_t zero = 0;
538 inspace.selectHyperslab(H5S_SELECT_SET, &count, &zero);
539 outspace.setExtentSimple(1, &new_size);
540 outspace.selectHyperslab(H5S_SELECT_SET, &count, &offset);
542 data_ds.write(vptr, dstype, inspace, outspace);
543 index_ds.write(iptr, ixtype, inspace, outspace);
549 auto fill_datasets_from_sparse = [&](
const Value_* vptr,
const Index_* iptr, Index_ n, H5::DataSpace& inspace) ->
void {
550 for (Index_ i = 0; i < n; ++i) {
551 check_data_value_fit(requested_dtype, vptr[i]);
552 if (!does_non_negative_integer_fit(requested_itype, iptr[i])) {
553 throw std::runtime_error(
"specified type cannot store the largest index");
557 const auto count = sanisizer::cast<hsize_t>(n);
558 fill_datasets(vptr, iptr, count, inspace);
561 if (layout == WriteStorageLayout::ROW) {
562 ptrs.resize(sanisizer::sum<
decltype(ptrs.size())>(NR, 1));
565 const hsize_t extent = NC;
566 H5::DataSpace inspace(1, &extent);
569 for (Index_ r = 0; r < NR; ++r) {
570 auto extracted = wrk->fetch(r, xbuffer.data(), ibuffer.data());
571 fill_datasets_from_sparse(extracted.value, extracted.index, extracted.number, inspace);
572 ptrs[r + 1] = offset;
576 ptrs.resize(sanisizer::sum<
decltype(ptrs.size())>(NC, 1));
579 const hsize_t extent = NR;
580 H5::DataSpace inspace(1, &extent);
583 for (Index_ c = 0; c < NC; ++c) {
584 auto extracted = wrk->fetch(c, xbuffer.data(), ibuffer.data());
585 fill_datasets_from_sparse(extracted.value, extracted.index, extracted.number, inspace);
586 ptrs[c + 1] = offset;
591 std::vector<Value_> sparse_xbuffer;
592 std::vector<Index_> sparse_ibuffer;
593 auto fill_datasets_from_dense = [&](
const Value_* extracted, Index_ n, H5::DataSpace& inspace) ->
void {
594 sparse_xbuffer.clear();
595 sparse_ibuffer.clear();
596 for (Index_ i = 0; i < n; ++i) {
598 check_data_value_fit(requested_dtype, extracted[i]);
599 sparse_xbuffer.push_back(extracted[i]);
600 if (!does_non_negative_integer_fit(requested_itype, i)) {
601 throw std::runtime_error(
"specified type cannot store the largest index");
603 sparse_ibuffer.push_back(i);
607 const auto count = sanisizer::cast<hsize_t>(sparse_xbuffer.size());
608 fill_datasets(sparse_xbuffer.data(), sparse_ibuffer.data(), count, inspace);
611 if (layout == WriteStorageLayout::ROW) {
612 ptrs.resize(sanisizer::sum<
decltype(ptrs.size())>(NR, 1));
614 const hsize_t extent = NC;
615 H5::DataSpace inspace(1, &extent);
618 for (Index_ r = 0; r < NR; ++r) {
619 auto extracted = wrk->fetch(r, dbuffer.data());
620 fill_datasets_from_dense(extracted, NC, inspace);
621 ptrs[r + 1] = offset;
625 ptrs.resize(sanisizer::sum<
decltype(ptrs.size())>(NC, 1));
627 const hsize_t extent = NR;
628 H5::DataSpace inspace(1, &extent);
631 for (Index_ c = 0; c < NC; ++c) {
632 auto extracted = wrk->fetch(c, dbuffer.data());
633 fill_datasets_from_dense(extracted, NR, inspace);
634 ptrs[c + 1] = offset;
640 auto ptr_len = sanisizer::cast<hsize_t>(ptrs.size());
641 H5::DataSet ptr_ds = create_1d_compressed_hdf5_dataset(
643 choose_ptr_type(params.ptr_type, ptrs.back()),
646 params.deflate_level,
650 H5::DataSpace ptr_space(1, &ptr_len);
651 ptr_ds.write(ptrs.data(), H5::PredType::NATIVE_HSIZE, ptr_space);
670template<
typename Value_,
typename Index_>
678 layout = WriteStorageLayout::ROW;
680 layout = WriteStorageLayout::COLUMN;
685 std::string data_name;
692 std::string index_name;
696 index_name =
"indices";
699 std::string ptr_name;
708 write_compressed_sparse_matrix_two_pass(mat, location, layout, data_name, index_name, ptr_name, params);
710 write_compressed_sparse_matrix_one_pass(mat, location, layout, data_name, index_name, ptr_name, params);
723template<
typename Value_,
typename Index_>
733template<
typename Value_,
typename Index_>
738template<
typename Value_,
typename Index_>
virtual Index_ ncol() const=0
virtual Index_ nrow() const=0
virtual bool prefer_rows() const=0
virtual std::unique_ptr< MyopicSparseExtractor< Value_, Index_ > > sparse(bool row, const Options &opt) const=0
Representations for matrix data in HDF5 files.
Definition CompressedSparseMatrix.hpp:24
WriteStorageLayout
Definition utils.hpp:26
WriteStorageType
Definition utils.hpp:31
void write_compressed_sparse_matrix(const tatami::Matrix< Value_, Index_ > &mat, H5::Group &location, const WriteCompressedSparseMatrixOptions ¶ms)
Definition write_compressed_sparse_matrix.hpp:671
int parallelize(Function_ fun, const Index_ tasks, const int workers)
Container_ create_container_of_Index_size(const Index_ x, Args_ &&... args)
auto consecutive_extractor(const Matrix< Value_, Index_ > &matrix, const bool row, const Index_ iter_start, const Index_ iter_length, Args_ &&... args)
Parameters for write_compressed_sparse_matrix().
Definition write_compressed_sparse_matrix.hpp:26
std::optional< WriteStorageLayout > columnar
Definition write_compressed_sparse_matrix.hpp:49
bool two_pass
Definition write_compressed_sparse_matrix.hpp:102
bool force_integer
Definition write_compressed_sparse_matrix.hpp:63
int deflate_level
Definition write_compressed_sparse_matrix.hpp:82
std::optional< WriteStorageType > ptr_type
Definition write_compressed_sparse_matrix.hpp:75
hsize_t chunk_size
Definition write_compressed_sparse_matrix.hpp:88
std::optional< std::string > data_name
Definition write_compressed_sparse_matrix.hpp:31
std::optional< std::string > ptr_name
Definition write_compressed_sparse_matrix.hpp:43
std::optional< std::string > index_name
Definition write_compressed_sparse_matrix.hpp:37
std::optional< WriteStorageType > index_type
Definition write_compressed_sparse_matrix.hpp:69
std::optional< WriteStorageType > data_type
Definition write_compressed_sparse_matrix.hpp:55
int num_threads
Definition write_compressed_sparse_matrix.hpp:108
bool shuffle
Definition write_compressed_sparse_matrix.hpp:95
Utilities for HDF5 extraction.