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