PLearn 0.1
SplitModule.cc
Go to the documentation of this file.
00001 // -*- C++ -*-
00002 
00003 // SplitModule.cc
00004 //
00005 // Copyright (C) 2007 Yoshua Bengio
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: Yoshua Bengio
00036 
00041 #include "SplitModule.h"
00042 
00043 namespace PLearn {
00044 using namespace std;
00045 
00046 PLEARN_IMPLEMENT_OBJECT(
00047     SplitModule,
00048     "Split a single down-port into multiple up-ports or concatenate the up-ports into the down-port",
00049     "There is a single down-port and multiple up-ports.\n"
00050     "After an fprop, we always have that the down-port\n"
00051     "is the horizontal concatenation of the up-ports.\n"
00052     "When the down-port is input and the up-ports are output\n"
00053     "the module SPLITS the input into its output. When the\n"
00054     "up-ports are input and the down-port is output, the module\n"
00055     "CONCATENATES the up-ports into the down-port.\n"
00056     "The user specifies names and sizes of each port.\n"
00057     "The port values are expected to be matrices of the\n"
00058     "same length. The splitting or concatenation operations\n"
00059     "can be seen as performed on each row of these matrices.\n"
00060     );
00061 
00063 // SplitModule //
00065 SplitModule::SplitModule()
00066     : down_port_name("down_port")
00067 {
00068 }
00069 
00071 // declareOptions //
00073 void SplitModule::declareOptions(OptionList& ol)
00074 {
00075     declareOption(ol, "down_port_name", &SplitModule::down_port_name,
00076                   OptionBase::buildoption,
00077                   "Name of the down-port. Default = 'down_port'.");
00078 
00079     declareOption(ol, "up_port_sizes", &SplitModule::up_port_sizes,
00080                   OptionBase::buildoption,
00081                   "Sizes of the up-ports. Mandatory.\n");
00082 
00083     declareOption(ol, "up_port_names", &SplitModule::up_port_names,
00084                   OptionBase::buildoption,
00085                   "Names of the up-ports. Default = ['up_port_1','up_port_2',...].");
00086 
00087     // Now call the parent class' declareOptions
00088     inherited::declareOptions(ol);
00089 
00090     redeclareOption(ol, "input_size", &SplitModule::input_size,
00091                     OptionBase::learntoption,
00092                     "input_size = down_port's size = sum(up_ports_size) but is set automatically.");
00093 
00094     redeclareOption(ol, "output_size", &SplitModule::output_size,
00095                     OptionBase::learntoption,
00096                     "output_size = sum(up_ports_size) but is set automatically.");
00097 }
00098 
00100 // build_ //
00102 void SplitModule::build_()
00103 {
00104     int n_up_ports = up_port_sizes.length();
00105     if (n_up_ports>0) // build cannot be completed until then
00106     {
00107         if (up_port_names.length()==0) // set default up_port_names
00108         {
00109             up_port_names.resize(n_up_ports);
00110             for (int i=0;i<n_up_ports;i++)
00111                 up_port_names[i] = "up_port_" + tostring(i+1);
00112         }
00113         input_size = output_size = sum(up_port_sizes);
00114     }
00115 }
00116 
00117 // ### Nothing to add here, simply calls build_
00118 void SplitModule::build()
00119 {
00120     inherited::build();
00121     build_();
00122 }
00123 
00124 
00125 void SplitModule::makeDeepCopyFromShallowCopy(CopiesMap& copies)
00126 {
00127     inherited::makeDeepCopyFromShallowCopy(copies);
00128 
00129     deepCopyField(up_port_sizes, copies);
00130     deepCopyField(up_port_names, copies);
00131 }
00132 
00134 // fprop //
00136 void SplitModule::fprop(const TVec<Mat*>& ports_value)
00137 {
00138     PLASSERT( ports_value.length() == nPorts() );
00139     PLASSERT_MSG( up_port_sizes.length()>0, "SplitModule should have up_port_sizes specified with length>0");
00140     PLASSERT_MSG( ports_value[0], "SplitModule's down_port should always be connected.\n");
00141     if (!ports_value[0]->isEmpty()) // the down_port is an input, do a SPLIT
00142     {
00143         int start=0;
00144         Mat& input = *ports_value[0];
00145         for (int i=0;i<up_port_sizes.length();i++)
00146         {
00147             int width = up_port_sizes[i];
00148             if (ports_value[i+1])
00149             {
00150                 PLASSERT_MSG(ports_value[i+1]->isEmpty(),"In SplitModule, when the down_port is an input, the up_ports should either be outputs or not connected.\n");
00151                 ports_value[i+1]->resize(input.length(),width);
00152                 *ports_value[i+1] << input.subMatColumns(start,width);
00153             }
00154             start += width;
00155         }
00156         return;
00157     } else // the down_port is an output, do a CONCATENATE
00158     {
00159         int start=0;
00160         Mat& output = *ports_value[0];
00161         // hacky check so that the resize does not fail
00162         PLASSERT_MSG(ports_value[1] && !ports_value[1]->isEmpty(), "In SplitModule, when the down_port is an output, all up_ports should be connected.\n");
00163         output.resize(ports_value[1]->length(),input_size);
00164         for (int i=0;i<up_port_sizes.length();i++)
00165         {
00166             int width = up_port_sizes[i];
00167             PLASSERT_MSG(ports_value[i+1] && !ports_value[i+1]->isEmpty(), "In SplitModule, when the down_port is an output, all up_ports should be connected.\n");
00168             output.subMatColumns(start,width) << *ports_value[i+1];
00169             start += width;
00170         }
00171         return;
00172     }
00173     PLERROR("In SplitModule::fprop - Not implemented for class "
00174             "'%s'", classname().c_str());
00175 }
00176 
00178 // bpropUpdate //
00180 
00181 
00182 void SplitModule::bpropAccUpdate(const TVec<Mat*>& ports_value,
00183                                  const TVec<Mat*>& ports_gradient)
00184 {
00185     PLASSERT( ports_value.length() == nPorts() && ports_gradient.length() == nPorts());
00186     PLASSERT_MSG( up_port_sizes.length()>0, "SplitModule should have up_port_sizes specified with length>0");
00187     if (ports_gradient[0] && ports_gradient[0]->isEmpty()) // the down_port is an input, do a backprop on SPLIT
00188     {
00189         int start=0;
00190         Mat& input_gradient = *ports_gradient[0];
00191         for (int i=0;i<up_port_sizes.length();i++)
00192         {
00193             int width = up_port_sizes[i];
00194             PLASSERT_MSG( ports_gradient[i+1] &&
00195                           !ports_gradient[i+1]->isEmpty(),
00196                           "In SplitModule::bpropAccUpdate - When the down_port"
00197                           " is an input and its gradient is required, one must"
00198                           " provide a gradient on the up_ports" );
00199             input_gradient.resize(ports_gradient[i+1]->length(),
00200                                   input_gradient.width());
00201             input_gradient.subMatColumns(start, width) += *ports_gradient[i+1];
00202             start += width;
00203         }
00204         return;
00205     }
00206     bool one_of_the_up_ports_wants_a_gradient = false;
00207     for (int i=0;i<up_port_sizes.length();i++)
00208         if (ports_gradient[i+1] && ports_gradient[i+1]->isEmpty())
00209         {
00210             one_of_the_up_ports_wants_a_gradient = true;
00211             break;
00212         }
00213     if (one_of_the_up_ports_wants_a_gradient)
00214     // the down_port is an output, do a CONCATENATE
00215     {
00216         int start=0;
00217         Mat& output_gradient = *ports_gradient[0];
00218 
00219         for (int i=0;i<up_port_sizes.length();i++)
00220         {
00221             int width = up_port_sizes[i];
00222             if (ports_gradient[i+1] && ports_gradient[i+1]->isEmpty())
00223             {
00224                 ports_gradient[i+1]->resize(output_gradient.length(),width);
00225                 (*ports_gradient[i+1]) += output_gradient.subMatColumns(start,width);
00226             }
00227             start += width;
00228         }
00229         return;
00230     }
00231     //PLERROR("In SplitModule::fprop - This configuration of ports not implemented for class "
00232     //       "'%s'", classname().c_str());
00233 }
00234 
00236 // getPorts //
00238 const TVec<string>& SplitModule::getPorts() {
00239     if (port_names.isEmpty())
00240     {
00241         port_names.resize(nPorts());
00242         port_names[0] = down_port_name;
00243         port_names.subVec(1,nPorts()-1) << up_port_names;
00244     }
00245     return port_names;
00246 }
00247 
00249 // getPortSizes //
00251 const TMat<int>& SplitModule::getPortSizes() {
00252     if (sizes.isEmpty())
00253     {
00254         sizes.resize(nPorts(),2);
00255         sizes.column(0).fill(-1);
00256         sizes(0,1) = input_size;
00257         sizes.subMat(1,1,nPorts()-1,1) << up_port_sizes.toMat(nPorts()-1,1);
00258     }
00259     return sizes;
00260 }
00261 
00263 // nPorts //
00265 int SplitModule::nPorts()
00266 {
00267     return 1+up_port_sizes.length();
00268 }
00269 
00270 
00271 }
00272 // end of namespace PLearn
00273 
00274 
00275 /*
00276   Local Variables:
00277   mode:c++
00278   c-basic-offset:4
00279   c-file-style:"stroustrup"
00280   c-file-offsets:((innamespace . 0)(inline-open . 0))
00281   indent-tabs-mode:nil
00282   fill-column:79
00283   End:
00284 */
00285 // 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