Belle II Software  release-08-01-10
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  // Silence Doxygen which is complaining that "no uniquely matching class member found for"
66  // But there should be a better way that I just don't know of / find
68  struct {
69  double operator()(int i)
70  {
71  return std::tan(i) / std::sin(i);
72  }
73  } secans;
75 
77  // Silence Doxygen which is complaining that "no uniquely matching class member found for".
78  // But there should be a better way that I just don't know of / find
80  struct {
81  double operator()(int i)
82  {
83  return std::sin(i * .5);
84  }
85  } sinHalf;
87 
91  class MapHelperFunctionsTest : public ::testing::Test {
92  protected:
94  virtual void SetUp()
95  {
96  _nEntries = 20;
97  _map = createMap(_nEntries, sinHalf);
98  _multimap = createMultiMap(_nEntries, sinHalf);
99  _nanMap = createMap(_nEntries, secans);
100  _nanMultiMap = createMultiMap(_nEntries, secans);
101  }
102 
107  int _nEntries;
108  };
109 
111  TEST_F(MapHelperFunctionsTest, testCreatorFunctions)
112  {
113  unsigned int N = _nEntries; // same number as in setup
114  EXPECT_EQ(N, _nanMultiMap.size()); // test if creation worked (very basically by comparing the size of the returned _nanMap)
115  EXPECT_EQ(N, _nanMap.size());
116  EXPECT_EQ(N, _multimap.size());
117  EXPECT_EQ(N, _map.size());
118 
119  ASSERT_TRUE(std::isnan(secans.operator()(0))) << "For the following tests to work properly this is expected to return a NaN (-nan)";
120 
121 // std::cout << printMap(_nanMap) << std::endl;
122 // std::cout << printMap(_nanMultiMap) << std::endl;
123 // std::cout << printMap(_multimap) << std::endl;
124 // std::cout << printMap(_map) << std::endl;
125 
126  // do some hard-coded queries to see if the _nanMaps were filled according to specified method
127  std::vector<int> possibleKeys = { 0, 1, 2, 3, 4, 5 };
128  for (int key : possibleKeys) {
129  std::vector<double> nanPossibleValues, possibleValues;
130  for (int arg = key; arg < _nEntries; arg += 6) {
131  nanPossibleValues.push_back(secans.operator()(arg));
132  possibleValues.push_back(sinHalf.operator()(arg));
133  }
134  // test the multimap with nan first
135  auto keyRange = _nanMultiMap.equal_range(key);
136  // check the numbers of stored values and of possible values
137  EXPECT_EQ(nanPossibleValues.size(), std::distance(keyRange.first, keyRange.second));
138  i2dMultiMap::iterator multiMapIt = keyRange.first;
139  for (int i = 0; i < std::distance(keyRange.first, keyRange.second); ++i) {
140  if (!std::isnan(multiMapIt->second)) { // cannot find nan value with std::find (NaN == NaN is always false)
141  auto findIt = std::find(nanPossibleValues.begin(), nanPossibleValues.end(), multiMapIt->second);
142  EXPECT_FALSE(findIt == nanPossibleValues.end()); // all stored values have to be possible
143  } else { // check if there is at least on NaN value in the possible Values
144  EXPECT_TRUE(std::count_if(nanPossibleValues.begin(), nanPossibleValues.end(),
145  [](const double & val) { return std::isnan(val); }) > 0);
146  }
147  ++multiMapIt;
148  }
149  keyRange = _multimap.equal_range(key);
150  EXPECT_EQ(possibleValues.size(), std::distance(keyRange.first, keyRange.second));
151  multiMapIt = keyRange.first;
152  for (int i = 0; i < std::distance(keyRange.first, keyRange.second); ++i) {
153  auto findIt = std::find(possibleValues.begin(), possibleValues.end(), multiMapIt->second);
154  EXPECT_FALSE(findIt == possibleValues.end());
155  ++multiMapIt;
156  }
157  }
158 
159  for (unsigned int i = 0; i < N; ++i) {
160  auto range = _nanMap.equal_range(i);
161  i2dMap::iterator mapIt = range.first;
162  for (int j = 0; j < std::distance(range.first, range.second); ++j) {
163  if (std::isnan(mapIt->second)) {
164  B2INFO("Not Comparing value to key " << i << " because value is NaN, checking if the functor returns NaN for this key");
165  EXPECT_TRUE(std::isnan(secans.operator()(i))); // expect a NaN value from the operator
166  continue; // do not compare NaNs! (will always fail due to their nature)
167  }
168  EXPECT_DOUBLE_EQ(mapIt->second, secans.operator()(i));
169  ++mapIt;
170  }
171 
172  range = _map.equal_range(i);
173  mapIt = range.first;
174  for (int j = 0; j < std::distance(range.first, range.second); ++j) {
175  EXPECT_DOUBLE_EQ(mapIt->second, sinHalf.operator()(i));
176  }
177  }
178  }
179 
181  TEST_F(MapHelperFunctionsTest, testGetUniqueKeys)
182  {
183  // get the unique keys for the map (assuming that the key_type is int!)
184  std::vector<int> uniqueKeys = getUniqueKeys(_map);
185  EXPECT_EQ(uniqueKeys.size(), _map.size()); // for the map the number of uniqueKeys has to be the same as the size!
186  for (size_t i = 0; i < uniqueKeys.size(); ++i) {
187  // using std::find because in this way there is no assumption on the order of the keys!
188  auto findIt = std::find(uniqueKeys.begin(), uniqueKeys.end(), i);
189  EXPECT_FALSE(findIt == uniqueKeys.end());
190  }
191 
192  // get the unique keys for the multimap
193  std::vector<int> uniqueMultiKeys = getUniqueKeys(_multimap);
194  unsigned int expectedSize = _multimap.size() > 6 ? 6 : _multimap.size(); // (creation of the map is % 6 -> only 6 keys possible)
195  EXPECT_EQ(expectedSize, uniqueMultiKeys.size());
196  for (size_t i = 0; i < uniqueMultiKeys.size(); ++i) {
197  // using std::find because in this way there is no assumption on the order of the keys!
198  auto findIt = std::find(uniqueMultiKeys.begin(), uniqueMultiKeys.end(), i);
199  EXPECT_FALSE(findIt == uniqueMultiKeys.end());
200  }
201  }
202 
204  TEST_F(MapHelperFunctionsTest, testGetValuesToKey)
205  {
206  // multimap
207  std::vector<int> possibleKeys = { 0, 1, 2, 3, 4, 5 };
208  for (int key : possibleKeys) {
209  std::vector<double> expectedValues;
210  for (int arg = key; arg < _nEntries; arg += 6) { expectedValues.push_back(sinHalf.operator()(arg)); }
211  std::vector<double> actualValues = getValuesToKey(_multimap, key);
212  EXPECT_EQ(expectedValues.size(), actualValues.size());
213 
214  // check if all values are present
215  for (const double& d : actualValues) {
216  EXPECT_FALSE(std::find(expectedValues.begin(), expectedValues.end(), d) == expectedValues.end());
217  }
218  expectedValues.clear();
219  for (int arg = key; arg < _nEntries; arg += 6) {
220  expectedValues.push_back(secans.operator()(arg));
221  }
222  actualValues = getValuesToKey(_nanMultiMap, key);
223  EXPECT_EQ(expectedValues.size(), actualValues.size());
224 
225  // check if all values are present
226  for (const double& d : actualValues) {
227  if (std::isnan(d)) {
228  B2INFO("Not comparing NaN value!");
229  continue;
230  }
231  EXPECT_FALSE(std::find(expectedValues.begin(), expectedValues.end(), d) == expectedValues.end());
232  }
233  }
234 
235  // map
236  for (int i = 0; i < _nEntries; ++i) {
237  std::vector<double> values = getValuesToKey(_map, i);
238  EXPECT_EQ(values.size(), 1);
239  EXPECT_DOUBLE_EQ(values[0], sinHalf.operator()(i));
240 
241  values = getValuesToKey(_nanMap, i);
242  EXPECT_EQ(values.size(), 1);
243  if (!std::isnan(values[0])) { // not checking here (already checked, that the functor leads to this above)
244  EXPECT_DOUBLE_EQ(values[0], secans.operator()(i));
245  }
246  }
247  }
248 
250  TEST_F(MapHelperFunctionsTest, testGetNValuesPerKey)
251  {
252  std::vector<std::pair<int, unsigned int> > mapKeys = getNValuesPerKey(_map);
253  unsigned int oneKey2Value = std::count_if(mapKeys.begin(), mapKeys.end(),
254  [](const std::pair<int, unsigned int>& pair) { return pair.second == 1; });
255  EXPECT_EQ(_map.size(), oneKey2Value); // all keys should only be there once in a map!
256 
257  mapKeys = getNValuesPerKey(_nanMap);
258  oneKey2Value = std::count_if(mapKeys.begin(), mapKeys.end(),
259  [](const std::pair<int, unsigned int>& pair) { return pair.second == 1; });
260  EXPECT_EQ(_nanMap.size(), oneKey2Value);
261 
262  // multimaps (NOTE: hardcoded expectations because of knowledge how the maps should be filled!)
263  mapKeys = getNValuesPerKey(_nanMultiMap);
264  EXPECT_EQ(mapKeys.size(), 6);
265  for (const auto& key : mapKeys) {
266  EXPECT_EQ(key.second, key.first < 2 ? 4 : 3);
267  }
268 
269  mapKeys = getNValuesPerKey(_multimap);
270  EXPECT_EQ(mapKeys.size(), 6);
271  for (const auto& key : mapKeys) {
272  EXPECT_EQ(key.second, key.first < 2 ? 4 : 3);
273  }
274  }
275 
277  TEST_F(MapHelperFunctionsTest, testGetSortedKeyValueTuples)
278  {
279  // multimap without nans first
280  std::vector<int> possibleKeys = { 0, 1, 2, 3, 4, 5 };
281  std::vector<double> summedValues;
282  std::vector<double> summedNanValues;
283  for (int key : possibleKeys) {
284  std::vector<double> possibleValues;
285  std::vector<double> possibleNanValues;
286  for (int arg = key; arg < _nEntries; arg += 6) {
287  possibleValues.push_back(sinHalf.operator()(arg));
288  possibleNanValues.push_back(secans.operator()(arg));
289  }
290  summedNanValues.push_back(std::accumulate(possibleNanValues.begin(), possibleNanValues.end(), 0.0));
291  summedValues.push_back(std::accumulate(possibleValues.begin(), possibleValues.end(), 0.0));
292  }
293 
294  std::vector<std::tuple<int, double, unsigned int> > keyValPairs = getSortedKeyValueTuples(_nanMultiMap);
295 
296  EXPECT_EQ(keyValPairs.size(), 6);
297  // hardcoded checking here (only sampling here! not checking the whole thing!)
298  EXPECT_EQ(get<0>(keyValPairs[0]), 0);
299  EXPECT_TRUE(std::isnan(get<1>(keyValPairs[0])));
300  EXPECT_EQ(get<2>(keyValPairs[0]), 4);
301 
302  // all the other checks with multimap without NaNs
303  keyValPairs = getSortedKeyValueTuples(_multimap);
304  EXPECT_EQ(keyValPairs.size(), 6);
305 
306  EXPECT_EQ(get<0>(keyValPairs[0]), 0);
307  EXPECT_DOUBLE_EQ(get<1>(keyValPairs[0]), summedValues[0]);
308  EXPECT_EQ(get<2>(keyValPairs[0]), 4);
309 
310  EXPECT_EQ(get<0>(keyValPairs[2]), 3);
311  EXPECT_DOUBLE_EQ(get<1>(keyValPairs[2]), summedValues[3]);
312  EXPECT_EQ(get<2>(keyValPairs[2]), 3);
313 
314  keyValPairs = getSortedKeyValueTuples(_map);
315  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
316  EXPECT_DOUBLE_EQ(get<1>(keyValPairs[0]), sinHalf.operator()(3));
317 
318  EXPECT_EQ(get<0>(keyValPairs[19]), 9);
319  EXPECT_DOUBLE_EQ(get<1>(keyValPairs[19]), sinHalf.operator()(9));
320  }
321 
323  TEST_F(MapHelperFunctionsTest, testGetAllValues)
324  {
325  // set up vectors with all values there are in the maps
326  std::vector<double> possibleValues, nanPossibleValues;
327  for (int i = 0; i < _nEntries; ++i) {
328  possibleValues.push_back(sinHalf.operator()(i));
329  nanPossibleValues.push_back(secans.operator()(i));
330  }
331  // sort vectors to be able to simply loop over them for comparison since the order is unimportant for this function
332  std::sort(possibleValues.begin(), possibleValues.end());
333  std::sort(nanPossibleValues.begin(), nanPossibleValues.end());
334 
335  // test normal map first
336  std::vector<double> allValues = getAllValues(_map);
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  // test multimap with non NaN entries
344  allValues = getAllValues(_multimap);
345  EXPECT_EQ(allValues.size(), _nEntries);
346  std::sort(allValues.begin(), allValues.end());
347  for (int i = 0; i < _nEntries; ++i) {
348  EXPECT_DOUBLE_EQ(allValues[i], possibleValues[i]);
349  }
350 
351  // TODO: does not work at the moment, FIX THIS!!
352  // test the nan maps
353  // allValues = getAllValues(_nanMap);
354  // EXPECT_EQ(allValues.size(), _nEntries);
355  // std::sort(allValues.begin(), allValues.end());
356  // for(int i = 0; i < _nEntries; ++i) {
357  // EXPECT_DOUBLE_EQ(allValues[i], possibleValues[i]);
358  // }
359 
360  // allValues = getAllValues(_nanMultiMap);
361  // EXPECT_EQ(allValues.size(), _nEntries);
362  // std::sort(allValues.begin(), allValues.end());
363  // for(int i = 0; i < _nEntries; ++i) {
364  // EXPECT_DOUBLE_EQ(allValues[i], possibleValues[i]);
365  // }
366 
367 
368  }
370 }
function object that implements cosecans(x) == 1/cos(x) by tan(x) / sin(x).
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
double tan(double a)
tan for double
Definition: beamHelpers.h:31
i2dMultiMap createMultiMap(int nEntries, Functor funct)
create a multimap with
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
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.