PLearn 0.1
|
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 :