PLearn 0.1
NetworkModule.cc
Go to the documentation of this file.
00001 // -*- C++ -*-
00002 
00003 // NetworkModule.cc
00004 //
00005 // Copyright (C) 2007 Olivier Delalleau
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: Olivier Delalleau
00036 
00041 #include "NetworkModule.h"
00042 #include <plearn_learners/online/NullModule.h>
00043 
00044 #define PL_LOG_MODULE_NAME "NetworkModule"
00045 #include <plearn/io/pl_log.h>
00046 
00047 namespace PLearn {
00048 using namespace std;
00049 
00050 PLEARN_IMPLEMENT_OBJECT(
00051     NetworkModule,
00052     "A module that encapsulates a whole network of underlying modules.",
00053     "The network is defined by the list of modules, and the list of\n"
00054     "connections between these modules.\n"
00055     "The network's ports are given through the 'ports' option. A typical\n"
00056     "value for 'ports' would be for instance something like:\n"
00057     "   [ \"input\":\"rbm.visible\",\n"
00058     "     \"target\":\"nll.target\",\n"
00059     "     \"output\":\"rbm.hidden\",\n"
00060     "     \"NLL\":\"nll.cost\"\n"
00061     "   ]\n"
00062     "which means this module has four ports (input, target, output and NLL)\n"
00063     "which redirect to specific ports of modules in the network.\n"
00064 );
00065 
00067 // NetworkModule //
00069 NetworkModule::NetworkModule():
00070     save_states(true)
00071 {}
00072 
00074 // declareOptions //
00076 void NetworkModule::declareOptions(OptionList& ol)
00077 {
00078     declareOption(ol, "modules", &NetworkModule::modules,
00079                   OptionBase::buildoption,
00080        "List of modules contained in the network.");
00081 
00082     declareOption(ol, "connections", &NetworkModule::connections,
00083                   OptionBase::buildoption,
00084        "List of connections between modules.");
00085 
00086     declareOption(ol, "ports", &NetworkModule::ports,
00087                   OptionBase::buildoption,
00088        "A sequence of pairs of strings, where each pair is of the form\n"
00089        "\"P\":\"M.N\" with 'M' the name of an underlying module, 'N' one of\n"
00090        "its ports, and 'P' the name under which the NetworkModule sees this\n"
00091        "port. See the class help for an example. If 'P' is an empty string,\n"
00092        "then the port name will be 'M.N'.");
00093 
00094     declareOption(ol, "save_states", &NetworkModule::save_states,
00095                   OptionBase::buildoption,
00096        "If set to 1, then any port of an underlying module whose name ends\n"
00097        "with 'state' will be automatically saved, even if it has no incoming\n"
00098        "or outgoing connection.");
00099 
00100     // Now call the parent class' declareOptions
00101     inherited::declareOptions(ol);
00102 
00103     // Hide unused parent options.
00104 
00105     redeclareOption(ol, "input_size", &NetworkModule::input_size,
00106                     OptionBase::nosave,
00107                     "Not used.");
00108 
00109     redeclareOption(ol, "output_size", &NetworkModule::output_size,
00110                     OptionBase::nosave,
00111                     "Not used.");
00112 
00113 }
00114 
00116 // bpropAccUpdate //
00118 void NetworkModule::bpropAccUpdate(const TVec<Mat*>& ports_value,
00119                                    const TVec<Mat*>& ports_gradient)
00120 {
00121 
00122     // Reset internal gradients, either:
00123     // - to zero if no additional gradient is provided in 'ports_gradient'
00124     // - to the gradient provided in 'ports_gradient' otherwise
00125     PLASSERT( fprop_path.length() == bprop_path.length() );
00126     for (int i = 0; i < bprop_path.length(); i++) {
00127         const TVec<int>& toresize = bprop_toresize[i];
00128         int fprop_idx = fprop_data.length() - 1 - i;
00129         const TVec<Mat*>& f_data = fprop_data[fprop_idx];
00130         for (int j = 0; j < toresize.length(); j++) {
00131             if (j == 0) {
00132                 DBG_MODULE_LOG << "CLEAR: " <<
00133                     all_modules[bprop_path[i]]->classname() << endl;
00134             }
00135             int mat_idx = toresize[j];
00136             DBG_MODULE_LOG << "  grad = " <<
00137                 all_modules[bprop_path[i]]->getPortName(mat_idx) << endl;
00138             Mat* mat_toresize = bprop_data[i][mat_idx];
00139             Mat* mat_tpl = f_data[mat_idx];
00140             mat_toresize->resize(mat_tpl->length(), mat_tpl->width());
00141             mat_toresize->clear();
00142         }
00143         const TMat<int>& f_toplug = fprop_toplug[fprop_idx];
00144         for (int j = 0; j < f_toplug.length(); j++) {
00145             int mod_idx = f_toplug(j, 1);
00146             Mat* current = bprop_data[i][mod_idx];
00147             if (!current)
00148                 // There is no gradient currently computed for this port, thus
00149                 // nothing to initialize.
00150                 continue;
00151             int this_idx = f_toplug(j, 0);
00152             Mat* provided = ports_gradient[this_idx];
00153             if (provided) {
00154                 // We are provided with some additional gradient, either as
00155                 // input or output. If it is an output, there is nothing more
00156                 // to do. However, if it is an input, we must use it to
00157                 // initialize the gradient.
00158                 if (!provided->isEmpty()) {
00159                     PLASSERT( current->length() == provided->length() &&
00160                             current->width() == provided->width() );
00161                     PLASSERT( (*current)(0, 0) == 0 );
00162                     *current << *provided;
00163                 } else {
00164                     // Just for safety reason, crash here. It seems weird that
00165                     // we may want to accumulate the gradient w.r.t. a port for
00166                     // which a gradient is already computed internally (i.e. is
00167                     // used in the bprop). If we want to allow this, care
00168                     // should be taken as to what to do with the current value
00169                     // of the gradient in 'provided' (in particular, do we use
00170                     // it to update the internal paremeters?)
00171                     PLASSERT( false );
00172                 }
00173             }
00174         }
00175     }
00176 
00177     // Backpropagate gradient and update parameters.
00178     for (int i = 0; i < bprop_path.length(); i++) {
00179         PP<OnlineLearningModule> module = all_modules[bprop_path[i]];
00180         DBG_MODULE_LOG << "BPROP: " << module->name << endl;
00181         // First resize some gradient matrices, so that the gradient is
00182         // properly computed.
00183         const TVec<int>& toresize = bprop_toresize[i];
00184         const TVec<Mat*> b_data = bprop_data[i];
00185         for (int j = 0; j < toresize.length(); j++) {
00186             int mat_idx = toresize[j];
00187             DBG_MODULE_LOG << "  grad = " << module->getPortName(mat_idx)
00188                            << endl;
00189             Mat* mat_toresize = b_data[mat_idx];
00190             PLASSERT( mat_toresize->width() > 0 );
00191             mat_toresize->resize(0, mat_toresize->width());
00192         }
00193 
00194         // Plug in the matrices provided as parameters of this method.
00195         int fprop_idx = fprop_data.length() - 1 - i;
00196         const TVec<Mat*> f_data = fprop_data[fprop_idx];
00197         const TMat<int>& f_toplug = fprop_toplug[fprop_idx];
00198         for (int j = 0; j < f_toplug.length(); j++) {
00199             int this_idx = f_toplug(j, 0);
00200             int mod_idx = f_toplug(j, 1);
00201             Mat* provided = ports_gradient[this_idx];
00202             if (provided) {
00203                 Mat* current = b_data[mod_idx];
00204                 if (!current) {
00205                     // We can directly plug in the provided matrix.
00206                     b_data[mod_idx] = provided;
00207                 } else {
00208                     if (!provided->isEmpty()) {
00209                         PLASSERT( !current->isEmpty() );
00210                         // This gradient is some external gradient we are
00211                         // provided with. It has already been added in the step
00212                         // where we reset (see above), thus there is nothing
00213                         // more to do.  TODO Note that it may be cleaner to
00214                         // actually add it here to simplify the code above.
00215                     } else {
00216                         PLASSERT( current->isEmpty() );
00217                         // This gradient must be computed, but is already
00218                         // computed somewhere here. We will copy it after the
00219                         // update step.
00220                         PLASSERT( false); // This should not happen (cf. above)
00221                     }
00222                 }
00223             }
00224             // Same for the fprop data since it is also given in argument.
00225             Mat* f_provided = ports_value[this_idx];
00226             if (f_provided) {
00227                 Mat* f_current = f_data[mod_idx];
00228                 if (!f_current)
00229                     f_data[mod_idx] = f_provided;
00230                 else {
00231                     // 'f_provided' is thus the result of the computation, that
00232                     // was copied from the value of 'f_current' at fprop time.
00233                     // We must fill in 'f_current' with this value (once again,
00234                     // this is not best for efficiency).
00235                     f_current->resize(f_provided->length(),
00236                                       f_provided->width());
00237                     *f_current << *f_provided;
00238                 }
00239             }
00240         }
00241 
00242         // Then perform the bpropUpdate step.
00243         module->bpropAccUpdate(
00244                 fprop_data[fprop_data.length() - 1 - i], bprop_data[i]);
00245 
00246         // Restore the 'bprop_data' and 'fprop_data' parameters.
00247         for (int j = 0; j < f_toplug.length(); j++) {
00248             int this_idx = f_toplug(j, 0);
00249             int mod_idx = f_toplug(j, 1);
00250             Mat* provided = ports_gradient[this_idx];
00251             if (provided) {
00252                 Mat* current = b_data[mod_idx];
00253                 PLASSERT_MSG(current, "This should not happen: if we are "
00254                         "provided with a matrix, it should have been plugged "
00255                         "in here");
00256                 if (provided == current)
00257                     // They are the same matrices: this means there used to be
00258                     // a NULL matrix before.
00259                     b_data[mod_idx] = NULL;
00260                 else {
00261                     // This should never happen.
00262                     PLASSERT( false );
00263                 }
00264             }
00265             Mat* f_provided = ports_value[this_idx];
00266             if (f_provided) {
00267                 Mat* f_current = f_data[mod_idx];
00268                 if (f_current == f_provided)
00269                     // This used to be a null pointer, that must be restored.
00270                     f_data[mod_idx] = NULL;
00271             }
00272         }
00273     }
00274 }
00275 
00276 
00278 // build //
00280 void NetworkModule::build()
00281 {
00282     inherited::build();
00283     build_();
00284 }
00285 
00287 // build_ //
00289 void NetworkModule::build_()
00290 {
00291     // Initialize the 'all_modules' and 'all_connections' lists.
00292     all_modules.resize(modules.length());
00293     all_modules << modules;
00294     all_connections.resize(connections.length());
00295     all_connections << connections;
00296     if (save_states) {
00297         // Ensure all ports which are states are correctly saved.
00298         // This is done by adding connections to a NullModule in order to force
00299         // them being saved.
00300         int count_null = 0;
00301         for (int i = 0; i < modules.length(); i++) {
00302             const TVec<string>& mod_ports = modules[i]->getPorts();
00303             for (int j = 0; j < mod_ports.length(); j++) {
00304                 const string& port = mod_ports[j];
00305                 if (port.size() >= 5 &&
00306                     port.substr(port.size() - 5) == "state")
00307                 {
00308                     // Look for either an incoming or outgoing connection.
00309                     string port_name = modules[i]->name + "." + port;
00310                     bool has_a_connection = false;
00311                     for (int k = 0; k < connections.length(); k++)
00312                         if (connections[k]->destination == port_name ||
00313                             connections[k]->source == port_name)
00314                         {
00315                             has_a_connection = true;
00316                             break;
00317                         }
00318                     if (!has_a_connection) {
00319                         // Currently we create one NullModule / state, since
00320                         // multiple incoming connections are not correctly
00321                         // handled.
00322                         PP<OnlineLearningModule> null;
00323                         /*
00324                         if (all_modules.length() == modules.length()) {
00325                             // No null module added yet.
00326                             */
00327                             null = new NullModule("_null_" +
00328                                     tostring(count_null++));
00329                             all_modules.append(null);
00330                         /*} else {
00331                             PLASSERT( all_modules.length() ==
00332                                         modules.length() + 1 );
00333                             null = all_modules.lastElement();
00334                         }*/
00335                         all_connections.append( new NetworkConnection(
00336                                     modules[i], port, null, "null", false));
00337                     }
00338                 }
00339             }
00340         }
00341     }
00342 
00343     // Forward random number generator to all underlying modules.
00344     if (random_gen) {
00345         for (int i = 0; i < all_modules.length(); i++) {
00346             if (!all_modules[i]->random_gen) {
00347                 all_modules[i]->random_gen = random_gen;
00348                 all_modules[i]->build();
00349                 all_modules[i]->forget();
00350             }
00351         }
00352     }
00353 
00354     // Construct fprop and bprop paths from the list of modules and
00355     // connections.
00356 
00357     // First create a dictionary of modules.
00358     map<string, PP<OnlineLearningModule> > name_to_module;
00359     for (int i = 0; i < all_modules.length(); i++) {
00360         string module = all_modules[i]->name;
00361         if (name_to_module.find(module) != name_to_module.end()) {
00362             // There is already a module with the same name. For safety
00363             // reasons, we make it point to a NULL pointer to ensure we do not
00364             // accidentally use the wrong module.
00365             PLWARNING("In NetworkModule::build_ - There are two modules with "
00366                     "the same name '%s': any connection attempting to use "
00367                     "one of these modules will probably crash",
00368                     module.c_str());
00369             name_to_module[module] = NULL;
00370         } else
00371             name_to_module[module] = all_modules[i];
00372     }
00373 
00374     // Initialize connections.
00375     for (int i = 0; i < all_connections.length(); i++)
00376         all_connections[i]->initialize(name_to_module);
00377 
00378     // Preprocess some convenience data structures from the list of
00379     // connections.
00380 
00381     // 'module_to_index' maps module pointers to their corresponding index in
00382     // the 'all_modules' list.
00383     map<const OnlineLearningModule*, int> module_to_index;
00384     for (int i = 0; i < all_modules.length(); i++)
00385         module_to_index[all_modules[i]] = i;
00386 
00387     // Analyze the list of ports.
00388     // The 'port_correspondances' lists, for each module, the correspondances
00389     // between the modules' ports and the ports of the NetworkModule.
00390     TVec< TMat<int> > port_correspondances(all_modules.length());
00391     for (int i = 0; i < all_modules.length(); i++)
00392         port_correspondances[i].resize(0, 2);
00393     TVec<int> new_row(2);
00394     all_ports.resize(0);
00395     port_sizes.resize(0, 2);
00396     int n_ports = ports.length();
00397     for (int i = 0; i < n_ports; i++) {
00398         const pair<string, string>& new_old = ports[i];
00399         string new_name = new_old.first;
00400         const string& old_name = new_old.second;
00401         if (new_name.empty())
00402             // Empty string: we use the full name of the form 'module.port'.
00403             new_name = old_name;
00404         all_ports.append(new_name);
00405         size_t dot_pos = old_name.find('.');
00406         PLASSERT( dot_pos != string::npos );
00407         string old_module_name = old_name.substr(0, dot_pos);
00408         string old_module_port = old_name.substr(dot_pos + 1);
00409         if ( name_to_module.find(old_module_name) == name_to_module.end())
00410             PLERROR("NetworkModule::Can't find module with name %s\n",old_module_name.c_str());
00411 
00412         new_row[0] = i;
00413         PP<OnlineLearningModule> old_module = name_to_module[old_module_name];
00414         new_row[1] = old_module->getPortIndex(old_module_port);
00415         port_correspondances[module_to_index[old_module]].appendRow(new_row);
00416         new_row[0] = old_module->getPortLength(old_module_port);
00417         new_row[1] = old_module->getPortWidth(old_module_port);
00418         port_sizes.appendRow(new_row);
00419     }
00420 
00421     // The i-th element of 'in_connections' maps each port in the i-th module
00422     // to the connection that has it as destination (there may be only one).
00423     TVec< map<string, PP<NetworkConnection> > > in_connections;
00424     in_connections.resize(all_modules.length());
00425     // The i-th element of 'out_connections' maps each port in the i-th module
00426     // to the connections that have it as source (there may be many).
00427     TVec< map<string, TVec< PP<NetworkConnection> > > > out_connections;
00428     out_connections.resize(all_modules.length());
00429     // The 'inputs_needed' vector contains the number of inputs that must be
00430     // fed to a module before it can compute a fprop.
00431     TVec<int> inputs_needed(all_modules.length(), 0);
00432     // The 'compute_input_of' list gives, for each module M, the indices of
00433     // other modules that take an output of M as input.
00434     TVec< TVec<int> > compute_input_of(all_modules.length());
00435     for (int i = 0; i < all_connections.length(); i++) {
00436         PP<NetworkConnection> connection = all_connections[i];
00437         int src = module_to_index[connection->getSourceModule()];
00438         int dest = module_to_index[connection->getDestinationModule()];
00439         inputs_needed[dest]++;
00440         compute_input_of[src].append(dest);
00441         map<string, PP<NetworkConnection> >& in_conn = in_connections[dest];
00442         if (in_conn.find(connection->getDestinationPort()) != in_conn.end()) {
00443             /*
00444             // The port has more than one incoming connection. Currently, we
00445             // only allow this to happen if the module is a NullModule (this is
00446             // for safety). If in the future we want more modules to allow
00447             // multiple incoming connections, we may get rid of this error.
00448             NullModule* test = dynamic_cast<NullModule*>(
00449                     get_pointer(connection->getDestinationModule()));
00450                     */
00451             // TODO ACTUALLY this does not work, as the fprop/bprop paths may
00452             // not be correctly computed in the case of multiple incoming
00453             // connections. Until this is fixed, multiple incoming connections
00454             // are not allowed.
00455             bool test = false;
00456             if (!test)
00457                 PLERROR("In LearningNetwork::build_ - A port may have only one "
00458                         "incoming connection");
00459         }
00460         in_conn[connection->getDestinationPort()] = connection;
00461         out_connections[src][connection->getSourcePort()].append(connection);
00462     }
00463     // The fprop and bprop paths can now be computed.
00464     fprop_path.resize(0);
00465     bprop_path.resize(all_modules.length());
00466     bprop_path.fill(-1);
00467     TVec<bool> is_done(all_modules.length(), false);
00468     fprop_data.resize(0);
00469     fprop_data_idx.resize(0);
00470     bprop_data.resize(all_modules.length());
00471     bprop_data_idx.resize(all_modules.length());
00472     fprop_toresize.resize(0);
00473     bprop_toresize.resize(all_modules.length());
00474     fprop_toplug.resize(0);
00475     // This is getting a little hackish here... If not enough memory is
00476     // allocated to store the work matrices, then when appending a new Mat to
00477     // 'all_mats', we will have to create a new Storage and copy previous data.
00478     // The problem is then that the pointers in 'fprop_data' and 'bprop_data'
00479     // will become invalid. So an easy fix is to allocate enough memory to
00480     // ensure we never have to resize the Storage later.
00481     int max_n_mats = 1000;
00482     all_mats.resize(max_n_mats);
00483     all_mats.resize(0);
00484     // A vector that stores the index of a module in the fprop path.
00485     TVec<int> module_index_to_path_index(all_modules.length(), -1);
00486     while (is_done.find(false) >= 0) {
00487         bool at_least_one_done = false;
00488         for (int i = 0; i < all_modules.length(); i++) {
00489             if (!is_done[i] && inputs_needed[i] == 0) {
00490                 for (int j = 0; j < compute_input_of[i].length(); j++)
00491                     inputs_needed[compute_input_of[i][j]]--;
00492                 // Save any correspondance between ports of this module and
00493                 // ports of the encapsulating NetworkModule.
00494                 fprop_toplug.append(port_correspondances[i]);
00495                 // Compute the list of matrices that must be provided to this
00496                 // module when doing a fprop and bprop.
00497                 TVec<string> mod_ports = all_modules[i]->getPorts();
00498                 map<string, PP<NetworkConnection> >& in_conn =
00499                     in_connections[i];
00500                 map<string, TVec< PP<NetworkConnection> > >& out_conn =
00501                     out_connections[i];
00502                 TVec<int> fprop_tores;
00503                 TVec<int> bprop_tores;
00504                 TVec<Mat*> fprop_mats;
00505                 TVec<Mat*> bprop_mats;
00506                 TVec<int> fprop_mats_idx;
00507                 TVec<int> bprop_mats_idx;
00508                 int n_in_conn = in_conn.size();
00509                 int n_in_conn_found = 0;
00510                 for (int j = 0; j < mod_ports.length(); j++) {
00511                     int count_inc_conn = in_conn.count(mod_ports[j]);
00512                     if (count_inc_conn > 0) {
00513                         // This port has an incoming connection: it is thus an
00514                         // input, and the corresponding matrices for storing
00515                         // its value and gradient are found by looking at the
00516                         // source port of the connection.
00517                         n_in_conn_found += count_inc_conn;
00518                         PP<NetworkConnection> conn = in_conn[mod_ports[j]];
00519                         int src_mod = module_to_index[conn->getSourceModule()];
00520                         int path_index = module_index_to_path_index[src_mod];
00521                         int port_index = conn->getSourceModule()->getPortIndex(
00522                                 conn->getSourcePort());
00523                         if (port_index < 0)
00524                             PLERROR("In NetworkModule::build_ - no port named"
00525                                     " \"%s\"\n"
00526                                     "in module \"%s\". Valid port names are:\n"
00527                                     "%s\n",
00528                                     conn->getSourcePort().c_str(),
00529                                     conn->getSourceModule()->name.c_str(),
00530                                     tostring(conn->getSourceModule()
00531                                              ->getPorts()).c_str()
00532                                     );
00533                         fprop_mats.append(fprop_data[path_index][port_index]);
00534                         fprop_mats_idx.append(
00535                                 fprop_data_idx[path_index][port_index]);
00536                         if (!conn->propagate_gradient) {
00537                             // This connection does not propagate the gradient,
00538                             // and thus we do not want to accumulate it.
00539                             bprop_mats.append(NULL);
00540                             bprop_mats_idx.append(-1);
00541                         } else {
00542                             int b_idx = all_modules.length() - 1 - path_index;
00543                             bprop_mats.append(bprop_data[b_idx][port_index]);
00544                             bprop_mats_idx.append(
00545                                     bprop_data_idx[b_idx][port_index]);
00546                             bprop_tores.append(j);
00547                         }
00548                         PLASSERT_MSG(
00549                                 out_conn.find(mod_ports[j]) == out_conn.end(),
00550                                 "A port with an incoming connection cannot "
00551                                 "also have an outgoing connection" );
00552                     } else if (out_conn.find(mod_ports[j]) != out_conn.end()) {
00553                         // This port has (at least) one outgoing connection: it
00554                         // is thus an output, and it must be provided with
00555                         // matrices to store its value (and gradient if the
00556                         // connection propagates it).
00557                         all_mats.append(Mat());
00558                         if (all_mats.length() > max_n_mats)
00559                             PLERROR("In NetworkModule::build_ - Will need to "
00560                                     "increase 'max_n_mats'");
00561                         Mat* new_mat = &all_mats.lastElement();
00562                         fprop_mats.append(new_mat);
00563                         fprop_mats_idx.append(all_mats.length() - 1);
00564                         fprop_tores.append(j);
00565                         // Ensure there exists a connection propagating the
00566                         // gradient to this port.
00567                         bool must_store_grad = false;
00568                         const TVec< PP<NetworkConnection> >& out_j =
00569                             out_conn[mod_ports[j]];
00570                         for (int k = 0; k < out_j.length(); k++)
00571                             if (out_j[k]->propagate_gradient) {
00572                                 must_store_grad = true;
00573                                 break;
00574                             }
00575                         if (must_store_grad) {
00576                             all_mats.append(Mat());
00577                             if (all_mats.length() > max_n_mats)
00578                                 PLERROR("In NetworkModule::build_ - Will need "
00579                                         "to increase 'max_n_mats'");
00580                             new_mat = &all_mats.lastElement();
00581                             bprop_mats.append(new_mat);
00582                             bprop_mats_idx.append(all_mats.length() - 1);
00583                         } else {
00584                             // No connection propagating gradient to this port.
00585                             bprop_mats.append(NULL);
00586                             bprop_mats_idx.append(-1);
00587                         }
00588                     } else {
00589                         // This port is not used (we do not provide its value,
00590                         // and we do not care about obtaining it).
00591                         fprop_mats.append(NULL);
00592                         bprop_mats.append(NULL);
00593                         fprop_mats_idx.append(-1);
00594                         bprop_mats_idx.append(-1);
00595                     }
00596                 }
00597                 if (n_in_conn != n_in_conn_found)
00598                     PLERROR("In NetworkModule::build_ - Not all incoming "
00599                             "connections were found for module '%s', please "
00600                             "ensure all destination ports do exist",
00601                             all_modules[i]->name.c_str());
00602                 module_index_to_path_index[i] = fprop_path.length();
00603                 // Update fprop path.
00604                 PLASSERT( fprop_mats.length() == fprop_mats_idx.length() );
00605                 fprop_data.append(fprop_mats);
00606                 fprop_data_idx.append(fprop_mats_idx);
00607                 fprop_toresize.append(fprop_tores);
00608                 fprop_path.append(i);
00609                 // Update bprop path.
00610                 PLASSERT( bprop_mats_idx.length() == bprop_mats.length() );
00611                 int bprop_idx = bprop_path.length() - fprop_path.length();
00612                 bprop_data[bprop_idx] = bprop_mats;
00613                 bprop_data_idx[bprop_idx] = bprop_mats_idx;
00614                 bprop_toresize[bprop_idx] = bprop_tores;
00615                 bprop_path[bprop_idx] = i;
00616 
00617                 is_done[i] = true;
00618                 at_least_one_done = true;
00619             }
00620         }
00621         if (!at_least_one_done) {
00622             // Infinite loop: the algorithm cannot find a module that can be
00623             // propagated at this point.
00624             string err;
00625             for (int i = 0; i < all_modules.length(); i++)
00626                 if (!is_done[i])
00627                     err += all_modules[i]->name + ", ";
00628             PLERROR("In NetworkModule::build_ - Cannot build propagation path "
00629                     "due to the following modules waiting for incoming "
00630                     "connections (there may be an infinite loop): %s",
00631                     err.c_str());
00632         }
00633     }
00634     PLASSERT( module_index_to_path_index.find(-1) == -1 );
00635 }
00636 
00638 // forget //
00640 void NetworkModule::forget()
00641 {
00642     // Forward forget to the underlying modules.
00643     for (int i = 0; i < all_modules.length(); i++) {
00644         PLASSERT( all_modules[i]->random_gen );
00645         all_modules[i]->forget();
00646     }
00647 }
00648 
00650 // fprop //
00652 void NetworkModule::fprop(const Vec& input, Vec& output) const
00653 {
00654     PLERROR("In NetworkModule::fprop - Not implemented for single vector "
00655             "operations");
00656 }
00657 
00658 void NetworkModule::fprop(const TVec<Mat*>& ports_value) {
00659     for (int i = 0; i < fprop_path.length(); i++) {
00660         PP<OnlineLearningModule> module = all_modules[fprop_path[i]];
00661         DBG_MODULE_LOG << "FPROP: " << module->name << endl;
00662 
00663         // First resize some data matrices, so that the outputs are properly
00664         // computed.
00665         const TVec<int>& toresize = fprop_toresize[i];
00666         const TVec<Mat*>& data = fprop_data[i];
00667         for (int j = 0; j < toresize.length(); j++) {
00668             DBG_MODULE_LOG << "  out = " << module->getPortName(toresize[j])
00669                            << endl;
00670             data[toresize[j]]->resize(0, 0);
00671         }
00672 
00673         // Then plug in the extra data provided in this method's fprop.
00674         const TMat<int>& toplug = fprop_toplug[i];
00675         for (int j = 0; j < toplug.length(); j++) {
00676             int swap_1 = toplug(j, 0);
00677             Mat* provided = ports_value[swap_1];
00678             if (!provided)
00679                 // There is no provided matrix for this port, thus no extra
00680                 // information to use or compute.
00681                 continue;
00682             int swap_2 = toplug(j, 1);
00683             Mat* current = data[swap_2];
00684             if (!provided->isEmpty()) {
00685                 // A full matrix is provided. This means the data is readily
00686                 // available (= input), and thus should not have been computed
00687                 // anywhere else.
00688                 PLASSERT( !current );
00689                 data[swap_2] = provided;
00690             } else {
00691                 // An empty matrix is provided. This means we want to compute
00692                 // this data. If it is already computed in the network, we do a
00693                 // copy (may not be the most efficient way to do it though). If
00694                 // it is not already computed, we can directly plug the
00695                 // provided matrix.
00696                 if (!current) {
00697                     data[swap_2] = provided;
00698                 } else {
00699                     // This data should be something we compute.
00700                     PLASSERT( current->isEmpty() );
00701                 }
00702             }
00703         }
00704 
00705         // Now perform fprop of the i-th module.
00706         module->fprop(data);
00707 
00708         // And restore 'data', in addition to copy any computed value that
00709         // needs to be retrieved.
00710         for (int j = 0; j < toplug.length(); j++) {
00711             int swap_1 = toplug(j, 0);
00712             Mat* provided = ports_value[swap_1];
00713             if (!provided)
00714                 continue;
00715             int swap_2 = toplug(j, 1);
00716             Mat* current = data[swap_2];
00717             if (!provided->isEmpty()) {
00718                 // This is either a matrix given as input, or directly plugged
00719                 // in the network. In both cases, the old value in 'data' was a
00720                 // null pointer.
00721                 PLASSERT( current == provided );
00722                 data[swap_2] = NULL;
00723             } else {
00724                 // This is an output we need to copy.
00725                 PLASSERT( current && !current->isEmpty() );
00726                 provided->resize(current->length(), current->width());
00727                 *provided << *current;
00728             }
00729         }
00730     }
00731 }
00732 
00734 // getPorts //
00736 const TVec<string>& NetworkModule::getPorts()
00737 {
00738     return all_ports;
00739 }
00740 
00742 // getPortSizes //
00744 const TMat<int>& NetworkModule::getPortSizes()
00745 {
00746     return port_sizes;
00747 }
00748 
00750 // makeDeepCopyFromShallowCopy //
00752 void NetworkModule::makeDeepCopyFromShallowCopy(CopiesMap& copies)
00753 {
00754     inherited::makeDeepCopyFromShallowCopy(copies);
00755 
00756     deepCopyField(modules, copies);
00757     deepCopyField(connections, copies);
00758     deepCopyField(ports, copies);
00759     deepCopyField(all_modules, copies);
00760     deepCopyField(all_connections, copies);
00761     deepCopyField(fprop_path, copies);
00762     deepCopyField(bprop_path, copies);
00763     deepCopyField(fprop_data_idx, copies);
00764     deepCopyField(fprop_toresize, copies);
00765     deepCopyField(fprop_toplug, copies);
00766     deepCopyField(bprop_data_idx, copies);
00767     deepCopyField(bprop_toresize, copies);
00768     deepCopyField(all_mats, copies);
00769     deepCopyField(all_ports, copies);
00770 
00771     // Special code to handle the deep copy of 'fprop_data' and 'bprop_data'.
00772     // A better way may exist to do this!
00773     fprop_data = TVec< TVec<Mat*> >(fprop_data.length());
00774     bprop_data = TVec< TVec<Mat*> >(bprop_data.length());
00775     PLASSERT( fprop_data.length() == bprop_data.length() );
00776     for (int i = 0; i < fprop_data.length(); i++) {
00777         TVec<Mat*>& fdata = fprop_data[i];
00778         TVec<Mat*>& bdata = bprop_data[i];
00779         const TVec<int>& fdata_idx = fprop_data_idx[i];
00780         const TVec<int>& bdata_idx = bprop_data_idx[i];
00781         fdata.resize(fdata_idx.length());
00782         bdata.resize(bdata_idx.length());
00783         for (int k = 0; k < fdata_idx.length(); k++) {
00784             int idx = fdata_idx[k];
00785             if (idx == -1)
00786                 fdata[k] = NULL;
00787             else
00788                 fdata[k] = &all_mats[idx];
00789         }
00790         for (int k = 0; k < bdata_idx.length(); k++) {
00791             int idx = bdata_idx[k];
00792             if (idx == -1)
00793                 bdata[k] = NULL;
00794             else
00795                 bdata[k] = &all_mats[idx];
00796         }
00797     }
00798 }
00799 
00800 
00801 } // end of namespace PLearn
00802 
00803 
00804 /*
00805   Local Variables:
00806   mode:c++
00807   c-basic-offset:4
00808   c-file-style:"stroustrup"
00809   c-file-offsets:((innamespace . 0)(inline-open . 0))
00810   indent-tabs-mode:nil
00811   fill-column:79
00812   End:
00813 */
00814 // 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