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
00042
00043
00044
00045
00046
00048
00049
00050
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
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
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
00115
00116
00117
00118
00119
00121
00122
00123
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
00133 LGrammar grammar2;
00134 grammar2.addRule ("X", "F");
00135 grammar2.applyTo (result, 1);
00136
00137 return result;
00138 }
00139
00140
00141
00142
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
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,
00265 double scale
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
00286 devcon.circle (mCoord, 0.5, mExpression);
00287
00288
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
00306
00307
00308
00309
00310
00312
00319 NolfiNet::NolfiNet (
00320 int inputs,
00321 int maxhidden,
00322 int outputs,
00323 double xsize,
00324 double ysize,
00325 double tipRadius,
00326 double axonScale)
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
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);
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
00372 int inputs, hiddens, outputs;
00373 resolveTypes (inputs, hiddens, outputs);
00374
00375
00376
00377 if (inputs==0 || hiddens==0 || outputs==0 || outputs<mOutputs)
00378 return false;
00379
00380 indexInputs ();
00381 indexHiddens (hiddens);
00382 indexOutputs (hiddens);
00383
00384
00385
00386
00387
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
00397
00398 for (int i=0; i<cells.size(); i++)
00399 if (cells[i].mExpression) {
00400 if (cells[i].mCoord.x<mInputBorder) {
00401
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
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
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
00430 for (int i=0; i<cells.size(); i++) {
00431
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
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
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
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
00475 for (int i=0; i<cells.size(); i++) {
00476
00477
00478 cells[i].mCoord.x *= mYSize;
00479 cells[i].mCoord.y *= mYSize;
00480
00481
00482
00483
00484 cells[i].mSegmentLength *= 0.1*mYSize;
00485 }
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
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
00507 for (int i=0; i<mInputs; i++)
00508 (*result)[i].setTFunc (Neuron::LINEAR_TF);
00509
00510
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
00525
00526 Neuron& neuron = network[cells[i].mFinalID];
00527
00528
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
00538
00539 if (cells[i].mFinalType==CT_INPUT) {
00540
00541 network.connect (cells[i].mTypeID % mInputs, cells[i].mFinalID);
00542
00543 }
00544
00545
00546
00547 if (cells[i].mFinalType==CT_OUTPUT) {
00548
00549 network.connect (cells[i].mFinalID,
00550 network.size()-mOutputs+(cells[i].mTypeID % mOutputs));
00551 }
00552
00553
00554 Array<Coord2D> tips;
00555 cells[i].developAxon (tips, mAxonScale);
00556
00557
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
00565 if (!neuron.connectedFrom (network[cells[j].mFinalID])) {
00566
00567 network.connect (cells[i].mFinalID, cells[j].mFinalID);
00568 connections++;
00569 break;
00570 }
00571 }
00572 }
00573
00574
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);
00591 epsdevice.framedStyle (mYSize, mYSize);
00592
00593
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
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
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652