PLearn 0.1
|
00001 // -*- C++ -*- 00002 00003 // PythonProcessedVMatrix.cc 00004 // 00005 // Copyright (C) 2005 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 /* ******************************************************* 00036 * $Id: .pyskeleton_header 544 2003-09-01 00:05:31Z plearner $ 00037 ******************************************************* */ 00038 00039 // Authors: Nicolas Chapados 00040 00044 #include "PythonProcessedVMatrix.h" 00045 00046 namespace PLearn { 00047 using namespace std; 00048 00049 PLEARN_IMPLEMENT_OBJECT( 00050 PythonProcessedVMatrix, 00051 "Preprocess a source VMatrix using a Python code snippet.", 00052 "The Python code snippet (see the option 'code' below) must define the\n" 00053 "following functions:\n" 00054 "\n" 00055 " from numarray import *\n" 00056 "\n" 00057 " def getRow(row_no, source_row):\n" 00058 " \"\"\"Return a numarray vector containing the processed row, given\n" 00059 " the original (source) row vector at the given row number.\"\"\"\n" 00060 "\n" 00061 " def getFieldNames(source_field_names):\n" 00062 " \"\"\"Return the new fieldnames (after processing), given the\n" 00063 " original fieldnames. This function also determines the NEW\n" 00064 " width of the VMatrix.\"\"\"\n" 00065 "\n" 00066 " def getSizes(source_inputsize, source_targetsize, source_weightsize):\n" 00067 " \"\"\"This function is optional. If defined, return a list of 3 integers\n" 00068 " containing, respectively, the new inputsize, targetsize and\n" 00069 " weightsize.\"\"\"\n" 00070 "\n" 00071 " def build():\n" 00072 " \"\"\"This function is optional. If defined, it is called on the\n" 00073 " build_() of the underlying C++ object. Its purpose is to allow\n" 00074 " matrix-wide processings to be performed and stored in the\n" 00075 " Python snippet internal variables. Use this, for instance,\n" 00076 " to compute matrix-wide means and variances for some normalization\n" 00077 " functions.\"\"\"\n" 00078 "\n" 00079 "The Python code snippet has, in turn, access to the following interface:\n" 00080 "\n" 00081 "- source_field_names (global variable): a Python list of strings containing\n" 00082 " the fieldnames in the source matrix. This is the same as the argument\n" 00083 " passed to 'getFieldNames()' when called.\n" 00084 "\n" 00085 "- source_length (global variable): number of rows in the Source matrix.\n" 00086 " Right now, this cannot be changed and the processed number of rows is\n" 00087 " always the same as the original number of rows.\n" 00088 "\n" 00089 "- source_width (global variable): number of columns in the Source matrix.\n" 00090 "\n" 00091 "- source_inputsize, source_targetsize, source_weightsize (global\n" 00092 " variables): the corresponding fields as they were set in the source\n" 00093 " matrix.\n" 00094 "\n" 00095 "- getSourceRow(row_no): a function that returns a numarray vector\n" 00096 " containing the given row in the source matrix.\n" 00097 "\n" 00098 "In addition, a map (from string to string) named \"params\" is defined as an\n" 00099 "option at the C++ level, and is made available to the Python code via two\n" 00100 "injected functions: getParam(name), and setParam(name,value). Both\n" 00101 "functions directly modify the C++ map, so that there is never any\n" 00102 "synchronization issues, and further enable Python to communicate back some\n" 00103 "'side information' to C++. In addition, the injected function getParams()\n" 00104 "returns the entire map.\n" 00105 ); 00106 00107 00108 //##### Constructor ######################################################### 00109 00110 PythonProcessedVMatrix::PythonProcessedVMatrix() 00111 : inherited(), 00112 m_code(""), 00113 python() 00114 { } 00115 00116 00117 //##### declareOption ####################################################### 00118 00119 void PythonProcessedVMatrix::declareOptions(OptionList& ol) 00120 { 00121 declareOption(ol, "code", &PythonProcessedVMatrix::m_code, 00122 OptionBase::buildoption, 00123 "The Python code snippet used to process the matrix. The functions\n" 00124 "described in the class documentation must be provided. Note that,\n" 00125 "after an initial build(), changing this string calling build() again\n" 00126 "DOES NOT result in the recompilation of the code.\n"); 00127 00128 declareOption(ol, "params", &PythonProcessedVMatrix::m_params, 00129 OptionBase::buildoption, 00130 "General-purpose parameters that are injected into the Python code\n" 00131 "snippet and accessible via the getParam/setParam functions. Can be\n" 00132 "used for passing processing arguments to the Python code.\n"); 00133 00134 // Now call the parent class' declareOptions 00135 inherited::declareOptions(ol); 00136 } 00137 00138 00139 //##### build ############################################################### 00140 00141 void PythonProcessedVMatrix::build() 00142 { 00143 // ### Nothing to add here, simply calls build_ 00144 inherited::build(); 00145 build_(); 00146 } 00147 00148 void PythonProcessedVMatrix::build_() 00149 { 00150 // First step, compile the Python code 00151 compileAndInject(); 00152 PLASSERT( python ); 00153 PLASSERT( source ); 00154 00155 // Next ensure that we have some consistency 00156 if (! python->isInvokable("getRow")) 00157 PLERROR("PythonProcessedVMatrix: the Python code snippet must define the function " 00158 "'getRow'"); 00159 if (! python->isInvokable("getFieldNames")) 00160 PLERROR("PythonProcessedVMatrix: the Python code snippet must define the function " 00161 "'getFieldNames'"); 00162 00163 // Set the global variables into the Python environment 00164 python->setGlobalObject("source_field_names", source->fieldNames()); 00165 python->setGlobalObject("source_length", source->length()); 00166 python->setGlobalObject("source_width", source->width()); 00167 python->setGlobalObject("source_inputsize", source->inputsize()); 00168 python->setGlobalObject("source_targetsize", source->targetsize()); 00169 python->setGlobalObject("source_weightsize", source->weightsize()); 00170 00171 // Get the new fieldnames (and the new width by the same token) 00172 TVec<string> fieldnames = 00173 python->invoke("getFieldNames", source->fieldNames()).as< TVec<string> >(); 00174 length_ = source->length(); 00175 width_ = fieldnames.size(); 00176 declareFieldNames(fieldnames); 00177 00178 // Obtain and set the new sizes if defined 00179 if (python->isInvokable("getSizes")) { 00180 TVec<int> sizes = python->invoke("getSizes", 00181 source->inputsize(), 00182 source->targetsize(), 00183 source->weightsize()).as< TVec<int> >(); 00184 if (sizes.size() != 3) 00185 PLERROR("PythonProcessedVMatrix: the python function 'getSizes' must return\n" 00186 "a vector of integers of size 3 (exactly), containing respectively\n" 00187 "the new inputsize, targetsize and weightsize"); 00188 inputsize_ = sizes[0]; 00189 targetsize_ = sizes[1]; 00190 weightsize_ = sizes[2]; 00191 } 00192 00193 // Finish building from source informations... 00194 setMetaInfoFromSource(); 00195 00196 // Finally, call the Python builder if one exists 00197 if (python->isInvokable("build")) 00198 python->invoke("build"); 00199 } 00200 00201 00202 //##### getNewRow ########################################################### 00203 00204 void PythonProcessedVMatrix::getNewRow(int i, const Vec& v) const 00205 { 00206 // Get the correct row from the source matrix 00207 sourcerow.resize(source->width()); 00208 source->getRow(i, sourcerow); 00209 00210 // Process this row using Python 00211 Vec processed = python->invoke("getRow", i, sourcerow).as<Vec>(); 00212 00213 // Ensure it has the correct size and copy into v 00214 if (processed.size() != v.size()) 00215 PLERROR("PythonProcessedVMatrix: the Python function 'getRow' returned a vector " 00216 "of the wrong length; at row %d, obtained length=%d, expected length=%d", 00217 i, processed.size(), v.size()); 00218 00219 v << processed; 00220 } 00221 00222 00223 //##### getSourceRow ######################################################## 00224 00225 PythonObjectWrapper 00226 PythonProcessedVMatrix::getSourceRow(const TVec<PythonObjectWrapper>& args) const 00227 { 00228 if (args.size() != 1) 00229 PLERROR("PythonProcessedVMatrix::getSourceRow: expected 1 argument; got %d", 00230 args.size()); 00231 int i = args[0].as<int>(); 00232 if (i < 0 || i >= source->length()) 00233 PLERROR("PythonProcessedVMatrix::getSourceRow: desired row number %d is " 00234 "outside the range [0,%d).", i, source->length()); 00235 00236 sourcerow.resize(source->width()); 00237 source->getRow(i, sourcerow); 00238 return PythonObjectWrapper(sourcerow); 00239 } 00240 00241 00242 //##### getParams ########################################################### 00243 00244 PythonObjectWrapper 00245 PythonProcessedVMatrix::getParams(const TVec<PythonObjectWrapper>& args) const 00246 { 00247 if (args.size() != 0) 00248 PLERROR("PythonProcessedVMatrix::getParams: expected 0 argument; got %d", 00249 args.size()); 00250 00251 return PythonObjectWrapper(m_params); 00252 } 00253 00254 00255 //##### getParam ############################################################ 00256 00257 PythonObjectWrapper 00258 PythonProcessedVMatrix::getParam(const TVec<PythonObjectWrapper>& args) const 00259 { 00260 if (args.size() != 1) 00261 PLERROR("PythonProcessedVMatrix::getParam: expected 1 argument; got %d", 00262 args.size()); 00263 string key = args[0].as<string>(); 00264 map<string,string>::const_iterator found = m_params.find(key); 00265 if (found != m_params.end()) 00266 return PythonObjectWrapper(found->second); 00267 else 00268 return PythonObjectWrapper(); // None 00269 } 00270 00271 00272 //##### setParam ############################################################ 00273 00274 PythonObjectWrapper 00275 PythonProcessedVMatrix::setParam(const TVec<PythonObjectWrapper>& args) 00276 { 00277 if (args.size() != 2) 00278 PLERROR("PythonProcessedVMatrix::setParam: expected 2 arguments; got %d", 00279 args.size()); 00280 string key = args[0].as<string>(); 00281 string value = args[1].as<string>(); 00282 m_params[key] = value; 00283 return PythonObjectWrapper(); 00284 } 00285 00286 00287 //##### compileAndInject #################################################### 00288 00289 void PythonProcessedVMatrix::compileAndInject() 00290 { 00291 if (! python) { 00292 python = new PythonCodeSnippet(m_code); 00293 PLASSERT( python ); 00294 python->build(); 00295 python->inject("getSourceRow", this, &PythonProcessedVMatrix::getSourceRow); 00296 python->inject("getParams", this, &PythonProcessedVMatrix::getParams); 00297 python->inject("getParam", this, &PythonProcessedVMatrix::getParam); 00298 python->inject("setParam", this, &PythonProcessedVMatrix::setParam); 00299 } 00300 } 00301 00302 00303 //##### makeDeepCopyFromShallowCopy ######################################### 00304 00305 void PythonProcessedVMatrix::makeDeepCopyFromShallowCopy(CopiesMap& copies) 00306 { 00307 inherited::makeDeepCopyFromShallowCopy(copies); 00308 00309 // Need to recompile the Python code 00310 python = 0; 00311 compileAndInject(); 00312 } 00313 00314 } // end of namespace PLearn 00315 00316 00317 /* 00318 Local Variables: 00319 mode:c++ 00320 c-basic-offset:4 00321 c-file-style:"stroustrup" 00322 c-file-offsets:((innamespace . 0)(inline-open . 0)) 00323 indent-tabs-mode:nil 00324 fill-column:79 00325 End: 00326 */ 00327 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=79 :