chihaya
Validating delayed array operations in HDF5
Loading...
Searching...
No Matches
sparse_matrix.hpp
Go to the documentation of this file.
1#ifndef CHIHAYA_SPARSE_MATRIX_HPP
2#define CHIHAYA_SPARSE_MATRIX_HPP
3
4#include "H5Cpp.h"
5#include "ritsuko/ritsuko.hpp"
6
7#include <vector>
8#include <stdexcept>
9#include <cstdint>
10#include <cstddef>
11#include <string>
12
13#include "utils_public.hpp"
14#include "utils_misc.hpp"
15#include "utils_type.hpp"
16#include "utils_dimensions.hpp"
17
23namespace chihaya {
24
28template<typename Index_>
29void validate_sparse_indices(
30 const H5::DataSet& ihandle,
31 const std::vector<std::uint64_t>& indptrs,
32 const std::size_t primary,
33 const std::size_t secondary,
34 const bool csc,
35 const hsize_t contiguous_chunk_size
36) {
37 ritsuko::hdf5::Stream1dNumericDataset<Index_> stream(
38 &ihandle,
39 sanisizer::cast<hsize_t>(indptrs.back()),
40 [&]{
41 ritsuko::hdf5::Stream1dNumericDatasetOptions opt;
42 opt.contiguous_chunk_size = contiguous_chunk_size;
43 return opt;
44 }()
45 );
46 auto buffer = sanisizer::create<std::vector<Index_> >(stream.chunk_size());
47
48 hsize_t available = 0, at = 0;
49 auto next = [&]() -> Index_ {
50 if (at == available) {
51 at = 0;
52 available = stream.load(buffer.data());
53 }
54 return buffer[at++];
55 };
56
57 for (std::size_t p = 0; p < primary; ++p) {
58 const auto start = indptrs[p];
59 const auto end = indptrs[p + 1];
60 if (start > end) {
61 throw std::runtime_error("entries of 'indptr' must be sorted");
62 }
63 if (start == end) {
64 continue;
65 }
66
67 Index_ previous = next();
68 if (previous < 0) {
69 throw std::runtime_error("entries of 'indices' should be non-negative");
70 }
71
72 // If it's sorted in strictly increasing order, we only need to check the first entry for negative values.
73 // Similarly, we only need to check the last entry for whether it exceeds the secondary limit.
74 for (I<decltype(start)> x = start + 1; x < end; ++x) {
75 const auto i = next();
76 if (i <= previous) {
77 throw std::runtime_error("'indices' should be strictly increasing within each " + (csc ? std::string("column") : std::string("row")));
78 }
79 previous = i;
80 }
81
82 if (sanisizer::is_greater_than_or_equal(previous, secondary)) {
83 throw std::runtime_error("entries of 'indices' should be less than the number of " + (csc ? std::string("row") : std::string("column")) + "s");
84 }
85 }
86}
99inline ArrayDetails validate_sparse_matrix(const H5::Group& group, const ritsuko::Version& version, const Options& options) {
100 std::vector<std::size_t> dims;
101 ArrayType array_type;
102
103 {
104 auto shandle = group.openDataSet("shape");
105 auto sspace = shandle.getSpace();
106 if (sspace.getSimpleExtentNdims() != 1) {
107 throw std::runtime_error("'shape' dataset should be 1-dimensional");
108 }
109 hsize_t len;
110 sspace.getSimpleExtentDims(&len);
111 if (len != 2) {
112 throw std::runtime_error("'shape' dataset should have length 2");
113 }
114
115 if (version.lt(1, 1, 0)) {
116 dims = load_non_negative_integer_vector_0_99<std::size_t>(shandle, 2);
117 } else {
118 if (ritsuko::hdf5::exceeds_integer_limit(shandle, 64, false)) {
119 throw std::runtime_error("'shape' should have a datatype that can fit into a 64-bit unsigned integer");
120 }
121 dims = load_dimensions_from_uint64_contents<std::size_t>(shandle, 2);
122 }
123 }
124
125 hsize_t nnz;
126 {
127 auto dhandle = group.openDataSet("data");
128 auto dspace = dhandle.getSpace();
129 if (dspace.getSimpleExtentNdims() != 1) {
130 throw std::runtime_error("'data' dataset should be 1-dimensional");
131 }
132 dspace.getSimpleExtentDims(&nnz);
133
134 if (version.lt(1, 1, 0)) {
135 array_type = translate_type_0_99(dhandle.getTypeClass());
136 if (is_boolean_0_99(dhandle)) {
137 array_type = BOOLEAN;
138 }
139 } else {
140 auto type = load_scalar_string_attribute(dhandle, "type");
141 array_type = translate_type_1_1(type);
142 if (!options.details_only) {
143 check_type_1_1(dhandle, array_type);
144 }
145 }
146
147 if (!options.details_only) {
148 if (array_type != INTEGER && array_type != BOOLEAN && array_type != FLOAT) {
149 throw std::runtime_error("dataset should be integer, float or boolean");
150 }
151 validate_missing_placeholder(dhandle, version);
152 }
153 }
154
155 if (!options.details_only) {
156 bool csc = true;
157 if (!version.lt(1, 1, 0)) {
158 auto bhandle = group.openDataSet("by_column");
159 if (bhandle.getSpace().getSimpleExtentNdims() != 0) {
160 throw std::runtime_error("'by_column' dataset should be scalar");
161 }
162 if (ritsuko::hdf5::exceeds_integer_limit(bhandle, 8, true)) {
163 throw std::runtime_error("datatype of the 'by_column' dataset should fit into an 8-bit signed integer");
164 }
165 std::int8_t val;
166 bhandle.read(&val, H5::PredType::NATIVE_INT8);
167 csc = (val != 0);
168 }
169
170 const auto primary = (csc ? dims[1] : dims[0]);
171 const auto secondary = (csc ? dims[0] : dims[1]);
172
173 std::vector<std::uint64_t> indptrs;
174 {
175 auto iphandle = group.openDataSet("indptr");
176 auto ipspace = iphandle.getSpace();
177 if (ipspace.getSimpleExtentNdims() != 1) {
178 throw std::runtime_error("'indptr' dataset should be 1-dimensional");
179 }
180 hsize_t iplen;
181 ipspace.getSimpleExtentDims(&iplen);
182
183 if (iplen == 0 || !sanisizer::is_equal(iplen - 1, primary)) { // avoid risk of potential overflow with primary + 1.
184 throw std::runtime_error("'indptr' should have length equal to the number of " + (csc ? std::string("columns") : std::string("rows")) + " plus 1");
185 }
186
187 if (version.lt(1, 1, 0)) {
188 if (iphandle.getTypeClass() != H5T_INTEGER) {
189 throw std::runtime_error("'indptr' should be integer");
190 }
191 indptrs = load_non_negative_integer_vector_0_99<std::uint64_t>(iphandle, iplen);
192 } else {
193 if (ritsuko::hdf5::exceeds_integer_limit(iphandle, 64, false)) {
194 throw std::runtime_error("datatype of 'indptr' should fit into a 64-bit unsigned integer");
195 }
196 sanisizer::resize(indptrs, iplen);
197 iphandle.read(indptrs.data(), H5::PredType::NATIVE_UINT64);
198 }
199
200 iphandle.read(indptrs.data(), H5::PredType::NATIVE_UINT64);
201 if (indptrs[0] != 0) {
202 throw std::runtime_error("first entry of 'indptr' should be 0");
203 }
204 if (!sanisizer::is_equal(indptrs.back(), nnz)) {
205 throw std::runtime_error("last entry of 'indptr' should be equal to the length of 'data'");
206 }
207 }
208
209 {
210 auto ihandle = group.openDataSet("indices");
211 auto ispace = ihandle.getSpace();
212 if (ispace.getSimpleExtentNdims() != 1) {
213 throw std::runtime_error("'indices' dataset should be 1-dimensional");
214 }
215 hsize_t inum;
216 ispace.getSimpleExtentDims(&inum);
217 if (nnz != inum) {
218 throw std::runtime_error("'indices' and 'data' should have the same length");
219 }
220
221 if (version.lt(1, 1, 0)) {
222 if (!ritsuko::hdf5::exceeds_integer_limit(ihandle, 64, true)) {
223 validate_sparse_indices<std::int64_t>(ihandle, indptrs, primary, secondary, csc, options.contiguous_chunk_size);
224 } else if (!ritsuko::hdf5::exceeds_integer_limit(ihandle, 64, false)) {
225 validate_sparse_indices<std::uint64_t>(ihandle, indptrs, primary, secondary, csc, options.contiguous_chunk_size);
226 } else {
227 throw std::runtime_error("'indices' should be integer");
228 }
229
230 } else {
231 if (ritsuko::hdf5::exceeds_integer_limit(ihandle, 64, false)) {
232 throw std::runtime_error("datatype of 'indices' should fit into a 64-bit unsigned integer");
233 }
234 validate_sparse_indices<std::uint64_t>(ihandle, indptrs, primary, secondary, csc, options.contiguous_chunk_size);
235 }
236 }
237
238 // Validating dimnames.
239 if (group.exists("dimnames")) {
240 validate_dimnames_internal(group, dims, version, options.contiguous_chunk_size);
241 }
242 }
243
244 return ArrayDetails(array_type, std::move(dims));
245}
246
247}
248
249#endif
Namespace for all chihaya functions.
Definition binary_arithmetic.hpp:20
ArrayDetails validate_sparse_matrix(const H5::Group &group, const ritsuko::Version &version, const Options &options)
Definition sparse_matrix.hpp:99
ArrayType
Definition utils_public.hpp:29
Container_ create(Value_ x, Args_ &&... args)
void resize(Container_ &container, Value_ x, Args_ &&... args)
constexpr bool is_equal(Left_ left, Right_ right)
constexpr bool is_greater_than_or_equal(Left_ left, Right_ right)
constexpr Dest_ cast(Value_ x)
Details about an array.
Definition utils_public.hpp:38
Options for validate().
Definition utils_public.hpp:79
bool details_only
Definition utils_public.hpp:84
hsize_t contiguous_chunk_size
Definition utils_public.hpp:90
Various public utilities.