1 #include <string>
2 #include <vector>
3 #include <iostream>
4 #include "testlib/testlib_test.h"
5 #include "vpdl/vpdl_mixture.h"
6 #include "vpdl/vpdl_gaussian.h"
7 #include "vpdl/vpdl_gaussian_sphere.h"
8 #include "vpdl/vpdl_gaussian_indep.h"
9 #include "vnl/vnl_random.h"
10 #ifdef _MSC_VER
11 # include "vcl_msvc_warnings.h"
12 #endif
13
14
15 template <class T>
16 vpdl_gaussian<T, 3>
fit_gaussian(typename std::vector<vnl_vector_fixed<T,3>>::const_iterator begin,typename std::vector<vnl_vector_fixed<T,3>>::const_iterator end)17 fit_gaussian(typename std::vector<vnl_vector_fixed<T, 3>>::const_iterator begin,
18 typename std::vector<vnl_vector_fixed<T, 3>>::const_iterator end)
19 {
20 vnl_vector_fixed<T, 3> mean(T(0));
21 vnl_matrix_fixed<T, 3, 3> covar(T(0));
22 unsigned int count = 0;
23 typedef typename std::vector<vnl_vector_fixed<T, 3>>::const_iterator vitr;
24 for (vitr i = begin; i != end; ++i)
25 {
26 mean += *i;
27 covar += outer_product(*i, *i);
28 ++count;
29 }
30 mean /= T(count);
31 covar /= T(count);
32 covar -= outer_product(mean, mean);
33 return vpdl_gaussian<T, 3>(mean, covar);
34 }
35
36
37 // function to sort distributions by increasing weight
38 template <class T, unsigned int n>
39 bool
weight_less(const vpdl_distribution<T,n> &,const T & w1,const vpdl_distribution<T,n> &,const T & w2)40 weight_less(const vpdl_distribution<T, n> & /*d1*/, const T & w1, const vpdl_distribution<T, n> & /*d2*/, const T & w2)
41 {
42 return w1 < w2;
43 }
44
45 // test the computation of covariance and mean of a mixture
46 // by using mixtures of gaussians obtained from data points
47 template <class T>
48 void
test_covar_mean(T epsilon,const std::string & type_name)49 test_covar_mean(T epsilon, const std::string & type_name)
50 {
51 // an arbitrary collection of data points
52 std::vector<vnl_vector_fixed<T, 3>> data;
53 vnl_random rnd;
54 for (unsigned int i = 0; i < 10; ++i)
55 data.push_back(vnl_vector_fixed<T, 3>(T(rnd.normal64() + 1), T(2 * rnd.normal64()), T(rnd.normal64())));
56 for (unsigned int i = 0; i < 15; ++i)
57 data.push_back(vnl_vector_fixed<T, 3>(T(3 * rnd.normal64() - 1), T(rnd.normal64() + 3), T(1.5 * rnd.normal64())));
58 for (unsigned int i = 0; i < 10; ++i)
59 data.push_back(vnl_vector_fixed<T, 3>(T(rnd.normal64()), T(rnd.normal64() + 3), T(rnd.normal64() + 3)));
60
61 // make a gaussian for each group of data
62 vpdl_gaussian<T, 3> gauss1(fit_gaussian<T>(data.begin(), data.begin() + 10));
63 vpdl_gaussian<T, 3> gauss2(fit_gaussian<T>(data.begin() + 10, data.begin() + 25));
64 vpdl_gaussian<T, 3> gauss3(fit_gaussian<T>(data.begin() + 25, data.begin() + 35));
65 // make a gaussian for all of the data together
66 vpdl_gaussian<T, 3> gaussT(fit_gaussian<T>(data.begin(), data.begin() + 35));
67
68 vpdl_mixture<T, 3> mixture;
69 mixture.insert(gauss1, T(10.0 / 35));
70 mixture.insert(gauss2, T(15.0 / 35));
71 mixture.insert(gauss3, T(10.0 / 35));
72
73 vnl_vector_fixed<T, 3> mean;
74 mixture.compute_mean(mean);
75 TEST_NEAR(("compute_mean <" + type_name + ">").c_str(), (gaussT.mean() - mean).inf_norm(), T(0), epsilon);
76
77 vnl_matrix_fixed<T, 3, 3> covar;
78 mixture.compute_covar(covar);
79 TEST_NEAR(
80 ("compute_covar <" + type_name + ">").c_str(), (gaussT.covariance() - covar).array_inf_norm(), T(0), epsilon);
81 }
82
83
84 template <class T>
85 void
test_mixture_type(T epsilon,const std::string & type_name)86 test_mixture_type(T epsilon, const std::string & type_name)
87 {
88 // create a few sample distributions
89 vnl_vector_fixed<T, 3> mean1(T(1), T(1), T(1));
90 T var1 = T(0.5);
91
92 vnl_vector_fixed<T, 3> mean2(T(2), T(0), T(-3));
93 vnl_vector_fixed<T, 3> var2(T(1), T(0.25), T(2));
94
95 vnl_vector_fixed<T, 3> mean3(T(1), T(0), T(0));
96 vnl_vector_fixed<T, 3> var3(T(0.5), T(1.5), T(0.25));
97
98 std::cout << "=================== fixed<3> =======================" << std::endl;
99 {
100 vpdl_gaussian_sphere<T, 3> gauss1(mean1, var1);
101 vpdl_gaussian_indep<T, 3> gauss2(mean2, var2);
102 vpdl_gaussian_indep<T, 3> gauss3(mean3, var3);
103
104 vpdl_mixture<T, 3> mixture;
105 TEST(("initial num_components <" + type_name + ">").c_str(), mixture.num_components(), 0);
106
107 mixture.insert(gauss1, T(0.2));
108 bool valid = mixture.num_components() == 1;
109 mixture.insert(gauss2, T(0.5));
110 valid = valid && mixture.num_components() == 2;
111 TEST(("num_components after insert <" + type_name + ">").c_str(), valid, true);
112
113 const vpdl_distribution<T, 3> & dist1 = mixture.distribution(0);
114 const vpdl_gaussian_sphere<T, 3> * dist1_ptr = dynamic_cast<const vpdl_gaussian_sphere<T, 3> *>(&dist1);
115 TEST(("distribution access <" + type_name + ">").c_str(),
116 dist1_ptr && dist1_ptr->mean() == gauss1.mean() && dist1_ptr->covariance() == gauss1.covariance(),
117 true);
118 TEST(("weight access <" + type_name + ">").c_str(), mixture.weight(1), T(0.5));
119
120 valid = mixture.remove_last();
121 TEST(("remove last <" + type_name + ">").c_str(), valid && mixture.num_components() == 1, true);
122
123 mixture.insert(gauss2, T(0.3));
124 mixture.insert(gauss3, T(0.15));
125 mixture.set_weight(0, T(0.05));
126 TEST(("set_weight <" + type_name + ">").c_str(), mixture.weight(0), T(0.05));
127
128 mixture.sort();
129 TEST(("default sort <" + type_name + ">").c_str(),
130 mixture.weight(0) == T(0.3) && mixture.weight(1) == T(0.15) && mixture.weight(2) == T(0.05) &&
131 &mixture.distribution(2) == &dist1,
132 true);
133
134 vpdl_mixture<T, 3> mixture2(mixture);
135 mixture.remove_last();
136 mixture.remove_last();
137 mixture.remove_last();
138 const vpdl_gaussian_indep<T, 3> * gauss_indep =
139 dynamic_cast<const vpdl_gaussian_indep<T, 3> *>(&mixture2.distribution(0));
140 TEST(("copy constructor <" + type_name + ">").c_str(),
141 mixture2.num_components() == 3 && mixture2.weight(1) == T(0.15) && gauss_indep != nullptr,
142 true);
143
144 vnl_vector_fixed<T, 3> pt(T(0), T(1.5), T(1));
145 T prob = T(0.1 * gauss1.prob_density(pt) + 0.6 * gauss2.prob_density(pt) + 0.3 * gauss3.prob_density(pt));
146 TEST_NEAR(("probability density <" + type_name + ">").c_str(), mixture2.prob_density(pt), prob, epsilon);
147
148 // test gradient virtual functions against numerical difference
149 vnl_vector_fixed<T, 3> g3;
150 T dp = std::sqrt(epsilon);
151 T den = mixture2.density(pt);
152 T den_x = mixture2.density(pt + vnl_vector_fixed<T, 3>(dp, 0, 0));
153 T den_y = mixture2.density(pt + vnl_vector_fixed<T, 3>(0, dp, 0));
154 T den_z = mixture2.density(pt + vnl_vector_fixed<T, 3>(0, 0, dp));
155 vnl_vector_fixed<T, 3> grad(den_x - den, den_y - den, den_z - den);
156 grad /= dp;
157 T density = mixture2.gradient_density(pt, g3);
158 TEST_NEAR(("gradient density <" + type_name + ">").c_str(), (g3 - grad).inf_norm(), 0, dp);
159 TEST_NEAR(("density <" + type_name + ">").c_str(), density, den, epsilon);
160
161 prob = T(0.1 * gauss1.cumulative_prob(pt) + 0.6 * gauss2.cumulative_prob(pt) + 0.3 * gauss3.cumulative_prob(pt));
162 TEST_NEAR(("cumulative probability <" + type_name + ">").c_str(), mixture2.cumulative_prob(pt), prob, epsilon);
163
164 vnl_vector_fixed<T, 3> pt2(T(10), T(5), T(8));
165 prob = T(0.1 * gauss1.box_prob(pt, pt2) + 0.6 * gauss2.box_prob(pt, pt2) + 0.3 * gauss3.box_prob(pt, pt2));
166 TEST_NEAR(("box probability <" + type_name + ">").c_str(), mixture2.box_prob(pt, pt2), prob, epsilon);
167
168 mixture2.sort(weight_less<T, 3>);
169 TEST(("sort by increasing weight <" + type_name + ">").c_str(),
170 mixture2.weight(0) == T(0.05) && mixture2.weight(1) == T(0.15) && mixture2.weight(2) == T(0.3),
171 true);
172
173 mixture2.normalize_weights();
174 TEST(("normalize <" + type_name + ">").c_str(),
175 mixture2.weight(0) == T(0.1) && mixture2.weight(1) == T(0.3) && mixture2.weight(2) == T(0.6),
176 true);
177 }
178
179 // test the covariance and mean computations
180 test_covar_mean(epsilon, type_name);
181
182 std::cout << "=================== scalar =======================" << std::endl;
183 {
184 vpdl_gaussian<T, 1> gauss1(mean1[0], var1);
185 vpdl_gaussian<T, 1> gauss2(mean2[0], var2[0]);
186 vpdl_gaussian<T, 1> gauss3(mean3[0], var3[0]);
187
188 vpdl_mixture<T, 1> mixture;
189 TEST(("initial num_components <" + type_name + ">").c_str(), mixture.num_components(), 0);
190
191 mixture.insert(gauss1, T(0.2));
192 bool valid = mixture.num_components() == 1;
193 mixture.insert(gauss2, T(0.5));
194 valid = valid && mixture.num_components() == 2;
195 TEST(("num_components after insert <" + type_name + ">").c_str(), valid, true);
196
197 const vpdl_distribution<T, 1> & dist1 = mixture.distribution(0);
198 const vpdl_gaussian<T, 1> * dist1_ptr = dynamic_cast<const vpdl_gaussian<T, 1> *>(&dist1);
199 TEST(("distribution access <" + type_name + ">").c_str(),
200 dist1_ptr && dist1_ptr->mean() == gauss1.mean() && dist1_ptr->covariance() == gauss1.covariance(),
201 true);
202 TEST(("weight access <" + type_name + ">").c_str(), mixture.weight(1), T(0.5));
203
204 valid = mixture.remove_last();
205 TEST(("remove last <" + type_name + ">").c_str(), valid && mixture.num_components() == 1, true);
206
207 mixture.insert(gauss2, T(0.3));
208 mixture.insert(gauss3, T(0.15));
209 mixture.set_weight(0, T(0.05));
210 TEST(("set_weight <" + type_name + ">").c_str(), mixture.weight(0), T(0.05));
211
212 mixture.sort();
213 TEST(("default sort <" + type_name + ">").c_str(),
214 mixture.weight(0) == T(0.3) && mixture.weight(1) == T(0.15) && mixture.weight(2) == T(0.05) &&
215 &mixture.distribution(2) == &dist1,
216 true);
217
218 vpdl_mixture<T, 1> mixture2(mixture);
219 mixture.remove_last();
220 mixture.remove_last();
221 mixture.remove_last();
222 const vpdl_gaussian<T, 1> * gaussian = dynamic_cast<const vpdl_gaussian<T, 1> *>(&mixture2.distribution(0));
223 TEST(("copy constructor <" + type_name + ">").c_str(),
224 mixture2.num_components() == 3 && mixture2.weight(1) == T(0.15) && gaussian != nullptr,
225 true);
226
227 T pt = T(1);
228 T prob = T(0.1 * gauss1.prob_density(pt) + 0.6 * gauss2.prob_density(pt) + 0.3 * gauss3.prob_density(pt));
229 TEST_NEAR(("probability density <" + type_name + ">").c_str(), mixture2.prob_density(pt), prob, epsilon);
230
231 // test gradient virtual functions against numerical difference
232 T g;
233 T dp = std::sqrt(epsilon);
234 T den = mixture2.density(pt);
235 T den_x = mixture2.density(pt + dp);
236 T grad = (den_x - den) / dp;
237 T density = mixture2.gradient_density(pt, g);
238 TEST_NEAR(("gradient density <" + type_name + ">").c_str(), g, grad, dp);
239 TEST_NEAR(("density <" + type_name + ">").c_str(), density, den, epsilon);
240
241 prob = T(0.1 * gauss1.cumulative_prob(pt) + 0.6 * gauss2.cumulative_prob(pt) + 0.3 * gauss3.cumulative_prob(pt));
242 TEST_NEAR(("cumulative probability <" + type_name + ">").c_str(), mixture2.cumulative_prob(pt), prob, epsilon);
243
244 T pt2 = T(10);
245 prob = T(0.1 * gauss1.box_prob(pt, pt2) + 0.6 * gauss2.box_prob(pt, pt2) + 0.3 * gauss3.box_prob(pt, pt2));
246 TEST_NEAR(("box probability <" + type_name + ">").c_str(), mixture2.box_prob(pt, pt2), prob, epsilon);
247
248 mixture2.sort(weight_less<T, 1>);
249 TEST(("sort by increasing weight <" + type_name + ">").c_str(),
250 mixture2.weight(0) == T(0.05) && mixture2.weight(1) == T(0.15) && mixture2.weight(2) == T(0.3),
251 true);
252
253 mixture2.normalize_weights();
254 TEST(("normalize <" + type_name + ">").c_str(),
255 mixture2.weight(0) == T(0.1) && mixture2.weight(1) == T(0.3) && mixture2.weight(2) == T(0.6),
256 true);
257
258 T mean = T(0.1 * gauss1.mean() + 0.6 * gauss2.mean() + 0.3 * gauss3.mean());
259 T cmp_mean;
260 mixture2.compute_mean(cmp_mean);
261 TEST_NEAR(("compute_mean <" + type_name + ">").c_str(), cmp_mean, mean, epsilon);
262
263 T var = T(0.1 * gauss1.covariance() + 0.6 * gauss2.covariance() + 0.3 * gauss3.covariance());
264 var += T(0.1 * gauss1.mean() * gauss1.mean() + 0.6 * gauss2.mean() * gauss2.mean() +
265 0.3 * gauss3.mean() * gauss3.mean());
266 var -= mean * mean;
267 T cmp_var;
268 mixture2.compute_covar(cmp_var);
269 TEST_NEAR(("compute_covar <" + type_name + ">").c_str(), cmp_var, var, epsilon);
270 }
271
272 std::cout << "=================== variable =======================" << std::endl;
273 {
274 vpdl_gaussian_sphere<T> gauss1(mean1.as_ref(), var1);
275 vpdl_gaussian_indep<T> gauss2(mean2.as_ref(), var2.as_ref());
276 vpdl_gaussian_indep<T> gauss3(mean3.as_ref(), var3.as_ref());
277
278 vpdl_mixture<T> mixture;
279 TEST(("initial num_components <" + type_name + ">").c_str(), mixture.num_components(), 0);
280 TEST(("initial dimension <" + type_name + ">").c_str(), mixture.dimension(), 0);
281
282 mixture.insert(gauss1, T(0.2));
283 bool valid = mixture.num_components() == 1;
284 mixture.insert(gauss2, T(0.5));
285 valid = valid && mixture.num_components() == 2;
286 TEST(("num_components after insert <" + type_name + ">").c_str(), valid, true);
287 TEST(("dimension after insert <" + type_name + ">").c_str(), mixture.dimension(), 3);
288
289 const vpdl_distribution<T> & dist1 = mixture.distribution(0);
290 const vpdl_gaussian_sphere<T> * dist1_ptr = dynamic_cast<const vpdl_gaussian_sphere<T> *>(&dist1);
291 TEST(("distribution access <" + type_name + ">").c_str(),
292 dist1_ptr && dist1_ptr->mean() == gauss1.mean() && dist1_ptr->covariance() == gauss1.covariance(),
293 true);
294 TEST(("weight access <" + type_name + ">").c_str(), mixture.weight(1), T(0.5));
295
296 valid = mixture.remove_last();
297 TEST(("remove last <" + type_name + ">").c_str(), valid && mixture.num_components() == 1, true);
298
299 mixture.insert(gauss2, T(0.3));
300 mixture.insert(gauss3, T(0.15));
301 mixture.set_weight(0, T(0.05));
302 TEST(("set_weight <" + type_name + ">").c_str(), mixture.weight(0), T(0.05));
303
304 mixture.sort();
305 TEST(("default sort <" + type_name + ">").c_str(),
306 mixture.weight(0) == T(0.3) && mixture.weight(1) == T(0.15) && mixture.weight(2) == T(0.05) &&
307 &mixture.distribution(2) == &dist1,
308 true);
309
310 vpdl_mixture<T> mixture2(mixture);
311 mixture.remove_last();
312 mixture.remove_last();
313 mixture.remove_last();
314 const vpdl_gaussian_indep<T> * gauss_indep =
315 dynamic_cast<const vpdl_gaussian_indep<T> *>(&mixture2.distribution(0));
316 TEST(("copy constructor <" + type_name + ">").c_str(),
317 mixture2.num_components() == 3 && mixture2.weight(1) == T(0.15) && mixture2.dimension() == 3 && gauss_indep,
318 true);
319
320 vnl_vector_fixed<T, 3> pt(T(0), T(1.5), T(1));
321 T prob = T(0.1 * gauss1.prob_density(pt.as_ref()) + 0.6 * gauss2.prob_density(pt.as_ref()) +
322 0.3 * gauss3.prob_density(pt.as_ref()));
323 TEST_NEAR(("probability density <" + type_name + ">").c_str(), mixture2.prob_density(pt.as_ref()), prob, epsilon);
324
325 // test gradient virtual functions against numerical difference
326 vnl_vector<T> g;
327 T dp = std::sqrt(epsilon);
328 T den = mixture2.density(pt.as_ref());
329 T den_x = mixture2.density((pt + vnl_vector_fixed<T, 3>(dp, 0, 0)).as_ref());
330 T den_y = mixture2.density((pt + vnl_vector_fixed<T, 3>(0, dp, 0)).as_ref());
331 T den_z = mixture2.density((pt + vnl_vector_fixed<T, 3>(0, 0, dp)).as_ref());
332 vnl_vector_fixed<T, 3> grad(den_x - den, den_y - den, den_z - den);
333 grad /= dp;
334 T density = mixture2.gradient_density(pt.as_ref(), g);
335 TEST_NEAR(("gradient density <" + type_name + ">").c_str(), (g - grad).inf_norm(), 0, dp);
336 TEST_NEAR(("density <" + type_name + ">").c_str(), density, den, epsilon);
337
338 prob = T(0.1 * gauss1.cumulative_prob(pt.as_ref()) + 0.6 * gauss2.cumulative_prob(pt.as_ref()) +
339 0.3 * gauss3.cumulative_prob(pt.as_ref()));
340 TEST_NEAR(
341 ("cumulative probability <" + type_name + ">").c_str(), mixture2.cumulative_prob(pt.as_ref()), prob, epsilon);
342
343 vnl_vector_fixed<T, 3> pt2(T(10), T(5), T(8));
344 prob = T(0.1 * gauss1.box_prob(pt.as_ref(), pt2.as_ref()) + 0.6 * gauss2.box_prob(pt.as_ref(), pt2.as_ref()) +
345 0.3 * gauss3.box_prob(pt.as_ref(), pt2.as_ref()));
346 TEST_NEAR(
347 ("box probability <" + type_name + ">").c_str(), mixture2.box_prob(pt.as_ref(), pt2.as_ref()), prob, epsilon);
348
349 mixture2.sort(weight_less<T, 0>);
350 TEST(("sort by increasing weight <" + type_name + ">").c_str(),
351 mixture2.weight(0) == T(0.05) && mixture2.weight(1) == T(0.15) && mixture2.weight(2) == T(0.3),
352 true);
353
354 mixture2.normalize_weights();
355 TEST(("normalize <" + type_name + ">").c_str(),
356 mixture2.weight(0) == T(0.1) && mixture2.weight(1) == T(0.3) && mixture2.weight(2) == T(0.6),
357 true);
358 }
359 }
360
361
362 static void
test_mixture()363 test_mixture()
364 {
365 test_mixture_type(1e-5f, "float");
366 test_mixture_type(1e-13, "double");
367 }
368
369 TESTMAIN(test_mixture);
370