PLearn 0.1
PythonProcessedVMatrix.cc
Go to the documentation of this file.
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 :
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines