SHOGUN  v3.0.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MMDKernelSelectionComb.cpp
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 3 of the License, or
5  * (at your option) any later version.
6  *
7  * Written (W) 2012-2013 Heiko Strathmann
8  */
9 
13 
14 using namespace shogun;
15 
18 {
19  init();
20 }
21 
24 {
25  init();
26 }
27 
29 {
30 }
31 
32 void CMMDKernelSelectionComb::init()
33 {
34 #ifdef HAVE_LAPACK
35  SG_ADD(&m_opt_max_iterations, "opt_max_iterations", "Maximum number of "
36  "iterations for qp solver", MS_NOT_AVAILABLE);
37  SG_ADD(&m_opt_epsilon, "opt_epsilon", "Stopping criterion for qp solver",
39  SG_ADD(&m_opt_low_cut, "opt_low_cut", "Low cut value for optimization "
40  "kernel weights", MS_NOT_AVAILABLE);
41 
42  /* sensible values for optimization */
44  m_opt_epsilon=10E-15;
45  m_opt_low_cut=10E-7;
46 #endif
47 }
48 
49 #ifdef HAVE_LAPACK
50 /* no reference counting, use the static context constructor of SGMatrix */
52 
54 {
55  return &m_Q[m_Q.num_rows*i];
56 }
57 
59 void CMMDKernelSelectionComb::print_state(libqp_state_T state)
60 {
61  SG_SDEBUG("CMMDKernelSelectionComb::print_state: libqp state:"
62  " primal=%f\n", state.QP);
63 }
64 
66 {
67  /* cast is safe due to assertion in constructor */
69 
70  /* optimise for kernel weights and set them */
72  combined->set_subkernel_weights(weights);
73 
74  /* note that kernel is SG_REF'ed from getter above */
75  return combined;
76 }
77 
80 {
81  /* readability */
82  index_t num_kernels=mmds.vlen;
83 
84  /* compute sum of mmds to generate feasible point for convex program */
85  float64_t sum_mmds=0;
86  for (index_t i=0; i<mmds.vlen; ++i)
87  sum_mmds+=mmds[i];
88 
89  /* QP: 0.5*x'*Q*x + f'*x
90  * subject to
91  * mmds'*x = b
92  * LB[i] <= x[i] <= UB[i] for all i=1..n */
93  SGVector<float64_t> Q_diag(num_kernels);
94  SGVector<float64_t> f(num_kernels);
95  SGVector<float64_t> lb(num_kernels);
96  SGVector<float64_t> ub(num_kernels);
97  SGVector<float64_t> weights(num_kernels);
98 
99  /* init everything, there are two cases possible: i) at least one mmd is
100  * is positive, ii) all mmds are negative */
101  bool one_pos;
102  for (index_t i=0; i<mmds.vlen; ++i)
103  {
104  if (mmds[i]>0)
105  {
106  SG_DEBUG("found at least one positive MMD\n")
107  one_pos=true;
108  break;
109  }
110  one_pos=false;
111  }
112 
113  if (!one_pos)
114  {
115  SG_WARNING("CMMDKernelSelectionComb::solve_optimization(): all mmd "
116  "estimates are negative. This is techically possible, although "
117  "extremely rare. Consider using different kernels. "
118  "This combination will lead to a bad two-sample test. Since any"
119  "combination is bad, will now just return equally distributed "
120  "kernel weights\n");
121 
122  /* if no element is positive, we can choose arbritary weights since
123  * the results will be bad anyway */
124  weights.set_const(1.0/num_kernels);
125  }
126  else
127  {
128  SG_DEBUG("one MMD entry is positive, performing optimisation\n")
129  /* do optimisation, init vectors */
130  for (index_t i=0; i<num_kernels; ++i)
131  {
132  Q_diag[i]=m_Q(i,i);
133  f[i]=0;
134  lb[i]=0;
135  ub[i]=CMath::INFTY;
136 
137  /* initial point has to be feasible, i.e. mmds'*x = b */
138  weights[i]=1.0/sum_mmds;
139  }
140 
141  /* start libqp solver with desired parameters */
142  SG_DEBUG("starting libqp optimization\n")
143  libqp_state_T qp_exitflag=libqp_gsmo_solver(&get_Q_col, Q_diag.vector,
144  f.vector, mmds.vector,
145  one_pos ? 1 : -1,
146  lb.vector, ub.vector,
147  weights.vector, num_kernels, m_opt_max_iterations,
149 
150  SG_DEBUG("libqp returns: nIts=%d, exit_flag: %d\n", qp_exitflag.nIter,
151  qp_exitflag.exitflag);
152 
153  /* set really small entries to zero and sum up for normalization */
154  float64_t sum_weights=0;
155  for (index_t i=0; i<weights.vlen; ++i)
156  {
157  if (weights[i]<m_opt_low_cut)
158  {
159  SG_DEBUG("lowcut: weight[%i]=%f<%f setting to zero\n", i, weights[i],
160  m_opt_low_cut);
161  weights[i]=0;
162  }
163 
164  sum_weights+=weights[i];
165  }
166 
167  /* normalize (allowed since problem is scale invariant) */
168  for (index_t i=0; i<weights.vlen; ++i)
169  weights[i]/=sum_weights;
170  }
171 
172  return weights;
173 }
174 #else
176 {
177  SG_ERROR("CMMDKernelSelectionComb::select_kernel(): LAPACK needs to be "
178  "installed in order to use weight optimisation for combined "
179  "kernels!\n");
180  return NULL;
181 }
182 
184 {
185  SG_ERROR("CMMDKernelSelectionComb::select_kernel(): LAPACK needs to be "
186  "installed in order to use weight optimisation for combined "
187  "kernels!\n");
188  return SGVector<float64_t>();
189 }
190 
192  SGVector<float64_t> mmds)
193 {
194  SG_ERROR("CMMDKernelSelectionComb::solve_optimization(): LAPACK needs to be "
195  "installed in order to use weight optimisation for combined "
196  "kernels!\n");
197  return SGVector<float64_t>();
198 }
199 #endif

SHOGUN Machine Learning Toolbox - Documentation