test_pf.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2016-2017, the mcl_3dl authors
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of the copyright holder nor the names of its
14  * contributors may be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <cmath>
31 #include <cstddef>
32 #include <vector>
33 
34 #include <mcl_3dl/nd.h>
35 #include <mcl_3dl/pf.h>
36 
37 #include <gtest/gtest.h>
38 
39 class State : public mcl_3dl::pf::ParticleBase<float>
40 {
41 public:
42  float x;
43  float& operator[](const size_t i) override
44  {
45  switch (i)
46  {
47  case 0:
48  return x;
49  }
50  return x;
51  }
52  const float& operator[](const size_t i) const
53  {
54  switch (i)
55  {
56  case 0:
57  return x;
58  }
59  return x;
60  }
61  size_t size() const override
62  {
63  return 1;
64  };
65  explicit State(const float x)
66  {
67  this->x = x;
68  }
70  {
71  x = 0;
72  }
73  void normalize()
74  {
75  }
76 };
77 
78 TEST(Pf, BayesianEstimation)
79 {
81  const float center_list[] =
82  {
83  10.0,
84  11.0,
85  9.5,
86  };
87 
88  const float abs_error = 2e-1;
89  const float sigma = 1.0;
90  const float sigma2 = 2.0;
91 
92  for (auto center : center_list)
93  {
94  for (auto center2 : center_list)
95  {
96  pf.init(
97  State(center),
98  State(sigma));
99 
100  ASSERT_NEAR(center, pf.expectation()[0], abs_error);
101  ASSERT_NEAR(sigma, pf.covariance()[0][0], abs_error);
102 
103  auto likelihood = [center2, sigma2](const State& s) -> float
104  {
105  return std::exp(-std::pow(s[0] - center2, 2) / (2.0 * std::pow(sigma2, 2)));
106  };
107  pf.measure(likelihood);
108 
109  // Numerical representation
110  const int HISTOGRAM_SIZE = 4000;
111  const float HISTOGRAM_RESOLUTION = 0.02;
112 
113  float dist[HISTOGRAM_SIZE];
116  double avg = 0;
117  float total = 0;
118  for (int i = 0; i < HISTOGRAM_SIZE; i++)
119  {
120  const float x = (i - HISTOGRAM_SIZE / 2.0) * HISTOGRAM_RESOLUTION;
121  dist[i] = nd1(x - center) * nd2(x - center2);
122 
123  avg += dist[i] * x;
124  total += dist[i];
125  }
126  avg /= total;
127  double var = 0;
128  for (int i = 0; i < HISTOGRAM_SIZE; i++)
129  {
130  const float x = (i - HISTOGRAM_SIZE / 2.0) * HISTOGRAM_RESOLUTION - avg;
131  var += std::pow(x, 2) * dist[i];
132  }
133  var /= total;
134 
135  // Compare with numerical result
136  ASSERT_NEAR(avg, pf.expectation()[0], abs_error);
137  ASSERT_NEAR(var, pf.covariance()[0][0], abs_error);
138 
139  // Try resampling
140  pf.resample(State(0.0));
141  ASSERT_NEAR(avg, pf.expectation()[0], abs_error);
142  ASSERT_NEAR(var, pf.covariance()[0][0], abs_error);
143 
144  // 50% Random sampled covaliance calculation
145  ASSERT_NEAR(var, pf.covariance(1.0, 0.5)[0][0], abs_error * 2);
146  }
147  }
148 }
149 
150 TEST(Pf, VariableParticleSize)
151 {
152  const size_t size_num = 3;
153  const size_t size[size_num] =
154  {
155  1024,
156  2048,
157  900,
158  };
160 
161  const float center = 12.3;
162  const float sigma = 0.45;
163  pf.init(
164  State(center),
165  State(sigma));
166 
167  for (size_t i = 0; i < size_num; ++i)
168  {
169  ASSERT_EQ(pf.getParticleSize(), size[i]);
170 
171  const State e = pf.expectation();
172  const std::vector<State> v = pf.covariance();
173  ASSERT_LT(fabs(e[0] - center), 1e-1);
174  ASSERT_LT(fabs(std::sqrt(v[0][0]) - sigma), 1e-1);
175 
176  pf.resample(State());
177 
178  const State e_r = pf.expectation();
179  const std::vector<State> v_r = pf.covariance();
180  ASSERT_LT(fabs(e_r[0] - center), 1e-1);
181  ASSERT_LT(fabs(std::sqrt(v_r[0][0]) - sigma), 1e-1);
182 
183  if (i + 1 != size_num)
184  {
185  pf.resizeParticle(size[i + 1]);
186  }
187  }
188 }
189 
190 TEST(Pf, ResampleFlatLikelihood)
191 {
193  const float center = 12.3;
194  const float sigma = 0.45;
195  pf.init(
196  State(center),
197  State(sigma));
198 
199  std::vector<float> orig;
200 
201  for (size_t i = 0; i < pf.getParticleSize(); ++i)
202  orig.push_back(pf.getParticle(i)[0]);
203 
204  pf.resample(State());
205 
206  for (size_t i = 0; i < pf.getParticleSize(); ++i)
207  ASSERT_EQ(pf.getParticle(i)[0], orig[i]);
208 }
209 
210 void testResample(const std::vector<float>& probs, const std::vector<float>& states,
211  const std::vector<float>& expected_resampled_states)
212 {
213  const size_t particle_num = probs.size();
214  mcl_3dl::pf::ParticleFilter<State, float> pf(particle_num, 12345);
215  auto it = pf.begin();
216  for (size_t i = 0; i < particle_num; ++i, ++it)
217  {
218  it->state_.x = states.at(i);
219  it->probability_ = probs.at(i);
220  }
221  pf.resample(State());
222 
223  ASSERT_EQ(particle_num, pf.getParticleSize());
224  for (size_t i = 0; i < pf.getParticleSize(); ++i)
225  {
226  EXPECT_FLOAT_EQ(expected_resampled_states.at(i), pf.getParticle(i)[0]);
227  }
228 }
229 
230 TEST(Pf, ResampleFirstAndLastParticle)
231 {
232  const float small_prob = 1.0e-06f;
233  {
234  SCOPED_TRACE("ResampleFirstParticle");
235  const std::vector<float> probs =
236  {
237  small_prob,
238  0.2f,
239  0.2f,
240  0.2f,
241  0.4f - small_prob,
242  };
243  const std::vector<float> states =
244  {
245  0.0f,
246  1.0f,
247  2.0f,
248  3.0f,
249  4.0f,
250  };
251  const std::vector<float> expected_resampled_states =
252  {
253  1.0f,
254  2.0f,
255  3.0f,
256  4.0f,
257  4.0f,
258  };
259  testResample(probs, states, expected_resampled_states);
260  }
261  {
262  SCOPED_TRACE("ResampleLastParticle");
263  const std::vector<float> probs =
264  {
265  0.2f,
266  0.2f,
267  0.2f,
268  0.4f - small_prob,
269  small_prob,
270  };
271  const std::vector<float> states =
272  {
273  0.0f,
274  1.0f,
275  2.0f,
276  3.0f,
277  4.0f,
278  };
279  const std::vector<float> expected_resampled_states =
280  {
281  0.0f,
282  1.0f,
283  2.0f,
284  3.0f,
285  3.0f,
286  };
287  testResample(probs, states, expected_resampled_states);
288  }
289 }
290 
291 TEST(Pf, Iterators)
292 {
294  const float val0 = 12.3;
295  const float val1 = 45.6;
296  pf.init(
297  State(val0),
298  State(0.0));
299 
300  for (auto it = pf.begin(); it != pf.end(); ++it)
301  {
302  ASSERT_EQ(it->state_[0], val0);
303  it->state_[0] = val1;
304  }
305  for (auto it = pf.begin(); it != pf.end(); ++it)
306  ASSERT_EQ(it->state_[0], val1);
307 }
308 
309 TEST(Pf, AppendParticles)
310 {
312  const float val0 = 12.3;
313  const float val1 = 45.6;
314  pf.init(
315  State(val0),
316  State(0.0));
317  // particles 0-9 has val0
318 
319  for (auto it = pf.appendParticle(10); it != pf.end(); ++it)
320  it->state_[0] = val1;
321  // appended particles 10-19 has val1
322 
323  ASSERT_EQ(pf.getParticleSize(), 20u);
324  for (size_t i = 0; i < 10; ++i)
325  ASSERT_EQ(pf.getParticle(i)[0], val0);
326  for (size_t i = 10; i < 20; ++i)
327  ASSERT_EQ(pf.getParticle(i)[0], val1);
328 }
329 
330 TEST(Pf, Entropy)
331 {
333 
334  // no uncertainty
335  {
336  unsigned int idx = 0;
337  auto likelihood = [&idx](const State& s) -> float
338  {
339  return idx++ == 0 ? 1.0 : 0.0;
340  };
341  pf.init(State(0), State(0.1));
342  pf.measure(likelihood);
343  ASSERT_EQ(pf.getEntropy(), 0);
344  }
345 
346  // uniform distribution
347  {
348  auto likelihood = [](const State& s) -> float
349  {
350  return 0.1;
351  };
352  pf.init(State(0), State(0.1));
353  pf.measure(likelihood);
354  ASSERT_NEAR(pf.getEntropy(), 2.303, 1e-3);
355  }
356 
357  // compare the entropy of two distributions with first one having a narrower peak
358  // i.e. less uncertainty that the other
359  {
360  unsigned int idx = 0;
361  auto likelihood1 = [&idx](const State& s) -> float
362  {
363  float lk = 0.025;
364  if (idx >= 4 && idx < 6)
365  {
366  lk = 0.4;
367  }
368  idx++;
369  return lk;
370  };
371  pf.init(State(0), State(0.1));
372  pf.measure(likelihood1);
373  const float entropy1 = pf.getEntropy();
374 
375  idx = 0;
376  auto likelihood2 = [&idx](const State& s) -> float
377  {
378  float lk = 0.025;
379  if (idx >= 2 && idx < 8)
380  {
381  lk = 0.15;
382  }
383  idx++;
384  return lk;
385  };
386  pf.init(State(0), State(0.1));
387  pf.measure(likelihood2);
388  const float entropy2 = pf.getEntropy();
389  ASSERT_GT(entropy2, entropy1);
390  }
391 }
392 
393 int main(int argc, char** argv)
394 {
395  testing::InitGoogleTest(&argc, argv);
396 
397  return RUN_ALL_TESTS();
398 }
State
Definition: test_pf.cpp:39
mcl_3dl::pf::ParticleFilter::getParticle
T getParticle(const size_t i) const
Definition: pf.h:391
pf.h
testResample
void testResample(const std::vector< float > &probs, const std::vector< float > &states, const std::vector< float > &expected_resampled_states)
Definition: test_pf.cpp:210
mcl_3dl::pf::ParticleFilter::getParticleSize
size_t getParticleSize() const
Definition: pf.h:395
mcl_3dl::pf::ParticleFilter::init
void init(T mean, T sigma)
Definition: pf.h:169
s
XmlRpcServer s
State::operator[]
const float & operator[](const size_t i) const
Definition: test_pf.cpp:52
mcl_3dl::pf::ParticleFilter
Definition: pf.h:160
main
int main(int argc, char **argv)
Definition: test_pf.cpp:393
mcl_3dl::pf::ParticleFilter::resizeParticle
void resizeParticle(const size_t num)
Definition: pf.h:399
State::x
float x
Definition: test_pf.cpp:42
f
f
mcl_3dl::pf::ParticleFilter::appendParticle
std::vector< Particle< T, FLT_TYPE > >::iterator appendParticle(const size_t num)
Definition: pf.h:437
State::normalize
void normalize()
Definition: test_pf.cpp:73
mcl_3dl::pf::ParticleFilter::begin
std::vector< Particle< T, FLT_TYPE > >::iterator begin()
Definition: pf.h:443
mcl_3dl::pf::ParticleFilter::resample
void resample(T sigma)
Definition: pf.h:182
mcl_3dl::pf::ParticleFilter::measure
void measure(std::function< FLT_TYPE(const T &)> likelihood)
Definition: pf.h:252
State::State
State(const float x)
Definition: test_pf.cpp:65
State::operator[]
float & operator[](const size_t i) override
Definition: test_pf.cpp:43
mcl_3dl::NormalLikelihood< float >
State::State
State()
Definition: test_pf.cpp:69
mcl_3dl::pf::ParticleFilter::end
std::vector< Particle< T, FLT_TYPE > >::iterator end()
Definition: pf.h:447
mcl_3dl::pf::ParticleBase
Definition: pf.h:47
nd.h
State::size
size_t size() const override
Definition: test_pf.cpp:61
TEST
TEST(Pf, BayesianEstimation)
Definition: test_pf.cpp:78
mcl_3dl::pf::ParticleFilter::expectation
T expectation(const FLT_TYPE pass_ratio=1.0)
Definition: pf.h:280
mcl_3dl::pf::ParticleFilter::getEntropy
FLT_TYPE getEntropy() const
Definition: pf.h:451
mcl_3dl::pf::ParticleFilter::covariance
std::vector< T > covariance(const FLT_TYPE pass_ratio=1.0, const FLT_TYPE random_sample_ratio=1.0)
Definition: pf.h:304


mcl_3dl
Author(s): Atsushi Watanabe
autogenerated on Thu Oct 17 2024 02:18:04