32 const std::string type_name =
"compressed_sparse_matrix";
35 const auto& type_meta = extract_json_object(metadata.
other, type_name);
37 const auto& vstring = extract_json_version_string(type_meta);
38 auto version = ritsuko::parse_version_string(vstring.c_str(), vstring.size(),
true);
39 if (version.major != 1) {
40 throw std::runtime_error(
"unsupported version '" + vstring +
"'");
43 std::throw_with_nested(std::runtime_error(
"failed to validate 'version'"));
46 std::throw_with_nested(std::runtime_error(
"failed to validate '" + type_name +
"' in the object metadata"));
50 H5::H5File handle(path /
"matrix.h5", H5F_ACC_RDONLY);
51 auto ghandle = handle.openGroup(type_name);
55 auto thandle = ghandle.openAttribute(
"type");
56 type = open_and_load_scalar_string_attribute(thandle);
58 std::throw_with_nested(std::runtime_error(
"failed to validate the 'type' attribute"));
63 auto lhandle = ghandle.openAttribute(
"layout");
64 auto layout = open_and_load_scalar_string_attribute(lhandle);
65 if (layout ==
"CSC") {
67 }
else if (layout !=
"CSR") {
68 throw std::runtime_error(
"'layout' should be either 'CSC' or 'CSR'");
71 std::throw_with_nested(std::runtime_error(
"failed to validate the 'layout' attribute"));
75 std::vector<std::uint64_t> shape(2);
77 auto shandle = ghandle.openDataSet(
"shape");
78 if (ritsuko::hdf5::exceeds_integer_limit(shandle, 64,
false)) {
79 throw std::runtime_error(
"expected datatype to be a subset of a 64-bit unsigned integer");
82 auto sspace = shandle.getSpace();
83 if (sspace.getSimpleExtentNdims() != 1) {
84 throw std::runtime_error(
"expected a 1-dimensional dataset");
87 sspace.getSimpleExtentDims(&len);
89 throw std::runtime_error(
"expected a dataset of length 2");
92 shandle.read(shape.data(), H5::PredType::NATIVE_UINT64);
94 std::throw_with_nested(std::runtime_error(
"failed to validate 'shape'"));
100 auto dhandle = ghandle.openDataSet(
"data");
102 auto dspace = dhandle.getSpace();
103 if (dspace.getSimpleExtentNdims() != 1) {
104 throw std::runtime_error(
"expected a 1-dimensional dataset");
106 dspace.getSimpleExtentDims(&num_nonzero);
108 if (type ==
"integer") {
109 if (ritsuko::hdf5::exceeds_integer_limit(dhandle, 32,
true)) {
110 throw std::runtime_error(
"expected datatype to fit inside a 32-bit signed integer");
112 }
else if (type ==
"boolean") {
113 if (ritsuko::hdf5::exceeds_integer_limit(dhandle, 32,
true)) {
114 throw std::runtime_error(
"expected datatype to fit inside a 32-bit signed integer");
116 }
else if (type ==
"number") {
117 if (ritsuko::hdf5::exceeds_float_limit(dhandle, 64)) {
118 throw std::runtime_error(
"expected datatype to fit inside a 64-bit float");
121 throw std::runtime_error(
"unknown matrix type '" + type +
"'");
124 validate_numeric_missing_placeholder(dhandle,
"missing-value-placeholder");
126 std::throw_with_nested(std::runtime_error(
"failed to validate 'data'"));
130 const auto primary_dim = (is_csr ? shape[0] : shape[1]);
131 std::vector<std::uint64_t> indptrs(sanisizer::sum<
typename std::vector<std::uint64_t>::size_type>(primary_dim, 1));
133 auto phandle = ghandle.openDataSet(
"indptr");
134 if (ritsuko::hdf5::exceeds_integer_limit(phandle, 64,
false)) {
135 throw std::runtime_error(
"expected datatype to be a subset of a 64-bit unsigned integer");
138 auto pspace = phandle.getSpace();
139 if (pspace.getSimpleExtentNdims() != 1) {
140 throw std::runtime_error(
"expected a 1-dimensional dataset");
143 pspace.getSimpleExtentDims(&numptrs);
144 if (!sanisizer::is_equal(numptrs, indptrs.size())) {
145 auto dimname = (is_csr ? std::string(
"rows") : std::string(
"columns"));
146 throw std::runtime_error(
"dataset should have length equal to the number of " + dimname +
" plus 1");
149 phandle.read(indptrs.data(), H5::PredType::NATIVE_UINT64);
150 if (indptrs[0] != 0) {
151 throw std::runtime_error(
"first entry should be zero");
153 if (!sanisizer::is_equal(indptrs.back(), num_nonzero)) {
154 throw std::runtime_error(
"last entry should equal the number of non-zero elements");
157 for (I<
decltype(primary_dim)> i = 1; i < primary_dim; ++i) {
158 if (indptrs[i] < indptrs[i-1]) {
159 throw std::runtime_error(
"pointers should be sorted in increasing order");
163 std::throw_with_nested(std::runtime_error(
"failed to validate 'indptr'"));
168 auto ihandle = ghandle.openDataSet(
"indices");
169 if (ritsuko::hdf5::exceeds_integer_limit(ihandle, 64,
false)) {
170 throw std::runtime_error(
"expected datatype to be a subset of a 64-bit unsigned integer");
173 auto ispace = ihandle.getSpace();
174 if (ispace.getSimpleExtentNdims() != 1) {
175 throw std::runtime_error(
"expected a 1-dimensional dataset");
178 ispace.getSimpleExtentDims(&len);
179 if (num_nonzero != len) {
180 throw std::runtime_error(
"length of dataset should be equal to the number of non-zero elements");
183 StreamIterator<std::uint64_t> indstream(
184 ritsuko::hdf5::Stream1dNumericDataset<std::uint64_t>(
188 ritsuko::hdf5::Stream1dNumericDatasetOptions opt;
195 const auto secondary_dim = (is_csr ? shape[1] : shape[0]);
196 for (I<
decltype(primary_dim)> i = 0; i < primary_dim; ++i) {
197 const auto start = indptrs[i];
198 const auto end = indptrs[i + 1];
203 auto previous = indstream.next();
204 for (I<
decltype(start)> j = start + 1; j < end; ++j) {
205 auto i = indstream.next();
207 auto dimname = (is_csr ? std::string(
"rows") : std::string(
"columns"));
208 throw std::runtime_error(
"entries should be strictly increasing within each " + dimname);
213 if (sanisizer::is_greater_than_or_equal(previous, secondary_dim)) {
214 auto dimname = (is_csr ? std::string(
"columns") : std::string(
"rows"));
215 throw std::runtime_error(
"entries should be less than the number of " + dimname);
219 std::throw_with_nested(std::runtime_error(
"failed to validate 'indices'"));
222 validate_array_dimnames(ghandle,
"names", shape, options);
224 std::throw_with_nested(std::runtime_error(
"failed to validate '" + type_name +
"' in 'matrix.h5'"));