1 // Copyright (C) 2013  Davis E. King (davis@dlib.net)
2 // License: Boost Software License   See LICENSE.txt for the full license.
3 
4 #include "opaque_types.h"
5 #include <dlib/python.h>
6 #include "testing_results.h"
7 #include <dlib/svm.h>
8 #include <chrono>
9 
10 using namespace dlib;
11 using namespace std;
12 
13 namespace py = pybind11;
14 
15 typedef matrix<double,0,1> sample_type;
16 typedef std::vector<std::pair<unsigned long,double> > sparse_vect;
17 
np_to_cpp(const numpy_image<double> & x_,std::vector<matrix<double,0,1>> & samples)18 void np_to_cpp (
19     const numpy_image<double>& x_,
20     std::vector<matrix<double,0,1>>& samples
21 )
22 {
23     auto x = make_image_view(x_);
24     DLIB_CASSERT(x.nc() > 0);
25     DLIB_CASSERT(x.nr() > 0);
26     samples.resize(x.nr());
27     for (long r = 0; r < x.nr(); ++r)
28     {
29         samples[r].set_size(x.nc());
30         for (long c = 0; c < x.nc(); ++c)
31         {
32             samples[r](c) = x[r][c];
33         }
34     }
35 }
36 
np_to_cpp(const numpy_image<double> & x_,const py::array_t<double> & y,std::vector<matrix<double,0,1>> & samples,std::vector<double> & labels)37 void np_to_cpp (
38     const numpy_image<double>& x_,
39     const py::array_t<double>& y,
40     std::vector<matrix<double,0,1>>& samples,
41     std::vector<double>& labels
42 )
43 {
44     DLIB_CASSERT(y.ndim() == 1 && y.size() > 0);
45     labels.assign(y.data(), y.data()+y.size());
46     auto x = make_image_view(x_);
47     DLIB_CASSERT(x.nr() == y.size(), "The x matrix must have as many rows as y has elements.");
48     DLIB_CASSERT(x.nc() > 0);
49     samples.resize(x.nr());
50     for (long r = 0; r < x.nr(); ++r)
51     {
52         samples[r].set_size(x.nc());
53         for (long c = 0; c < x.nc(); ++c)
54         {
55             samples[r](c) = x[r][c];
56         }
57     }
58 }
59 
60 
61 template <typename decision_function>
predict(const decision_function & df,const typename decision_function::kernel_type::sample_type & samp)62 double predict (
63     const decision_function& df,
64     const typename decision_function::kernel_type::sample_type& samp
65 )
66 {
67     typedef typename decision_function::kernel_type::sample_type T;
68     if (df.basis_vectors.size() == 0)
69     {
70         return 0;
71     }
72     else if (is_matrix<T>::value && df.basis_vectors(0).size() != samp.size())
73     {
74         std::ostringstream sout;
75         sout << "Input vector should have " << df.basis_vectors(0).size()
76              << " dimensions, not " << samp.size() << ".";
77         PyErr_SetString( PyExc_ValueError, sout.str().c_str() );
78         throw py::error_already_set();
79     }
80     return df(samp);
81 }
82 
np_to_mat(const py::array_t<double> & samp)83 inline matrix<double,0,1> np_to_mat(
84     const py::array_t<double>& samp
85 )
86 {
87     matrix<double,0,1> temp(samp.size());
88 
89     const auto data = samp.data();
90     for (long i = 0; i < temp.size(); ++i)
91         temp(i) = data[i];
92     return temp;
93 }
94 
95 template <typename decision_function>
normalized_predict(const normalized_function<decision_function> & df,const typename decision_function::kernel_type::sample_type & samp)96 double normalized_predict (
97     const normalized_function<decision_function>& df,
98     const typename decision_function::kernel_type::sample_type& samp
99 )
100 {
101     typedef typename decision_function::kernel_type::sample_type T;
102     if (df.function.basis_vectors.size() == 0)
103     {
104         return 0;
105     }
106     else if (is_matrix<T>::value && df.function.basis_vectors(0).size() != samp.size())
107     {
108         std::ostringstream sout;
109         sout << "Input vector should have " << df.function.basis_vectors(0).size()
110              << " dimensions, not " << samp.size() << ".";
111         PyErr_SetString( PyExc_ValueError, sout.str().c_str() );
112         throw py::error_already_set();
113     }
114     return df(samp);
115 }
116 
117 template <typename decision_function>
normalized_predict_vec(const normalized_function<decision_function> & df,const std::vector<typename decision_function::kernel_type::sample_type> & samps)118 std::vector<double> normalized_predict_vec (
119     const normalized_function<decision_function>& df,
120     const std::vector<typename decision_function::kernel_type::sample_type>& samps
121 )
122 {
123     std::vector<double> out;
124     out.reserve(samps.size());
125     for (const auto& x : samps)
126         out.push_back(normalized_predict(df,x));
127     return out;
128 }
129 
130 template <typename decision_function>
normalized_predict_np_vec(const normalized_function<decision_function> & df,const numpy_image<double> & samps_)131 py::array_t<double> normalized_predict_np_vec (
132     const normalized_function<decision_function>& df,
133     const numpy_image<double>& samps_
134 )
135 {
136     auto samps = make_image_view(samps_);
137 
138     if (df.function.basis_vectors(0).size() != samps.nc())
139     {
140         std::ostringstream sout;
141         sout << "Input vector should have " << df.function.basis_vectors(0).size()
142              << " dimensions, not " << samps.nc() << ".";
143         PyErr_SetString( PyExc_ValueError, sout.str().c_str() );
144         throw py::error_already_set();
145     }
146 
147     py::array_t<double, py::array::c_style> out((size_t)samps.nr());
148     matrix<double,0,1> temp(samps.nc());
149     auto data = out.mutable_data();
150     for (long r = 0; r < samps.nr(); ++r)
151     {
152         for (long c = 0; c < samps.nc(); ++c)
153             temp(c) = samps[r][c];
154         *data++ = df(temp);
155     }
156     return out;
157 }
158 
159 template <typename decision_function>
normalized_predict_np(const normalized_function<decision_function> & df,const py::array_t<double> & samp)160 double normalized_predict_np (
161     const normalized_function<decision_function>& df,
162     const py::array_t<double>& samp
163 )
164 {
165     typedef typename decision_function::kernel_type::sample_type T;
166     if (df.function.basis_vectors.size() == 0)
167     {
168         return 0;
169     }
170     else if (is_matrix<T>::value && df.function.basis_vectors(0).size() != samp.size())
171     {
172         std::ostringstream sout;
173         sout << "Input vector should have " << df.function.basis_vectors(0).size()
174              << " dimensions, not " << samp.size() << ".";
175         PyErr_SetString( PyExc_ValueError, sout.str().c_str() );
176         throw py::error_already_set();
177     }
178     return df(np_to_mat(samp));
179 }
180 
181 template <typename kernel_type>
add_df(py::module & m,const std::string name)182 void add_df (
183     py::module& m,
184     const std::string name
185 )
186 {
187     typedef decision_function<kernel_type> df_type;
188     py::class_<df_type>(m, name.c_str())
189         .def("__call__", &predict<df_type>)
190         .def_property_readonly("alpha", [](const df_type& df) {return df.alpha;})
191         .def_property_readonly("b", [](const df_type& df) {return df.b;})
192         .def_property_readonly("kernel_function", [](const df_type& df) {return df.kernel_function;})
193         .def_property_readonly("basis_vectors", [](const df_type& df) {
194             std::vector<matrix<double,0,1>> temp;
195             for (long i = 0; i < df.basis_vectors.size(); ++i)
196                 temp.push_back(sparse_to_dense(df.basis_vectors(i)));
197             return temp;
198         })
199         .def(py::pickle(&getstate<df_type>, &setstate<df_type>));
200 }
201 
202 template <typename kernel_type>
add_normalized_df(py::module & m,const std::string name)203 void add_normalized_df (
204     py::module& m,
205     const std::string name
206 )
207 {
208     using df_type = normalized_function<decision_function<kernel_type>>;
209 
210     py::class_<df_type>(m, name.c_str())
211         .def("__call__", &normalized_predict<decision_function<kernel_type>>)
212         .def("__call__", &normalized_predict_np<decision_function<kernel_type>>)
213         .def("batch_predict", &normalized_predict_vec<decision_function<kernel_type>>)
214         .def("batch_predict", &normalized_predict_np_vec<decision_function<kernel_type>>)
215         .def_property_readonly("alpha", [](const df_type& df) {return df.function.alpha;})
216         .def_property_readonly("b", [](const df_type& df) {return df.function.b;})
217         .def_property_readonly("kernel_function", [](const df_type& df) {return df.function.kernel_function;})
218         .def_property_readonly("basis_vectors", [](const df_type& df) {
219             std::vector<matrix<double,0,1>> temp;
220             for (long i = 0; i < df.function.basis_vectors.size(); ++i)
221             temp.push_back(sparse_to_dense(df.function.basis_vectors(i)));
222             return temp;
223         })
224     .def_property_readonly("means", [](const df_type& df) {return df.normalizer.means();},
225         "Input vectors are normalized by the equation, (x-means)*invstd_devs, before being passed to the underlying RBF function.")
226     .def_property_readonly("invstd_devs", [](const df_type& df) {return df.normalizer.std_devs();},
227         "Input vectors are normalized by the equation, (x-means)*invstd_devs, before being passed to the underlying RBF function.")
228     .def(py::pickle(&getstate<df_type>, &setstate<df_type>));
229 }
230 
231 template <typename df_type>
get_weights(const df_type & df)232 typename df_type::sample_type get_weights(
233     const df_type& df
234 )
235 {
236     if (df.basis_vectors.size() == 0)
237     {
238         PyErr_SetString( PyExc_ValueError, "Decision function is empty." );
239         throw py::error_already_set();
240     }
241     df_type temp = simplify_linear_decision_function(df);
242     return temp.basis_vectors(0);
243 }
244 
245 template <typename df_type>
get_bias(const df_type & df)246 typename df_type::scalar_type get_bias(
247     const df_type& df
248 )
249 {
250     if (df.basis_vectors.size() == 0)
251     {
252         PyErr_SetString( PyExc_ValueError, "Decision function is empty." );
253         throw py::error_already_set();
254     }
255     return df.b;
256 }
257 
258 template <typename df_type>
set_bias(df_type & df,double b)259 void set_bias(
260     df_type& df,
261     double b
262 )
263 {
264     if (df.basis_vectors.size() == 0)
265     {
266         PyErr_SetString( PyExc_ValueError, "Decision function is empty." );
267         throw py::error_already_set();
268     }
269     df.b = b;
270 }
271 
272 template <typename kernel_type>
add_linear_df(py::module & m,const std::string name)273 void add_linear_df (
274     py::module &m,
275     const std::string name
276 )
277 {
278     typedef decision_function<kernel_type> df_type;
279     py::class_<df_type>(m, name.c_str())
280         .def("__call__", predict<df_type>)
281         .def_property_readonly("weights", &get_weights<df_type>)
282         .def_property("bias", get_bias<df_type>, set_bias<df_type>)
283         .def(py::pickle(&getstate<df_type>, &setstate<df_type>));
284 }
285 
286 // ----------------------------------------------------------------------------------------
287 
radial_basis_kernel__repr__(const radial_basis_kernel<sample_type> & item)288 std::string radial_basis_kernel__repr__(const radial_basis_kernel<sample_type>& item)
289 {
290     std::ostringstream sout;
291     sout << "radial_basis_kernel(gamma="<< item.gamma<<")";
292     return sout.str();
293 }
294 
linear_kernel__repr__(const linear_kernel<sample_type> & item)295 std::string linear_kernel__repr__(const linear_kernel<sample_type>& item)
296 {
297     std::ostringstream sout;
298     sout << "linear_kernel()";
299     return sout.str();
300 }
301 
302 // ----------------------------------------------------------------------------------------
303 
binary_test__str__(const binary_test & item)304 std::string binary_test__str__(const binary_test& item)
305 {
306     std::ostringstream sout;
307     sout << "class1_accuracy: "<< item.class1_accuracy << "  class2_accuracy: "<< item.class2_accuracy;
308     return sout.str();
309 }
binary_test__repr__(const binary_test & item)310 std::string binary_test__repr__(const binary_test& item) { return "< " + binary_test__str__(item) + " >";}
311 
regression_test__str__(const regression_test & item)312 std::string regression_test__str__(const regression_test& item)
313 {
314     std::ostringstream sout;
315     sout << "mean_squared_error: "<< item.mean_squared_error << "  R_squared: "<< item.R_squared;
316     sout << "  mean_average_error: "<< item.mean_average_error << "  mean_error_stddev: "<< item.mean_error_stddev;
317     return sout.str();
318 }
regression_test__repr__(const regression_test & item)319 std::string regression_test__repr__(const regression_test& item) { return "< " + regression_test__str__(item) + " >";}
320 
ranking_test__str__(const ranking_test & item)321 std::string ranking_test__str__(const ranking_test& item)
322 {
323     std::ostringstream sout;
324     sout << "ranking_accuracy: "<< item.ranking_accuracy << "  mean_ap: "<< item.mean_ap;
325     return sout.str();
326 }
ranking_test__repr__(const ranking_test & item)327 std::string ranking_test__repr__(const ranking_test& item) { return "< " + ranking_test__str__(item) + " >";}
328 
329 // ----------------------------------------------------------------------------------------
330 
331 template <typename K>
_normalized_test_binary_decision_function(const normalized_function<decision_function<K>> & dec_funct,const std::vector<typename K::sample_type> & x_test,const std::vector<double> & y_test)332 binary_test  _normalized_test_binary_decision_function (
333     const normalized_function<decision_function<K>>& dec_funct,
334     const std::vector<typename K::sample_type>& x_test,
335     const std::vector<double>& y_test
336 ) { return binary_test(test_binary_decision_function(dec_funct, x_test, y_test)); }
337 
338 template <typename K>
_normalized_test_binary_decision_function_np(const normalized_function<decision_function<K>> & dec_funct,const numpy_image<double> & x_test_,const py::array_t<double> & y_test_)339 binary_test  _normalized_test_binary_decision_function_np (
340     const normalized_function<decision_function<K>>& dec_funct,
341     const numpy_image<double>& x_test_,
342     const py::array_t<double>& y_test_
343 )
344 {
345     std::vector<typename K::sample_type> x_test;
346     std::vector<double> y_test;
347     np_to_cpp(x_test_,y_test_, x_test,y_test);
348     return binary_test(test_binary_decision_function(dec_funct, x_test, y_test));
349 }
350 
351 template <typename K>
_test_binary_decision_function(const decision_function<K> & dec_funct,const std::vector<typename K::sample_type> & x_test,const std::vector<double> & y_test)352 binary_test  _test_binary_decision_function (
353     const decision_function<K>& dec_funct,
354     const std::vector<typename K::sample_type>& x_test,
355     const std::vector<double>& y_test
356 ) { return binary_test(test_binary_decision_function(dec_funct, x_test, y_test)); }
357 
358 template <typename K>
_test_regression_function(const decision_function<K> & reg_funct,const std::vector<typename K::sample_type> & x_test,const std::vector<double> & y_test)359 regression_test _test_regression_function (
360     const decision_function<K>& reg_funct,
361     const std::vector<typename K::sample_type>& x_test,
362     const std::vector<double>& y_test
363 ) { return regression_test(test_regression_function(reg_funct, x_test, y_test)); }
364 
365 template < typename K >
_test_ranking_function1(const decision_function<K> & funct,const std::vector<ranking_pair<typename K::sample_type>> & samples)366 ranking_test _test_ranking_function1 (
367     const decision_function<K>& funct,
368     const std::vector<ranking_pair<typename K::sample_type> >& samples
369 ) { return ranking_test(test_ranking_function(funct, samples)); }
370 
371 template < typename K >
_test_ranking_function2(const decision_function<K> & funct,const ranking_pair<typename K::sample_type> & sample)372 ranking_test _test_ranking_function2 (
373     const decision_function<K>& funct,
374     const ranking_pair<typename K::sample_type>& sample
375 ) { return ranking_test(test_ranking_function(funct, sample)); }
376 
377 // ----------------------------------------------------------------------------------------
378 
379 
setup_auto_train_rbf_classifier(py::module & m)380 void setup_auto_train_rbf_classifier (py::module& m)
381 {
382     m.def("auto_train_rbf_classifier", [](
383         const std::vector<matrix<double,0,1>>& x,
384         const std::vector<double>& y,
385         double max_runtime_seconds,
386         bool be_verbose
387     ) { return auto_train_rbf_classifier(x,y,std::chrono::microseconds((uint64_t)(max_runtime_seconds*1e6)),be_verbose); },
388         py::arg("x"), py::arg("y"), py::arg("max_runtime_seconds"), py::arg("be_verbose")=true,
389 "requires \n\
390     - y contains at least 6 examples of each class.  Moreover, every element in y \n\
391       is either +1 or -1. \n\
392     - max_runtime_seconds >= 0 \n\
393     - len(x) == len(y) \n\
394     - all the vectors in x have the same dimension. \n\
395 ensures \n\
396     - This routine trains a radial basis function SVM on the given binary \n\
397       classification training data.  It uses the svm_c_trainer to do this.  It also \n\
398       uses find_max_global() and 6-fold cross-validation to automatically determine \n\
399       the best settings of the SVM's hyper parameters. \n\
400     - Note that we interpret y[i] as the label for the vector x[i].  Therefore, the \n\
401       returned function, df, should generally satisfy sign(df(x[i])) == y[i] as \n\
402       often as possible. \n\
403     - The hyperparameter search will run for about max_runtime and will print \n\
404       messages to the screen as it runs if be_verbose==true."
405     /*!
406         requires
407             - y contains at least 6 examples of each class.  Moreover, every element in y
408               is either +1 or -1.
409             - max_runtime_seconds >= 0
410             - len(x) == len(y)
411             - all the vectors in x have the same dimension.
412         ensures
413             - This routine trains a radial basis function SVM on the given binary
414               classification training data.  It uses the svm_c_trainer to do this.  It also
415               uses find_max_global() and 6-fold cross-validation to automatically determine
416               the best settings of the SVM's hyper parameters.
417             - Note that we interpret y[i] as the label for the vector x[i].  Therefore, the
418               returned function, df, should generally satisfy sign(df(x[i])) == y[i] as
419               often as possible.
420             - The hyperparameter search will run for about max_runtime and will print
421               messages to the screen as it runs if be_verbose==true.
422     !*/
423     );
424 
425     m.def("auto_train_rbf_classifier", [](
426         const numpy_image<double>& x_,
427         const py::array_t<double>& y_,
428         double max_runtime_seconds,
429         bool be_verbose
430     ) {
431         std::vector<matrix<double,0,1>> x;
432         std::vector<double> y;
433         np_to_cpp(x_,y_, x, y);
434         return auto_train_rbf_classifier(x,y,std::chrono::microseconds((uint64_t)(max_runtime_seconds*1e6)),be_verbose); },
435         py::arg("x"), py::arg("y"), py::arg("max_runtime_seconds"), py::arg("be_verbose")=true,
436 "requires \n\
437     - y contains at least 6 examples of each class.  Moreover, every element in y \n\
438       is either +1 or -1. \n\
439     - max_runtime_seconds >= 0 \n\
440     - len(x.shape(0)) == len(y) \n\
441     - x.shape(1) > 0 \n\
442 ensures \n\
443     - This routine trains a radial basis function SVM on the given binary \n\
444       classification training data.  It uses the svm_c_trainer to do this.  It also \n\
445       uses find_max_global() and 6-fold cross-validation to automatically determine \n\
446       the best settings of the SVM's hyper parameters. \n\
447     - Note that we interpret y[i] as the label for the vector x[i].  Therefore, the \n\
448       returned function, df, should generally satisfy sign(df(x[i])) == y[i] as \n\
449       often as possible. \n\
450     - The hyperparameter search will run for about max_runtime and will print \n\
451       messages to the screen as it runs if be_verbose==true."
452     /*!
453         requires
454             - y contains at least 6 examples of each class.  Moreover, every element in y
455               is either +1 or -1.
456             - max_runtime_seconds >= 0
457             - len(x.shape(0)) == len(y)
458             - x.shape(1) > 0
459         ensures
460             - This routine trains a radial basis function SVM on the given binary
461               classification training data.  It uses the svm_c_trainer to do this.  It also
462               uses find_max_global() and 6-fold cross-validation to automatically determine
463               the best settings of the SVM's hyper parameters.
464             - Note that we interpret y[i] as the label for the vector x[i].  Therefore, the
465               returned function, df, should generally satisfy sign(df(x[i])) == y[i] as
466               often as possible.
467             - The hyperparameter search will run for about max_runtime and will print
468               messages to the screen as it runs if be_verbose==true.
469     !*/
470     );
471 
472 
473     m.def("reduce", [](const normalized_function<decision_function<radial_basis_kernel<matrix<double,0,1>>>>& df,
474             const std::vector<matrix<double,0,1>>& x,
475             long num_bv,
476             double eps)
477         {
478             auto out = df;
479             // null_trainer doesn't use y so we can leave it empty.
480             std::vector<double> y;
481             out.function = reduced2(null_trainer(df.function),num_bv,eps).train(x,y);
482             return out;
483         }, py::arg("df"), py::arg("x"), py::arg("num_basis_vectors"), py::arg("eps")=1e-3
484         );
485 
486     m.def("reduce", [](const normalized_function<decision_function<radial_basis_kernel<matrix<double,0,1>>>>& df,
487             const numpy_image<double>& x_,
488             long num_bv,
489             double eps)
490         {
491             std::vector<matrix<double,0,1>> x;
492             np_to_cpp(x_, x);
493             // null_trainer doesn't use y so we can leave it empty.
494             std::vector<double> y;
495             auto out = df;
496             out.function = reduced2(null_trainer(df.function),num_bv,eps).train(x,y);
497             return out;
498         }, py::arg("df"), py::arg("x"), py::arg("num_basis_vectors"), py::arg("eps")=1e-3,
499 "requires \n\
500     - eps > 0 \n\
501     - num_bv > 0 \n\
502 ensures \n\
503     - This routine takes a learned radial basis function and tries to find a \n\
504       new RBF function with num_basis_vectors basis vectors that approximates \n\
505       the given df() as closely as possible.  In particular, it finds a \n\
506       function new_df() such that new_df(x[i])==df(x[i]) as often as possible. \n\
507     - This is accomplished using a reduced set method that begins by using a \n\
508       projection, in kernel space, onto a random set of num_basis_vectors \n\
509       vectors in x.  Then, L-BFGS is used to further optimize new_df() to match \n\
510       df().  The eps parameter controls how long L-BFGS will run, smaller \n\
511       values of eps possibly giving better solutions but taking longer to \n\
512       execute."
513         /*!
514             requires
515                 - eps > 0
516                 - num_bv > 0
517             ensures
518                 - This routine takes a learned radial basis function and tries to find a
519                   new RBF function with num_basis_vectors basis vectors that approximates
520                   the given df() as closely as possible.  In particular, it finds a
521                   function new_df() such that new_df(x[i])==df(x[i]) as often as possible.
522                 - This is accomplished using a reduced set method that begins by using a
523                   projection, in kernel space, onto a random set of num_basis_vectors
524                   vectors in x.  Then, L-BFGS is used to further optimize new_df() to match
525                   df().  The eps parameter controls how long L-BFGS will run, smaller
526                   values of eps possibly giving better solutions but taking longer to
527                   execute.
528         !*/
529         );
530 }
531 
532 // ----------------------------------------------------------------------------------------
533 
bind_decision_functions(py::module & m)534 void bind_decision_functions(py::module &m)
535 {
536     add_linear_df<linear_kernel<sample_type> >(m, "_decision_function_linear");
537     add_linear_df<sparse_linear_kernel<sparse_vect> >(m, "_decision_function_sparse_linear");
538 
539     add_df<histogram_intersection_kernel<sample_type> >(m, "_decision_function_histogram_intersection");
540     add_df<sparse_histogram_intersection_kernel<sparse_vect> >(m, "_decision_function_sparse_histogram_intersection");
541 
542     add_df<polynomial_kernel<sample_type> >(m, "_decision_function_polynomial");
543     add_df<sparse_polynomial_kernel<sparse_vect> >(m, "_decision_function_sparse_polynomial");
544 
545 
546     py::class_<radial_basis_kernel<sample_type>>(m, "_radial_basis_kernel")
547         .def("__repr__", radial_basis_kernel__repr__)
548         .def_property_readonly("gamma", [](const radial_basis_kernel<sample_type>& k){return k.gamma; });
549 
550     py::class_<linear_kernel<sample_type>>(m, "_linear_kernel")
551         .def("__repr__", linear_kernel__repr__);
552 
553     add_df<radial_basis_kernel<sample_type> >(m, "_decision_function_radial_basis");
554     add_df<sparse_radial_basis_kernel<sparse_vect> >(m, "_decision_function_sparse_radial_basis");
555     add_normalized_df<radial_basis_kernel<sample_type>>(m, "_normalized_decision_function_radial_basis");
556 
557     setup_auto_train_rbf_classifier(m);
558 
559 
560     add_df<sigmoid_kernel<sample_type> >(m, "_decision_function_sigmoid");
561     add_df<sparse_sigmoid_kernel<sparse_vect> >(m, "_decision_function_sparse_sigmoid");
562 
563     m.def("test_binary_decision_function", _normalized_test_binary_decision_function<radial_basis_kernel<sample_type> >,
564         py::arg("function"), py::arg("samples"), py::arg("labels"));
565     m.def("test_binary_decision_function", _normalized_test_binary_decision_function_np<radial_basis_kernel<sample_type> >,
566         py::arg("function"), py::arg("samples"), py::arg("labels"));
567 
568     m.def("test_binary_decision_function", _test_binary_decision_function<linear_kernel<sample_type> >,
569         py::arg("function"), py::arg("samples"), py::arg("labels"));
570     m.def("test_binary_decision_function", _test_binary_decision_function<sparse_linear_kernel<sparse_vect> >,
571         py::arg("function"), py::arg("samples"), py::arg("labels"));
572     m.def("test_binary_decision_function", _test_binary_decision_function<radial_basis_kernel<sample_type> >,
573         py::arg("function"), py::arg("samples"), py::arg("labels"));
574     m.def("test_binary_decision_function", _test_binary_decision_function<sparse_radial_basis_kernel<sparse_vect> >,
575         py::arg("function"), py::arg("samples"), py::arg("labels"));
576     m.def("test_binary_decision_function", _test_binary_decision_function<polynomial_kernel<sample_type> >,
577         py::arg("function"), py::arg("samples"), py::arg("labels"));
578     m.def("test_binary_decision_function", _test_binary_decision_function<sparse_polynomial_kernel<sparse_vect> >,
579         py::arg("function"), py::arg("samples"), py::arg("labels"));
580     m.def("test_binary_decision_function", _test_binary_decision_function<histogram_intersection_kernel<sample_type> >,
581         py::arg("function"), py::arg("samples"), py::arg("labels"));
582     m.def("test_binary_decision_function", _test_binary_decision_function<sparse_histogram_intersection_kernel<sparse_vect> >,
583         py::arg("function"), py::arg("samples"), py::arg("labels"));
584     m.def("test_binary_decision_function", _test_binary_decision_function<sigmoid_kernel<sample_type> >,
585         py::arg("function"), py::arg("samples"), py::arg("labels"));
586     m.def("test_binary_decision_function", _test_binary_decision_function<sparse_sigmoid_kernel<sparse_vect> >,
587         py::arg("function"), py::arg("samples"), py::arg("labels"));
588 
589     m.def("test_regression_function", _test_regression_function<linear_kernel<sample_type> >,
590         py::arg("function"), py::arg("samples"), py::arg("targets"));
591     m.def("test_regression_function", _test_regression_function<sparse_linear_kernel<sparse_vect> >,
592         py::arg("function"), py::arg("samples"), py::arg("targets"));
593     m.def("test_regression_function", _test_regression_function<radial_basis_kernel<sample_type> >,
594         py::arg("function"), py::arg("samples"), py::arg("targets"));
595     m.def("test_regression_function", _test_regression_function<sparse_radial_basis_kernel<sparse_vect> >,
596         py::arg("function"), py::arg("samples"), py::arg("targets"));
597     m.def("test_regression_function", _test_regression_function<histogram_intersection_kernel<sample_type> >,
598         py::arg("function"), py::arg("samples"), py::arg("targets"));
599     m.def("test_regression_function", _test_regression_function<sparse_histogram_intersection_kernel<sparse_vect> >,
600         py::arg("function"), py::arg("samples"), py::arg("targets"));
601     m.def("test_regression_function", _test_regression_function<sigmoid_kernel<sample_type> >,
602         py::arg("function"), py::arg("samples"), py::arg("targets"));
603     m.def("test_regression_function", _test_regression_function<sparse_sigmoid_kernel<sparse_vect> >,
604         py::arg("function"), py::arg("samples"), py::arg("targets"));
605     m.def("test_regression_function", _test_regression_function<polynomial_kernel<sample_type> >,
606         py::arg("function"), py::arg("samples"), py::arg("targets"));
607     m.def("test_regression_function", _test_regression_function<sparse_polynomial_kernel<sparse_vect> >,
608         py::arg("function"), py::arg("samples"), py::arg("targets"));
609 
610     m.def("test_ranking_function", _test_ranking_function1<linear_kernel<sample_type> >,
611         py::arg("function"), py::arg("samples"));
612     m.def("test_ranking_function", _test_ranking_function1<sparse_linear_kernel<sparse_vect> >,
613         py::arg("function"), py::arg("samples"));
614     m.def("test_ranking_function", _test_ranking_function2<linear_kernel<sample_type> >,
615         py::arg("function"), py::arg("sample"));
616     m.def("test_ranking_function", _test_ranking_function2<sparse_linear_kernel<sparse_vect> >,
617         py::arg("function"), py::arg("sample"));
618 
619 
620     py::class_<binary_test>(m, "_binary_test")
621         .def("__str__", binary_test__str__)
622         .def("__repr__", binary_test__repr__)
623         .def_readwrite("class1_accuracy", &binary_test::class1_accuracy,
624             "A value between 0 and 1, measures accuracy on the +1 class.")
625         .def_readwrite("class2_accuracy", &binary_test::class2_accuracy,
626             "A value between 0 and 1, measures accuracy on the -1 class.");
627 
628     py::class_<ranking_test>(m, "_ranking_test")
629         .def("__str__", ranking_test__str__)
630         .def("__repr__", ranking_test__repr__)
631         .def_readwrite("ranking_accuracy", &ranking_test::ranking_accuracy,
632             "A value between 0 and 1, measures the fraction of times a relevant sample was ordered before a non-relevant sample.")
633         .def_readwrite("mean_ap", &ranking_test::mean_ap,
634             "A value between 0 and 1, measures the mean average precision of the ranking.");
635 
636     py::class_<regression_test>(m, "_regression_test")
637         .def("__str__", regression_test__str__)
638         .def("__repr__", regression_test__repr__)
639         .def_readwrite("mean_average_error", &regression_test::mean_average_error,
640             "The mean average error of a regression function on a dataset.")
641         .def_readwrite("mean_error_stddev", &regression_test::mean_error_stddev,
642             "The standard deviation of the absolute value of the error of a regression function on a dataset.")
643         .def_readwrite("mean_squared_error", &regression_test::mean_squared_error,
644             "The mean squared error of a regression function on a dataset.")
645         .def_readwrite("R_squared", &regression_test::R_squared,
646             "A value between 0 and 1, measures the squared correlation between the output of a \n"
647             "regression function and the target values.");
648 }
649 
650 
651 
652