PLearn 0.1
SummationKernel.cc
Go to the documentation of this file.
00001 // -*- C++ -*-
00002 
00003 // SummationKernel.cc
00004 //
00005 // Copyright (C) 2007 Nicolas Chapados
00006 //
00007 // Redistribution and use in source and binary forms, with or without
00008 // modification, are permitted provided that the following conditions are met:
00009 //
00010 //  1. Redistributions of source code must retain the above copyright
00011 //     notice, this list of conditions and the following disclaimer.
00012 //
00013 //  2. Redistributions in binary form must reproduce the above copyright
00014 //     notice, this list of conditions and the following disclaimer in the
00015 //     documentation and/or other materials provided with the distribution.
00016 //
00017 //  3. The name of the authors may not be used to endorse or promote
00018 //     products derived from this software without specific prior written
00019 //     permission.
00020 //
00021 // THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
00022 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00023 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
00024 // NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00025 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
00026 // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00027 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00028 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00029 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00030 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00031 //
00032 // This file is part of the PLearn library. For more information on the PLearn
00033 // library, go to the PLearn Web site at www.plearn.org
00034 
00035 // Authors: Nicolas Chapados
00036 
00039 #include "SummationKernel.h"
00040 #include <plearn/base/stringutils.h>
00041 #include <plearn/base/lexical_cast.h>
00042 #include <plearn/vmat/SelectColumnsVMatrix.h>
00043 
00044 namespace PLearn {
00045 using namespace std;
00046 
00047 PLEARN_IMPLEMENT_OBJECT(
00048     SummationKernel,
00049     "Kernel computing the sum of other kernels",
00050     "This kernel computes the summation of several subkernel objects.  It can\n"
00051     "also chop up parts of its input vector and send it to each kernel (so that\n"
00052     "each kernel can operate on a subset of the variables).\n"
00053     );
00054 
00055 
00056 //#####  Constructor  #########################################################
00057 
00058 SummationKernel::SummationKernel()
00059 { }
00060 
00061 
00062 //#####  declareOptions  ######################################################
00063 
00064 void SummationKernel::declareOptions(OptionList& ol)
00065 {
00066     declareOption(
00067         ol, "terms", &SummationKernel::m_terms, OptionBase::buildoption,
00068         "Individual kernels to add to produce the final result.  The\n"
00069         "hyperparameters of kernel i can be accesed under the option names\n"
00070         "'terms[i].hyperparam' for, e.g. GaussianProcessRegressor.\n");
00071 
00072     declareOption(
00073         ol, "input_indexes", &SummationKernel::m_input_indexes,
00074         OptionBase::buildoption,
00075         "Optionally, one can specify which of individual input variables should\n"
00076         "be routed to each kernel.  The format is as a vector of vectors: for\n"
00077         "each kernel in 'terms', one must list the INDEXES in the original input\n"
00078         "vector(zero-based) that should be passed to that kernel.  If a list of\n"
00079         "indexes is empty for a given kernel, it means that the COMPLETE input\n"
00080         "vector should be passed to the kernel.\n");
00081     
00082     // Now call the parent class' declareOptions
00083     inherited::declareOptions(ol);
00084 }
00085 
00086 
00087 //#####  build  ###############################################################
00088 
00089 void SummationKernel::build()
00090 {
00091     // ### Nothing to add here, simply calls build_
00092     inherited::build();
00093     build_();
00094 }
00095 
00096 
00097 //#####  build_  ##############################################################
00098 
00099 void SummationKernel::build_()
00100 {
00101     // Preallocate buffers for kernel evaluation
00102     const int N = m_input_indexes.size();
00103     m_input_buf1.resize(N);
00104     m_input_buf2.resize(N);
00105     for (int i=0 ; i<N ; ++i) {
00106         const int M = m_input_indexes[i].size();
00107         m_input_buf1[i].resize(M);
00108         m_input_buf2[i].resize(M);
00109     }
00110 
00111     // Kernel is symmetric only if all terms are
00112     is_symmetric = true;
00113     for (int i=0, n=m_terms.size() ; i<n ; ++i) {
00114         if (! m_terms[i])
00115             PLERROR("SummationKernel::build_: kernel for term[%d] is not specified",i);
00116         is_symmetric = is_symmetric && m_terms[i]->is_symmetric;
00117     }
00118 
00119     if (m_input_indexes.size() > 0 && m_terms.size() != m_input_indexes.size())
00120         PLERROR("SummationKernel::build_: if 'input_indexes' is specified "
00121                 "it must have the same size (%d) as 'terms'; found %d elements",
00122                 m_terms.size(), m_input_indexes.size());
00123 }
00124 
00125 
00126 //#####  setDataForKernelMatrix  ##############################################
00127 
00128 void SummationKernel::setDataForKernelMatrix(VMat the_data)
00129 {
00130     inherited::setDataForKernelMatrix(the_data);
00131     bool split_inputs = m_input_indexes.size() > 0;
00132     for (int i=0, n=m_terms.size() ; i<n ; ++i) {
00133         if (split_inputs && m_input_indexes[i].size() > 0) {
00134             VMat sub_inputs = new SelectColumnsVMatrix(the_data, m_input_indexes[i]);
00135             m_terms[i]->setDataForKernelMatrix(sub_inputs);
00136         }
00137         else
00138             m_terms[i]->setDataForKernelMatrix(the_data);
00139     }
00140 }
00141 
00142 
00143 //#####  addDataForKernelMatrix  ##############################################
00144 
00145 void SummationKernel::addDataForKernelMatrix(const Vec& newRow)
00146 {
00147     inherited::addDataForKernelMatrix(newRow);
00148     bool split_inputs = m_input_indexes.size() > 0;
00149     for (int i=0, n=m_terms.size() ; i<n ; ++i) {
00150         if (split_inputs && m_input_indexes[i].size() > 0) {
00151             selectElements(newRow, m_input_indexes[i], m_input_buf1[i]);
00152             m_terms[i]->addDataForKernelMatrix(m_input_buf1[i]);
00153         }
00154         else
00155             m_terms[i]->addDataForKernelMatrix(newRow);
00156     }
00157 }
00158 
00159 
00160 //#####  evaluate  ############################################################
00161 
00162 real SummationKernel::evaluate(const Vec& x1, const Vec& x2) const
00163 {
00164     real kernel_value = 0.0;
00165     bool split_inputs = m_input_indexes.size() > 0;
00166     for (int i=0, n=m_terms.size() ; i<n ; ++i) {
00167         if (split_inputs && m_input_indexes[i].size() > 0) {
00168             selectElements(x1, m_input_indexes[i], m_input_buf1[i]);
00169             selectElements(x2, m_input_indexes[i], m_input_buf2[i]);
00170             kernel_value += m_terms[i]->evaluate(m_input_buf1[i],
00171                                                  m_input_buf2[i]);
00172         }
00173         else
00174             kernel_value += m_terms[i]->evaluate(x1,x2);
00175     }
00176     return kernel_value;
00177 }
00178 
00179 
00180 //#####  evaluate_i_x  ########################################################
00181 
00182 real SummationKernel::evaluate_i_x(int j, const Vec& x, real) const
00183 {
00184     real kernel_value = 0.0;
00185     bool split_inputs = m_input_indexes.size() > 0;
00186     for (int i=0, n=m_terms.size() ; i<n ; ++i) {
00187         if (split_inputs && m_input_indexes[i].size() > 0) {
00188             selectElements(x, m_input_indexes[i], m_input_buf1[i]);
00189             kernel_value += m_terms[i]->evaluate_i_x(j, m_input_buf1[i]);
00190         }
00191         else
00192             kernel_value += m_terms[i]->evaluate_i_x(j, x);
00193     }
00194     return kernel_value;
00195 }
00196 
00197 
00198 //#####  evaluate_all_i_x  ####################################################
00199 
00200 void SummationKernel::evaluate_all_i_x(const Vec& x, const Vec& k_xi_x,
00201                                        real sq_norm_of_x, int istart) const
00202 {
00203     k_xi_x.fill(0.0);
00204     m_eval_buf.resize(k_xi_x.size());
00205     bool split_inputs = m_input_indexes.size() > 0;
00206     for (int i=0, n=m_terms.size() ; i<n ; ++i) {
00207         // Note: if we slice x, we cannot rely on sq_norm_of_x any more...
00208         if (split_inputs && m_input_indexes[i].size() > 0) {
00209             selectElements(x, m_input_indexes[i], m_input_buf1[i]);
00210             m_terms[i]->evaluate_all_i_x(m_input_buf1[i], m_eval_buf, -1, istart);
00211         }
00212         else
00213             m_terms[i]->evaluate_all_i_x(x, m_eval_buf, sq_norm_of_x, istart);
00214 
00215         k_xi_x += m_eval_buf;
00216     }
00217 }
00218 
00219 //#####  computeGramMatrix  ###################################################
00220 
00221 void SummationKernel::computeGramMatrix(Mat K) const
00222 {
00223     // Assume that K has the right size; will have error in subkernels
00224     // evaluation if not the right size in any case.
00225     m_gram_buf.resize(K.width(), K.length());
00226 
00227     for (int i=0, n=m_terms.size() ; i<n ; ++i) {
00228         if (i==0)
00229             m_terms[i]->computeGramMatrix(K);
00230         else {
00231             m_terms[i]->computeGramMatrix(m_gram_buf);
00232             K += m_gram_buf;
00233         }
00234     }
00235 }
00236 
00237 
00238 //#####  computeGramMatrixDerivative  #########################################
00239 void SummationKernel::computeGramMatrixDerivative(
00240     Mat& KD, const string& kernel_param, real epsilon) const
00241 {
00242     // Find which term we want to compute the derivative for
00243     if (string_begins_with(kernel_param, "terms[")) {
00244         string::size_type rest = kernel_param.find("].");
00245         if (rest == string::npos)
00246             PLERROR("%s: malformed hyperparameter name for computing derivative '%s'",
00247                     __FUNCTION__, kernel_param.c_str());
00248 
00249         string sub_param  = kernel_param.substr(rest+2);
00250         string term_index = kernel_param.substr(6,rest-6); // len("terms[") == 6
00251         int i = lexical_cast<int>(term_index);
00252         if (i < 0 || i >= m_terms.size())
00253             PLERROR("%s: out of bounds access to term %d when computing derivative\n"
00254                     "for kernel parameter '%s'; only %d terms (0..%d) are available\n"
00255                     "in the SummationKernel", __FUNCTION__, i, kernel_param.c_str(),
00256                     m_terms.size(), m_terms.size()-1);
00257         
00258         m_terms[i]->computeGramMatrixDerivative(KD, sub_param, epsilon);
00259     }
00260     else
00261         inherited::computeGramMatrixDerivative(KD, kernel_param, epsilon);
00262 
00263     // Compare against finite differences
00264     // Mat KD1;
00265     // Kernel::computeGramMatrixDerivative(KD1, kernel_param, epsilon);
00266     // cerr << "Kernel hyperparameter: " << kernel_param << endl;
00267     // cerr << "Analytic derivative (15th row):" << endl
00268     //      << KD(15) << endl
00269     //      << "Finite differences:" << endl
00270     //      << KD1(15) << endl;
00271 }
00272 
00273 
00274 //#####  makeDeepCopyFromShallowCopy  #########################################
00275 
00276 void SummationKernel::makeDeepCopyFromShallowCopy(CopiesMap& copies)
00277 {
00278     inherited::makeDeepCopyFromShallowCopy(copies);
00279 
00280     deepCopyField(m_terms,          copies);
00281     deepCopyField(m_input_indexes,  copies);
00282     deepCopyField(m_input_buf1,     copies);
00283     deepCopyField(m_input_buf2,     copies);
00284     deepCopyField(m_gram_buf,       copies);
00285 }
00286 
00287 } // end of namespace PLearn
00288 
00289 
00290 /*
00291   Local Variables:
00292   mode:c++
00293   c-basic-offset:4
00294   c-file-style:"stroustrup"
00295   c-file-offsets:((innamespace . 0)(inline-open . 0))
00296   indent-tabs-mode:nil
00297   fill-column:79
00298   End:
00299 */
00300 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=79 :
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines