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

nolfinet.cc

Go to the documentation of this file.
00001 
00025 #include <magic/mgdev-eps.h>
00026 #include <magic/mlsystem.h>
00027 #include <magic/mturtle.h>
00028 #include <magic/mtextstream.h>
00029 
00030 #include <inanna/annetwork.h>
00031 #include "nhp/individual.h"
00032 
00033 #include "annalee/nolfi.h"
00034 #include "annalee/nolfinet.h"
00035 
00036 
00037 
00038 
00040 //                                                                          //
00041 //              |   |      |     o -----               |                    //
00042 //              |\  |      |  __     |              |  |  ___               //
00043 //              | \ |  __  | /   |   |   |   | |/\ -+- | /   )              //
00044 //              |  \| /  \ | +-- |   |   |   | |    |  | |---               //
00045 //              |   | \__/ | |   |   |    \__! |     \ |  \__               //
00046 //                           |                                              //
00048 
00049 // This turtle device calculates the axon tip positions for neurons in
00050 // the Nolfi encoding
00051 class NolfiTurtle : public TurtleDevice {
00052     Array<Coord2D>  mTips;
00053     int             mTipPos;
00054   public:
00055 
00056             NolfiTurtle (int tipEstimate=64) {
00057                 mTips.make (tipEstimate);
00058                 mTipPos=0;
00059             }
00060 
00061     virtual void    forwardLine (const Coord2D& s, const Coord2D& e) {}
00062 
00063     void    tip         (const Coord2D& tp) {
00064         if (mTipPos>=mTips.size())
00065             ASSERT (false);
00066         // mTips.resize (int(mTips.size*1.5));
00067         mTips[mTipPos++] = tp;
00068     }
00069 
00070     void    getTips     (Array<Coord2D>& tips) const {
00071         tips.make (mTipPos);
00072         for (int i=0; i<tips.size(); i++)
00073             tips[i] = mTips[i];
00074     }
00075 
00076 };
00077 
00078 
00079 
00081 //                                                                           //
00082 //               ----- ----   ---- -----               |                     //
00083 //               |     |   ) (       |              |  |  ___                //
00084 //               |---  |---   ---    |   |   | |/\ -+- | /   )               //
00085 //               |     |         )   |   |   | |    |  | |---                //
00086 //               |____ |     ___/    |    \__! |     \ |  \__                //
00087 //                                                                           //
00089 
00090 // This turtle is used for making nice pictures of the Nolfi networks
00091 class EPSTurtle : public TurtleDevice {
00092     EPSDevice&  mDevice;
00093     double      mXScale, mYScale;
00094     double      mTipRadius;
00095   public:
00096                 EPSTurtle   () : mDevice ((EPSDevice&)*((EPSDevice*)NULL)) {}
00097                 EPSTurtle   (EPSDevice& dev, double tipR) : mDevice(dev), mTipRadius(tipR) {}
00098     
00099     void        forwardLine (const Coord2D& start, const Coord2D& end) {
00100         mDevice.line (start,end);
00101     }
00102 
00103     void        tip         (const Coord2D& tipPoint) {
00104         mDevice.saveState ().setGray(0.5);
00105         mDevice.circle (tipPoint, mTipRadius);
00106         mDevice.restoreState ();
00107     }
00108 };
00109 
00110 
00111 
00113 //                                                                          //
00114 //                    |   |      |     o  ___        | |                    //
00115 //                    |\  |      |  __   /   \  ___  | |                    //
00116 //                    | \ |  __  | /   | |     /   ) | |                    //
00117 //                    |  \| /  \ | +-- | |     |---  | |                    //
00118 //                    |   | \__/ | |   | \___/  \__  | |                    //
00119 //                                 |                                        //
00121 
00122 // Creates the axon description string (see below) for NolfiCells
00123 // using an L-System.
00124 String makeAxonString () {
00125     String result;
00126 
00127     LGrammar grammar;
00128     grammar.addRule ("X", "F[-X][+X]");
00129     result = "X";
00130     grammar.applyTo (result, 4);
00131 
00132     // Change the Xs to Fs
00133     LGrammar grammar2;
00134     grammar2.addRule ("X", "F");
00135     grammar2.applyTo (result, 1);
00136 
00137     return result;
00138 }
00139 
00140 // The axon description string for NolfiCells.  The letters in the
00141 // string are used for guiding the NolfiTurtle (or EPSTurtle) that
00142 // determines the locations of axon tips.
00143 String NolfiCell::smAxonString = makeAxonString ();
00144 
00147 void NolfiCell::make ()
00148 {
00149     mExpression = true;
00150     mCoord.x = 0.0;
00151     mCoord.y = 0.0;
00152     mSegmentAngle = 0.5;
00153     mSegmentLength = 0.5;
00154     mWeight = 0.5;
00155     mBias = 0.5;
00156     mTypeID = 0;
00157     mTipRadius = 0.5;
00158     
00159     mFinalID = EMPTYID;
00160     mFinalType = CT_NONE;
00161 }
00162 
00163 void NolfiCell::copy (const NolfiCell& o) {
00164     mExpression = o.mExpression;
00165     mCoord.x = o.mCoord.x;
00166     mCoord.y = o.mCoord.y;
00167     mSegmentAngle = o.mSegmentAngle;
00168     mSegmentLength = o.mSegmentLength;
00169     mWeight = o.mWeight;
00170     mBias = o.mBias;
00171     mTypeID = o.mTypeID;
00172     mTipRadius = o.mTipRadius;
00173 
00174     mFinalID = o.mFinalID;
00175     mFinalType = o.mFinalType;
00176 }
00177 
00178 
00200 void NolfiCell::addGenesTo (
00201     Gentainer&       g,
00202     int              i,
00203     int              types,
00204     int              xsize,
00205     int              ysize,
00206     const StringMap& params)
00207 {
00208     if (params["NolfiEncoding.existenceGene"].toInt())
00209         g.add (&(new BinaryGene (format ("e%d", i)))->hide());
00210     g.add (&(new BitFloatGene   (format ("x%d", i), 0, 1, 3, params))->hide());
00211     g.add (&(new BitFloatGene   (format ("y%d", i), 0, 1, 5, params))->hide());
00212     g.add (&(new BitFloatGene   (format ("a%d", i), -1, 1, 6, params))->hide());
00213     g.add (&(new BitFloatGene   (format ("s%d", i), 0, 1, 4, params))->hide());
00214     g.add (&(new BitFloatGene   (format ("w%d", i), -1, 1, 10, params))->hide());
00215     g.add (&(new BitFloatGene   (format ("b%d", i), -1, 1, 10, params))->hide());
00216     int typebits = int(log(types*1.0)/log(2.0)+.999);
00217     g.add (&(new BitIntGene     (format ("t%d", i), 0, (1<<typebits)-1, typebits, params))->hide());
00218     if (params["NolfiEncoding.tipRadius"]=="auto-cell")
00219         g.add (&(new BitFloatGene   (format ("r%d", i), 1, 10, 8, params))->hide());
00220 }
00221 
00229 void NolfiCell::decodeFrom (const Gentainer& g, int i) {
00230     if (!isnull(g[(CONSTR)format ("e%d", i)]))
00231         mExpression = ((const BinaryGene&)      g[(CONSTR)format ("e%d", i)]).getvalue();
00232     else
00233         mExpression = true;
00234     mCoord.x        = ((const AnyFloatGene&)    g[(CONSTR)format ("x%d", i)]).getvalue();
00235     mCoord.y        = ((const AnyFloatGene&)    g[(CONSTR)format ("y%d", i)]).getvalue();
00236     mBias           = ((const AnyFloatGene&)    g[(CONSTR)format ("b%d", i)]).getvalue();
00237     mWeight         = ((const AnyFloatGene&)    g[(CONSTR)format ("w%d", i)]).getvalue();
00238     mSegmentLength  = ((const AnyFloatGene&)    g[(CONSTR)format ("s%d", i)]).getvalue();
00239     mSegmentAngle   = ((const AnyFloatGene&)    g[(CONSTR)format ("a%d", i)]).getvalue();
00240     mTypeID         = ((const AnyIntGene&)      g[(CONSTR)format ("t%d", i)]).getvalue();
00241     if (!isnull(g[(CONSTR)format ("r%d", i)]))
00242         mTipRadius  = ((const AnyFloatGene&)    g[(CONSTR)format ("r%d", i)]).getvalue();
00243 
00244     mFinalID        = EMPTYID;
00245     mFinalType      = CT_NONE;
00246     //sout << *this; sout.print("\n");
00247 }
00248 
00249 /*******************************************************************************
00250  *
00251 **/
00252 OStream& NolfiCell::operator>> (OStream& out) const
00253 {
00254     out.printf ("e=%01d, b=%+02.2f, w=%+02.2f, seglen=%02.2f, segang=%+02.2f, "
00255                 "x=%+02.2f, y=%+02.2f, t=%02d, n=%03d, nt=%02d, r=%02.2f",
00256                 int(mExpression), mBias, mWeight, mSegmentLength, mSegmentAngle,
00257                 mCoord.x, mCoord.y, mTypeID, mFinalID, mFinalType, mTipRadius);
00258     return out;
00259 }
00260 
00263 void NolfiCell::developAxon (
00264     Array<Coord2D>& result, //< An array where the coordinates of the axon tips are stored.
00265     double          scale   //< parameter tells usually the size of the neural space.
00266     ) const
00267 {
00268     if (true) {
00269         NolfiTurtle turtleDevice;
00270         Turtle turtle (turtleDevice, scale*mSegmentLength, mSegmentAngle*180/M_PI);
00271         turtle.jumpTo (mCoord+Coord2D(0.5,0));
00272         turtle.drawLSystem (smAxonString);
00273         turtleDevice.getTips (result);
00274     } else {
00275         result.make (11);
00276         for (int d=0; d<=10; d++) {
00277             double angle = mSegmentAngle*(d-5)*M_PI/10.0;
00278             result[d].x = mCoord.x+scale*fabs(mSegmentLength)*cos(angle);
00279             result[d].y = mCoord.y+scale*fabs(mSegmentLength)*sin(angle);
00280         }
00281     }
00282 }
00283 
00284 void NolfiCell::drawEPS (EPSDevice& devcon, double scale) const {
00285     // Cell body
00286     devcon.circle (mCoord, 0.5, mExpression);
00287     
00288     // Axon tree
00289     if (mExpression) {
00290         EPSTurtle turtleDevice (devcon, mTipRadius);
00291         Turtle turtle (turtleDevice, scale*mSegmentLength, mSegmentAngle*180/M_PI);
00292         turtle.jumpTo (mCoord+Coord2D(0.5,0));
00293         turtle.drawLSystem (smAxonString);
00294     }
00295 }
00296 
00297 void NolfiCell::check () const {
00298     ASSERTWITH (mTipRadius>=0.5, "Internal error");
00299 }
00300 
00301 
00302 
00304 //                                                                          //
00305 //                    |   |      |     o |   |                              //
00306 //                    |\  |      |  __   |\  |  ___   |                     //
00307 //                    | \ |  __  | /   | | \ | /   ) -+-                    //
00308 //                    |  \| /  \ | +-- | |  \| |---   |                     //
00309 //                    |   | \__/ | |   | |   |  \__    \                    //
00310 //                                 |                                        //
00312 
00319 NolfiNet::NolfiNet (
00320     int    inputs,     //< Number of inputs in the final network.
00321     int    maxhidden,  //< Maximum number of hidden neurons in the final network.
00322     int    outputs,    //< Number of output neurons in the final network.
00323     double xsize,      //< Length of the x-dimension of the cell space.
00324     double ysize,      //< Length of the y-dimension of the cell space.
00325     double tipRadius,
00326     double axonScale)  //< Axon length scaling factor. Typically 1.0.
00327 {
00328     mInputs = inputs;
00329     mMaxHidden = maxhidden;
00330     mHiddens = 0;
00331     mOutputs = outputs;
00332     mXSize = xsize;
00333     mYSize = ysize;
00334     mSize = ((mInputs>mOutputs)? mInputs+1: mOutputs+1);
00335     mInputBorder = 0.3;
00336     mOutputBorder = 0.7;
00337     mTipRadius = tipRadius;
00338     mAxonScale = axonScale;
00339 }
00340 
00343 void NolfiNet::decodeFrom (const Gentainer& g)
00344 {
00345     // Fetch genome-global tip radius, if it is encoded
00346     if (!isnull(g["tipr"]))
00347         mTipRadius  = ((const AnyFloatGene&)    g["tipr"]).getvalue();
00348 
00349     cells.make (mMaxHidden);
00350     for (int i=0; i<cells.size(); i++) {
00351         cells[i].setTipRadius (mTipRadius); // This can be changed by the decodeFrom below
00352         cells[i].decodeFrom (g, i);
00353         cells[i].check ();
00354     }
00355     
00356 }
00357 
00358 OStream& NolfiNet::operator>> (OStream& out) const
00359 {
00360     out.printf ("{%d-%d(%d)-%d (=%d), %fx%f}\n",
00361                 mInputs, mMaxHidden, mHiddens, mOutputs,
00362                 cells.size(), mXSize, mYSize);
00363 
00364     for (int i=0; i<cells.size(); i++)
00365         out << cells[i] << "\n";
00366     return out;
00367 }
00368 
00369 bool NolfiNet::indexCells () {
00370 
00371     // Resolve the type for each cell and count the types
00372     int inputs, hiddens, outputs;
00373     resolveTypes (inputs, hiddens, outputs);
00374     // TRACE3 ("%d-%d-%d", inputs, hiddens, outputs);
00375     
00376     // If there are no neurons of some neuron type, abort
00377     if (inputs==0 || hiddens==0 || outputs==0 || outputs<mOutputs)
00378         return false;
00379 
00380     indexInputs ();
00381     indexHiddens (hiddens);
00382     indexOutputs (hiddens);
00383 
00384     // Remove the cells with no index
00385     //for (int i=0; i<cells.size; i++)
00386     //  if (cells[i].mFinalID==EMPTYID)
00387     //      ASSERT (false);
00388     
00389     mHiddens = hiddens;
00390     return true;
00391 }
00392 
00393 void NolfiNet::resolveTypes (int& inputs, int& hiddens, int& outputs) {
00394     inputs = hiddens = outputs = 0;
00395 
00396     // Resolve the type of the cell according to it's X coordinate
00397 
00398     for (int i=0; i<cells.size(); i++)
00399         if (cells[i].mExpression) {
00400             if (cells[i].mCoord.x<mInputBorder) {
00401                 // Input unit.
00402                 if (cells[i].mCoord.x>=0 && cells[i].mCoord.y>=0 && cells[i].mCoord.y<=1) {
00403                     cells[i].mFinalType = CT_INPUT;
00404                     inputs++;
00405                 }
00406             } else if (cells[i].mCoord.x>mOutputBorder) {
00407                 // Output unit.
00408                 if (cells[i].mCoord.x<=1 && cells[i].mCoord.y>=0 && cells[i].mCoord.y<=1) {
00409                     cells[i].mFinalType = CT_OUTPUT;
00410                     outputs++;
00411                 }
00412             } else {
00413                 // Hidden unit.
00414                 if (cells[i].mCoord.y>=0 && cells[i].mCoord.y<=1) {
00415                     cells[i].mFinalType = CT_HIDDEN;
00416                     hiddens++;
00417                 } else {
00418                     sout << cells[i] << "\n";
00419                     ASSERT (false);
00420                 }
00421             }
00422         }
00423 }
00424 
00425 void NolfiNet::indexInputs () {
00426 }
00427 
00428 void NolfiNet::indexHiddens (int hiddens) {
00429     // Index ALL units according to their X-position
00430     for (int i=0; i<cells.size(); i++) {
00431         // Find the unindexed hidden cell with the smallest X coordinate
00432         double minX = 666.0;
00433         int minNeuron = -1;
00434         
00435         for (int j=0; j<cells.size(); j++)
00436             if (cells[j].mCoord.x<=minX && cells[j].mFinalID==EMPTYID) {
00437                 minX = cells[j].mCoord.x;
00438                 minNeuron = j;
00439             }
00440         
00441         // Set the index of the smallest found unindexed cell
00442         cells[minNeuron].mFinalID = i+mInputs;
00443     }
00444 }
00445 
00446 void NolfiNet::indexOutputs (int hiddens) {
00447 }
00448 
00449 void NolfiNet::removeDuplicates (int& rOutputs)
00450 {
00451     // Remove input and output cells with duplicate indices
00452     for (int i=0; i<cells.size(); i++)
00453         for (int j=i+1; j<cells.size(); j++)
00454             if (cells[i].mFinalID!=EMPTYID && cells[i].mFinalID == cells[j].mFinalID) {
00455                 cells[j].mFinalID = EMPTYID;
00456                 // We want to calculate the number of output units exactly
00457                 if (cells[i].mFinalType==CT_OUTPUT)
00458                     rOutputs--;
00459             }
00460 }
00461 
00462 
00469 ANNetwork* NolfiNet::growNet ()
00470 {
00471     if (!indexCells ())
00472         return NULL;
00473 
00474     // Do some scaling
00475     for (int i=0; i<cells.size(); i++) {
00476         // Scale the coordinates
00477         //cells[i].mCoord.x = (mYSize/mXSize)*(0.25+int(mXSize*cells[i].mCoord.x));
00478         cells[i].mCoord.x *= mYSize;
00479         cells[i].mCoord.y *= mYSize;
00480         
00481         // Scale the axon segment length. Since there are five
00482         // segments, this leads to axons about ½ the size of the neural
00483         // space
00484         cells[i].mSegmentLength *= 0.1*mYSize;
00485     }
00486 
00487     // :DEBUG:
00488     /*
00489     sout << *this;
00490     String pic = this->drawEPS();
00491     FILE* fout = fopen ("cangelosipic.eps", "w");
00492     fprintf (fout, "%s", (CONSTR) pic);
00493     fclose (fout);
00494     */
00495     
00496     // Create the network object
00497     ANNetwork* result = new ANNetwork (format("%d-%d-%d", mInputs, cells.size(), mOutputs));
00498 
00499     int connections = connect (*result);
00500     
00501     if (connections==0) {
00502         delete result;
00503         return NULL;
00504     }
00505 
00506     // Set final input units to be linear (propably unnecessary)
00507     for (int i=0; i<mInputs; i++)
00508         (*result)[i].setTFunc (Neuron::LINEAR_TF);
00509 
00510     // Set the coordinates for output units
00511     double spacing = mYSize/mOutputs;
00512     double startY = spacing/2+(mSize-mYSize)/2.0;
00513     for (int i=0; i<mOutputs; i++)
00514         (*result)[cells.size()-mOutputs+i].moveTo (Coord2D(9+mYSize, startY+i*spacing));
00515     
00516     return result;
00517 }
00518 
00519 int NolfiNet::connect (ANNetwork& network) const
00520 {
00521     int connections=0;
00522     Coord2D scaling ((mYSize+1)/mYSize, mSize/mYSize);
00523     for (int i=0; i<cells.size(); i++) {
00524         //(sout << cells[i]).print("\n");
00525 
00526         Neuron& neuron = network[cells[i].mFinalID];
00527 
00528         // Set the neuron attributes
00529         neuron.setBias (cells[i].mBias);
00530         neuron.moveTo (cells[i].mCoord*scaling+Coord2D(3,0.5));
00531 
00532         if (!cells[i].mExpression) {
00533             neuron.enable (false);
00534             continue;
00535         }
00536 
00537         // If an "input unit", make linear connection from a final
00538         // input unit
00539         if (cells[i].mFinalType==CT_INPUT) {
00540             // neuron.setTFunc (FreeNeuron::LINEAR_TF);
00541             network.connect (cells[i].mTypeID % mInputs, cells[i].mFinalID);
00542             //TRACE1 ("%d", cells[i].mTypeID);
00543         }
00544 
00545         // If an "output unit", make linear connection to a final
00546         // output unit
00547         if (cells[i].mFinalType==CT_OUTPUT) {
00548             // neuron.setTFunc (FreeNeuron::LINEAR_TF);
00549             network.connect (cells[i].mFinalID,
00550                              network.size()-mOutputs+(cells[i].mTypeID % mOutputs));
00551         }
00552         
00553         // Generate branch tip points
00554         Array<Coord2D> tips;
00555         cells[i].developAxon (tips, mAxonScale); //mXSize
00556 
00557         // Now find other cells that lie near these points
00558         for (int j=0; j<cells.size()-mOutputs; j++)
00559             if (cells[j].mFinalID > cells[i].mFinalID && cells[j].mFinalType!=CT_INPUT
00560                 && !(cells[i].mFinalType==CT_OUTPUT && cells[j].mFinalType==CT_OUTPUT))
00561                 for (int k=0; k<tips.size(); k++) {
00562                     double d = sqrt(cells[j].mCoord.sqdist (tips[k]));
00563                     if (d < cells[i].mTipRadius) {
00564                         // TRACE4 ("%d->%d: d(%d)=%f", i, j, k, d);
00565                         if (!neuron.connectedFrom (network[cells[j].mFinalID])) {
00566                             // Found a new target, add it to the network
00567                             network.connect (cells[i].mFinalID, cells[j].mFinalID);
00568                             connections++;
00569                             break;  // Break from the k loop
00570                         }
00571                     }
00572                 }
00573         
00574         // Set the initial connection weights to the encoded weight
00575         for (int j=0; j<neuron.incomings(); j++)
00576             neuron.incoming(j).setWeight (cells[i].mWeight);
00577     }
00578 
00579     return connections;
00580 }
00581 
00582 
00587 String NolfiNet::drawEPS () const
00588 {
00589     Coord2D picSize (175, 175);
00590     EPSDevice epsdevice (picSize);                  // Graphics device
00591     epsdevice.framedStyle (mYSize, mYSize);         // Clipping with frame
00592     
00593     // Draw input/output region borders
00594     epsdevice.lineWidth (0);
00595     epsdevice.lineStyle ("dashed", mYSize/picSize.y);
00596     epsdevice.line (Coord2D(mYSize*mInputBorder, 0),
00597                     Coord2D(mYSize*mInputBorder, mYSize));
00598     epsdevice.line (Coord2D(mYSize*mOutputBorder, 0),
00599                     Coord2D(mYSize*mOutputBorder, mYSize));
00600     epsdevice.lineStyle ("solid");
00601 
00602     // Draw cells
00603     for (int i=0; i<cells.size(); i++)
00604         cells[i].drawEPS (epsdevice, mAxonScale);
00605 
00606     epsdevice.printFooter ();
00607     return epsdevice.getBuffer ();
00608 }
00609 
00610 /*
00611 CString NolfiNet::drawLatex () const {
00612     CString result;
00613     result.reserve (10000);
00614     double picXSize=176.0, picYSize=152;
00615     result = format ("\\psset{xunit=1pt,yunit=1pt}\n"
00616                      "\\psclip{\\psframe(0,0)(%f,%f)}\n"
00617                      "\\psset{fillstyle=solid,fillcolor=white}\n"
00618                      "\\psline[linestyle=dashed](%f,0)(%f,%f)\n"
00619                      "\\psline[linestyle=dashed](%f,0)(%f,%f)\n",
00620                      picXSize, picYSize,
00621                      picXSize*mInputBorder, picXSize*mInputBorder, picYSize,
00622                      picXSize*0.75, picXSize*0.75, picYSize
00623         );
00624     
00625         double xscale = picXSize/mXSize; 
00626     double yscale = picYSize/mYSize;
00627     
00628     for (int i=0; i<cells.size; i++) {
00629         // Cell body
00630         result += format ("\\cnode(%f,%f){5pt}{N%d}\n",
00631                           cells[i].mCoord.x*xscale, picYSize-cells[i].mCoord.y*yscale, i);
00632 
00633         result += cells[i].drawLatex (picYSize, yscale);
00634 
00635         // Generate branch tip points
00636         PackArray<Coord2D> tips;
00637         cells[i].developAxon (tips, mXSize);
00638 
00639         for (int j=0; j<tips.size; j++)
00640             result += format ("\\psline[linestyle=dotted](%f,%f)(%f,%f)\n",
00641                               cells[i].mCoord.x*xscale, cells[i].mCoord.y*yscale,
00642                               tips[j].x*xscale, tips[j].y*yscale);
00643                         
00644     }
00645     
00646     result += format ("\\endpsclip\n");
00647 
00648     return result;
00649 }
00650 
00651 */
00652 

Generated on Thu Feb 10 20:21:26 2005 for Annalee by doxygen1.2.18