42 const std::string type_name =
"multi_sample_dataset";
45 const auto& dmap = extract_json_object(metadata.
other, type_name);
47 const std::string& vstring = extract_json_version_string(dmap);
48 auto version = ritsuko::parse_version_string(vstring.c_str(), vstring.size(),
true);
49 if (version.major != 1) {
50 throw std::runtime_error(
"unsupported version string '" + vstring +
"'");
53 std::throw_with_nested(std::runtime_error(
"failed to read 'version'"));
56 std::throw_with_nested(std::runtime_error(
"failed to read '" + type_name +
"' in object metadata"));
60 std::size_t num_samples;
62 auto sd_path = path /
"sample_data";
65 throw std::runtime_error(
"object in 'sample_data' should satisfy the 'DATA_FRAME' interface");
68 num_samples =
height(sd_path, sdmeta, options);
70 std::throw_with_nested(std::runtime_error(
"failed to validate 'sample_data'"));
74 std::vector<size_t> num_columns;
75 auto edir = path /
"experiments";
76 if (std::filesystem::exists(edir)) {
78 const auto num_experiments = [&]{
80 return extract_summarized_experiment_names(edir /
"names.json").size();
82 std::throw_with_nested(std::runtime_error(
"failed to read 'names.json'"));
86 num_columns.reserve(num_experiments);
87 for (I<
decltype(num_experiments)> e = 0; e < num_experiments; ++e) {
88 auto ename = ritsuko::itoa(e);
89 auto epath = edir / ename;
93 throw std::runtime_error(
"object should satisfy the 'SUMMARIZED_EXPERIMENT' interface");
97 num_columns.push_back(dims[1]);
99 std::throw_with_nested(std::runtime_error(
"failed to validate experiment " + ename));
103 const auto num_dir_obj = count_directory_entries(edir);
104 if (!sanisizer::is_equal(num_dir_obj - 1, num_experiments)) {
105 throw std::runtime_error(
"more objects than expected inside the subdirectory");
108 std::throw_with_nested(std::runtime_error(
"failed to validate 'experiments'"));
113 const auto mappath = path /
"sample_map.h5";
114 if (!num_columns.empty() || std::filesystem::exists(mappath)) {
116 H5::H5File handle(mappath, H5F_ACC_RDONLY);
117 auto ghandle = handle.openGroup(type_name);
119 const auto num_experiments = num_columns.size();
120 for (I<
decltype(num_experiments)> e = 0; e < num_experiments; ++e) {
121 auto ename = ritsuko::itoa(e);
123 auto dhandle = ghandle.openDataSet(ename);
124 if (ritsuko::hdf5::exceeds_integer_limit(dhandle, 64,
false)) {
125 throw std::runtime_error(
"expected a datatype that fits into a 64-bit unsigned integer");
128 auto dspace = dhandle.getSpace();
129 if (dspace.getSimpleExtentNdims() != 1) {
130 throw std::runtime_error(
"expected a 1-dimensional dataset");
133 dspace.getSimpleExtentDims(&len);
134 if (!sanisizer::is_equal(len, num_columns[e])) {
135 throw std::runtime_error(
"dataset extent should equal the number of columns of 'experiments/" + ename +
"'");
138 ritsuko::hdf5::Stream1dNumericDataset<std::uint64_t> stream(
142 ritsuko::hdf5::Stream1dNumericDatasetOptions opt;
147 iterate_stream<std::uint64_t>(
149 [&](hsize_t, std::uint64_t val) ->
void {
150 if (sanisizer::is_greater_than_or_equal(val, num_samples)) {
151 throw std::runtime_error(
"entries should be less than the number of samples");
156 std::throw_with_nested(std::runtime_error(
"failed to validate mapping for experiment " + ename));
160 if (!sanisizer::is_equal(num_columns.size(), ghandle.getNumObjs())) {
161 throw std::runtime_error(
"more objects present in the 'multi_sample_dataset' group than expected");
164 std::throw_with_nested(std::runtime_error(
"failed to validate the sample mapping in 'sample_map.h5'"));
168 validate_metadata(path,
"other_data", options);