nnfw/src/neuralnet.cpp

00001 /********************************************************************************
00002  *  Neural Network Framework.                                                   *
00003  *  Copyright (C) 2005-2011 Gianluca Massera <emmegian@yahoo.it>                *
00004  *                                                                              *
00005  *  This program is free software; you can redistribute it and/or modify        *
00006  *  it under the terms of the GNU General Public License as published by        *
00007  *  the Free Software Foundation; either version 2 of the License, or           *
00008  *  (at your option) any later version.                                         *
00009  *                                                                              *
00010  *  This program is distributed in the hope that it will be useful,             *
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of              *
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               *
00013  *  GNU General Public License for more details.                                *
00014  *                                                                              *
00015  *  You should have received a copy of the GNU General Public License           *
00016  *  along with this program; if not, write to the Free Software                 *
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  *
00018  ********************************************************************************/
00019 
00020 #include "neuralnet.h"
00021 
00022 namespace farsa {
00023 
00024 NeuralNet::NeuralNet() {
00025     dimUps = 0;
00026 }
00027 
00028 NeuralNet::~NeuralNet() {
00029 }
00030 
00031 void NeuralNet::addCluster( Cluster* c, bool isInput, bool isOutput ) {
00032 #ifdef FARSA_DEBUG
00033     if ( !c ) {
00034         qWarning() << "Null Pointer passed to addCluster! This operation will be ignored" ;
00035         return;
00036     }
00037 #endif
00038     // Check if the Cluster is already added
00039     if ( find( c ) ) {
00040 #ifdef FARSA_DEBUG
00041         qWarning() << "Cluster already added! addCluster will be ignored" ;
00042 #endif
00043         return;
00044     }
00045     clustersv.append( c );
00046     if ( isInput ) {
00047         inclusters.append( c );
00048     }
00049     if ( isOutput ) {
00050         outclusters.append( c );
00051     }
00052     if ( !isInput && !isOutput ) {
00053         hidclusters.append( c );
00054     }
00055     clsMap[c->name()] = c;
00056     return;
00057 }
00058 
00059 bool NeuralNet::removeCluster( Cluster* c ) {
00060 #ifdef FARSA_DEBUG
00061     if ( !c ) {
00062         qWarning() << "Null Pointer passed to removeCluster! This operation will return false" ;
00063         return false;
00064     }
00065 #endif
00066     if ( !find( c ) ) {
00067         return false;
00068     }
00069     clustersv.removeOne( c );
00070     inclusters.removeOne( c );
00071     outclusters.removeOne( c );
00072     hidclusters.removeOne( c );
00073     clsMap.remove( c->name() );
00074     return true;
00075 }
00076 
00077 void NeuralNet::markAsInput( Cluster* c ) {
00078 #ifdef FARSA_DEBUG
00079     if ( !c ) {
00080         qWarning() << "Null Pointer passed to addCluster! This operation will be ignored" ;
00081         return;
00082     }
00083 #endif
00084     // Check if the Cluster exists
00085     if ( !find( c ) ) {
00086 #ifdef FARSA_DEBUG
00087         qWarning() << "attempt to mark a Cluster not present in this net!" ;
00088 #endif
00089         return;
00090     }
00091     if ( inclusters.contains( c ) ) {
00092         return;
00093     }
00094     inclusters.append( c );
00095     hidclusters.removeOne( c );
00096 }
00097 
00098 void NeuralNet::markAsOutput( Cluster* c ) {
00099 #ifdef FARSA_DEBUG
00100     if ( !c ) {
00101         qWarning() << "Null Pointer passed to addCluster! This operation will be ignored" ;
00102         return;
00103     }
00104 #endif
00105     // Check if the Cluster exists
00106     if ( !find( c ) ) {
00107 #ifdef FARSA_DEBUG
00108         qWarning() << "attempt to mark a Cluster not present in this net!" ;
00109 #endif
00110         return;
00111     }
00112     if ( outclusters.contains( c ) ) {
00113         return;
00114     }
00115     outclusters.append( c );
00116     hidclusters.removeOne( c );
00117 }
00118 
00119 void NeuralNet::unmark( Cluster* c ) {
00120 #ifdef FARSA_DEBUG
00121     if ( !c ) {
00122         qWarning() << "Null Pointer passed to addCluster! This operation will be ignored" ;
00123         return;
00124     }
00125 #endif
00126     // Check if the Cluster exists
00127     if ( !find( c ) ) {
00128 #ifdef FARSA_DEBUG
00129         qWarning() << "attempt to unmark a Cluster not present in this net!" ;
00130 #endif
00131         return;
00132     }
00133     inclusters.removeOne( c );
00134     outclusters.removeOne( c );
00135     hidclusters.append( c );
00136     return;
00137 }
00138 
00139 void NeuralNet::unmarkAll( ) {
00140     inclusters.clear();
00141     outclusters.clear();
00142     hidclusters = clustersv;
00143     return;
00144 }
00145 
00146 bool NeuralNet::isIsolated( Cluster* c ) const {
00147 #ifdef FARSA_DEBUG
00148     if ( !c ) {
00149         qWarning() << "Null Pointer passed to isIsolato! This operation will return false" ;
00150         return false;
00151     }
00152 #endif
00153     return ( inLinks.count( c ) == 0 && outLinks.count( c ) == 0 );
00154 }
00155 
00156 ClusterList NeuralNet::clusters() const {
00157     return clustersv;
00158 }
00159 
00160 ClusterList NeuralNet::inputClusters() const {
00161     return inclusters;
00162 }
00163 
00164 ClusterList NeuralNet::outputClusters() const {
00165     return outclusters;
00166 }
00167 
00168 ClusterList NeuralNet::hiddenClusters() const {
00169     return hidclusters;
00170 }
00171 
00172 void NeuralNet::addLinker( Linker* l ) {
00173 #ifdef FARSA_DEBUG
00174     if ( !l ) {
00175         qWarning() << "Null Pointer passed to addLinker! This operation will be ignored" ;
00176         return;
00177     }
00178 #endif
00179     // Check if the Linker is already added
00180     if ( find( l ) ) {
00181 #ifdef FARSA_DEBUG
00182         qWarning() << "Linker already added! addLinker will be ignored" ;
00183 #endif
00184         return;
00185     }
00186 #ifdef FARSA_DEBUG
00187     // --- Check: Are There in this net the Clusters that linker l connects ???
00188     if ( ! find( l->from() ) ) {
00189         qWarning() << "The linker that you want add links cluster" << l->from()->name() << "that doesn't exist in this net! This operation will be ignored" ;
00190         return;
00191     }
00192     if ( ! find( l->to() ) ) {
00193         qWarning() << "The linker that you want add links cluster" << l->to()->name() << "that doesn't exist in this net! This operation will be ignored" ;
00194         return;
00195     }
00196 #endif
00197     linkersv.append( l );
00198     // Adding information in outLinks map
00199     outLinks[ l->from() ].append( l );
00200     // Adding information in inLinks map
00201     inLinks[ l->to() ].append( l );
00202     lksMap[l->name()] = l;
00203     return;
00204 }
00205 
00206 bool NeuralNet::removeLinker( Linker* l ) {
00207 #ifdef FARSA_DEBUG
00208     if ( !l ) {
00209         qWarning() << "Null Pointer passed to removeLinker! This operation will return false" ;
00210         return false;
00211     }
00212 #endif
00213     if ( !find(l) ) {
00214         return false;
00215     }
00216     linkersv.removeOne( l );
00217     outLinks[ l->from() ].removeOne( l );
00218     inLinks[ l->to() ].removeOne( l );
00219     lksMap.remove( l->name() );
00220     return true;
00221 }
00222 
00223 LinkerList NeuralNet::linkers() const {
00224     return linkersv;
00225 }
00226 
00227 LinkerList NeuralNet::linkers( Cluster* c, bool out ) const {
00228 #ifdef FARSA_DEBUG
00229     if ( !c ) {
00230         qWarning() << "Null Pointer passed to linkers! This operation will return an empty LinkerList" ;
00231         return LinkerList();
00232     }
00233 #endif
00234     if ( out ) {
00235         // Return outgoing linkers
00236         if ( outLinks.contains( c ) ) {
00237             return outLinks[c];
00238         }
00239     } else {
00240         // Return incoming linkers
00241         if ( inLinks.contains( c ) ) {
00242             return inLinks[c];
00243         }
00244     }
00245     return LinkerList();
00246 }
00247 
00248 void NeuralNet::setOrder( Updatable* u[], unsigned int dim ) {
00249     ups.clear();
00250     for( unsigned int i = 0; i<dim; i++ ) {
00251         if ( find( u[i] ) ) {
00252             ups.append( u[i] );
00253         }
00254 #ifdef FARSA_DEBUG
00255         else {
00256             qWarning() << "In the Updatable order list passed there are some Clusters and/or Linkers not present in this NeuralNet";
00257         }
00258 #endif
00259     }
00260     dimUps = ups.size();
00261     return;
00262 }
00263 
00264 void NeuralNet::setOrder( const UpdatableList& u ) {
00265     ups.clear();
00266     unsigned int dim = u.size();
00267     for( unsigned int i = 0; i<dim; i++ ) {
00268         if ( find( u[i] ) ) {
00269             ups.append( u[i] );
00270         }
00271 #ifdef FARSA_DEBUG
00272         else {
00273             qWarning() << "In the Updatable order list passed there are some Clusters and/or Linkers not present in this NeuralNet";
00274         }
00275 #endif
00276     }
00277     dimUps = ups.size();
00278     return;
00279 }
00280 
00281 void NeuralNet::randomize( double min, double max ) {
00282     int dim = clustersv.size();
00283     for( int i=0; i<dim; i++ ) {
00284         clustersv[i]->randomize( min, max );
00285     }
00286     dim = linkersv.size();
00287     for( int i=0; i<dim; i++ ) {
00288         linkersv[i]->randomize( min, max );
00289     }
00290 }
00291 
00292 Updatable* NeuralNet::getByName( QString name ) {
00293     if ( clsMap.contains( name ) ) {
00294         return clsMap[name];
00295     }
00296     if ( lksMap.contains( name ) ) {
00297         return lksMap[name];
00298     }
00299 #ifdef FARSA_DEBUG
00300     qWarning() << "getByName: Updatable " << name << " not present in BaseNeuralNet";
00301 #endif
00302     return NULL;
00303 }
00304 
00305 bool NeuralNet::find( const Cluster* cl ) const {
00306     return clustersv.contains( (Cluster*)cl );
00307 }
00308 
00309 bool NeuralNet::find( const Linker* l ) const {
00310     return linkersv.contains( (Linker*)l );
00311 }
00312 
00313 bool NeuralNet::find( const Updatable* u ) const {
00314     return (
00315         clustersv.contains( (Cluster*)u ) ||
00316         linkersv.contains( (Linker*)u )
00317     );
00318 }
00319 
00320 void NeuralNet::configure(ConfigurationParameters& params, QString prefix) {
00321     params.startRememberingGroupObjectAssociations();
00322     // the name will correspond to the group where the declaration of NeuralNet is
00323     QString myname = prefix;
00324     if ( myname.startsWith( ConfigurationParameters::GroupSeparator() ) ) {
00325         myname.remove( 0, 1 );
00326     }
00327     if ( myname.endsWith( ConfigurationParameters::GroupSeparator() ) ) {
00328         myname.chop( 1 );
00329     }
00330     setName( myname );
00331     
00332     // All Cluster has to be added before the creation of linkers, that's why the insertion is done after
00333     ClusterList clsToAdd;
00334     ClusterList clsInput;
00335     ClusterList clsOutput;
00336     LinkerList lnsToAdd;
00337 
00338     //--- get all subgroups, merge this list with clustersList and linkersList
00339     QStringList subgroups;
00340     foreach( QString sub, params.getGroupsList( prefix ) ) {
00341         // append the full path
00342         subgroups << (prefix+sub);
00343     }
00344     //--- clustersList and linkersList are supposed to be full path
00345     QString str = params.getValue(prefix + "clustersList");
00346     if (!str.isEmpty()) {
00347         subgroups << str.split(QRegExp("\\s+"), QString::SkipEmptyParts);
00348     }
00349     str = params.getValue(prefix + "linkersList");
00350     if (!str.isEmpty()) {
00351         subgroups << str.split(QRegExp("\\s+"), QString::SkipEmptyParts);
00352     }
00353     foreach( QString sub, subgroups ) {
00354         Updatable* up = params.getObjectFromGroup<Updatable>( sub, true );
00355         //--- check if is a Cluster
00356         Cluster* cl = dynamic_cast<Cluster*>(up);
00357         if ( cl && !clsToAdd.contains( cl ) ) clsToAdd << cl;
00358         //--- check if is a Linker
00359         Linker* ln = dynamic_cast<Linker*>(up);
00360         if ( ln && !lnsToAdd.contains( ln ) ) lnsToAdd << ln;
00361     }
00362     //--- parse the parameter inputClusters
00363     str = params.getValue(prefix + "inputClusters");
00364     if (!str.isEmpty()) {
00365         QStringList list = str.split(QRegExp("\\s+"), QString::SkipEmptyParts);
00366         foreach( QString sub, list ) {
00367             Cluster* cl = params.getObjectFromGroup<Cluster>( sub, true );
00368             if ( !clsToAdd.contains( cl ) ) clsToAdd << cl;
00369             clsInput << cl;
00370         }
00371     }
00372     //--- parse the parameter outputClusters
00373     str = params.getValue(prefix + "outputClusters");
00374     if (!str.isEmpty()) {
00375         QStringList list = str.split(QRegExp("\\s+"), QString::SkipEmptyParts);
00376         foreach( QString sub, list ) {
00377             Cluster* cl = params.getObjectFromGroup<Cluster>( sub, true );
00378             if ( !clsToAdd.contains( cl ) ) clsToAdd << cl;
00379             clsOutput << cl;
00380         }
00381     }
00382     //--- parse the parameter spreadOrder
00383     str = params.getValue(prefix + "spreadOrder");
00384     if (!str.isEmpty()) {
00385         QStringList list = str.split(QRegExp("\\s+"), QString::SkipEmptyParts);
00386         ups.clear();
00387         foreach( QString sub, list ) {
00388             Updatable* up = params.getObjectFromGroup<Updatable>( sub, true );
00389             // add it to the list of objects to add
00390             Cluster* cl = dynamic_cast<Cluster*>(up);
00391             if ( cl && !clsToAdd.contains( cl ) ) clsToAdd << cl;
00392             //--- check if is a Linker
00393             Linker* ln = dynamic_cast<Linker*>(up);
00394             if ( ln && !lnsToAdd.contains( ln ) ) lnsToAdd << ln;
00395             ups.append( up );
00396         }
00397         dimUps = ups.size();
00398     }
00399     foreach( Cluster* cl, clsToAdd ) {
00400         addCluster( cl );
00401     }
00402     foreach( Cluster* cl, clsInput ) {
00403         markAsInput( cl );
00404     }
00405     foreach( Cluster* cl, clsOutput ) {
00406         markAsOutput( cl );
00407     }
00408     foreach( Linker* ln, lnsToAdd ) {
00409         addLinker( ln );
00410     }
00411     params.stopRememberingGroupObjectAssociations();
00412 }
00413 
00414 void NeuralNet::save(ConfigurationParameters& params, QString prefix) {
00415     params.startObjectParameters( prefix, "NeuralNet", this );
00416     //--- save all Clusters and generate parameter clustersList
00417     QStringList list;
00418     list.clear();
00419     foreach( Cluster* cl, clustersv ) {
00420         params.createGroup( cl->name() );
00421         cl->save( params, cl->name() );
00422         list << cl->name();
00423     }
00424     params.createParameter( prefix, "clustersList", list.join(" ") );
00425     //--- save all Linkers and generate parameter linkersList
00426     list.clear();
00427     foreach( Linker* ln, linkersv ) {
00428         params.createGroup( ln->name() );
00429         ln->save( params, ln->name() );
00430         list << ln->name();
00431     }
00432     params.createParameter( prefix, "linkersList", list.join(" ") );
00433     //--- save parameter inputClusters
00434     list.clear();
00435     foreach( Cluster* cl, inclusters ) {
00436         list << cl->name();
00437     }
00438     params.createParameter( prefix, "inputClusters", list.join(" ") );
00439     //--- save parameter outputClusters
00440     list.clear();
00441     foreach( Cluster* cl, outclusters ) {
00442         list << cl->name();
00443     }
00444     params.createParameter( prefix, "outputClusters", list.join(" ") );
00445     //--- save parameter spreadOrder
00446     list.clear();
00447     foreach( Updatable* up, ups ) {
00448         list << up->name();
00449     }
00450     params.createParameter( prefix, "spreadOrder", list.join(" ") );
00451 }
00452 
00453 void NeuralNet::describe( QString type ) {
00454     Descriptor d = addTypeDescription( type, "Neural Network", "The neural network is a container for Clusters and Linkers. The topology and connectivity of the neural network is specified by the Linkers contained, while the order on which the Clusters and Linkers are updated is specified by the spreadOrder parameter of neural network" );
00455     d.describeObject( "clustersList" ).type( "Cluster" ).props( IsList ).help( "The list of all Clusters" );
00456     d.describeObject( "linkersList" ).type( "Linker" ).props( IsList ).help( "The list of all Linkers" );
00457     d.describeObject( "inputClusters" ).type( "Cluster" ).props( IsList ).help( "The list of Clusters marked as input of the neural network" );
00458     d.describeObject( "outputClusters" ).type( "Cluster" ).props( IsList ).help( "The list of Clusters marked as output of the neural network" );
00459     d.describeObject( "spreadOrder" ).type( "Updatable" ).props( IsMandatory | IsList ).help( "The order on which the Clusters and Linkers are updated", "All Clusters and Linkers specified here are also automatically added into the neural network. If this list contains all Clusters and Linkers, then the clustersList and the linkersList parameters are superfluous" );
00460 }
00461 
00462 }