neuralnet.cpp
1 /********************************************************************************
2  * Neural Network Framework. *
3  * Copyright (C) 2005-2011 Gianluca Massera <emmegian@yahoo.it> *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the Free Software *
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
18  ********************************************************************************/
19 
20 #include "neuralnet.h"
21 
22 namespace farsa {
23 
25  dimUps = 0;
26 }
27 
29 }
30 
31 void NeuralNet::addCluster( Cluster* c, bool isInput, bool isOutput ) {
32 #ifdef FARSA_DEBUG
33  if ( !c ) {
34  qWarning() << "Null Pointer passed to addCluster! This operation will be ignored" ;
35  return;
36  }
37 #endif
38  // Check if the Cluster is already added
39  if ( find( c ) ) {
40 #ifdef FARSA_DEBUG
41  qWarning() << "Cluster already added! addCluster will be ignored" ;
42 #endif
43  return;
44  }
45  clustersv.append( c );
46  if ( isInput ) {
47  inclusters.append( c );
48  }
49  if ( isOutput ) {
50  outclusters.append( c );
51  }
52  if ( !isInput && !isOutput ) {
53  hidclusters.append( c );
54  }
55  clsMap[c->name()] = c;
56  return;
57 }
58 
60 #ifdef FARSA_DEBUG
61  if ( !c ) {
62  qWarning() << "Null Pointer passed to removeCluster! This operation will return false" ;
63  return false;
64  }
65 #endif
66  if ( !find( c ) ) {
67  return false;
68  }
69  clustersv.removeOne( c );
70  inclusters.removeOne( c );
71  outclusters.removeOne( c );
72  hidclusters.removeOne( c );
73  clsMap.remove( c->name() );
74  return true;
75 }
76 
78 #ifdef FARSA_DEBUG
79  if ( !c ) {
80  qWarning() << "Null Pointer passed to addCluster! This operation will be ignored" ;
81  return;
82  }
83 #endif
84  // Check if the Cluster exists
85  if ( !find( c ) ) {
86 #ifdef FARSA_DEBUG
87  qWarning() << "attempt to mark a Cluster not present in this net!" ;
88 #endif
89  return;
90  }
91  if ( inclusters.contains( c ) ) {
92  return;
93  }
94  inclusters.append( c );
95  hidclusters.removeOne( c );
96 }
97 
99 #ifdef FARSA_DEBUG
100  if ( !c ) {
101  qWarning() << "Null Pointer passed to addCluster! This operation will be ignored" ;
102  return;
103  }
104 #endif
105  // Check if the Cluster exists
106  if ( !find( c ) ) {
107 #ifdef FARSA_DEBUG
108  qWarning() << "attempt to mark a Cluster not present in this net!" ;
109 #endif
110  return;
111  }
112  if ( outclusters.contains( c ) ) {
113  return;
114  }
115  outclusters.append( c );
116  hidclusters.removeOne( c );
117 }
118 
120 #ifdef FARSA_DEBUG
121  if ( !c ) {
122  qWarning() << "Null Pointer passed to addCluster! This operation will be ignored" ;
123  return;
124  }
125 #endif
126  // Check if the Cluster exists
127  if ( !find( c ) ) {
128 #ifdef FARSA_DEBUG
129  qWarning() << "attempt to unmark a Cluster not present in this net!" ;
130 #endif
131  return;
132  }
133  inclusters.removeOne( c );
134  outclusters.removeOne( c );
135  hidclusters.append( c );
136  return;
137 }
138 
140  inclusters.clear();
141  outclusters.clear();
143  return;
144 }
145 
146 bool NeuralNet::isIsolated( Cluster* c ) const {
147 #ifdef FARSA_DEBUG
148  if ( !c ) {
149  qWarning() << "Null Pointer passed to isIsolato! This operation will return false" ;
150  return false;
151  }
152 #endif
153  return ( inLinks.count( c ) == 0 && outLinks.count( c ) == 0 );
154 }
155 
156 ClusterList NeuralNet::clusters() const {
157  return clustersv;
158 }
159 
160 ClusterList NeuralNet::inputClusters() const {
161  return inclusters;
162 }
163 
164 ClusterList NeuralNet::outputClusters() const {
165  return outclusters;
166 }
167 
168 ClusterList NeuralNet::hiddenClusters() const {
169  return hidclusters;
170 }
171 
173 #ifdef FARSA_DEBUG
174  if ( !l ) {
175  qWarning() << "Null Pointer passed to addLinker! This operation will be ignored" ;
176  return;
177  }
178 #endif
179  // Check if the Linker is already added
180  if ( find( l ) ) {
181 #ifdef FARSA_DEBUG
182  qWarning() << "Linker already added! addLinker will be ignored" ;
183 #endif
184  return;
185  }
186 #ifdef FARSA_DEBUG
187  // --- Check: Are There in this net the Clusters that linker l connects ???
188  if ( ! find( l->from() ) ) {
189  qWarning() << "The linker that you want add links cluster" << l->from()->name() << "that doesn't exist in this net! This operation will be ignored" ;
190  return;
191  }
192  if ( ! find( l->to() ) ) {
193  qWarning() << "The linker that you want add links cluster" << l->to()->name() << "that doesn't exist in this net! This operation will be ignored" ;
194  return;
195  }
196 #endif
197  linkersv.append( l );
198  // Adding information in outLinks map
199  outLinks[ l->from() ].append( l );
200  // Adding information in inLinks map
201  inLinks[ l->to() ].append( l );
202  lksMap[l->name()] = l;
203  return;
204 }
205 
207 #ifdef FARSA_DEBUG
208  if ( !l ) {
209  qWarning() << "Null Pointer passed to removeLinker! This operation will return false" ;
210  return false;
211  }
212 #endif
213  if ( !find(l) ) {
214  return false;
215  }
216  linkersv.removeOne( l );
217  outLinks[ l->from() ].removeOne( l );
218  inLinks[ l->to() ].removeOne( l );
219  lksMap.remove( l->name() );
220  return true;
221 }
222 
223 LinkerList NeuralNet::linkers() const {
224  return linkersv;
225 }
226 
227 LinkerList NeuralNet::linkers( Cluster* c, bool out ) const {
228 #ifdef FARSA_DEBUG
229  if ( !c ) {
230  qWarning() << "Null Pointer passed to linkers! This operation will return an empty LinkerList" ;
231  return LinkerList();
232  }
233 #endif
234  if ( out ) {
235  // Return outgoing linkers
236  if ( outLinks.contains( c ) ) {
237  return outLinks[c];
238  }
239  } else {
240  // Return incoming linkers
241  if ( inLinks.contains( c ) ) {
242  return inLinks[c];
243  }
244  }
245  return LinkerList();
246 }
247 
248 void NeuralNet::setOrder( Updatable* u[], unsigned int dim ) {
249  ups.clear();
250  for( unsigned int i = 0; i<dim; i++ ) {
251  if ( find( u[i] ) ) {
252  ups.append( u[i] );
253  }
254 #ifdef FARSA_DEBUG
255  else {
256  qWarning() << "In the Updatable order list passed there are some Clusters and/or Linkers not present in this NeuralNet";
257  }
258 #endif
259  }
260  dimUps = ups.size();
261  return;
262 }
263 
264 void NeuralNet::setOrder( const UpdatableList& u ) {
265  ups.clear();
266  unsigned int dim = u.size();
267  for( unsigned int i = 0; i<dim; i++ ) {
268  if ( find( u[i] ) ) {
269  ups.append( u[i] );
270  }
271 #ifdef FARSA_DEBUG
272  else {
273  qWarning() << "In the Updatable order list passed there are some Clusters and/or Linkers not present in this NeuralNet";
274  }
275 #endif
276  }
277  dimUps = ups.size();
278  return;
279 }
280 
281 void NeuralNet::randomize( double min, double max ) {
282  int dim = clustersv.size();
283  for( int i=0; i<dim; i++ ) {
284  clustersv[i]->randomize( min, max );
285  }
286  dim = linkersv.size();
287  for( int i=0; i<dim; i++ ) {
288  linkersv[i]->randomize( min, max );
289  }
290 }
291 
292 Updatable* NeuralNet::getByName( QString name ) {
293  if ( clsMap.contains( name ) ) {
294  return clsMap[name];
295  }
296  if ( lksMap.contains( name ) ) {
297  return lksMap[name];
298  }
299 #ifdef FARSA_DEBUG
300  qWarning() << "getByName: Updatable " << name << " not present in BaseNeuralNet";
301 #endif
302  return NULL;
303 }
304 
305 bool NeuralNet::find( const Cluster* cl ) const {
306  return clustersv.contains( (Cluster*)cl );
307 }
308 
309 bool NeuralNet::find( const Linker* l ) const {
310  return linkersv.contains( (Linker*)l );
311 }
312 
313 bool NeuralNet::find( const Updatable* u ) const {
314  return (
315  clustersv.contains( (Cluster*)u ) ||
316  linkersv.contains( (Linker*)u )
317  );
318 }
319 
320 void NeuralNet::configure(ConfigurationParameters& params, QString prefix) {
322  // the name will correspond to the group where the declaration of NeuralNet is
323  QString myname = prefix;
324  if ( myname.startsWith( ConfigurationParameters::GroupSeparator() ) ) {
325  myname.remove( 0, 1 );
326  }
327  if ( myname.endsWith( ConfigurationParameters::GroupSeparator() ) ) {
328  myname.chop( 1 );
329  }
330  setName( myname );
331 
332  // All Cluster has to be added before the creation of linkers, that's why the insertion is done after
333  ClusterList clsToAdd;
334  ClusterList clsInput;
335  ClusterList clsOutput;
336  LinkerList lnsToAdd;
337 
338  //--- get all subgroups, merge this list with clustersList and linkersList
339  QStringList subgroups;
340  foreach( QString sub, params.getGroupsList( prefix ) ) {
341  // append the full path
342  subgroups << (prefix+sub);
343  }
344  //--- clustersList and linkersList are supposed to be full path
345  QString str = params.getValue(prefix + "clustersList");
346  if (!str.isEmpty()) {
347  subgroups << str.split(QRegExp("\\s+"), QString::SkipEmptyParts);
348  }
349  str = params.getValue(prefix + "linkersList");
350  if (!str.isEmpty()) {
351  subgroups << str.split(QRegExp("\\s+"), QString::SkipEmptyParts);
352  }
353  foreach( QString sub, subgroups ) {
354  Updatable* up = params.getObjectFromGroup<Updatable>( sub, true );
355  //--- check if is a Cluster
356  Cluster* cl = dynamic_cast<Cluster*>(up);
357  if ( cl && !clsToAdd.contains( cl ) ) clsToAdd << cl;
358  //--- check if is a Linker
359  Linker* ln = dynamic_cast<Linker*>(up);
360  if ( ln && !lnsToAdd.contains( ln ) ) lnsToAdd << ln;
361  }
362  //--- parse the parameter inputClusters
363  str = params.getValue(prefix + "inputClusters");
364  if (!str.isEmpty()) {
365  QStringList list = str.split(QRegExp("\\s+"), QString::SkipEmptyParts);
366  foreach( QString sub, list ) {
367  Cluster* cl = params.getObjectFromGroup<Cluster>( sub, true );
368  if ( !clsToAdd.contains( cl ) ) clsToAdd << cl;
369  clsInput << cl;
370  }
371  }
372  //--- parse the parameter outputClusters
373  str = params.getValue(prefix + "outputClusters");
374  if (!str.isEmpty()) {
375  QStringList list = str.split(QRegExp("\\s+"), QString::SkipEmptyParts);
376  foreach( QString sub, list ) {
377  Cluster* cl = params.getObjectFromGroup<Cluster>( sub, true );
378  if ( !clsToAdd.contains( cl ) ) clsToAdd << cl;
379  clsOutput << cl;
380  }
381  }
382  //--- parse the parameter spreadOrder
383  str = params.getValue(prefix + "spreadOrder");
384  if (!str.isEmpty()) {
385  QStringList list = str.split(QRegExp("\\s+"), QString::SkipEmptyParts);
386  ups.clear();
387  foreach( QString sub, list ) {
388  Updatable* up = params.getObjectFromGroup<Updatable>( sub, true );
389  // add it to the list of objects to add
390  Cluster* cl = dynamic_cast<Cluster*>(up);
391  if ( cl && !clsToAdd.contains( cl ) ) clsToAdd << cl;
392  //--- check if is a Linker
393  Linker* ln = dynamic_cast<Linker*>(up);
394  if ( ln && !lnsToAdd.contains( ln ) ) lnsToAdd << ln;
395  ups.append( up );
396  }
397  dimUps = ups.size();
398  }
399  foreach( Cluster* cl, clsToAdd ) {
400  addCluster( cl );
401  }
402  foreach( Cluster* cl, clsInput ) {
403  markAsInput( cl );
404  }
405  foreach( Cluster* cl, clsOutput ) {
406  markAsOutput( cl );
407  }
408  foreach( Linker* ln, lnsToAdd ) {
409  addLinker( ln );
410  }
412 }
413 
414 void NeuralNet::save(ConfigurationParameters& params, QString prefix) {
415  params.startObjectParameters( prefix, "NeuralNet", this );
416  //--- save all Clusters and generate parameter clustersList
417  QStringList list;
418  list.clear();
419  foreach( Cluster* cl, clustersv ) {
420  params.createGroup( cl->name() );
421  cl->save( params, cl->name() );
422  list << cl->name();
423  }
424  params.createParameter( prefix, "clustersList", list.join(" ") );
425  //--- save all Linkers and generate parameter linkersList
426  list.clear();
427  foreach( Linker* ln, linkersv ) {
428  params.createGroup( ln->name() );
429  ln->save( params, ln->name() );
430  list << ln->name();
431  }
432  params.createParameter( prefix, "linkersList", list.join(" ") );
433  //--- save parameter inputClusters
434  list.clear();
435  foreach( Cluster* cl, inclusters ) {
436  list << cl->name();
437  }
438  params.createParameter( prefix, "inputClusters", list.join(" ") );
439  //--- save parameter outputClusters
440  list.clear();
441  foreach( Cluster* cl, outclusters ) {
442  list << cl->name();
443  }
444  params.createParameter( prefix, "outputClusters", list.join(" ") );
445  //--- save parameter spreadOrder
446  list.clear();
447  foreach( Updatable* up, ups ) {
448  list << up->name();
449  }
450  params.createParameter( prefix, "spreadOrder", list.join(" ") );
451 }
452 
453 void NeuralNet::describe( QString type ) {
454  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" );
455  d.describeObject( "clustersList" ).type( "Cluster" ).props( IsList ).help( "The list of all Clusters" );
456  d.describeObject( "linkersList" ).type( "Linker" ).props( IsList ).help( "The list of all Linkers" );
457  d.describeObject( "inputClusters" ).type( "Cluster" ).props( IsList ).help( "The list of Clusters marked as input of the neural network" );
458  d.describeObject( "outputClusters" ).type( "Cluster" ).props( IsList ).help( "The list of Clusters marked as output of the neural network" );
459  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" );
460 }
461 
462 }