DiSMEC++
common_test.cpp
Go to the documentation of this file.
1 // Copyright (c) 2022, Aalto University, developed by Erik Schultheis
2 // All rights reserved.
3 //
4 // SPDX-License-Identifier: MIT
5 
6 #include "common.h"
7 #include "doctest.h"
8 using namespace dismec;
9 
10 // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers,readability-function-cognitive-complexity)
11 
15 TEST_CASE("check write dense vector")
16 {
17  std::stringstream target;
18  SUBCASE("empty") {
19  DenseRealVector v(0);
20  CHECK(&io::write_vector_as_text(target, v) == &target);
21  CHECK(target.str().empty());
22  }
23 
24  SUBCASE("one element") {
25  DenseRealVector v(1);
26  v << 2.5;
27  CHECK(&io::write_vector_as_text(target, v) == &target);
28  CHECK(target.str() == "2.5");
29  }
30 
31  SUBCASE("three elements") {
32  DenseRealVector v(3);
33  v << 2.5, -1.0, 8e12;
34  CHECK(&io::write_vector_as_text(target, v) == &target);
35  CHECK(target.str() == "2.5 -1 8e+12");
36  }
37 }
38 
42 TEST_CASE("read dense vector from text") {
43  std::stringstream source;
44  SUBCASE("empty") {
45  DenseRealVector v(0);
46  CHECK(&io::read_vector_from_text(source, v) == &source);
47  }
48 
49  SUBCASE("one element") {
50  source.str("2.5");
51  DenseRealVector v(1);
52  CHECK(&io::read_vector_from_text(source, v) == &source);
53  CHECK(v.coeff(0) == 2.5);
54  }
55 
56  SUBCASE("three elements") {
57  DenseRealVector v(3);
58  source.str("2.5 -1 8e+12");
59  CHECK(&io::read_vector_from_text(source, v) == &source);
60  CHECK(v.coeff(0) == 2.5);
61  CHECK(v.coeff(1) == -1);
62  CHECK(v.coeff(2) == doctest::Approx(8e+12));
63  }
64 }
65 
75 TEST_CASE("parse sparse vector") {
76  std::vector<long> expect_ids;
77  std::vector<double> expect_vals;
78 
79  auto run_test = [&](std::string source) {
80  REQUIRE(expect_ids.size() == expect_vals.size());
81  int pos = 0;
82  CAPTURE(source);
83  try {
84  io::parse_sparse_vector_from_text(source.data(), [&](long i, double v) {
85  CHECK(expect_ids.at(pos) == i);
86  CHECK(expect_vals.at(pos) == v);
87  ++pos;
88  });
89  } catch (std::runtime_error &err) {
90  FAIL("parsing failed");
91  }
92  CHECK(expect_ids.size() == pos);
93  };
94 
95  SUBCASE("no leading space") {
96  expect_ids = {12, 7};
97  expect_vals = {2.6, 4.4};
98  run_test("12:2.6 7:4.4");
99  }
100 
101  SUBCASE("simple valid features") {
102  expect_ids = {12, 7};
103  expect_vals = {2.6, 4.4};
104  run_test(" 12:2.6 7:4.4");
105  }
106 
107  SUBCASE("leading space in front of values") {
108  expect_ids = {12, 7};
109  expect_vals = {2.6, 4.4};
110  run_test(" 12: 2.6 7: 4.4");
111  }
112 
113  SUBCASE("tab separation") {
114  expect_ids = {12, 7};
115  expect_vals = {2.6, 4.4};
116  run_test("\t12:2.6\t7:4.4");
117  }
118 
119  SUBCASE("scientific notation") {
120  expect_ids = {12, 7};
121  expect_vals = {2.6e-5, 4.4e4};
122  run_test(" 12:2.6e-5 7:4.4e4");
123  }
124 
125  SUBCASE("ends with space") {
126  expect_ids = {12, 7};
127  expect_vals = {2, 4};
128  run_test(" 12:2 7:4\t ");
129  }
130 }
131 
142 TEST_CASE("parse sparse vector errors") {
143  CHECK_THROWS(io::parse_sparse_vector_from_text(" 5.4:2.0", [&](long i, double v) {}));
144  CHECK_THROWS(io::parse_sparse_vector_from_text(" x:2.0", [&](long i, double v) {}));
145  CHECK_THROWS(io::parse_sparse_vector_from_text(" 5:2.x", [&](long i, double v) {}));
146  CHECK_THROWS(io::parse_sparse_vector_from_text(" 5:", [&](long i, double v) {}));
147  CHECK_THROWS(io::parse_sparse_vector_from_text(" 5: ", [&](long i, double v) {}));
148  CHECK_THROWS(io::parse_sparse_vector_from_text(" 5", [&](long i, double v) {}));
149  CHECK_THROWS(io::parse_sparse_vector_from_text(" 5 ", [&](long i, double v) {}));
150  CHECK_THROWS(io::parse_sparse_vector_from_text(" 5-4", [&](long i, double v) {}));
151  CHECK_THROWS_WITH(io::parse_sparse_vector_from_text(" 5 : 2.0", [&](long i, double v) {}),
152  "Error parsing feature index. Expected ':' at position 2, got ' '");
153  CHECK_THROWS_WITH(io::parse_sparse_vector_from_text(" 5", [&](long i, double v) {}),
154  "Error parsing feature index. Expected ':' at position 2, got '\\0'");
155  CHECK_THROWS_WITH(io::parse_sparse_vector_from_text("1:3.0 5", [&](long i, double v) {}),
156  "Error parsing feature index. Expected ':' at position 7, got '\\0'");
157  CHECK_THROWS(io::parse_sparse_vector_from_text(":2.0", [&](long i, double v) {}));
158 }
159 
163 TEST_CASE("binary dump/load") {
164  std::stringbuf buffer;
165  std::vector<float> data = {4.0, 2.0, 8.0, -2.0};
166  io::binary_dump(buffer, &*(data.begin()), &*(data.end()));
167  CHECK(buffer.pubseekoff(0, std::ios_base::cur, std::ios_base::out) == sizeof(float) * data.size());
168  buffer.pubseekpos(0);
169  std::vector<float> load(data.size());
170  io::binary_load(buffer, &*(load.begin()), &*(load.end()));
171  for(int i = 0; i < ssize(data); ++i) {
172  CHECK(data[i] == load[i]);
173  }
174 }
175 
177 TEST_CASE("parse valid header") {
178  std::string input;
179  SUBCASE("minimal") {
180  input = "12 54";
181  }
182  SUBCASE("trailing space") {
183  input = "12 54 ";
184  }
185  SUBCASE("tab separated") {
186  input = "12\t 54";
187  }
188  io::MatrixHeader valid = io::parse_header(input);
189  CHECK(valid.NumRows == 12);
190  CHECK(valid.NumCols == 54);
191 }
192 
193 
196 TEST_CASE("parse invalid header") {
197  // check number of arguments
198  CHECK_THROWS(io::parse_header("6 "));
199  CHECK_THROWS(io::parse_header("6 1 5"));
200 
201  // we also know that something is wrong if any of the counts are <= 0
202  CHECK_THROWS(io::parse_header("0 5"));
203  CHECK_THROWS(io::parse_header("5 0"));
204  CHECK_THROWS(io::parse_header("-1 5"));
205  CHECK_THROWS(io::parse_header("5 -1"));
206 }
207 
208 // NOLINTEND(cppcoreguidelines-avoid-magic-numbers,readability-function-cognitive-complexity)
building blocks for io procedures that are used by multiple io subsystems
TEST_CASE("check write dense vector")
Definition: common_test.cpp:15
std::ostream & write_vector_as_text(std::ostream &stream, const Eigen::Ref< const DenseRealVector > &data)
Writes the given vector as space-separated human-readable numbers.
Definition: common.cpp:21
std::istream & read_vector_from_text(std::istream &stream, Eigen::Ref< DenseRealVector > data)
Reads the given vector as space-separated human-readable numbers.
Definition: common.cpp:37
void binary_dump(std::streambuf &target, const T *begin, const T *end)
Definition: common.h:110
MatrixHeader parse_header(const std::string &content)
Definition: common.cpp:49
void binary_load(std::streambuf &target, T *begin, T *end)
Definition: common.h:120
void parse_sparse_vector_from_text(const char *feature_part, F &&callback)
parses sparse features given in index:value text format.
Definition: common.h:52
Main namespace in which all types, classes, and functions are defined.
Definition: app.h:15
constexpr auto ssize(const C &c) -> std::common_type_t< std::ptrdiff_t, std::make_signed_t< decltype(c.size())>>
signed size free function. Taken from https://en.cppreference.com/w/cpp/iterator/size
Definition: conversion.h:42
types::DenseVector< real_t > DenseRealVector
Any dense, real values vector.
Definition: matrix_types.h:40
Collects the rows and columns parsed from a plain-text matrix file.
Definition: common.h:130