NTRT Simulator  Version: Master
 All Classes Namespaces Files Functions Variables Typedefs Friends Pages
NeuroEvolution.cpp
Go to the documentation of this file.
1 /*
2  * Copyright © 2012, United States Government, as represented by the
3  * Administrator of the National Aeronautics and Space Administration.
4  * All rights reserved.
5  *
6  * The NASA Tensegrity Robotics Toolkit (NTRT) v1 platform is licensed
7  * under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * http://www.apache.org/licenses/LICENSE-2.0.
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
15  * either express or implied. See the License for the specific language
16  * governing permissions and limitations under the License.
17 */
18 
27 #include "NeuroEvolution.h"
29 #include "core/tgString.h"
30 #include "helpers/FileHelpers.h"
31 // The C++ Standard Library
32 #include <iostream>
33 #include <numeric>
34 #include <string>
35 #include <sstream>
36 #include <stdexcept>
37 
38 using namespace std;
39 
40 #ifdef _WIN32
41 
42 // Windows
43 #define rdtsc __rdtsc
44 
45 #else
46 
47 // For everything else
48 unsigned long long rdtsc(){
49  unsigned int lo,hi;
50  __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
51  return ((unsigned long long)hi << 32) | lo;
52 }
53 
54 #endif
55 
56 NeuroEvolution::NeuroEvolution(std::string suff, std::string config, std::string path) :
57 suffix(suff)
58 {
59  currentTest=0;
60  generationNumber=0;
61  if (path != "")
62  {
63  resourcePath = FileHelpers::getResourcePath(path);
64  }
65  else
66  {
67  resourcePath = "";
68  }
69 
70  std::string configPath = resourcePath + config;
71 
72  configuration myconfigdataaa;
73  myconfigdataaa.readFile(configPath);
74  populationSize=myconfigdataaa.getintvalue("populationSize");
75  numberOfElementsToMutate=myconfigdataaa.getintvalue("numberOfElementsToMutate");
76  numberOfChildren=myconfigdataaa.getintvalue("numberOfChildren");
77  numberOfTestsBetweenGenerations=myconfigdataaa.getintvalue("numberOfTestsBetweenGenerations");
78  numberOfSubtests=myconfigdataaa.getintvalue("numberOfSubtests");
79  numberOfControllers=myconfigdataaa.getintvalue("numberOfControllers"); //shared with ManhattanToyController
80  leniencyCoef=myconfigdataaa.getDoubleValue("leniencyCoef");
81  coevolution=myconfigdataaa.getintvalue("coevolution");
82  seeded = myconfigdataaa.getintvalue("startSeed");
83 
84  bool learning = myconfigdataaa.getintvalue("learning");
85 
86  if (populationSize < numberOfElementsToMutate + numberOfChildren)
87  {
88  throw std::invalid_argument("Population will grow with given parameters");
89  }
90 
91  srand(rdtsc());
92  eng.seed(rdtsc());
93 
94  for(int j=0;j<numberOfControllers;j++)
95  {
96  cout<<"creating Populations"<<endl;
97  populations.push_back(new NeuroEvoPopulation(populationSize,myconfigdataaa));
98  }
99 
100  // Overwrite the random parameters based on data
101  if(seeded) // Test that the file exists
102  {
103  for(int i = 0; i < numberOfControllers; i++)
104  {
105  NeuroEvoMember* seededPop = populations[i]->controllers.back();
106  stringstream ss;
107  ss<< resourcePath <<"logs/bestParameters-"<<this->suffix<<"-"<<i<<".nnw";
108  seededPop->loadFromFile(ss.str().c_str());
109  }
110  }
111  if(learning)
112  {
113  evolutionLog.open((resourcePath + "logs/evolution"+suffix+".csv").c_str(),ios::out);
114  if (!evolutionLog.is_open())
115  {
116  throw std::runtime_error("Logs does not exist. Please create a logs folder in your build directory or update your cmake file");
117  }
118  }
119 }
120 
121 NeuroEvolution::~NeuroEvolution()
122 {
123  // @todo - solve the invalid pointer that occurs here
124  #if (0)
125  for(std::size_t i = 0; i < populations.size(); i++)
126  {
127  delete populations[i];
128  }
129  populations.clear();
130  #endif
131 }
132 
133 void NeuroEvolution::mutateEveryController()
134 {
135  for(std::size_t i=0;i<populations.size();i++)
136  {
137  populations.at(i)->mutate(&eng,numberOfElementsToMutate);
138  }
139 }
140 
141 void NeuroEvolution::combineAndMutate()
142 {
143  for(std::size_t i=0;i<populations.size();i++)
144  {
145  populations.at(i)->combineAndMutate(&eng, numberOfElementsToMutate, numberOfChildren);
146  }
147 }
148 
150 {
151  generationNumber++;
152  double aveScore1 = 0.0;
153  double aveScore2 = 0.0;
154 #if (0)
155  // Disable definition of unused variables to suppress compiler warning
156  double maxScore1,maxScore2;
157 #endif
158  for(std::size_t i=0;i<scoresOfTheGeneration.size();i++)
159  {
160  aveScore1+=scoresOfTheGeneration[i][0];
161  aveScore2+=scoresOfTheGeneration[i][1];
162  }
163  aveScore1 /= scoresOfTheGeneration.size();
164  aveScore2 /= scoresOfTheGeneration.size();
165 
166 
167  for(std::size_t i=0;i<populations.size();i++)
168  {
169  populations.at(i)->orderPopulation();
170  }
172  evolutionLog<<generationNumber*numberOfTestsBetweenGenerations<<","<<aveScore1<<","<<aveScore2<<",";
173  evolutionLog<<populations.at(0)->getMember(0)->maxScore<<","<<populations.at(0)->getMember(0)->maxScore1<<","<<populations.at(0)->getMember(0)->maxScore2<<endl;
174 
175 
176  // what if member at 0 isn't the best of all time for some reason?
177  // This seems biased towards average scores
178  ofstream logfileLeader;
179  for(std::size_t i=0;i<populations.size();i++)
180  {
181  stringstream ss;
182  ss << resourcePath <<"logs/bestParameters-"<<suffix<<"-"<<i<<".nnw";
183  populations[i]->getMember(0)->saveToFile(ss.str().c_str());
184  }
185 }
186 
187 double diffclock(clock_t clock1,clock_t clock2)
188 {
189  double diffticks=clock1-clock2;
190  double diffms=(diffticks*10)/CLOCKS_PER_SEC;
191  return diffms;
192 }
193 
194 vector <NeuroEvoMember *> NeuroEvolution::nextSetOfControllers()
195 {
196  int testsToDo=0;
197  if(coevolution)
198  testsToDo=numberOfTestsBetweenGenerations; //stop when we reach x amount of random tests
199  else
200  testsToDo=populationSize; //stop when we test each element once
201 
202  if(currentTest == testsToDo)
203  {
205  if (numberOfChildren == 0)
206  {
207  mutateEveryController();
208  }
209  else
210  {
211  combineAndMutate();
212  }
213  cout<<"mutated the populations"<<endl;
214  this->scoresOfTheGeneration.clear();
215 
216  if(coevolution)
217  currentTest=0;//Start from 0
218  else
219  currentTest=populationSize - numberOfElementsToMutate - numberOfChildren; //start from the mutated ones only (last x)
220  }
221 
222  selectedControllers.clear();
223  for(std::size_t i=0;i<populations.size();i++)
224  {
225  int selectedOne=0;
226  if(coevolution)
227  selectedOne=rand()%populationSize; //select random one from each pool
228  else
229  selectedOne=currentTest; //select the same from each pool
230 
231 // cout<<"selected: "<<selectedOne<<endl;
232  selectedControllers.push_back(populations.at(i)->getMember(selectedOne));
233  }
234  subTests++;
235 
236  if (subTests == numberOfSubtests)
237  {
238  currentTest++;
239  subTests = 0;
240  }
241 // cout<<"currentTest:"<<currentTest<<endl;
242 
243  return selectedControllers;
244 }
245 
246 void NeuroEvolution::updateScores(vector <double> multiscore)
247 {
248  if(multiscore.size()==2)
249  this->scoresOfTheGeneration.push_back(multiscore);
250  else
251  multiscore.push_back(-1.0);
252  double score=1.0* multiscore[0] - 0.0 * multiscore[1];
253  for(std::size_t oneElem=0;oneElem<selectedControllers.size();oneElem++)
254  {
255  NeuroEvoMember * controllerPointer=selectedControllers.at(oneElem);
256 
257  controllerPointer->pastScores.push_back(score);
258  double prevScore=controllerPointer->maxScore;
259  if(prevScore>score)
260  {
261  double newScore= leniencyCoef * prevScore + (1.0 - leniencyCoef) * score;
262  controllerPointer->maxScore=newScore;
263  }
264  else
265  {
266  controllerPointer->maxScore=score;
267  controllerPointer->maxScore1=multiscore[0];
268  controllerPointer->maxScore2=multiscore[1];
269  }
270  }
271 
272  //Record it to the file
273  ofstream payloadLog;
274  payloadLog.open((resourcePath + "logs/scores.csv").c_str(),ios::app);
275  payloadLog<<multiscore[0]<<","<<multiscore[1]<<endl;
276  payloadLog.close();
277  return;
278 }
Convenience function for combining strings with ints, mostly for naming structures.
void orderAllPopulations()
A class to read a learning configuration from a .ini file.
A series of functions to assist with file input/output.
static std::string getResourcePath(std::string relPath)
Definition: FileHelpers.cpp:40
unsigned long long rdtsc()
Rand seeding simular to the evolution and terrain classes.
Definition: tgUtil.cpp:28
Top level class for NeuroEvolution.
std::string resourcePath