1 // Copyright (C) 2009  Davis E. King (davis@dlib.net)
2 // License: Boost Software License   See LICENSE.txt for the full license.
3 
4 #include "tester.h"
5 #include <dlib/svm.h>
6 #include <dlib/rand.h>
7 #include <dlib/string.h>
8 #include <vector>
9 #include <sstream>
10 #include <ctime>
11 
12 namespace
13 {
14     using namespace test;
15     using namespace dlib;
16     using namespace std;
17     dlib::logger dlog("test.ekm_and_lisf");
18 
19 
20     class empirical_kernel_map_tester : public tester
21     {
22         /*!
23             WHAT THIS OBJECT REPRESENTS
24                 This object represents a unit test.  When it is constructed
25                 it adds itself into the testing framework.
26         !*/
27     public:
empirical_kernel_map_tester()28         empirical_kernel_map_tester (
29         ) :
30             tester (
31                 "test_ekm_and_lisf",       // the command line argument name for this test
32                 "Run tests on the empirical_kernel_map and linearly_independent_subset_finder objects.", // the command line argument description
33                 0                     // the number of command line arguments for this test
34             )
35         {
36             thetime = time(0);
37         }
38 
39         time_t thetime;
40         dlib::rand rnd;
41 
42         template <typename T>
validate(const T & ekm_small,const T & ekm_big)43         void validate (
44             const T& ekm_small,
45             const T& ekm_big
46         )
47         {
48             matrix<double> tmat;
49             projection_function<typename T::kernel_type> proj;
50 
51             ekm_small.get_transformation_to(ekm_big, tmat, proj);
52             DLIB_TEST(tmat.nr() == ekm_big.out_vector_size());
53             DLIB_TEST(tmat.nc() == ekm_small.out_vector_size());
54             DLIB_TEST((unsigned long)proj.basis_vectors.size() == ekm_big.basis_size() - ekm_small.basis_size());
55             for (unsigned long i = 0; i < 6; ++i)
56             {
57                 const typename T::sample_type temp = randm(4,1,rnd);
58                 DLIB_TEST(length(ekm_big.project(temp) - (tmat*ekm_small.project(temp) + proj(temp))) < 1e-10);
59             }
60         }
61 
62 
test_transformation_stuff()63         void test_transformation_stuff()
64         {
65             typedef matrix<double,0,1> sample_type;
66             typedef radial_basis_kernel<sample_type> kernel_type;
67             const kernel_type kern(1);
68 
69 
70             for (unsigned long n = 1; n < 6; ++n)
71             {
72                 print_spinner();
73                 for (unsigned long extra = 1; extra < 10; ++extra)
74                 {
75                     std::vector<sample_type> samps_small, samps_big;
76                     linearly_independent_subset_finder<kernel_type> lisf_small(kern, 1000);
77                     linearly_independent_subset_finder<kernel_type> lisf_big(kern, 1000);
78                     for (unsigned long i = 0; i < n; ++i)
79                     {
80                         samps_small.push_back(randm(4,1,rnd));
81                         samps_big.push_back(samps_small.back());
82                         lisf_big.add(samps_small.back());
83                         lisf_small.add(samps_small.back());
84                     }
85                     for (unsigned long i = 0; i < extra; ++i)
86                     {
87                         samps_big.push_back(randm(4,1,rnd));
88                         lisf_big.add(samps_big.back());
89                     }
90 
91 
92                     // test no lisf
93                     {
94                         empirical_kernel_map<kernel_type> ekm_small, ekm_big;
95                         ekm_small.load(kern, samps_small);
96                         ekm_big.load(kern, samps_big);
97 
98                         validate(ekm_small, ekm_big);
99                     }
100 
101                     // test with lisf
102                     {
103                         empirical_kernel_map<kernel_type> ekm_small, ekm_big;
104                         ekm_small.load(lisf_small);
105                         ekm_big.load(lisf_big);
106 
107                         validate(ekm_small, ekm_big);
108                     }
109 
110                     // test with partly lisf
111                     {
112                         empirical_kernel_map<kernel_type> ekm_small, ekm_big;
113                         ekm_small.load(kern, samps_small);
114                         ekm_big.load(lisf_big);
115 
116                         validate(ekm_small, ekm_big);
117                     }
118 
119                     // test with partly lisf
120                     {
121                         empirical_kernel_map<kernel_type> ekm_small, ekm_big;
122                         ekm_small.load(lisf_small);
123                         ekm_big.load(kern, samps_big);
124 
125                         validate(ekm_small, ekm_big);
126                     }
127 
128                 }
129             }
130 
131 
132             // test what happens if the bigger ekm only has repeated basis vectors
133             {
134                 empirical_kernel_map<kernel_type> ekm_big, ekm_small;
135                 std::vector<sample_type> samps_big, samps_small;
136 
137                 sample_type temp = randm(4,1,rnd);
138 
139                 samps_small.push_back(temp);
140                 samps_big.push_back(temp);
141                 samps_big.push_back(temp);
142 
143                 ekm_big.load(kern, samps_big);
144                 ekm_small.load(kern, samps_small);
145 
146                 validate(ekm_small, ekm_big);
147 
148             }
149             {
150                 empirical_kernel_map<kernel_type> ekm_big, ekm_small;
151                 linearly_independent_subset_finder<kernel_type> lisf_small(kern, 1000);
152                 std::vector<sample_type> samps_big;
153 
154                 sample_type temp = randm(4,1,rnd);
155 
156                 lisf_small.add(temp);
157                 samps_big.push_back(temp);
158                 samps_big.push_back(temp);
159 
160                 ekm_big.load(kern, samps_big);
161                 ekm_small.load(lisf_small);
162 
163                 validate(ekm_small, ekm_big);
164 
165             }
166             {
167                 empirical_kernel_map<kernel_type> ekm_big, ekm_small;
168                 std::vector<sample_type> samps_big, samps_small;
169 
170                 sample_type temp = randm(4,1,rnd);
171                 sample_type temp2 = randm(4,1,rnd);
172 
173                 samps_small.push_back(temp);
174                 samps_small.push_back(temp2);
175                 samps_big.push_back(temp);
176                 samps_big.push_back(temp2);
177                 samps_big.push_back(randm(4,1,rnd));
178 
179                 ekm_big.load(kern, samps_big);
180                 ekm_small.load(kern, samps_small);
181 
182                 validate(ekm_small, ekm_big);
183 
184             }
185             {
186                 empirical_kernel_map<kernel_type> ekm_big, ekm_small;
187                 linearly_independent_subset_finder<kernel_type> lisf_small(kern, 1000);
188                 std::vector<sample_type> samps_big;
189 
190                 sample_type temp = randm(4,1,rnd);
191                 sample_type temp2 = randm(4,1,rnd);
192 
193                 lisf_small.add(temp);
194                 lisf_small.add(temp2);
195                 samps_big.push_back(temp);
196                 samps_big.push_back(temp2);
197                 samps_big.push_back(temp);
198 
199                 ekm_big.load(kern, samps_big);
200                 ekm_small.load(lisf_small);
201 
202                 validate(ekm_small, ekm_big);
203 
204             }
205 
206 
207         }
208 
209 
210 
perform_test()211         void perform_test (
212         )
213         {
214             ++thetime;
215             typedef matrix<double,0,1> sample_type;
216             //dlog << LINFO << "time seed: " << thetime;
217             //rnd.set_seed(cast_to_string(thetime));
218 
219 
220             typedef radial_basis_kernel<sample_type> kernel_type;
221 
222 
223             for (int n = 1; n < 10; ++n)
224             {
225                 print_spinner();
226                 dlog << LINFO << "matrix size " << n;
227 
228                 std::vector<sample_type> samples;
229                 // make some samples
230                 for (int i = 0; i < n; ++i)
231                 {
232                     samples.push_back(randm(4,1,rnd));
233                     // double up the samples just to mess with the lisf
234                     if (n > 5)
235                         samples.push_back(samples.back());
236                 }
237 
238                 dlog << LINFO << "samples.size(): "<< samples.size();
239 
240                 const kernel_type kern(1);
241 
242                 linearly_independent_subset_finder<kernel_type> lisf(kern, 100, 1e-4);
243                 unsigned long count = 0;
244                 for (unsigned long i = 0; i < samples.size(); ++i)
245                 {
246                     if (lisf.add(samples[i]))
247                     {
248                         DLIB_TEST(equal(lisf[lisf.size()-1], samples[i]));
249                         ++count;
250                     }
251                 }
252                 DLIB_TEST(count == lisf.size());
253 
254                 DLIB_TEST(lisf.size() == (unsigned int)n);
255 
256 
257                 dlog << LINFO << "lisf.size(): "<< lisf.size();
258 
259                 // make sure the kernel matrices coming out of the lisf are correct
260                 DLIB_TEST(dlib::equal(lisf.get_kernel_matrix(), kernel_matrix(kern, lisf), 1e-8));
261                 DLIB_TEST(dlib::equal(lisf.get_inv_kernel_marix(), inv(kernel_matrix(kern, lisf.get_dictionary())), 1e-8));
262 
263                 empirical_kernel_map<kernel_type> ekm;
264                 ekm.load(lisf);
265                 DLIB_TEST(ekm.basis_size() == lisf.size());
266 
267                 std::vector<sample_type> proj_samples;
268                 for (unsigned long i = 0; i < samples.size(); ++i)
269                 {
270                     double err;
271                     proj_samples.push_back(ekm.project(samples[i], err));
272                     DLIB_TEST(err <= 1e-4);
273                     const double error_agreement = std::abs(err - lisf.projection_error(samples[i]));
274                     dlog << LTRACE << "err: " << err << "    error_agreement: "<< error_agreement;
275                     DLIB_TEST(error_agreement < 1e-11);
276                 }
277 
278                 for (int i = 0; i < 5; ++i)
279                 {
280                     sample_type temp = randm(4,1,rnd);
281                     double err;
282                     ekm.project(temp, err);
283                     const double error_agreement = std::abs(err - lisf.projection_error(temp));
284                     dlog << LTRACE << "err: " << err << "    error_agreement: "<< error_agreement;
285                     DLIB_TEST(error_agreement < 1e-11);
286                 }
287 
288                 // make sure the EKM did the projection correctly
289                 DLIB_TEST(dlib::equal(kernel_matrix(kern, samples), kernel_matrix(linear_kernel<sample_type>(), proj_samples), 1e-5));
290             }
291 
292 
293             test_transformation_stuff();
294 
295         }
296     };
297 
298     // Create an instance of this object.  Doing this causes this test
299     // to be automatically inserted into the testing framework whenever this cpp file
300     // is linked into the project.  Note that since we are inside an unnamed-namespace
301     // we won't get any linker errors about the symbol a being defined multiple times.
302     empirical_kernel_map_tester a;
303 
304 }
305 
306 
307