1#ifndef TATAMI_WRITE_SPARSE_MATRIX_TO_HDF5_HPP
2#define TATAMI_WRITE_SPARSE_MATRIX_TO_HDF5_HPP
99inline H5::DataSet create_1d_compressed_hdf5_dataset(H5::Group& location,
const H5::DataType& dtype,
const std::string& name, hsize_t length,
int deflate_level, hsize_t chunk) {
100 H5::DataSpace dspace(1, &length);
101 H5::DSetCreatPropList plist;
103 if (deflate_level >= 0 && length) {
104 plist.setDeflate(deflate_level);
105 if (chunk > length) {
106 plist.setChunk(1, &length);
108 plist.setChunk(1, &chunk);
112 return location.createDataSet(name, dtype, dspace, plist);
115inline 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) {
116 const H5::PredType* dtype;
118 case WriteStorageType::INT8:
119 dtype = &(H5::PredType::NATIVE_INT8);
121 case WriteStorageType::UINT8:
122 dtype = &(H5::PredType::NATIVE_UINT8);
124 case WriteStorageType::INT16:
125 dtype = &(H5::PredType::NATIVE_INT16);
127 case WriteStorageType::UINT16:
128 dtype = &(H5::PredType::NATIVE_UINT16);
130 case WriteStorageType::INT32:
131 dtype = &(H5::PredType::NATIVE_INT32);
133 case WriteStorageType::UINT32:
134 dtype = &(H5::PredType::NATIVE_UINT32);
136 case WriteStorageType::DOUBLE:
137 dtype = &(H5::PredType::NATIVE_DOUBLE);
140 throw std::runtime_error(
"automatic HDF5 output type must be resolved before creating a HDF5 dataset");
142 return create_1d_compressed_hdf5_dataset(location, *dtype, name, length, deflate_level, chunk);
145template<
typename Left_,
typename Right_>
146bool is_less_than_or_equal(Left_ l, Right_ r) {
147 constexpr bool lsigned = std::is_signed<Left_>::value;
148 constexpr bool rsigned = std::is_signed<Right_>::value;
149 if constexpr(lsigned == rsigned) {
151 }
else if constexpr(lsigned) {
152 return l <= 0 || static_cast<typename std::make_unsigned<Left_>::type>(l) <= r;
154 return r >= 0 && l <= static_cast<typename std::make_unsigned<Right_>::type>(r);
158template<
typename Native_,
typename Max_>
159bool fits_upper_limit(Max_ max) {
160 constexpr auto native_max = std::numeric_limits<Native_>::max();
161 if constexpr(std::is_integral<Max_>::value) {
162 return is_less_than_or_equal(max, native_max);
164 return max <= static_cast<double>(native_max);
168template<
typename Native_,
typename Min_>
169bool fits_lower_limit(Min_ min) {
170 constexpr auto native_min = std::numeric_limits<Native_>::min();
171 if constexpr(std::is_integral<Min_>::value) {
172 return is_less_than_or_equal(native_min, min);
174 return min >=
static_cast<double>(native_min);
184template<
typename Value_,
typename Index_>
185struct WriteSparseHdf5Statistics {
186 Value_ lower_data = 0;
187 Value_ upper_data = 0;
188 Index_ upper_index = 0;
189 hsize_t non_zeros = 0;
190 bool non_integer =
false;
192 void add_value(Value_ val) {
193 if constexpr(!std::is_integral<Value_>::value) {
194 if (std::trunc(val) != val || !std::isfinite(val)) {
199 if (val < lower_data) {
201 }
else if (val > upper_data) {
206 void add_index(Index_ idx) {
207 if (idx > upper_index) {
213template<
typename Value_,
typename Index_>
216 output.non_zeros = sanisizer::sum<hsize_t>(output.non_zeros, extracted.
number);
219 for (Index_ i = 0; i < extracted.
number; ++i) {
220 output.add_value(extracted.
value[i]);
225 for (Index_ i = 0; i < extracted.
number; ++i) {
226 output.add_index(extracted.
index[i]);
231template<
typename Value_,
typename Index_>
232void update_hdf5_stats(
const Value_* extracted, Index_ n, WriteSparseHdf5Statistics<Value_, Index_>& output) {
233 Index_ local_nonzero = 0;
234 for (Index_ i = 0; i < n; ++i) {
235 auto val = extracted[i];
240 output.add_value(val);
245 output.non_zeros = sanisizer::sum<hsize_t>(output.non_zeros, local_nonzero);
248template<
typename Value_,
typename Index_>
249WriteSparseHdf5Statistics<Value_, Index_> write_sparse_hdf5_statistics(
const tatami::Matrix<Value_, Index_>* mat,
bool infer_value,
bool infer_index,
int nthreads) {
250 auto NR = mat->
nrow(), NC = mat->
ncol();
251 std::vector<WriteSparseHdf5Statistics<Value_, Index_> > collected(nthreads);
261 std::vector<Value_> xbuffer(NC);
262 std::vector<Index_> ibuffer(NC);
263 for (Index_ r = start, end = start + len; r < end; ++r) {
264 auto extracted = wrk->fetch(r, xbuffer.data(), ibuffer.data());
265 update_hdf5_stats(extracted, collected[t], infer_value, infer_index);
272 std::vector<Value_> xbuffer(NR);
273 std::vector<Index_> ibuffer(NR);
274 for (Index_ c = start, end = start + len; c < end; ++c) {
275 auto extracted = wrk->fetch(c, xbuffer.data(), ibuffer.data());
276 update_hdf5_stats(extracted, collected[t], infer_value, infer_index);
285 std::vector<Value_> xbuffer(NC);
286 for (Index_ r = start, end = start + len; r < end; ++r) {
287 auto extracted = wrk->fetch(r, xbuffer.data());
288 update_hdf5_stats(extracted, NC, collected[t]);
295 std::vector<Value_> xbuffer(NR);
296 for (Index_ c = start, end = start + len; c < end; ++c) {
297 auto extracted = wrk->fetch(c, xbuffer.data());
298 update_hdf5_stats(extracted, NR, collected[t]);
304 auto& first = collected.front();
305 for (
int i = 1; i < nthreads; ++i) {
306 auto& current = collected[i];
307 first.lower_data = std::min(first.lower_data, current.lower_data);
308 first.upper_data = std::max(first.upper_data, current.upper_data);
309 first.upper_index = std::max(first.upper_index, current.upper_index);
310 first.non_zeros = sanisizer::sum<hsize_t>(first.non_zeros, current.non_zeros);
311 first.non_integer = first.non_integer || current.non_integer;
314 return std::move(first);
333template<
typename Value_,
typename Index_>
337 auto use_auto_data_type = (data_type == WriteStorageType::AUTOMATIC);
338 auto use_auto_index_type = (index_type == WriteStorageType::AUTOMATIC);
339 auto stats = write_sparse_hdf5_statistics(mat, use_auto_data_type, use_auto_index_type, params.
num_threads);
342 if (use_auto_data_type) {
344 data_type = WriteStorageType::DOUBLE;
346 auto lower_data = stats.lower_data;
347 auto upper_data = stats.upper_data;
348 if (lower_data < 0) {
349 if (fits_lower_limit<std::int8_t>(lower_data) && fits_upper_limit<std::int8_t>(upper_data)) {
350 data_type = WriteStorageType::INT8;
351 }
else if (fits_lower_limit<std::int16_t>(lower_data) && fits_upper_limit<std::int16_t>(upper_data)) {
352 data_type = WriteStorageType::INT16;
354 data_type = WriteStorageType::INT32;
357 if (fits_upper_limit<std::uint8_t>(upper_data)) {
358 data_type = WriteStorageType::UINT8;
359 }
else if (fits_upper_limit<std::uint16_t>(upper_data)) {
360 data_type = WriteStorageType::UINT16;
362 data_type = WriteStorageType::UINT32;
368 if (use_auto_index_type) {
369 auto upper_index = stats.upper_index;
370 if (fits_upper_limit<std::uint8_t>(upper_index)) {
371 index_type = WriteStorageType::UINT8;
372 }
else if (fits_upper_limit<std::uint16_t>(upper_index)) {
373 index_type = WriteStorageType::UINT16;
375 index_type = WriteStorageType::UINT32;
381 if (layout == WriteStorageLayout::AUTOMATIC) {
383 layout = WriteStorageLayout::ROW;
385 layout = WriteStorageLayout::COLUMN;
390 auto non_zeros = stats.non_zeros;
394 H5::DataSpace inspace(1, &non_zeros);
395 H5::DataSpace outspace(1, &non_zeros);
396 const auto& dstype = define_mem_type<Value_>();
397 const auto& ixtype = define_mem_type<Index_>();
399 Index_ NR = mat->
nrow(), NC = mat->
ncol();
400 std::vector<hsize_t> ptrs;
402 auto fill_datasets = [&](
const Value_* vptr,
const Index_* iptr, hsize_t count) ->
void {
404 inspace.setExtentSimple(1, &count);
405 outspace.selectHyperslab(H5S_SELECT_SET, &count, &offset);
406 data_ds.write(vptr, dstype, inspace, outspace);
407 index_ds.write(iptr, ixtype, inspace, outspace);
413 if (layout == WriteStorageLayout::ROW) {
414 ptrs.resize(sanisizer::sum<
decltype(ptrs.size())>(NR, 1));
419 for (Index_ r = 0; r < NR; ++r) {
420 auto extracted = wrk->fetch(r, xbuffer.data(), ibuffer.data());
421 fill_datasets(extracted.value, extracted.index, extracted.number);
422 ptrs[r+1] = ptrs[r] + extracted.number;
426 ptrs.resize(sanisizer::sum<
decltype(ptrs.size())>(NC, 1));
431 for (Index_ c = 0; c < NC; ++c) {
432 auto extracted = wrk->fetch(c, xbuffer.data(), ibuffer.data());
433 fill_datasets(extracted.value, extracted.index, extracted.number);
434 ptrs[c+1] = ptrs[c] + extracted.number;
439 std::vector<Value_> sparse_xbuffer;
440 std::vector<Index_> sparse_ibuffer;
441 auto fill_datasets_from_dense = [&](
const Value_* extracted, Index_ n) -> hsize_t {
442 sparse_xbuffer.clear();
443 sparse_ibuffer.clear();
444 for (Index_ i = 0; i < n; ++i) {
446 sparse_xbuffer.push_back(extracted[i]);
447 sparse_ibuffer.push_back(i);
451 hsize_t count = sparse_xbuffer.size();
452 fill_datasets(sparse_xbuffer.data(), sparse_ibuffer.data(), count);
456 if (layout == WriteStorageLayout::ROW) {
457 ptrs.resize(sanisizer::sum<
decltype(ptrs.size())>(NR, 1));
460 for (Index_ r = 0; r < NR; ++r) {
461 auto extracted = wrk->fetch(r, dbuffer.data());
462 auto count = fill_datasets_from_dense(extracted, NC);
463 ptrs[r+1] = ptrs[r] + count;
467 ptrs.resize(sanisizer::sum<
decltype(ptrs.size())>(NC, 1));
470 for (Index_ c = 0; c < NC; ++c) {
471 auto extracted = wrk->fetch(c, dbuffer.data());
472 auto count = fill_datasets_from_dense(extracted, NR);
473 ptrs[c+1] = ptrs[c] + count;
479 auto ptr_len = sanisizer::cast<hsize_t>(ptrs.size());
480 H5::DataSet ptr_ds = create_1d_compressed_hdf5_dataset(location, H5::PredType::NATIVE_HSIZE, params.
ptr_name, ptr_len, params.
deflate_level, params.
chunk_size);
481 H5::DataSpace ptr_space(1, &ptr_len);
482 ptr_ds.write(ptrs.data(), H5::PredType::NATIVE_HSIZE, ptr_space);
498template<
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:24
WriteStorageType
Definition utils.hpp:29
void write_compressed_sparse_matrix(const tatami::Matrix< Value_, Index_ > *mat, H5::Group &location, const WriteCompressedSparseMatrixOptions ¶ms)
Definition write_compressed_sparse_matrix.hpp:334
void parallelize(Function_ fun, Index_ tasks, int threads)
Container_ create_container_of_Index_size(Index_ x, Args_ &&... args)
auto consecutive_extractor(const Matrix< Value_, Index_ > &matrix, bool row, Index_ iter_start, Index_ iter_length, Args_ &&... args)
bool sparse_extract_index
bool sparse_extract_value
Parameters for write_compressed_sparse_matrix().
Definition write_compressed_sparse_matrix.hpp:25
std::string ptr_name
Definition write_compressed_sparse_matrix.hpp:50
WriteStorageType index_type
Definition write_compressed_sparse_matrix.hpp:76
bool force_integer
Definition write_compressed_sparse_matrix.hpp:70
int deflate_level
Definition write_compressed_sparse_matrix.hpp:81
std::string data_name
Definition write_compressed_sparse_matrix.hpp:38
std::string index_name
Definition write_compressed_sparse_matrix.hpp:44
WriteStorageType data_type
Definition write_compressed_sparse_matrix.hpp:62
hsize_t chunk_size
Definition write_compressed_sparse_matrix.hpp:86
int num_threads
Definition write_compressed_sparse_matrix.hpp:93
WriteStorageLayout columnar
Definition write_compressed_sparse_matrix.hpp:56
Utilities for HDF5 extraction.