tatami_test
Utilities for testing tatami libraries
Loading...
Searching...
No Matches
test_access.hpp
Go to the documentation of this file.
1#ifndef TATAMI_TEST_TEST_ACCESS_HPP
2#define TATAMI_TEST_TEST_ACCESS_HPP
3
4#include <gtest/gtest.h>
5
9
10#include "fetch.hpp"
12
13#include <vector>
14#include <limits>
15#include <random>
16#include <cmath>
17#include <memory>
18#include <cstdint>
19
25namespace tatami_test {
26
34enum class TestAccessOrder : char { FORWARD, REVERSE, RANDOM };
35
43 bool use_oracle = false;
44
49 bool use_row = true;
50
54 TestAccessOrder order = TestAccessOrder::FORWARD;
55
59 int jump = 1;
60
64 bool check_sparse = true;
65};
66
71typedef std::tuple<bool, bool, TestAccessOrder, int> StandardTestAccessOptions;
72
81 TestAccessOptions output;
82 output.use_row = std::get<0>(x);
83 output.use_oracle = std::get<1>(x);
84 output.order = std::get<2>(x);
85 output.jump = std::get<3>(x);
86 return output;
87}
88
94 return ::testing::Combine(
95 ::testing::Values(true, false), /* whether to access the rows. */
96 ::testing::Values(true, false), /* whether to use an oracle. */
97 ::testing::Values(TestAccessOrder::FORWARD, TestAccessOrder::REVERSE, TestAccessOrder::RANDOM), /* access order. */
98 ::testing::Values(1, 3) /* jump between rows/columns. */
99 );
100}
101
105namespace internal {
106
107template<typename Value_>
108void compare_vectors(const std::vector<Value_>& expected, const std::vector<Value_>& observed, const std::string& context) {
109 size_t n_expected = expected.size();
110 ASSERT_EQ(n_expected, observed.size()) << "mismatch in vector length (" << context << ")";
111 for (size_t i = 0; i < n_expected; ++i) {
112 auto expected_val = expected[i], observed_val = observed[i];
113 if (std::isnan(expected_val)) {
114 EXPECT_EQ(std::isnan(expected_val), std::isnan(observed_val)) << "mismatching NaNs at position " << i << " (" << context << ")";
115 } else {
116 EXPECT_EQ(expected_val, observed_val) << "different values at position " << i << " (" << context << ")";
117 }
118 }
119}
120
121template<typename Index_>
122uint64_t create_seed(Index_ NR, Index_ NC, const TestAccessOptions& options) {
123 uint64_t seed = static_cast<uint64_t>(NR) * static_cast<uint64_t>(NC);
124 seed += 13 * static_cast<uint64_t>(options.use_row);
125 seed += 57 * static_cast<uint64_t>(options.order);
126 seed += 101 * static_cast<uint64_t>(options.jump);
127 return seed;
128}
129
130template<typename Index_>
131std::vector<Index_> simulate_test_access_sequence(Index_ NR, Index_ NC, const TestAccessOptions& options) {
132 std::vector<Index_> sequence;
133 auto limit = (options.use_row ? NR : NC);
134
135 if (options.order == TestAccessOrder::REVERSE) {
136 for (int i = limit; i > 0; i -= options.jump) {
137 sequence.push_back(i - 1);
138 }
139 } else {
140 for (int i = 0; i < limit; i += options.jump) {
141 sequence.push_back(i);
142 }
143 if (options.order == TestAccessOrder::RANDOM) {
144 std::mt19937_64 rng(create_seed(NR, NC, options));
145 std::shuffle(sequence.begin(), sequence.end(), rng);
146 }
147 }
148
149 return sequence;
150}
151
152template<bool use_oracle_, typename Index_>
153tatami::MaybeOracle<use_oracle_, Index_> create_oracle(const std::vector<Index_>& sequence, const TestAccessOptions& options) {
154 if constexpr(use_oracle_) {
155 std::shared_ptr<tatami::Oracle<Index_> > oracle;
156 if (options.jump == 1 && options.order == TestAccessOrder::FORWARD) {
157 oracle.reset(new tatami::ConsecutiveOracle<Index_>(0, sequence.size()));
158 } else {
159 oracle.reset(new tatami::FixedViewOracle<Index_>(sequence.data(), sequence.size()));
160 }
161 return oracle;
162 } else {
163 return false;
164 }
165}
166
167template<bool use_oracle_, typename Value_, typename Index_, class SparseExpand_, typename ...Args_>
168void test_access_base(
169 const tatami::Matrix<Value_, Index_>& matrix,
170 const tatami::Matrix<Value_, Index_>& reference,
171 const TestAccessOptions& options,
172 Index_ extent,
173 SparseExpand_ sparse_expand,
174 Args_... args)
175{
176 auto NR = matrix.nrow();
177 ASSERT_EQ(NR, reference.nrow());
178 auto NC = matrix.ncol();
179 ASSERT_EQ(NC, reference.ncol());
180
181 auto refwork = (options.use_row ? reference.dense_row(args...) : reference.dense_column(args...));
182
183 auto sequence = simulate_test_access_sequence(NR, NC, options);
184 auto oracle = create_oracle<use_oracle_>(sequence, options);
185
186 auto pwork = tatami::new_extractor<false, use_oracle_>(&matrix, options.use_row, oracle, args...);
187 auto swork = tatami::new_extractor<true, use_oracle_>(&matrix, options.use_row, oracle, args...);
188
189 tatami::Options opt;
190 opt.sparse_extract_index = false;
191 auto swork_v = tatami::new_extractor<true, use_oracle_>(&matrix, options.use_row, oracle, args..., opt);
192
193 opt.sparse_extract_value = false;
194 auto swork_n = tatami::new_extractor<true, use_oracle_>(&matrix, options.use_row, oracle, args..., opt);
195
196 opt.sparse_extract_index = true;
197 auto swork_i = tatami::new_extractor<true, use_oracle_>(&matrix, options.use_row, oracle, args..., opt);
198
199 size_t sparse_counter = 0;
200
201 // Looping over rows/columns and checking extraction against the reference.
202 for (auto i : sequence) {
203 auto expected = fetch(*refwork, i, extent);
204
205 // Checking dense retrieval first.
206 {
207 auto observed = [&]() {
208 if constexpr(use_oracle_) {
209 return fetch(*pwork, extent);
210 } else {
211 return fetch(*pwork, i, extent);
212 }
213 }();
214 compare_vectors(expected, observed, "dense retrieval");
215 }
216
217 // Various flavors of sparse retrieval.
218 {
219 auto observed = [&]() {
220 if constexpr(use_oracle_) {
221 return fetch(*swork, extent);
222 } else {
223 return fetch(*swork, i, extent);
224 }
225 }();
226 compare_vectors(expected, sparse_expand(observed), "sparse retrieval");
227
228 sparse_counter += observed.value.size();
229 {
230 bool is_increasing = true;
231 for (size_t i = 1; i < observed.index.size(); ++i) {
232 if (observed.index[i] <= observed.index[i-1]) {
233 is_increasing = false;
234 break;
235 }
236 }
237 ASSERT_TRUE(is_increasing);
238 }
239
240 std::vector<Index_> indices(extent);
241 auto observed_i = [&]() {
242 if constexpr(use_oracle_) {
243 return swork_i->fetch(NULL, indices.data());
244 } else {
245 return swork_i->fetch(i, NULL, indices.data());
246 }
247 }();
248 ASSERT_TRUE(observed_i.value == NULL);
249 tatami::copy_n(observed_i.index, observed_i.number, indices.data());
250 indices.resize(observed_i.number);
251 ASSERT_EQ(observed.index, indices);
252
253 std::vector<Value_> values(extent);
254 auto observed_v = [&]() {
255 if constexpr(use_oracle_) {
256 return swork_v->fetch(values.data(), NULL);
257 } else {
258 return swork_v->fetch(i, values.data(), NULL);
259 }
260 }();
261 ASSERT_TRUE(observed_v.index == NULL);
262 tatami::copy_n(observed_v.value, observed_v.number, values.data());
263 values.resize(observed_v.number);
264 compare_vectors(values, observed.value, "sparse retrieval with values only");
265
266 auto observed_n = [&]() {
267 if constexpr(use_oracle_) {
268 return swork_n->fetch(NULL, NULL);
269 } else {
270 return swork_n->fetch(i, NULL, NULL);
271 }
272 }();
273 ASSERT_TRUE(observed_n.value == NULL);
274 ASSERT_TRUE(observed_n.index == NULL);
275 ASSERT_EQ(observed.value.size(), observed_n.number);
276 }
277 }
278
279 if (options.check_sparse && matrix.is_sparse()) {
280 EXPECT_TRUE(sparse_counter < static_cast<size_t>(NR) * static_cast<size_t>(NC));
281 }
282}
283
284template<bool use_oracle_, typename Value_, typename Index_>
286 const tatami::Matrix<Value_, Index_>& matrix,
287 const tatami::Matrix<Value_, Index_>& reference,
288 const TestAccessOptions& options)
289{
290 Index_ nsecondary = (options.use_row ? reference.ncol() : reference.nrow());
291 test_access_base<use_oracle_>(
292 matrix,
293 reference,
294 options,
295 nsecondary,
296 [&](const auto& svec) -> auto {
297 std::vector<Value_> output(nsecondary);
298 size_t nnz = svec.index.size();
299 for (size_t i = 0; i < nnz; ++i) {
300 output[svec.index[i]] = svec.value[i];
301 }
302 return output;
303 }
304 );
305}
306
307template<bool use_oracle_, typename Value_, typename Index_>
309 const tatami::Matrix<Value_, Index_>& matrix,
310 const tatami::Matrix<Value_, Index_>& reference,
311 double relative_start,
312 double relative_length,
313 const TestAccessOptions& options)
314{
315 Index_ nsecondary = (options.use_row ? reference.ncol() : reference.nrow());
316 Index_ start = nsecondary * relative_start;
317 Index_ length = nsecondary * relative_length;
318 test_access_base<use_oracle_>(
319 matrix,
320 reference,
321 options,
322 length,
323 [&](const auto& svec) -> auto {
324 std::vector<Value_> output(length);
325 size_t nnz = svec.index.size();
326 for (size_t i = 0; i < nnz; ++i) {
327 output[svec.index[i] - start] = svec.value[i];
328 }
329 return output;
330 },
331 start,
332 length
333 );
334}
335
336template<bool use_oracle_, typename Value_, typename Index_>
338 const tatami::Matrix<Value_, Index_>& matrix,
339 const tatami::Matrix<Value_, Index_>& reference,
340 double relative_start,
341 double probability,
342 const TestAccessOptions& options)
343{
344 Index_ nsecondary = (options.use_row ? reference.ncol() : reference.nrow());
345 auto index_ptr = create_indexed_subset(
346 nsecondary,
347 relative_start,
348 probability,
349 create_seed(matrix.nrow(), matrix.ncol(), options) + 999 * probability + 85 * relative_start
350 );
351
352 Index_ num_indices = index_ptr->size();
353 std::vector<size_t> reposition(nsecondary, -1);
354 {
355 const auto& indices = *index_ptr;
356 for (Index_ i = 0; i < num_indices; ++i) {
357 reposition[indices[i]] = i;
358 }
359 }
360
361 test_access_base<use_oracle_>(
362 matrix,
363 reference,
364 options,
365 num_indices,
366 [&](const auto& svec) -> auto {
367 std::vector<Value_> expected(num_indices);
368 size_t nnz = svec.index.size();
369 for (size_t i = 0; i < nnz; ++i) {
370 expected[reposition[svec.index[i]]] = svec.value[i];
371 }
372 return expected;
373 },
374 std::move(index_ptr)
375 );
376}
377
378}
395template<typename Value_, typename Index_>
397 const tatami::Matrix<Value_, Index_>& matrix,
398 const tatami::Matrix<Value_, Index_>& reference,
399 const TestAccessOptions& options)
400{
401 if (options.use_oracle) {
402 internal::test_full_access<true>(matrix, reference, options);
403 } else {
404 internal::test_full_access<false>(matrix, reference, options);
405 }
406}
407
426template<typename Value_, typename Index_>
428 const tatami::Matrix<Value_, Index_>& matrix,
429 const tatami::Matrix<Value_, Index_>& reference,
430 double relative_start,
431 double relative_length,
432 const TestAccessOptions& options)
433{
434 if (options.use_oracle) {
435 internal::test_block_access<true>(matrix, reference, relative_start, relative_length, options);
436 } else {
437 internal::test_block_access<false>(matrix, reference, relative_start, relative_length, options);
438 }
439}
440
459template<typename Value_, typename Index_>
461 const tatami::Matrix<Value_, Index_>& matrix,
462 const tatami::Matrix<Value_, Index_>& reference,
463 double relative_start,
464 double probability,
465 const TestAccessOptions& options)
466{
467 if (options.use_oracle) {
468 internal::test_indexed_access<true>(matrix, reference, relative_start, probability, options);
469 } else {
470 internal::test_indexed_access<false>(matrix, reference, relative_start, probability, options);
471 }
472}
473
486template<typename Value_, typename Index_>
488 TestAccessOptions options;
489 options.use_row = false;
490 test_full_access(matrix, reference, options);
491}
492
505template<typename Value_, typename Index_>
507 TestAccessOptions options;
508 options.use_row = false;
509 test_full_access(matrix, reference, options);
510}
511
512}
513
514#endif
std::unique_ptr< MyopicDenseExtractor< Value_, Index_ > > dense_row(const Options &opt) const
virtual Index_ ncol() const=0
std::unique_ptr< MyopicDenseExtractor< Value_, Index_ > > dense_column(const Options &opt) const
virtual Index_ nrow() const=0
virtual bool is_sparse() const=0
Create an indexed subset of dimension elements.
Fetch a row/column into a vector.
Utilities for testing tatami libraries.
Definition create_indexed_subset.hpp:15
void test_block_access(const tatami::Matrix< Value_, Index_ > &matrix, const tatami::Matrix< Value_, Index_ > &reference, double relative_start, double relative_length, const TestAccessOptions &options)
Definition test_access.hpp:427
std::tuple< bool, bool, TestAccessOrder, int > StandardTestAccessOptions
Definition test_access.hpp:71
TestAccessOrder
Definition test_access.hpp:34
void test_indexed_access(const tatami::Matrix< Value_, Index_ > &matrix, const tatami::Matrix< Value_, Index_ > &reference, double relative_start, double probability, const TestAccessOptions &options)
Definition test_access.hpp:460
void test_simple_row_access(const tatami::Matrix< Value_, Index_ > &matrix, const tatami::Matrix< Value_, Index_ > &reference)
Definition test_access.hpp:506
void test_simple_column_access(const tatami::Matrix< Value_, Index_ > &matrix, const tatami::Matrix< Value_, Index_ > &reference)
Definition test_access.hpp:487
tatami::VectorPtr< Index_ > create_indexed_subset(Index_ extent, double relative_start, double probability, uint64_t seed)
Definition create_indexed_subset.hpp:35
TestAccessOptions convert_test_access_options(const StandardTestAccessOptions &x)
Definition test_access.hpp:80
std::vector< Value_ > fetch(tatami::MyopicDenseExtractor< Value_, Index_ > &ext, Index_ i, size_t number)
Definition fetch.hpp:46
auto standard_test_access_options_combinations()
Definition test_access.hpp:93
void test_full_access(const tatami::Matrix< Value_, Index_ > &matrix, const tatami::Matrix< Value_, Index_ > &reference, const TestAccessOptions &options)
Definition test_access.hpp:396
typename std::conditional< oracle_, std::shared_ptr< const Oracle< Index_ > >, bool >::type MaybeOracle
Value_ * copy_n(const Value_ *input, Size_ n, Value_ *output)
bool sparse_extract_index
bool sparse_extract_value
Options for test_full_access() and friends.
Definition test_access.hpp:39
bool use_oracle
Definition test_access.hpp:43
int jump
Definition test_access.hpp:59
bool use_row
Definition test_access.hpp:49
bool check_sparse
Definition test_access.hpp:64
TestAccessOrder order
Definition test_access.hpp:54