Main Page   Class Hierarchy   Compound List   File List   Compound Members   File Members  

annetwork.cc

Go to the documentation of this file.
00001 
00025 #include <magic/mmath.h>
00026 #include <magic/mgdev-eps.h>
00027 #include <magic/mclass.h>
00028 
00029 #include "inanna/annetwork.h"
00030 #include "inanna/patternset.h"
00031 #include "inanna/initializer.h"
00032 #include "inanna/equalization.h"
00033 
00034 impl_dynamic (NeuronContainer, {});
00035 impl_dynamic (ANNetwork, {NeuronContainer});
00036 
00037 
00039 // |   |                             ___                       o                 //
00040 // |\  |  ___                   _   /   \        _    |   ___      _    ___      //
00041 // | \ | /   ) |   | |/\  __  |/ \  |      __  |/ \  -+-  ___| | |/ \  /   ) |/\ //
00042 // |  \| |---  |   | |   /  \ |   | |     /  \ |   |  |  (   | | |   | |---  |   //
00043 // |   |  \__   \__! |   \__/ |   | \___/ \__/ |   |   \  \__| | |   |  \__  |   //
00045 
00046 NeuronContainer::~NeuronContainer () {
00047     // Delete all connection objects
00048     for (int i=0; i<size(); i++)
00049         for (int j=0; j<(*this)[i].incomings(); j++) {
00050             (*this)[i].incoming(j).cut();
00051             delete & ((*this)[i].incoming(j));
00052         }
00053 
00054     // All connection pointers are now loose!
00055     // Clean them.
00056     for (int i=0; i<size(); i++)
00057         (*this)[i].shallowDisconnectAll ();
00058 }
00059 
00060 void NeuronContainer::add (Neuron* neuron) {
00061     Array<Neuron>::add (neuron);
00062     neuron->setId (size()-1);
00063 }
00064 
00065 void NeuronContainer::removeUnit (int unitID) {
00066     // Remove connections to and from the unit
00067     mUnits[unitID].disconnectAll ();
00068 
00069     // Remove the unit
00070     mUnits.removeFill (unitID);
00071 
00072     // Adjust unit IDs
00073     for (int i=unitID; i<mUnits.size(); i++)
00074         mUnits[i].setId (i);
00075 }
00076 
00077 /*
00078 void NeuronContainer::writeXML (OStream& out) const {
00079     sout.printf ("<%s>\n", (CONSTR) getclassname());
00080     for (int i=0; i<size; i++)
00081         mUnits[i].writeXML (out);
00082     sout.printf ("</%s>\n", (CONSTR) getclassname());
00083 }
00084 */
00085 
00086 
00088 //                                                                           //
00089 //         -----                 |   |                           |           //
00090 //         |          ___   ___  |\  |  ___   |                  |           //
00091 //         |---  |/\ /   ) /   ) | \ | /   ) -+- \    /  __  |/\ | /         //
00092 //         |     |   |---  |---  |  \| |---   |   \\//  /  \ |   |/          //
00093 //         |     |    \__   \__  |   |  \__    \   VV   \__/ |   | \         //
00094 //                                                                           //
00096 
00103 ANNetwork::ANNetwork (const char* desc)
00104 {
00105     mUnitTemplate = NULL;
00106     mInitializer = NULL;
00107     mTopology = NULL;
00108     mpEqualizer = NULL;
00109     if (desc)
00110         failtrace (makeUnits (desc));
00111 }
00112 
00113 ANNetwork::~ANNetwork   ()
00114 {
00115     delete mUnitTemplate;
00116     delete mInitializer;
00117     delete mTopology;
00118     delete mpEqualizer;
00119 }
00120 
00121 void ANNetwork::copy (const ANNetwork& fnet, bool onlyWeights)
00122 {
00123     Learner::copy (fnet, onlyWeights);
00124     copyFreeNet (fnet, onlyWeights);
00125 }
00126 
00127 void ANNetwork::makeUnits (const char* desc)
00128 {
00129     mTopology = new LayeredTopology (desc);
00130     mTopology->build (*this);
00131 }
00132 
00133 void ANNetwork::empty ()
00134 {
00135     mTopology->empty ();
00136     NeuronContainer::empty ();
00137 }
00138 
00139 void ANNetwork::connectFfw (const PackTable<int>& connmat)
00140 {
00141     ASSERT (connmat.rows == connmat.cols);
00142     ASSERT (connmat.rows == size());
00143     ANNLayering& mLayering = dynamic_cast<ANNLayering&>(*mTopology);
00144     
00145     // Disable according to the diagonal
00146     int outlindex = mLayering.layerIndex(-1);
00147     for (int i=0; i<outlindex; i++)
00148         mUnits[i].enable (connmat.get(i,i));
00149     
00150     // Connect forward
00151     for (int i=0; i<mUnits.size(); i++)
00152         for (int j=0; j<mUnits.size(); j++) {
00153             int ilayer, jlayer, dummy;
00154             mLayering.getPos (i, ilayer, dummy);
00155             mLayering.getPos (j, jlayer, dummy);
00156             if (mUnits[i].isEnabled() && mUnits[j].isEnabled() &&
00157                 ilayer==jlayer-1 && connmat.get(i,j))
00158                 connect (i,j);
00159         }
00160 }
00161 
00162 void ANNetwork::connectFullFfw (bool shortcuts)
00163 {
00164     ASSERT (mTopology);
00165     ANNLayering* mpLayering = dynamic_cast<ANNLayering*>(mTopology);
00166     if (!mpLayering)
00167         throw runtime_error (i18n("Neural network didn't have a topology when doing connectFullFfw()"));
00168 
00169     // Index of the first unit of previous layer
00170     for (int l=1; l<mpLayering->layers(); l++) {
00171 
00172         // Index of the first unit of current layer
00173         int loffset = mpLayering->layerIndex (l);
00174 
00175         // Connect the pair of layers
00176         for (int j=0; j<(*mpLayering)[l]; j++)
00177             for (int pl=shortcuts?0:l-1; pl<=l-1; pl++) {
00178                 int ploffset = mpLayering->layerIndex (pl);
00179                 for (int i=0; i<(*mpLayering)[pl]; i++)
00180                     connect (ploffset+i, loffset+j);
00181             }
00182     }
00183 }
00184 
00185 void ANNetwork::connectFull ()
00186 {
00187     // This is simple
00188     for (int i=0; i<mUnits.size(); i++)
00189         for (int j=0; j<mUnits.size(); j++)
00190             connect (i,j);
00191 }
00192 
00193 void ANNetwork::copyFreeNet (const ANNetwork& other, bool onlyWeights)
00194 {
00195     ANNLayering& mLayering = dynamic_cast<ANNLayering&>(*mTopology);
00196     if (onlyWeights) {
00197         ASSERTWITH (false, "onlyWeights-copy not implemented for ANNetwork");
00198     } else {
00199         // Full reconstructive copy
00200         mLayering = dynamic_cast<ANNLayering&>(*other.mTopology);
00201         failtrace (copyClone (mUnits, other.mUnits));
00202         mUnitTemplate = other.mUnitTemplate? other.mUnitTemplate->clone () : (Neuron*)NULL;
00203     }
00204 }
00205 
00206 void ANNetwork::init (double r)
00207 {
00208     for (int i=0; i<mUnits.size(); i++) {
00209         if (mInitializer)
00210             mInitializer->initialize (mUnits[i]);
00211         else
00212             mUnits[i].init (r);
00213     }
00214 }
00215 
00216 void ANNetwork::setInitializer (NeuronInitializer* initer)
00217 {
00218     delete mInitializer;
00219     mInitializer = initer;
00220 }
00221 
00222 void ANNetwork::setEqualizer (Equalizer* eq)
00223 {
00224     delete mpEqualizer;
00225     mpEqualizer = eq;
00226 }
00227 
00228 Connection* ANNetwork::connect (int i, int j) {
00229     ASSERTWITH (i>=0 && i<mUnits.size(),
00230                 format ("Invalid source index from(i)=%d, to(j)=%d", i, j));
00231 
00232     //newComment (format("Connection from %d to %d", i, j));
00233     Connection* conn = new Connection (&mUnits[i], &mUnits[j]);
00234     failtrace (mUnits[j].addIncoming (conn));
00235     failtrace (mUnits[i].addOutgoing (conn));
00236     return conn;
00237 }
00238 
00239 void ANNetwork::reset () {
00240     for (int i=0; i<mUnits.size(); i++)
00241         mUnits[i].reset ();
00242 }
00243 
00244 void ANNetwork::update () {
00245     for (int i=0; i<mUnits.size(); i++)
00246         if (mUnits[i].incomings()>0) // Do not transfer if there are no incoming connections
00247             mUnits[i].transfer (*this);
00248 }
00249 
00250 /*virtual*/ Vector ANNetwork::testPattern (const PatternSource& set, int pattern) const {
00251     Vector result;
00252     
00253     // Feed the pattern into the input layer
00254     for (int inp=0; inp<set.inputs; inp++)
00255         (*const_cast<ANNetwork*>(this))[inp].setActivation (set.input (pattern, inp));
00256 
00257     const_cast<ANNetwork*>(this)->update ();
00258 
00259     // Read results
00260     result.make (set.outputs);
00261     for (int outp=0; outp<set.outputs; outp++)
00262         result[outp] = (*this)[size() - set.outputs + outp].activation();
00263 
00264     return result;
00265 }
00266 
00270 OStream& ANNetwork::operator>> (OStream& out) const
00271 {
00272     for (int i=0; i<mUnits.size(); i++) {
00273         char ttype = "SLE"[mUnits[i].transferFunc()];
00274         out.printf ("%3d: A=%2.2f b=%+2.2f %c (%d): ",
00275                     mUnits[i].id(), mUnits[i].activation(), mUnits[i].bias(), ttype, mUnits[i].incomings());
00276         for (int j=0; j<mUnits[i].incomings(); j++)
00277             out.printf ("%2d:%+2.2f ",
00278                         mUnits[i].incoming(j).source().id(),
00279                         mUnits[i].incoming(j).weight());
00280         out << "  \n";
00281     }
00282     return out;
00283 }
00284 
00285 double quadatan (double x, double y) {
00286     if (y==0)
00287         return (x>=0)? 0 : M_PI;
00288 
00289     double at=atan (y/x);
00290     
00291     // Tuodaan oikeaan kvadranttiin
00292     if (y>0 && x<0) at+=M_PI;
00293     if (y<0 && x<0) at+=M_PI;
00294     if (y<0 && x>0) at+=2*M_PI;
00295 
00296     return at;
00297 }
00298 
00299 String ANNetwork::drawEPS (double xsize, double ysize) const {
00300     ANNLayering& mLayering = dynamic_cast<ANNLayering&>(*mTopology);
00301     if (xsize<0 || ysize<0)
00302         xsize=175, ysize=175;           
00303 
00304     // Calculate logical size
00305     int maxSize=0;
00306     for (int i=0; i<mLayering.layers(); i++)
00307         if (mLayering[i]>maxSize)
00308             maxSize = mLayering[i];
00309     double size = (mLayering.layers()>maxSize)? mLayering.layers():maxSize;
00310     
00311     Coord2D picSize (175, 175);
00312     EPSDevice dc (picSize);                                 // Graphics device
00313     dc.framedStyle (size, size);                            // Clipping with frame
00314 
00315     // Draw cells
00316     for (int i=0; i<mUnits.size(); i++) {
00317         const Neuron& unit = mUnits[i];
00318         const Coord2D& pos = Coord2D (unit.getPlace ());
00319 
00320         bool inOrOutUnit = (i<mLayering[0]) || (i>=mUnits.size()-mLayering[-1])
00321             || unit.transferFunc()==Neuron::LINEAR_TF;
00322         
00323         // Draw unit body
00324         double unitRadius = inOrOutUnit? 0.15 : 0.25;
00325         dc.saveState ();
00326         dc.lineWidth (0);
00327         dc.circle (pos, unitRadius, unit.isEnabled());
00328         dc.restoreState ();
00329 
00330         // Draw connections
00331         dc.lineStyle ("->", 0.2);
00332         if (unit.isEnabled())
00333             for (int c=0; c<unit.incomings(); c++) {
00334                 const Neuron& source = unit.incoming(c).source();
00335                 const Coord2D& tpos = Coord2D(source.getPlace ());
00336 
00337                 // Draw the arrow line to a short distance from source
00338                 double angle = quadatan (tpos.x-pos.x, tpos.y-pos.y);
00339                 double trgRadius = (unit.incoming(c).source().id() >= mUnits.size() - mLayering[-1]
00340                                     || source.transferFunc()==Neuron::LINEAR_TF)? 0.15:0.25;
00341                 Coord2D tmpos (tpos.x-trgRadius*cos(angle), tpos.y-trgRadius*sin(angle));
00342                 dc.lineWidth (0.5);
00343                 dc.saveState ();
00344                 if (unit.transferFunc()==Neuron::LINEAR_TF)
00345                     dc.lineStyle ("dashed", 0.02);
00346                 dc.line (pos, tmpos);
00347                 dc.restoreState ();
00348             }
00349 
00350         // For output units
00351         if (i >= mUnits.size() - mLayering[-1]) {
00352             dc.lineWidth (0.5);
00353             dc.saveState ();
00354             if (unit.transferFunc()==Neuron::LINEAR_TF)
00355                 dc.lineStyle ("dashed", 0.02);
00356             dc.line (pos, pos+Coord2D(0.5,0));
00357             dc.restoreState ();
00358         }
00359     }
00360 
00361     dc.printFooter ();
00362     return dc.getBuffer ();
00363 }
00364 
00365 void ANNetwork::cleanup (bool removeDisableds, bool removePassthroughs) {
00366     ANNLayering& mLayering = dynamic_cast<ANNLayering&>(*mTopology);
00367     int inputIndex = mLayering[0];
00368     int outputIndex = mUnits.size() - mLayering[-1];
00369 
00370     int disconnects;
00371     do {
00372         disconnects=0;  // Count the number of disabled units in this iteration
00373         for (int i=inputIndex; i<outputIndex; i++) {
00374             // Count incoming connections
00375             int ins=0;
00376             for (int j=0; j < mUnits.size(); j++)
00377                 if (mUnits[j].connectedFrom (mUnits[i]))
00378                     ins++;
00379             
00380             // If this unit is unnecessarily connected
00381             if ((ins>0 && mUnits[i].incomings()==0) || (ins==0 && mUnits[i].incomings()>0)) {
00382                 // Remove any connections to this node
00383                 for (int j=0; j < mUnits.size(); j++)
00384                     if (mUnits[j].connectedFrom (mUnits[i]))
00385                         mUnits[j].disconnectFrom (mUnits[i]);
00386                 
00387                 // Remove any connections from this node
00388                 mUnits[i].disconnectAll ();
00389                 
00390                 // Disable it
00391                 mUnits[i].enable (false);
00392                 
00393                 disconnects++;
00394                 continue;
00395             }
00396 
00397             // If this unit is a passthrough unit (one input, one or more outputs)
00398             if (removePassthroughs && (ins==1 && mUnits[i].incomings()>=1)) {
00399                 // Find the input connection
00400                 for (int j=0; j<mUnits.size(); j++)
00401                     if (mUnits[j].connectedFrom (mUnits[i])) {
00402                         // Found. Connect the source unit to all the source units
00403                         mUnits[j].disconnectFrom (mUnits[i]);
00404                         for (int trg=0; trg<mUnits[i].incomings(); trg++)
00405                             mUnits[j].connectFrom (mUnits[i].incoming(trg).source());
00406                         break;
00407                     }
00408                 mUnits[i].disconnectAll ();
00409                 mUnits[i].enable (false); // The unit will be removed
00410                 disconnects++;
00411                 continue;
00412             }
00413 
00414             // If this unit is a passthrough unit with one output, one or more inputs
00415             if (removePassthroughs && (ins>=1 && mUnits[i].incomings()==1)) {
00416                 // Find the input connections
00417                 for (int j=0; j<mUnits.size(); j++)
00418                     if (mUnits[j].connectedFrom (mUnits[i])) {
00419                         // Found. Connect the source unit to all the source units
00420                         mUnits[j].disconnectFrom (mUnits[i]);
00421                         mUnits[j].connectFrom (mUnits[i].incoming(0).source());
00422                     }
00423                 mUnits[i].disconnectAll ();
00424                 mUnits[i].enable (false); // The unit will be removed
00425                 disconnects++;
00426                 continue;
00427             }
00428         }
00429     } while (disconnects>0);
00430 
00431     // Disable units that do not have any connections
00432     for (int i=0; i<outputIndex; i++)
00433         mUnits[i].enable (mUnits[i].incomings()>0);
00434 
00435     // Remove all disabled hidden units
00436     if (removeDisableds)
00437         for (int i=inputIndex; i<mUnits.size()-mLayering[-1]; i++)
00438             if (!mUnits[i].isEnabled()) {
00439                 removeUnit (i);
00440                 
00441                 // Huh, now we have to change the layering info.. argh..
00442                 int layer, pos;
00443                 mLayering.getPos (i, layer, pos);
00444                 if (mLayering[layer]>1)
00445                     mLayering[layer]--;
00446                 else {
00447                     // We have to remove the layer because someone doesn't
00448                     // like 0-sized layers. Oh well...
00449                     mLayering.removeLayer (layer);
00450                 }
00451                 
00452                 // Stay on this index...
00453                 i--;
00454             }
00455 }
00456 
00457 struct ValueIndex : public Comparable {
00458     double  value;
00459     int     index;
00460     
00461     int operator== (const Comparable& other) const {return 0;}
00462     int compare (const Comparable& other) const {
00463         const ValueIndex& o = dynamic_cast<const ValueIndex&> (other);
00464         return (value<o.value)? -1 : (value>o.value)? 1 : (index<o.index)? -1 : 1;
00465     }
00466 };
00467 
00468 void ANNetwork::drawFeedForward () {
00469     ANNLayering& mLayering = dynamic_cast<ANNLayering&>(*mTopology);
00470     int inputs = mLayering[0];
00471     int outputs = mLayering[-1];
00472     int hiddens = mUnits.size()-inputs-outputs;
00473 
00474     // Collect layer sizes to this string
00475     String layeringDesc = String(inputs);
00476     
00477     // Position hidden and output units
00478     if (hiddens) {
00479         int column = 0;
00480         int firstOnColumn = inputs;
00481         for (int i=inputs; i<mUnits.size(); i++) {
00482             if (i==inputs+hiddens) {
00483                 // Always move to the next column when coming to the output layer
00484                 layeringDesc += String("-") + String(i-firstOnColumn);
00485                 column++;
00486                 firstOnColumn = i;
00487             } else
00488                 for (int j=firstOnColumn; j<i; j++) {
00489                     if (mUnits[j].isEnabled() && mUnits[j].connectedFrom(mUnits[i]) &&
00490                         i<inputs+hiddens) { // Inside-column connection found.
00491                         // -> Forced to move to the next column
00492                         layeringDesc += String("-") + String(i-firstOnColumn);
00493                         
00494                         // Move on to the next column
00495                         column++;
00496                         firstOnColumn = i;
00497                         break;
00498                     } // if (connected)
00499                 } // for j
00500             mUnits[i].mCoord.x = 3*(column+1);
00501         } // for i
00502     }
00503     
00504     // Add the last layer column
00505     layeringDesc += String("-") + String(outputs);
00506     // TRACE1("%s", (CONSTR) layeringDesc);
00507 
00508     // Change our layering according to this description (if this
00509     // fails, we are in deep shit)
00510     mLayering.make (layeringDesc);
00511 
00512     // Calculate the size of the biggest layer
00513     int maxSize=0;
00514     for (int i=0; i<mLayering.layers(); i++)
00515         if (mLayering[i]>maxSize)
00516             maxSize = mLayering[i];
00517     
00518     // Order the hidden units by their ideal y-positions
00519     double gridSize = (mLayering.layers()>maxSize)? mLayering.layers():maxSize;
00520     double layerStep = gridSize/double(mLayering.layers()+0.5);
00521     // One layer at a time (not the last layer).
00522     for (int l=0; l<mLayering.layers(); l++) {
00523         // Handle units on this layer
00524         Array<ValueIndex> idealY (mLayering[l]);
00525         int layerBase = mLayering.layerIndex(l);
00526         for (int i=0; i<mLayering[l]; i++) {
00527             // Calculate average position of source units
00528             double sum=0;
00529             int conns=0;
00530             for (int j=0; j<layerBase+i; j++)
00531                 if (mUnits[j].connectedFrom(mUnits[layerBase+i])) {
00532                     sum += mUnits[j].mCoord.y;
00533                     conns++;
00534                 }
00535             if (conns)
00536                 idealY[i].value = sum/conns;
00537             else
00538                 idealY[i].value = double(i)/mLayering[l];
00539             idealY[i].index = i+layerBase;
00540         }
00541 
00542         // Sort it (for all but the output layer)
00543         if (l<mLayering.layers()-1)
00544             idealY.quicksort ();
00545 
00546         // Now reorder them
00547         double unitStep = gridSize/double(mLayering[l]+1);
00548         for (int i=0; i<mLayering[l]; i++) {
00549             mUnits[idealY[i].index].moveTo (double(l)*layerStep+0.5,
00550                                             double(i+1)*unitStep);
00551         }
00552     }
00553 }
00554 
00555 inline int sgn (double x) {return (x==0)?0 : ((x<0)? -1:1);}
00556 
00557 void ANNetwork::check () const {
00558     ANNLayering& mLayering = dynamic_cast<ANNLayering&>(*mTopology);
00559     mLayering.check ();
00560     for (int i=0; i<mUnits.size(); i++)
00561         mUnits[i].check (mUnits.size());
00562     if (mUnitTemplate)
00563         mUnitTemplate->check (1);
00564 }
00565 
00566 

Generated on Thu Feb 10 20:06:44 2005 for Inanna by doxygen1.2.18