NTRT Simulator  v1.1
 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  numberOfControllers=myconfigdataaa.getintvalue("numberOfControllers"); //shared with ManhattanToyController
79  leniencyCoef=myconfigdataaa.getDoubleValue("leniencyCoef");
80  coevolution=myconfigdataaa.getintvalue("coevolution");
81  seeded = myconfigdataaa.getintvalue("startSeed");
82 
83  bool learning = myconfigdataaa.getintvalue("learning");
84 
85  if (populationSize < numberOfElementsToMutate + numberOfChildren)
86  {
87  throw std::invalid_argument("Population will grow with given parameters");
88  }
89 
90  srand(rdtsc());
91  eng.seed(rdtsc());
92 
93  for(int j=0;j<numberOfControllers;j++)
94  {
95  cout<<"creating Populations"<<endl;
96  populations.push_back(new NeuroEvoPopulation(populationSize,myconfigdataaa));
97  }
98 
99  // Overwrite the random parameters based on data
100  if(seeded) // Test that the file exists
101  {
102  for(int i = 0; i < numberOfControllers; i++)
103  {
104  NeuroEvoMember* seededPop = populations[i]->controllers.back();
105  stringstream ss;
106  ss<< resourcePath <<"logs/bestParameters-"<<this->suffix<<"-"<<i<<".nnw";
107  seededPop->loadFromFile(ss.str().c_str());
108  }
109  }
110  if(learning)
111  {
112  evolutionLog.open((resourcePath + "logs/evolution"+suffix+".csv").c_str(),ios::out);
113  if (!evolutionLog.is_open())
114  {
115  throw std::runtime_error("Logs does not exist. Please create a logs folder in your build directory or update your cmake file");
116  }
117  }
118 }
119 
120 NeuroEvolution::~NeuroEvolution()
121 {
122  // @todo - solve the invalid pointer that occurs here
123  #if (0)
124  for(std::size_t i = 0; i < populations.size(); i++)
125  {
126  delete populations[i];
127  }
128  populations.clear();
129  #endif
130 }
131 
132 void NeuroEvolution::mutateEveryController()
133 {
134  for(std::size_t i=0;i<populations.size();i++)
135  {
136  populations.at(i)->mutate(&eng,numberOfElementsToMutate);
137  }
138 }
139 
140 void NeuroEvolution::combineAndMutate()
141 {
142  for(std::size_t i=0;i<populations.size();i++)
143  {
144  populations.at(i)->combineAndMutate(&eng, numberOfElementsToMutate, numberOfChildren);
145  }
146 }
147 
149 {
150  generationNumber++;
151  double aveScore1 = 0.0;
152  double aveScore2 = 0.0;
153 #if (0)
154  // Disable definition of unused variables to suppress compiler warning
155  double maxScore1,maxScore2;
156 #endif
157  for(std::size_t i=0;i<scoresOfTheGeneration.size();i++)
158  {
159  aveScore1+=scoresOfTheGeneration[i][0];
160  aveScore2+=scoresOfTheGeneration[i][1];
161  }
162  aveScore1 /= scoresOfTheGeneration.size();
163  aveScore2 /= scoresOfTheGeneration.size();
164 
165 
166  for(std::size_t i=0;i<populations.size();i++)
167  {
168  populations.at(i)->orderPopulation();
169  }
171  evolutionLog<<generationNumber*numberOfTestsBetweenGenerations<<","<<aveScore1<<","<<aveScore2<<",";
172  evolutionLog<<populations.at(0)->getMember(0)->maxScore<<","<<populations.at(0)->getMember(0)->maxScore1<<","<<populations.at(0)->getMember(0)->maxScore2<<endl;
173 
174 
175  // what if member at 0 isn't the best of all time for some reason?
176  // This seems biased towards average scores
177  ofstream logfileLeader;
178  for(std::size_t i=0;i<populations.size();i++)
179  {
180  stringstream ss;
181  ss << resourcePath <<"logs/bestParameters-"<<suffix<<"-"<<i<<".nnw";
182  populations[i]->getMember(0)->saveToFile(ss.str().c_str());
183  }
184 }
185 
186 double diffclock(clock_t clock1,clock_t clock2)
187 {
188  double diffticks=clock1-clock2;
189  double diffms=(diffticks*10)/CLOCKS_PER_SEC;
190  return diffms;
191 }
192 
193 vector <NeuroEvoMember *> NeuroEvolution::nextSetOfControllers()
194 {
195  int testsToDo=0;
196  if(coevolution)
197  testsToDo=numberOfTestsBetweenGenerations; //stop when we reach x amount of random tests
198  else
199  testsToDo=populationSize; //stop when we test each element once
200 
201  if(currentTest == testsToDo)
202  {
204  if (numberOfChildren == 0)
205  {
206  mutateEveryController();
207  }
208  else
209  {
210  combineAndMutate();
211  }
212  cout<<"mutated the populations"<<endl;
213  this->scoresOfTheGeneration.clear();
214 
215  if(coevolution)
216  currentTest=0;//Start from 0
217  else
218  currentTest=populationSize - numberOfElementsToMutate - numberOfChildren; //start from the mutated ones only (last x)
219  }
220 
221  selectedControllers.clear();
222  for(std::size_t i=0;i<populations.size();i++)
223  {
224  int selectedOne=0;
225  if(coevolution)
226  selectedOne=rand()%populationSize; //select random one from each pool
227  else
228  selectedOne=currentTest; //select the same from each pool
229 
230 // cout<<"selected: "<<selectedOne<<endl;
231  selectedControllers.push_back(populations.at(i)->getMember(selectedOne));
232  }
233  currentTest++;
234 // cout<<"currentTest:"<<currentTest<<endl;
235 
236  return selectedControllers;
237 }
238 
239 void NeuroEvolution::updateScores(vector <double> multiscore)
240 {
241  if(multiscore.size()==2)
242  this->scoresOfTheGeneration.push_back(multiscore);
243  else
244  multiscore.push_back(-1.0);
245  double score=1.0* multiscore[0] - 0.0 * multiscore[1];
246  for(std::size_t oneElem=0;oneElem<selectedControllers.size();oneElem++)
247  {
248  NeuroEvoMember * controllerPointer=selectedControllers.at(oneElem);
249 
250  controllerPointer->pastScores.push_back(score);
251  double prevScore=controllerPointer->maxScore;
252  if(prevScore>score)
253  {
254  double newScore= leniencyCoef * prevScore + (1.0 - leniencyCoef) * score;
255  controllerPointer->maxScore=newScore;
256  }
257  else
258  {
259  controllerPointer->maxScore=score;
260  controllerPointer->maxScore1=multiscore[0];
261  controllerPointer->maxScore2=multiscore[1];
262  }
263  }
264 
265  //Record it to the file
266  ofstream payloadLog;
267  payloadLog.open((resourcePath + "logs/scores.csv").c_str(),ios::app);
268  payloadLog<<multiscore[0]<<","<<multiscore[1]<<endl;
269  payloadLog.close();
270  return;
271 }
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.
Top level class for NeuroEvolution.
std::string resourcePath