Belle II Software  release-06-02-00
mapHelperFunctions.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 // google test framework
10 #include <gtest/gtest.h>
11 
12 #include <tracking/spacePointCreation/MapHelperFunctions.h>
13 #include <framework/logging/Logger.h> // for B2INFO, etc...
14 
15 #include <unordered_map>
16 // #include <map> // only testing unordered maps here, but should be the same for maps (only slower)
17 #include <cmath>
18 #include <numeric>
19 
20 using namespace std;
21 
22 namespace Belle2 {
28  typedef std::unordered_multimap<int, double> i2dMultiMap;
29  typedef std::unordered_map<int, double> i2dMap;
36  template<typename Functor>
37  i2dMultiMap createMultiMap(int nEntries, Functor funct)
38  {
39  i2dMultiMap map;
40  for (int i = 0; i < nEntries; ++i) {
41  map.insert(std::make_pair((i % 6), funct.operator()(i)));
42  }
43  return map;
44  }
45 
51  template<typename Functor>
52  i2dMap createMap(int nEntries, Functor funct)
53  {
54  i2dMap map;
55  for (int i = 0; i < nEntries; ++i) {
56  map.insert(std::make_pair(i, funct.operator()(i)));
57  }
58  return map;
59  }
60 
65  struct {
66  double operator()(int i)
67  {
68  return std::tan(i) / std::sin(i);
69  }
70  } secans;
71 
73  struct {
74  double operator()(int i)
75  {
76  return std::sin(i * .5);
77  }
79 
83  class MapHelperFunctionsTest : public ::testing::Test {
84  protected:
86  virtual void SetUp()
87  {
88  _nEntries = 20;
89  _map = createMap(_nEntries, sinHalf);
90  _multimap = createMultiMap(_nEntries, sinHalf);
91  _nanMap = createMap(_nEntries, secans);
92  _nanMultiMap = createMultiMap(_nEntries, secans);
93  }
94 
99  int _nEntries;
100  };
101 
103  TEST_F(MapHelperFunctionsTest, testCreatorFunctions)
104  {
105  unsigned int N = _nEntries; // same number as in setup
106  EXPECT_EQ(N, _nanMultiMap.size()); // test if creation worked (very basically by comparing the size of the returned _nanMap)
107  EXPECT_EQ(N, _nanMap.size());
108  EXPECT_EQ(N, _multimap.size());
109  EXPECT_EQ(N, _map.size());
110 
111  ASSERT_TRUE(std::isnan(secans.operator()(0))) << "For the following tests to work properly this is expected to return a NaN (-nan)";
112 
113 // std::cout << printMap(_nanMap) << std::endl;
114 // std::cout << printMap(_nanMultiMap) << std::endl;
115 // std::cout << printMap(_multimap) << std::endl;
116 // std::cout << printMap(_map) << std::endl;
117 
118  // do some hard-coded queries to see if the _nanMaps were filled according to specified method
119  std::vector<int> possibleKeys = { 0, 1, 2, 3, 4, 5 };
120  for (int key : possibleKeys) {
121  std::vector<double> nanPossibleValues, possibleValues;
122  for (int arg = key; arg < _nEntries; arg += 6) {
123  nanPossibleValues.push_back(secans.operator()(arg));
124  possibleValues.push_back(sinHalf.operator()(arg));
125  }
126  // test the multimap with nan first
127  auto keyRange = _nanMultiMap.equal_range(key);
128  // check the numbers of stored values and of possible values
129  EXPECT_EQ(nanPossibleValues.size(), std::distance(keyRange.first, keyRange.second));
130  i2dMultiMap::iterator multiMapIt = keyRange.first;
131  for (int i = 0; i < std::distance(keyRange.first, keyRange.second); ++i) {
132  if (!std::isnan(multiMapIt->second)) { // cannot find nan value with std::find (NaN == NaN is always false)
133  auto findIt = std::find(nanPossibleValues.begin(), nanPossibleValues.end(), multiMapIt->second);
134  EXPECT_FALSE(findIt == nanPossibleValues.end()); // all stored values have to be possible
135  } else { // check if there is at least on NaN value in the possible Values
136  EXPECT_TRUE(std::count_if(nanPossibleValues.begin(), nanPossibleValues.end(),
137  [](const double & val) { return std::isnan(val); }) > 0);
138  }
139  ++multiMapIt;
140  }
141  keyRange = _multimap.equal_range(key);
142  EXPECT_EQ(possibleValues.size(), std::distance(keyRange.first, keyRange.second));
143  multiMapIt = keyRange.first;
144  for (int i = 0; i < std::distance(keyRange.first, keyRange.second); ++i) {
145  auto findIt = std::find(possibleValues.begin(), possibleValues.end(), multiMapIt->second);
146  EXPECT_FALSE(findIt == possibleValues.end());
147  ++multiMapIt;
148  }
149  }
150 
151  for (unsigned int i = 0; i < N; ++i) {
152  auto range = _nanMap.equal_range(i);
153  i2dMap::iterator mapIt = range.first;
154  for (int j = 0; j < std::distance(range.first, range.second); ++j) {
155  if (std::isnan(mapIt->second)) {
156  B2INFO("Not Comparing value to key " << i << " because value is NaN, checking if the functor returns NaN for this key");
157  EXPECT_TRUE(std::isnan(secans.operator()(i))); // expect a NaN value from the operator
158  continue; // do not compare NaNs! (will always fail due to their nature)
159  }
160  EXPECT_DOUBLE_EQ(mapIt->second, secans.operator()(i));
161  ++mapIt;
162  }
163 
164  range = _map.equal_range(i);
165  mapIt = range.first;
166  for (int j = 0; j < std::distance(range.first, range.second); ++j) {
167  EXPECT_DOUBLE_EQ(mapIt->second, sinHalf.operator()(i));
168  }
169  }
170  }
171 
173  TEST_F(MapHelperFunctionsTest, testGetUniqueKeys)
174  {
175  // get the unique keys for the map (assuming that the key_type is int!)
176  std::vector<int> uniqueKeys = getUniqueKeys(_map);
177  EXPECT_EQ(uniqueKeys.size(), _map.size()); // for the map the number of uniqueKeys has to be the same as the size!
178  for (size_t i = 0; i < uniqueKeys.size(); ++i) {
179  // using std::find because in this way there is no assumption on the order of the keys!
180  auto findIt = std::find(uniqueKeys.begin(), uniqueKeys.end(), i);
181  EXPECT_FALSE(findIt == uniqueKeys.end());
182  }
183 
184  // get the unique keys for the multimap
185  std::vector<int> uniqueMultiKeys = getUniqueKeys(_multimap);
186  unsigned int expectedSize = _multimap.size() > 6 ? 6 : _multimap.size(); // (creation of the map is % 6 -> only 6 keys possible)
187  EXPECT_EQ(expectedSize, uniqueMultiKeys.size());
188  for (size_t i = 0; i < uniqueMultiKeys.size(); ++i) {
189  // using std::find because in this way there is no assumption on the order of the keys!
190  auto findIt = std::find(uniqueMultiKeys.begin(), uniqueMultiKeys.end(), i);
191  EXPECT_FALSE(findIt == uniqueMultiKeys.end());
192  }
193  }
194 
196  TEST_F(MapHelperFunctionsTest, testGetValuesToKey)
197  {
198  // multimap
199  std::vector<int> possibleKeys = { 0, 1, 2, 3, 4, 5 };
200  for (int key : possibleKeys) {
201  std::vector<double> expectedValues;
202  for (int arg = key; arg < _nEntries; arg += 6) { expectedValues.push_back(sinHalf.operator()(arg)); }
203  std::vector<double> actualValues = getValuesToKey(_multimap, key);
204  EXPECT_EQ(expectedValues.size(), actualValues.size());
205 
206  // check if all values are present
207  for (const double& d : actualValues) {
208  EXPECT_FALSE(std::find(expectedValues.begin(), expectedValues.end(), d) == expectedValues.end());
209  }
210  expectedValues.clear();
211  for (int arg = key; arg < _nEntries; arg += 6) {
212  expectedValues.push_back(secans.operator()(arg));
213  }
214  actualValues = getValuesToKey(_nanMultiMap, key);
215  EXPECT_EQ(expectedValues.size(), actualValues.size());
216 
217  // check if all values are present
218  for (const double& d : actualValues) {
219  if (std::isnan(d)) {
220  B2INFO("Not comparing NaN value!");
221  continue;
222  }
223  EXPECT_FALSE(std::find(expectedValues.begin(), expectedValues.end(), d) == expectedValues.end());
224  }
225  }
226 
227  // map
228  for (int i = 0; i < _nEntries; ++i) {
229  std::vector<double> values = getValuesToKey(_map, i);
230  EXPECT_EQ(values.size(), 1);
231  EXPECT_DOUBLE_EQ(values[0], sinHalf.operator()(i));
232 
233  values = getValuesToKey(_nanMap, i);
234  EXPECT_EQ(values.size(), 1);
235  if (!std::isnan(values[0])) { // not checking here (already checked, that the functor leads to this above)
236  EXPECT_DOUBLE_EQ(values[0], secans.operator()(i));
237  }
238  }
239  }
240 
242  TEST_F(MapHelperFunctionsTest, testGetNValuesPerKey)
243  {
244  std::vector<std::pair<int, unsigned int> > mapKeys = getNValuesPerKey(_map);
245  unsigned int oneKey2Value = std::count_if(mapKeys.begin(), mapKeys.end(),
246  [](const std::pair<int, unsigned int>& pair) { return pair.second == 1; });
247  EXPECT_EQ(_map.size(), oneKey2Value); // all keys should only be there once in a map!
248 
249  mapKeys = getNValuesPerKey(_nanMap);
250  oneKey2Value = std::count_if(mapKeys.begin(), mapKeys.end(),
251  [](const std::pair<int, unsigned int>& pair) { return pair.second == 1; });
252  EXPECT_EQ(_nanMap.size(), oneKey2Value);
253 
254  // multimaps (NOTE: hardcoded expectations because of knowledge how the maps should be filled!)
255  mapKeys = getNValuesPerKey(_nanMultiMap);
256  EXPECT_EQ(mapKeys.size(), 6);
257  for (const auto& key : mapKeys) {
258  EXPECT_EQ(key.second, key.first < 2 ? 4 : 3);
259  }
260 
261  mapKeys = getNValuesPerKey(_multimap);
262  EXPECT_EQ(mapKeys.size(), 6);
263  for (const auto& key : mapKeys) {
264  EXPECT_EQ(key.second, key.first < 2 ? 4 : 3);
265  }
266  }
267 
269  TEST_F(MapHelperFunctionsTest, testGetSortedKeyValueTuples)
270  {
271  // multimap without nans first
272  std::vector<int> possibleKeys = { 0, 1, 2, 3, 4, 5 };
273  std::vector<double> summedValues;
274  std::vector<double> summedNanValues;
275  for (int key : possibleKeys) {
276  std::vector<double> possibleValues;
277  std::vector<double> possibleNanValues;
278  for (int arg = key; arg < _nEntries; arg += 6) {
279  possibleValues.push_back(sinHalf.operator()(arg));
280  possibleNanValues.push_back(secans.operator()(arg));
281  }
282  summedNanValues.push_back(std::accumulate(possibleNanValues.begin(), possibleNanValues.end(), 0.0));
283  summedValues.push_back(std::accumulate(possibleValues.begin(), possibleValues.end(), 0.0));
284  }
285 
286  std::vector<std::tuple<int, double, unsigned int> > keyValPairs = getSortedKeyValueTuples(_nanMultiMap);
287 
288  EXPECT_EQ(keyValPairs.size(), 6);
289  // hardcoded checking here (only sampling here! not checking the whole thing!)
290  EXPECT_EQ(get<0>(keyValPairs[0]), 0);
291  EXPECT_TRUE(std::isnan(get<1>(keyValPairs[0])));
292  EXPECT_EQ(get<2>(keyValPairs[0]), 4);
293 
294  // all the other checks with multimap without NaNs
295  keyValPairs = getSortedKeyValueTuples(_multimap);
296  EXPECT_EQ(keyValPairs.size(), 6);
297 
298  EXPECT_EQ(get<0>(keyValPairs[0]), 0);
299  EXPECT_DOUBLE_EQ(get<1>(keyValPairs[0]), summedValues[0]);
300  EXPECT_EQ(get<2>(keyValPairs[0]), 4);
301 
302  EXPECT_EQ(get<0>(keyValPairs[2]), 3);
303  EXPECT_DOUBLE_EQ(get<1>(keyValPairs[2]), summedValues[3]);
304  EXPECT_EQ(get<2>(keyValPairs[2]), 3);
305 
306  keyValPairs = getSortedKeyValueTuples(_map);
307  EXPECT_EQ(get<0>(keyValPairs[0]), 3); // sin(x/2) has its maximum at x = n*pi, 3 is the value that comes closest to this
308  EXPECT_DOUBLE_EQ(get<1>(keyValPairs[0]), sinHalf.operator()(3));
309 
310  EXPECT_EQ(get<0>(keyValPairs[19]), 9);
311  EXPECT_DOUBLE_EQ(get<1>(keyValPairs[19]), sinHalf.operator()(9));
312  }
313 
315  TEST_F(MapHelperFunctionsTest, testGetAllValues)
316  {
317  // set up vectors with all values there are in the maps
318  std::vector<double> possibleValues, nanPossibleValues;
319  for (int i = 0; i < _nEntries; ++i) {
320  possibleValues.push_back(sinHalf.operator()(i));
321  nanPossibleValues.push_back(secans.operator()(i));
322  }
323  // sort vectors to be able to simply loop over them for comparison since the order is unimportant for this function
324  std::sort(possibleValues.begin(), possibleValues.end());
325  std::sort(nanPossibleValues.begin(), nanPossibleValues.end());
326 
327  // test normal map first
328  std::vector<double> allValues = getAllValues(_map);
329  EXPECT_EQ(allValues.size(), _nEntries);
330  std::sort(allValues.begin(), allValues.end());
331  for (int i = 0; i < _nEntries; ++i) {
332  EXPECT_DOUBLE_EQ(allValues[i], possibleValues[i]);
333  }
334 
335  // test multimap with non NaN entries
336  allValues = getAllValues(_multimap);
337  EXPECT_EQ(allValues.size(), _nEntries);
338  std::sort(allValues.begin(), allValues.end());
339  for (int i = 0; i < _nEntries; ++i) {
340  EXPECT_DOUBLE_EQ(allValues[i], possibleValues[i]);
341  }
342 
343  // TODO: does not work at the moment, FIX THIS!!
344  // test the nan maps
345  // allValues = getAllValues(_nanMap);
346  // EXPECT_EQ(allValues.size(), _nEntries);
347  // std::sort(allValues.begin(), allValues.end());
348  // for(int i = 0; i < _nEntries; ++i) {
349  // EXPECT_DOUBLE_EQ(allValues[i], possibleValues[i]);
350  // }
351 
352  // allValues = getAllValues(_nanMultiMap);
353  // EXPECT_EQ(allValues.size(), _nEntries);
354  // std::sort(allValues.begin(), allValues.end());
355  // for(int i = 0; i < _nEntries; ++i) {
356  // EXPECT_DOUBLE_EQ(allValues[i], possibleValues[i]);
357  // }
358 
359 
360  }
362 }
class for testing the helper functions from MapHelperFunctions.h
int _nEntries
the number of pairs that will be put into the maps for the tests
i2dMultiMap _multimap
multimap filled with values obtained by sinHalf functor
virtual void SetUp()
set up the maps for the tests
i2dMultiMap _nanMultiMap
multimap filled with values obtained by secans functor -> contains a NaN!
i2dMap _nanMap
map filled with values obtained by secans functor -> contains a NaN!
i2dMap _map
map filled with values obtained by sinHalf functor
i2dMultiMap createMultiMap(int nEntries, Functor funct)
create a multimap with
struct Belle2::@299 sinHalf
function object that implements sin(i/2).
std::unordered_map< int, double > i2dMap
typedef for less writing effort
std::vector< std::tuple< typename MapType::key_type, typename MapType::mapped_type, unsigned int > > getSortedKeyValueTuples(const MapType &aMap)
get the (key, value, number of values) tuples stored in the map, sorted after the following scheme (d...
TEST_F(MapHelperFunctionsTest, testGetAllValues)
test the getAllValues() function actually returns all values that are stored in the map
std::unordered_multimap< int, double > i2dMultiMap
typedef for less writing effort
std::vector< std::pair< typename MapType::key_type, unsigned int > > getNValuesPerKey(const MapType &aMap)
get the unique keys of a map together with the number of values associated to each key.
std::vector< typename MapType::key_type > getUniqueKeys(const MapType &aMap)
get the unique keys of a map (i.e.
i2dMap createMap(int nEntries, Functor funct)
create a multimap with
struct Belle2::@298 secans
function object that implements cosecans(x) == 1/cos(x) by tan(x) / sin(x).
std::vector< typename MapType::mapped_type > getValuesToKey(const MapType &aMap, typename MapType::key_type aKey)
get all values stored in the map for a given key
std::vector< typename MapType::mapped_type > getAllValues(const MapType &aMap)
get all values in the map (i.e.
Abstract base class for different kinds of events.