tatami_hdf5
tatami bindings for HDF5-backed matrices
Loading...
Searching...
No Matches
write_compressed_sparse_matrix.hpp
Go to the documentation of this file.
1#ifndef TATAMI_WRITE_SPARSE_MATRIX_TO_HDF5_HPP
2#define TATAMI_WRITE_SPARSE_MATRIX_TO_HDF5_HPP
3
4#include "tatami/tatami.hpp"
5#include "utils.hpp"
6
7#include "H5Cpp.h"
8
9#include <cstdint>
10#include <string>
11#include <vector>
12#include <cmath>
13#include <limits>
14#include <optional>
15
21namespace tatami_hdf5 {
22
31 std::optional<std::string> data_name;
32
37 std::optional<std::string> index_name;
38
43 std::optional<std::string> ptr_name;
44
49 std::optional<WriteStorageLayout> columnar;
50
55 std::optional<WriteStorageType> data_type;
56
63 bool force_integer = false;
64
69 std::optional<WriteStorageType> index_type;
70
75 std::optional<WriteStorageType> ptr_type;
76
83
87 hsize_t chunk_size = sanisizer::cap<hsize_t>(10000);
88
94 bool two_pass = false;
95
100 int num_threads = 1;
101};
102
106inline 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) {
107 H5::DataSpace dspace(1, &length);
108 H5::DSetCreatPropList plist;
109
110 if (deflate_level >= 0 && length) {
111 plist.setDeflate(deflate_level);
112 if (chunk > length) {
113 plist.setChunk(1, &length);
114 } else {
115 plist.setChunk(1, &chunk);
116 }
117 }
118
119 const auto dtype = choose_pred_type(type);
120 return location.createDataSet(name, *dtype, dspace, plist);
121}
122
123template<typename Type_>
124bool does_non_negative_integer_fit(const WriteStorageType type, const Type_ x) {
125 static_assert(std::is_integral<Type_>::value);
126
127 bool okay = false;
128 switch (type) {
129 case WriteStorageType::INT8:
130 okay = fits_upper_limit<std::int8_t>(x);
131 break;
132 case WriteStorageType::UINT8:
133 okay = fits_upper_limit<std::uint8_t>(x);
134 break;
135 case WriteStorageType::INT16:
136 okay = fits_upper_limit<std::int16_t >(x);
137 break;
138 case WriteStorageType::UINT16:
139 okay = fits_upper_limit<std::uint16_t>(x);
140 break;
141 case WriteStorageType::INT32:
142 okay = fits_upper_limit<std::int32_t >(x);
143 break;
144 case WriteStorageType::UINT32:
145 okay = fits_upper_limit<std::uint32_t>(x);
146 break;
147 case WriteStorageType::INT64:
148 okay = fits_upper_limit<std::int64_t >(x);
149 break;
150 case WriteStorageType::UINT64:
151 okay = fits_upper_limit<std::uint64_t>(x);
152 break;
153 default:
154 // okay remains false, as the x must be integer.
155 break;
156 }
157 return okay;
158}
159
160template<typename Index_>
161WriteStorageType choose_index_type(const std::optional<WriteStorageType>& index_type, Index_ upper_index) {
162 static_assert(std::is_integral<Index_>::value);
163
164 if (!index_type.has_value()) {
165 if (fits_upper_limit<std::uint8_t>(upper_index)) {
166 return WriteStorageType::UINT8;
167 } else if (fits_upper_limit<std::uint16_t>(upper_index)) {
168 return WriteStorageType::UINT16;
169 } else if (fits_upper_limit<std::uint32_t>(upper_index)) {
170 return WriteStorageType::UINT32;
171 } else if (fits_upper_limit<std::uint64_t>(upper_index)) {
172 return WriteStorageType::UINT64;
173 }
174 throw std::runtime_error("no type can store the largest index");
175 }
176
177 const auto itype = *index_type;
178 if (!does_non_negative_integer_fit(itype, upper_index)) {
179 throw std::runtime_error("specified type cannot store the largest index");
180 }
181
182 return itype;
183}
184
185inline WriteStorageType choose_ptr_type(const std::optional<WriteStorageType>& ptr_type, hsize_t nnzero) {
186 if (!ptr_type.has_value()) {
187 if (fits_upper_limit<std::uint32_t>(nnzero)) {
188 return WriteStorageType::UINT32;
189 } else if (fits_upper_limit<std::uint64_t>(nnzero)) {
190 return WriteStorageType::UINT64;
191 }
192
193 throw std::runtime_error("no type can store the number of non-zero elements");
194 }
195
196 const auto ptype = *ptr_type;
197 if (!does_non_negative_integer_fit(ptype, nnzero)) {
198 throw std::runtime_error("specified type cannot store the number of non-zero elements");
199 }
200
201 return ptype;
202}
203
204template<typename Value_, typename Index_>
205struct WriteSparseHdf5Statistics {
206 Value_ lower_data = 0;
207 Value_ upper_data = 0;
208 Index_ upper_index = 0;
209 hsize_t non_zeros = 0;
210 bool has_decimal = false;
211 bool has_nonfinite = false;
212
213 void add_value(Value_ val) {
214 if constexpr(!std::is_integral<Value_>::value) {
215 if (std::trunc(val) != val) {
216 has_decimal = true;
217 }
218 if (!std::isfinite(val)) {
219 has_nonfinite = true;
220 }
221 }
222
223 if (val < lower_data) {
224 lower_data = val;
225 } else if (val > upper_data) {
226 upper_data = val;
227 }
228 }
229
230 void add_index(Index_ idx) {
231 if (idx > upper_index) {
232 upper_index = idx;
233 }
234 }
235};
236
237template<typename Value_, typename Index_>
238void update_hdf5_stats(const tatami::SparseRange<Value_, Index_>& extracted, WriteSparseHdf5Statistics<Value_, Index_>& output) {
239 // We need to protect the addition just in case it overflows from having too many non-zero elements.
240 output.non_zeros = sanisizer::sum<hsize_t>(output.non_zeros, extracted.number);
241
242 for (Index_ i = 0; i < extracted.number; ++i) {
243 output.add_value(extracted.value[i]);
244 }
245
246 for (Index_ i = 0; i < extracted.number; ++i) {
247 output.add_index(extracted.index[i]);
248 }
249}
250
251template<typename Value_, typename Index_>
252void update_hdf5_stats(const Value_* extracted, Index_ n, WriteSparseHdf5Statistics<Value_, Index_>& output) {
253 Index_ local_nonzero = 0;
254 for (Index_ i = 0; i < n; ++i) {
255 auto val = extracted[i];
256 if (val == 0) {
257 continue;
258 }
259 ++local_nonzero;
260 output.add_value(val);
261 output.add_index(i);
262 }
263
264 // Checking that there aren't overflows, but doing so outside of the hot loop for perf.
265 output.non_zeros = sanisizer::sum<hsize_t>(output.non_zeros, local_nonzero);
266}
267
268template<typename Value_, typename Index_>
269WriteSparseHdf5Statistics<Value_, Index_> write_sparse_hdf5_statistics(const tatami::Matrix<Value_, Index_>& mat, int nthreads) {
270 const auto NR = mat.nrow(), NC = mat.ncol();
271
272 WriteSparseHdf5Statistics<Value_, Index_> output;
273 auto collected = sanisizer::create<std::vector<WriteSparseHdf5Statistics<Value_, Index_> > >(nthreads - 1); // nthreads had better be >= 1.
274 int num_used;
275
276 if (mat.sparse()) {
277 if (mat.prefer_rows()) {
278 num_used = tatami::parallelize([&](int t, Index_ start, Index_ len) -> void {
279 WriteSparseHdf5Statistics<Value_, Index_> current_output;
280
281 auto wrk = tatami::consecutive_extractor<true>(mat, true, start, len);
282 std::vector<Value_> xbuffer(NC);
283 std::vector<Index_> ibuffer(NC);
284 for (Index_ r = start, end = start + len; r < end; ++r) {
285 auto extracted = wrk->fetch(r, xbuffer.data(), ibuffer.data());
286 update_hdf5_stats(extracted, current_output);
287 }
288
289 // Only move to the result buffer at the end, to avoid false sharing between threads.
290 (t ? collected[t - 1] : output) = std::move(current_output);
291 }, NR, nthreads);
292
293 } else {
294 num_used = tatami::parallelize([&](int t, Index_ start, Index_ len) -> void {
295 WriteSparseHdf5Statistics<Value_, Index_> current_output;
296
297 auto wrk = tatami::consecutive_extractor<true>(mat, false, start, len);
298 std::vector<Value_> xbuffer(NR);
299 std::vector<Index_> ibuffer(NR);
300 for (Index_ c = start, end = start + len; c < end; ++c) {
301 auto extracted = wrk->fetch(c, xbuffer.data(), ibuffer.data());
302 update_hdf5_stats(extracted, current_output);
303 }
304
305 // Only move to the result buffer at the end, to avoid false sharing between threads.
306 (t ? collected[t - 1] : output) = std::move(current_output);
307 }, NC, nthreads);
308 }
309
310 } else {
311 if (mat.prefer_rows()) {
312 num_used = tatami::parallelize([&](int t, Index_ start, Index_ len) -> void {
313 WriteSparseHdf5Statistics<Value_, Index_> current_output;
314
315 auto wrk = tatami::consecutive_extractor<false>(mat, true, start, len);
316 std::vector<Value_> xbuffer(NC);
317 for (Index_ r = start, end = start + len; r < end; ++r) {
318 auto extracted = wrk->fetch(r, xbuffer.data());
319 update_hdf5_stats(extracted, NC, current_output);
320 }
321
322 // Only move to the result buffer at the end, to avoid false sharing between threads.
323 (t ? collected[t - 1] : output) = std::move(current_output);
324 }, NR, nthreads);
325
326 } else {
327 num_used = tatami::parallelize([&](int t, Index_ start, Index_ len) -> void {
328 WriteSparseHdf5Statistics<Value_, Index_> current_output;
329
330 auto wrk = tatami::consecutive_extractor<false>(mat, false, start, len);
331 std::vector<Value_> xbuffer(NR);
332 for (Index_ c = start, end = start + len; c < end; ++c) {
333 auto extracted = wrk->fetch(c, xbuffer.data());
334 update_hdf5_stats(extracted, NR, current_output);
335 }
336
337 // Only move to the result buffer at the end, to avoid false sharing between threads.
338 (t ? collected[t - 1] : output) = std::move(current_output);
339 }, NC, nthreads);
340 }
341 }
342
343 for (int i = 1; i < num_used; ++i) {
344 auto& current = collected[i - 1];
345 output.lower_data = std::min(output.lower_data, current.lower_data);
346 output.upper_data = std::max(output.upper_data, current.upper_data);
347 output.upper_index = std::max(output.upper_index, current.upper_index);
348 output.non_zeros = sanisizer::sum<hsize_t>(output.non_zeros, current.non_zeros);
349 output.has_decimal = output.has_decimal || current.has_decimal;
350 output.has_nonfinite = output.has_nonfinite || current.has_nonfinite;
351 }
352
353 return output;
354}
355
356template<typename Value_, typename Index_>
357void write_compressed_sparse_matrix_two_pass(
359 H5::Group& location,
360 const WriteStorageLayout layout,
361 const std::string& data_name,
362 const std::string& index_name,
363 const std::string& ptr_name,
364 const WriteCompressedSparseMatrixOptions& params
365) {
366 auto stats = write_sparse_hdf5_statistics(mat, params.num_threads);
367 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);
368 const auto index_type = choose_index_type(params.index_type, stats.upper_index);
369
370 // And then saving it. This time we have no choice but to iterate by the desired dimension.
371 const auto non_zeros = stats.non_zeros;
372 H5::DataSet data_ds = create_1d_compressed_hdf5_dataset(location, data_type, data_name, non_zeros, params.deflate_level, params.chunk_size);
373 H5::DataSet index_ds = create_1d_compressed_hdf5_dataset(location, index_type, index_name, non_zeros, params.deflate_level, params.chunk_size);
374 hsize_t offset = 0;
375 H5::DataSpace inspace(1, &non_zeros);
376 H5::DataSpace outspace(1, &non_zeros);
377 const auto& dstype = define_mem_type<Value_>();
378 const auto& ixtype = define_mem_type<Index_>();
379
380 const Index_ NR = mat.nrow(), NC = mat.ncol();
381 std::vector<hsize_t> ptrs;
382
383 auto fill_datasets = [&](const Value_* vptr, const Index_* iptr, hsize_t count) -> void {
384 if (count) {
385 inspace.setExtentSimple(1, &count);
386 outspace.selectHyperslab(H5S_SELECT_SET, &count, &offset);
387 data_ds.write(vptr, dstype, inspace, outspace);
388 index_ds.write(iptr, ixtype, inspace, outspace);
389 offset += count; // sum is safe as we already know that the number of non-zeros fits in a hsize_t.
390 }
391 };
392
393 if (mat.sparse()) {
394 if (layout == WriteStorageLayout::ROW) {
395 ptrs.resize(sanisizer::sum<decltype(ptrs.size())>(NR, 1));
398
399 auto wrk = tatami::consecutive_extractor<true>(mat, true, static_cast<Index_>(0), NR);
400 for (Index_ r = 0; r < NR; ++r) {
401 auto extracted = wrk->fetch(r, xbuffer.data(), ibuffer.data());
402 fill_datasets(extracted.value, extracted.index, extracted.number);
403 ptrs[r + 1] = offset;
404 }
405
406 } else {
407 ptrs.resize(sanisizer::sum<decltype(ptrs.size())>(NC, 1));
410
411 auto wrk = tatami::consecutive_extractor<true>(mat, false, static_cast<Index_>(0), NC);
412 for (Index_ c = 0; c < NC; ++c) {
413 auto extracted = wrk->fetch(c, xbuffer.data(), ibuffer.data());
414 fill_datasets(extracted.value, extracted.index, extracted.number);
415 ptrs[c + 1] = offset;
416 }
417 }
418
419 } else {
420 std::vector<Value_> sparse_xbuffer;
421 std::vector<Index_> sparse_ibuffer;
422 auto fill_datasets_from_dense = [&](const Value_* extracted, Index_ n) -> void {
423 sparse_xbuffer.clear();
424 sparse_ibuffer.clear();
425 for (Index_ i = 0; i < n; ++i) {
426 if (extracted[i]) {
427 sparse_xbuffer.push_back(extracted[i]);
428 sparse_ibuffer.push_back(i);
429 }
430 }
431
432 hsize_t count = sparse_xbuffer.size();
433 fill_datasets(sparse_xbuffer.data(), sparse_ibuffer.data(), count);
434 };
435
436 if (layout == WriteStorageLayout::ROW) {
437 ptrs.resize(sanisizer::sum<decltype(ptrs.size())>(NR, 1));
439 auto wrk = tatami::consecutive_extractor<false>(mat, true, static_cast<Index_>(0), NR);
440 for (Index_ r = 0; r < NR; ++r) {
441 auto extracted = wrk->fetch(r, dbuffer.data());
442 fill_datasets_from_dense(extracted, NC);
443 ptrs[r + 1] = offset;
444 }
445
446 } else {
447 ptrs.resize(sanisizer::sum<decltype(ptrs.size())>(NC, 1));
449 auto wrk = tatami::consecutive_extractor<false>(mat, false, static_cast<Index_>(0), NC);
450 for (Index_ c = 0; c < NC; ++c) {
451 auto extracted = wrk->fetch(c, dbuffer.data());
452 fill_datasets_from_dense(extracted, NR);
453 ptrs[c + 1] = offset;
454 }
455 }
456 }
457
458 // Saving the pointers.
459 auto ptr_len = sanisizer::cast<hsize_t>(ptrs.size());
460 H5::DataSet ptr_ds = create_1d_compressed_hdf5_dataset(
461 location,
462 choose_ptr_type(params.ptr_type, ptrs.back()),
463 ptr_name,
464 ptr_len,
465 params.deflate_level,
466 params.chunk_size
467 );
468 H5::DataSpace ptr_space(1, &ptr_len);
469 ptr_ds.write(ptrs.data(), H5::PredType::NATIVE_HSIZE, ptr_space);
470
471 return;
472}
473
474inline H5::DataSet create_1d_compressed_hdf5_dataset(H5::Group& location, WriteStorageType type, const std::string& name, int deflate_level, hsize_t chunk) {
475 const hsize_t length = 0;
476 constexpr auto copy = H5S_UNLIMITED; // can't directly take an address to this, guess it's a macro.
477 H5::DataSpace dspace(1, &length, &copy);
478 H5::DSetCreatPropList plist;
479 plist.setDeflate(deflate_level); // extensible datasets must be chunked.
480 plist.setChunk(1, &chunk);
481 const auto dtype = choose_pred_type(type);
482 return location.createDataSet(name, *dtype, dspace, plist);
483}
484
485template<typename Value_, typename Index_>
486void write_compressed_sparse_matrix_one_pass(
488 H5::Group& location,
489 const WriteStorageLayout layout,
490 const std::string& data_name,
491 const std::string& index_name,
492 const std::string& ptr_name,
493 const WriteCompressedSparseMatrixOptions& params
494){
495 const auto requested_dtype = *(params.data_type);
496 const auto requested_itype = *(params.index_type);
497 H5::DataSet data_ds = create_1d_compressed_hdf5_dataset(location, requested_dtype, data_name, params.deflate_level, params.chunk_size);
498 H5::DataSet index_ds = create_1d_compressed_hdf5_dataset(location, requested_itype, index_name, params.deflate_level, params.chunk_size);
499
500 hsize_t offset = 0;
501 H5::DataSpace outspace;
502 const auto& dstype = define_mem_type<Value_>();
503 const auto& ixtype = define_mem_type<Index_>();
504
505 const Index_ NR = mat.nrow(), NC = mat.ncol();
506 std::vector<hsize_t> ptrs;
507
508 auto fill_datasets = [&](const Value_* vptr, const Index_* iptr, hsize_t count, H5::DataSpace& inspace) -> void {
509 if (count) {
510 // We need to check this because we don't know that the number of non-zeros fits in a hsize_t.
511 const hsize_t new_size = sanisizer::sum<hsize_t>(offset, count);
512 data_ds.extend(&new_size);
513 index_ds.extend(&new_size);
514
515 constexpr hsize_t zero = 0;
516 inspace.selectHyperslab(H5S_SELECT_SET, &count, &zero);
517 outspace.setExtentSimple(1, &new_size);
518 outspace.selectHyperslab(H5S_SELECT_SET, &count, &offset);
519
520 data_ds.write(vptr, dstype, inspace, outspace);
521 index_ds.write(iptr, ixtype, inspace, outspace);
522 offset = new_size;
523 }
524 };
525
526 if (mat.sparse()) {
527 auto fill_datasets_from_sparse = [&](const Value_* vptr, const Index_* iptr, Index_ n, H5::DataSpace& inspace) -> void {
528 for (Index_ i = 0; i < n; ++i) {
529 check_data_value_fit(requested_dtype, vptr[i]);
530 does_non_negative_integer_fit(requested_itype, iptr[i]);
531 }
532 // We need to check this because we don't even know that the dimension extent fits in a hsize_t.
533 const auto count = sanisizer::cast<hsize_t>(n);
534 fill_datasets(vptr, iptr, count, inspace);
535 };
536
537 if (layout == WriteStorageLayout::ROW) {
538 ptrs.resize(sanisizer::sum<decltype(ptrs.size())>(NR, 1));
541 const hsize_t extent = NC;
542 H5::DataSpace inspace(1, &extent);
543
544 auto wrk = tatami::consecutive_extractor<true>(mat, true, static_cast<Index_>(0), NR);
545 for (Index_ r = 0; r < NR; ++r) {
546 auto extracted = wrk->fetch(r, xbuffer.data(), ibuffer.data());
547 fill_datasets_from_sparse(extracted.value, extracted.index, extracted.number, inspace);
548 ptrs[r + 1] = offset;
549 }
550
551 } else {
552 ptrs.resize(sanisizer::sum<decltype(ptrs.size())>(NC, 1));
555 const hsize_t extent = NR;
556 H5::DataSpace inspace(1, &extent);
557
558 auto wrk = tatami::consecutive_extractor<true>(mat, false, static_cast<Index_>(0), NC);
559 for (Index_ c = 0; c < NC; ++c) {
560 auto extracted = wrk->fetch(c, xbuffer.data(), ibuffer.data());
561 fill_datasets_from_sparse(extracted.value, extracted.index, extracted.number, inspace);
562 ptrs[c + 1] = offset;
563 }
564 }
565
566 } else {
567 std::vector<Value_> sparse_xbuffer;
568 std::vector<Index_> sparse_ibuffer;
569 auto fill_datasets_from_dense = [&](const Value_* extracted, Index_ n, H5::DataSpace& inspace) -> void {
570 sparse_xbuffer.clear();
571 sparse_ibuffer.clear();
572 for (Index_ i = 0; i < n; ++i) {
573 if (extracted[i]) {
574 check_data_value_fit(requested_dtype, extracted[i]);
575 sparse_xbuffer.push_back(extracted[i]);
576 does_non_negative_integer_fit(requested_itype, i);
577 sparse_ibuffer.push_back(i);
578 }
579 }
580
581 const auto count = sanisizer::cast<hsize_t>(sparse_xbuffer.size());
582 fill_datasets(sparse_xbuffer.data(), sparse_ibuffer.data(), count, inspace);
583 };
584
585 if (layout == WriteStorageLayout::ROW) {
586 ptrs.resize(sanisizer::sum<decltype(ptrs.size())>(NR, 1));
588 const hsize_t extent = NC;
589 H5::DataSpace inspace(1, &extent);
590
591 auto wrk = tatami::consecutive_extractor<false>(mat, true, static_cast<Index_>(0), NR);
592 for (Index_ r = 0; r < NR; ++r) {
593 auto extracted = wrk->fetch(r, dbuffer.data());
594 fill_datasets_from_dense(extracted, NC, inspace);
595 ptrs[r + 1] = offset;
596 }
597
598 } else {
599 ptrs.resize(sanisizer::sum<decltype(ptrs.size())>(NC, 1));
601 const hsize_t extent = NR;
602 H5::DataSpace inspace(1, &extent);
603
604 auto wrk = tatami::consecutive_extractor<false>(mat, false, static_cast<Index_>(0), NC);
605 for (Index_ c = 0; c < NC; ++c) {
606 auto extracted = wrk->fetch(c, dbuffer.data());
607 fill_datasets_from_dense(extracted, NR, inspace);
608 ptrs[c + 1] = offset;
609 }
610 }
611 }
612
613 // Saving the pointers.
614 auto ptr_len = sanisizer::cast<hsize_t>(ptrs.size());
615 H5::DataSet ptr_ds = create_1d_compressed_hdf5_dataset(
616 location,
617 choose_ptr_type(params.ptr_type, ptrs.back()),
618 ptr_name,
619 ptr_len,
620 params.deflate_level,
621 params.chunk_size
622 );
623 H5::DataSpace ptr_space(1, &ptr_len);
624 ptr_ds.write(ptrs.data(), H5::PredType::NATIVE_HSIZE, ptr_space);
625}
643template<typename Value_, typename Index_>
645 // Choosing the layout.
646 WriteStorageLayout layout;
647 if (params.columnar.has_value()) {
648 layout = *(params.columnar);
649 } else {
650 if (mat.prefer_rows()) {
651 layout = WriteStorageLayout::ROW;
652 } else {
653 layout = WriteStorageLayout::COLUMN;
654 }
655 }
656
657 // Choosing the names.
658 std::string data_name;
659 if (params.data_name.has_value()) {
660 data_name = *(params.data_name);
661 } else {
662 data_name = "data";
663 }
664
665 std::string index_name;
666 if (params.index_name.has_value()) {
667 index_name = *(params.index_name);
668 } else {
669 index_name = "indices";
670 }
671
672 std::string ptr_name;
673 if (params.ptr_name.has_value()) {
674 ptr_name = *(params.ptr_name);
675 } else {
676 ptr_name = "indptr";
677 }
678
679 // Only executing a one-pass strategy if the types are already known.
680 if (params.two_pass || !params.data_type.has_value() || !params.index_type.has_value()) {
681 write_compressed_sparse_matrix_two_pass(mat, location, layout, data_name, index_name, ptr_name, params);
682 } else {
683 write_compressed_sparse_matrix_one_pass(mat, location, layout, data_name, index_name, ptr_name, params);
684 }
685}
686
696template<typename Value_, typename Index_>
699 write_compressed_sparse_matrix(mat, location, params);
700 return;
701}
702
706template<typename Value_, typename Index_>
707void write_compressed_sparse_matrix(const tatami::Matrix<Value_, Index_>* mat, H5::Group& location, const WriteCompressedSparseMatrixOptions& params) {
708 return write_compressed_sparse_matrix(*mat, location, params);
709}
710
711template<typename Value_, typename Index_>
712void write_compressed_sparse_matrix(const tatami::Matrix<Value_, Index_>* mat, H5::Group& location) {
713 return write_compressed_sparse_matrix(*mat, location);
714}
719}
720
721#endif
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 &params)
Definition write_compressed_sparse_matrix.hpp:644
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)
const Value_ * value
const Index_ * index
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:94
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:87
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:100
Utilities for HDF5 extraction.