experiments/evorobot/src/evorobotexperiment.cpp

00001 /********************************************************************************
00002  *  FARSA Experiments Library                                                   *
00003  *  Copyright (C) 2007-2012                                                     *
00004  *  Stefano Nolfi <stefano.nolfi@istc.cnr.it>                                   *
00005  *  Onofrio Gigliotta <onofrio.gigliotta@istc.cnr.it>                           *
00006  *  Gianluca Massera <emmegian@yahoo.it>                                        *
00007  *  Tomassino Ferrauto <tomassino.ferrauto@istc.cnr.it>                         *
00008  *                                                                              *
00009  *  This program is free software; you can redistribute it and/or modify        *
00010  *  it under the terms of the GNU General Public License as published by        *
00011  *  the Free Software Foundation; either version 2 of the License, or           *
00012  *  (at your option) any later version.                                         *
00013  *                                                                              *
00014  *  This program is distributed in the hope that it will be useful,             *
00015  *  but WITHOUT ANY WARRANTY; without even the implied warranty of              *
00016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               *
00017  *  GNU General Public License for more details.                                *
00018  *                                                                              *
00019  *  You should have received a copy of the GNU General Public License           *
00020  *  along with this program; if not, write to the Free Software                 *
00021  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  *
00022  ********************************************************************************/
00023 
00024 #include "evorobotexperiment.h"
00025 #include "sensors.h"
00026 #include "motors.h"
00027 #include "evoga.h"
00028 #include "configurationhelper.h"
00029 #include "factory.h"
00030 #include "logger.h"
00031 
00032 #include <QFile>
00033 #include <QTextStream>
00034 #include <QString>
00035 #include <QTime>
00036 #include <QThread>
00037 #include <iostream>
00038 #include <cstdlib>
00039 
00040 // All the suff below is to avoid warnings on Windows about the use of unsafe
00041 // functions. This should be only a temporary workaround, the solution is stop
00042 // using C string and file functions...
00043 #if defined(_MSC_VER)
00044     #pragma warning(push)
00045     #pragma warning(disable:4996)
00046 #endif
00047 
00048 namespace farsa {
00049 
00050 /*
00051  * Experiment constructor
00052  */
00053 EvoRobotExperiment::EvoRobotExperiment()
00054 {
00055     ga = NULL;
00056     evonet = NULL;
00057     neuronsIterator = new EvonetIterator();
00058     robot = NULL;
00059     world = NULL;
00060     savedConfigurationParameters = NULL;
00061     savedPrefix = NULL;
00062     ntrial = 0;
00063     stopCurrentTrial = false;
00064     skipCurrentTrial = false;
00065     restartCurrentTrial = false;
00066     endCurrentIndividualLife = false;
00067 
00068     gaPhase = NONE;
00069 
00070     // fitness and additional fitness components are not displayed by default
00071 
00072     // By default we don't run in batch mode
00073     batchRunning = false;
00074 
00075     // Stating which resources we use here. This is here in the constructor so that we are sure to
00076     // be the first to declare resources (if we did this later we should have used addUsableResources
00077     // because child classes could declare they will use resources before us)
00078     usableResources( QStringList() << "world" << "evonet" << "neuronsIterator" << "experiment" );
00079 }
00080 
00081 EvoRobotExperiment::~EvoRobotExperiment()
00082 {
00083     delete savedConfigurationParameters;
00084     delete savedPrefix;
00085     delete evonet;
00086     for (int i = 0; i < sensors.size(); i++) {
00087         delete sensors[i];
00088     }
00089     for (int i = 0; i < motors.size(); i++) {
00090         delete motors[i];
00091     }
00092     delete world;
00093 }
00094 
00095 void EvoRobotExperiment::configure(ConfigurationParameters& params, QString prefix)
00096 {
00097     // Saving configuration parameters and prefix for cloning
00098     delete savedConfigurationParameters;
00099     delete savedPrefix;
00100     savedConfigurationParameters = new ConfigurationParameters(params);
00101     savedPrefix = new QString(prefix);
00102 
00103     // Setting ourserlf as resource manger in the configuration parameters object
00104     params.setResourcesUser(this);
00105 
00106     ntrials = 1;
00107     nsteps = 1;
00108 
00109     batchRunning = ConfigurationHelper::getBool(params, "__INTERNAL__/BatchRunning", batchRunning); // If we are running in batch or not
00110     ntrials = ConfigurationHelper::getInt(params, prefix + "ntrials", ntrials); // number of trials to do
00111     nsteps = ConfigurationHelper::getInt(params, prefix + "nsteps", nsteps); // number of step for each trial
00112 
00113     // Reading the sensors parameters. For each sensor there must be a subgroup Sensor:NN where NN is a progressive number
00114     // (needed to specify the sensors order). Here we also actually create sensors
00115     QStringList sensorsList = params.getGroupsWithPrefixList(prefix, "Sensor:");
00116     sensorsList.sort();
00117     foreach( QString sensorGroup, sensorsList ) {
00118         Sensor* sensor = params.getObjectFromGroup<Sensor>(prefix + sensorGroup); 
00119         if ( sensor == NULL ) {
00120             Logger::error("Cannot create the Sensor from group " + prefix + sensorGroup + ". Aborting");
00121             abort();
00122         }
00123         // in order to avoid name clash when using more than one Sensor,
00124         // the Sensors are renamed using the same name of the Group when they don't have a name assigned
00125         if ( sensor->name() == QString("unnamed") ) {
00126             sensor->setName( sensorGroup );
00127         }
00128         sensors.append( sensor );
00129         sensors.last()->shareResourcesWith( this );
00130         Logger::info( "Created a Sensor named "+sensor->name() );
00131         // if the user manually set the name and create a name clash, it is only reported as error in Logger
00132         if ( sensorsMap.contains( sensor->name() ) ) {
00133             Logger::error( "More than one sensor has name "+sensor->name()+" !! The name has to be unique !!" );
00134         } else {
00135             // add to the map
00136             sensorsMap[sensor->name()] = sensor;
00137         }
00138     }
00139 
00140     // Now we do for motors what we did for sensors. Motor groups are in the form Motor:NN
00141     QStringList motorsList = params.getGroupsWithPrefixList(prefix, "Motor:");
00142     motorsList.sort();
00143     foreach( QString motorGroup, motorsList ) {
00144         Motor* motor = params.getObjectFromGroup<Motor>(prefix + motorGroup);
00145         if (motor == NULL) {
00146             Logger::error("Cannot create the Motor from group " + prefix + motorGroup + ". Aborting");
00147             abort();
00148         }
00149         // in order to avoid name clash when using more than one Motor,
00150         // the Motors are renamed using the same name of the Group when they don't have a name assigned
00151         if ( motor->name() == QString("unnamed") ) {
00152             motor->setName( motorGroup );
00153         }
00154         motors.append( motor );
00155         motors.last()->shareResourcesWith( this );
00156         Logger::info( "Created a Motor named "+motor->name() );
00157         // if the user manually set the name and create a name clash, it is only reported as error in Logger
00158         if ( motorsMap.contains( motor->name() ) ) {
00159             Logger::error( "More than one motor has name "+motor->name()+" !! The name has to be unique !!" );
00160         } else {
00161             // add to the map
00162             motorsMap[motor->name()] = motor;
00163         }
00164     }
00165 
00166     // create a World and iCub by default in order to exit from here with all configured properly
00167     // if they are already created it will not destroy and recreate
00168     recreateWorld();
00169     recreateRobot();
00170     // create the neural network - it will destroy the previous net
00171     recreateNeuralNetwork();
00172     // declaring the resources
00173     declareResource( "neuronsIterator", neuronsIterator, "evonet" );
00174     declareResource( "experiment", static_cast<ParameterSettableWithConfigureFunction*>(this) );
00175 
00176     Logger::info( params.getValue(prefix+"type") + " Configured" );
00177 }
00178 
00179 void EvoRobotExperiment::save(ConfigurationParameters&, QString)
00180 {
00181     Logger::error("NOT IMPLEMENTED (EvoRobotExperiment::save)");
00182     abort();
00183 }
00184 
00185 void EvoRobotExperiment::describe( QString type ) {
00186     Descriptor d = addTypeDescription( type, "The experimental setup that defines the conditions and the fitness function of the evolutionary experiment" );
00187     d.describeInt( "ntrials" ).def(1).limits(1,MaxInteger).help("The number of trials the individual will be tested to calculate its fitness");
00188     d.describeInt( "nsteps" ).def(1).limits(1,MaxInteger).help("The number of step a trials will last");
00189     d.describeSubgroup( "NET" ).props( IsMandatory ).type( "Evonet" ).help( "The Neural Network controlling the robot");
00190     d.describeSubgroup( "ROBOT" ).props( IsMandatory ).type( "Robot" ).help( "The Neural Network controlling the robot");
00191     d.describeSubgroup( "Sensor" ).props( AllowMultiple ).type( "Sensor" ).help( "One of the Sensors from which the neural network will receive information about the environment" );
00192     d.describeSubgroup( "Motor" ).props( AllowMultiple ).type( "Motor" ).help( "One of the Motors with which the neural network acts on the robot and on the environment" );
00193 }
00194 
00195 void EvoRobotExperiment::postConfigureInitialization()
00196 {
00197     if (!batchRunning) {
00198         // preventing gas from using multithread, which is not supported if the GUI is present
00199         ga->doNotUseMultipleThreads();
00200     }
00201     // Doing evolution by default
00202     gaPhase=EvoRobotExperiment::INEVOLUTION;
00203 }
00204 
00205 void EvoRobotExperiment::doTrial()
00206 {
00207     restartCurrentTrial = false;
00208     stopCurrentTrial = false;
00209     trialFitnessValue = 0.0;
00210     for(nstep = 0; nstep < nsteps; nstep++) {
00211         initStep( nstep );
00212         if ( ga->commitStep() || restartCurrentTrial ) {
00213             break;
00214         }
00215         doStep();
00216         if ( ga->commitStep() ) break;
00217         endStep( nstep );
00218         if (ga->commitStep() || stopCurrentTrial || restartCurrentTrial) {
00219             break;
00220         }
00221     }
00222 }
00223 
00224 void EvoRobotExperiment::doAllTrialsForIndividual(int individual)
00225 {
00226     endCurrentIndividualLife = false;
00227     totalFitnessValue = 0.0;
00228 
00229     initIndividual(individual);
00230     if ( ga->commitStep() ) return;
00231 
00232     for (ntrial = 0; ntrial < ntrials; ntrial++) {
00233         skipCurrentTrial = false;
00234 
00235         initTrial(ntrial);
00236         if ( ga->commitStep() ) break;
00237         if (skipCurrentTrial) { // && !ga->commitStep()) {
00238             continue;
00239         }
00240         if (!endCurrentIndividualLife) {
00241             doTrial();
00242         }
00243         if ( ga->isStopped() ) break;
00244         if (restartCurrentTrial) {
00245             ntrial--;
00246             continue;
00247         }
00248         endTrial(ntrial);
00249 
00250         if (gaPhase == INTEST) {
00251             Logger::info("Fitness for trial: " + QString::number(trialFitnessValue));
00252         }
00253 
00254         if ( ga->commitStep() || endCurrentIndividualLife ) {
00255             break;
00256         }
00257 
00258     }
00259 
00260     endIndividual(individual);
00261     ga->commitStep();
00262 }
00263 
00264 void EvoRobotExperiment::initGeneration(int)
00265 {
00266 }
00267 
00268 void EvoRobotExperiment::initIndividual(int)
00269 {
00270 }
00271 
00272 void EvoRobotExperiment::initTrial(int)
00273 {
00274     // reset the neural controller
00275     ResourcesLocker locker(this);
00276     evonet->resetNet();
00277 }
00278 
00279 void EvoRobotExperiment::initStep(int)
00280 {
00281 }
00282 
00283 double EvoRobotExperiment::getFitness()
00284 {
00285     return totalFitnessValue;
00286 }
00287 
00288 void EvoRobotExperiment::afterSensorsUpdate()
00289 {
00290 }
00291 
00292 void EvoRobotExperiment::beforeMotorsUpdate()
00293 {
00294 }
00295 
00296 void EvoRobotExperiment::endTrial(int)
00297 {
00298     totalFitnessValue += trialFitnessValue;
00299 }
00300 
00301 void EvoRobotExperiment::endIndividual(int)
00302 {
00303 }
00304 
00305 void EvoRobotExperiment::endGeneration(int)
00306 {
00307 }
00308 
00309 void EvoRobotExperiment::doStep()
00310 {
00311     // There is no getResource below, but we are actually using resources so we must take the lock.
00312     // We don't acquire the lock here, but lock and unlock when needed in the body of the function
00313     ResourcesLocker locker(this, false);
00314 
00315     // update sensors
00316     for (int s = 0; s < sensors.size(); s++) {
00317         sensors[s]->update();
00318     }
00319     afterSensorsUpdate();
00320     // update the neural controller
00321     locker.lock();
00322     evonet->updateNet();
00323     locker.unlock();
00324     if (!batchRunning) {
00325         // To use platform-independent sleep functions we have to do this...
00326         class T : public QThread
00327         {
00328         public:
00329             using QThread::sleep;
00330             using QThread::msleep;
00331             using QThread::usleep;
00332         };
00333         // We sleep for few milliseconds when the GUI is active to avoid flooding it with
00334         // events and freeze it
00335         T::msleep(3);
00336     }
00337     beforeMotorsUpdate();
00338     // setting motors
00339     for (int m = 0; m < motors.size(); m++) {
00340         motors[m]->update();
00341     }
00342     // advance the world simulation
00343     locker.lock();
00344     world->advance();
00345     locker.unlock();
00346 }
00347 
00348 void EvoRobotExperiment::stopTrial()
00349 {
00350     stopCurrentTrial = true;
00351 }
00352 
00353 void EvoRobotExperiment::skipTrial()
00354 {
00355     skipCurrentTrial = true;
00356 }
00357 
00358 void EvoRobotExperiment::restartTrial()
00359 {
00360     restartCurrentTrial = true;
00361 }
00362 
00363 void EvoRobotExperiment::endIndividualLife()
00364 {
00365     endCurrentIndividualLife = true;
00366     stopCurrentTrial = true;
00367 }
00368 
00369 int EvoRobotExperiment::getGenomeLength()
00370 {
00371     ResourcesLocker locker(this);
00372 
00373     return evonet->freeParameters();
00374 }
00375 
00376 Sensor* EvoRobotExperiment::getSensor( QString name ) {
00377     if ( sensorsMap.contains( name ) ) {
00378         return sensorsMap[name];
00379     } else {
00380         Logger::error( "getSensor returned NULL pointer because there is no sensor named "+name );
00381         return NULL;
00382     }
00383 }
00384 
00385 Motor* EvoRobotExperiment::getMotor( QString name ) {
00386     if ( motorsMap.contains( name ) ) {
00387         return motorsMap[name];
00388     } else {
00389         Logger::error( "getMotor returned NULL pointer because there is no motor named "+name );
00390         return NULL;
00391     }
00392 }
00393 
00394 bool EvoRobotExperiment::inBatchRunning()
00395 {
00396     return batchRunning;
00397 }
00398 
00399 Evonet* EvoRobotExperiment::getNeuralNetwork()
00400 {
00401     return evonet;
00402 }
00403 
00404 void EvoRobotExperiment::setEvoga( Evoga* ga )
00405 {
00406     this->ga = ga;
00407 }
00408 
00409 Evoga* EvoRobotExperiment::getEvoga() {
00410     return ga;
00411 }
00412 
00413 void EvoRobotExperiment::setNetParameters(int *genes)
00414 {
00415     ResourcesLocker locker(this);
00416 
00417     evonet->getParameters(genes);
00418 }
00419 
00420 void EvoRobotExperiment::setTestingAgentAndSeed(int,int)
00421 {
00422     Logger::error("EvoRobotExperiment::setTestingAgentAndSeed() not yet implemented");
00423 }
00424 
00425 void EvoRobotExperiment::recreateWorld() {
00426     // Saving the old robot and world to delete them after the new world has been created (as
00427     // world is a resource, we need the old instance to exists during notifications. It can be
00428     // safely deleted afterward)
00429     Robot* const old_robot = robot;
00430     World* const old_world = world;
00431     robot = NULL;
00432 
00433     // TODO: parametrize the name and the dontUseYarp and all other parameters
00434     world = new World( "World", true );
00435     world->setTimeStep(0.05f);
00436     world->setSize( wVector( -2.0f, -2.0f, -0.50f ), wVector( +2.0f, +2.0f, +2.0f ) );
00437     world->setFrictionModel( "exact" );
00438     world->setSolverModel( "exact" );
00439     world->setMultiThread( 1 );
00440 
00441     // Removing deleted resources (if they existed) and then re-declaring world
00442     if ( old_robot != NULL ) {
00443         deleteResource( "robot" );
00444         robot = NULL;
00445     }
00446     declareResource( "world", world );
00447 
00448     // Now we can actually free memory
00449     delete old_robot;
00450     delete old_world;
00451 }
00452 
00453 void EvoRobotExperiment::recreateRobot() {
00454     // to be sure that a World exist
00455     if ( !world ) {
00456         recreateWorld();
00457     }
00458 
00459     // Taking lock because we need to use world
00460     ResourcesLocker locker(this);
00461     // Saving the old robot to delete it after the new robot has been created
00462     Robot* const old_robot = robot;
00463 
00464     // Now creating the robot. We first set ourself as the resouce manager
00465     savedConfigurationParameters->setResourcesUser(this);
00466     robot = savedConfigurationParameters->getObjectFromGroup<Robot>((*savedPrefix) + "ROBOT");
00467 
00468     // Unlocking before redeclaring the robot resource
00469     locker.unlock();
00470 
00471     declareResource( "robot", robot, "world" );
00472     delete old_robot;
00473 }
00474 
00475 void EvoRobotExperiment::recreateNeuralNetwork() {
00476     // Saving the old evonet to delete it after the new evonet has been created
00477     Evonet* const old_evonet = evonet;
00478 
00479     // Check the subgroup [NET]
00480     if ( savedConfigurationParameters->getValue( (*savedPrefix)+"NET/netFile" ).isEmpty() ) {
00481         // calculate the number of sensors and motors neurons
00482         int nSensors = 0;
00483         foreach( Sensor* sensor, sensors ) {
00484             nSensors += sensor->size();
00485         }
00486         int nMotors = 0;
00487         foreach( Motor* motor, motors ) {
00488             nMotors += motor->size();
00489         }
00490         // it inject the calculated nSensor and nMotors
00491         savedConfigurationParameters->createParameter( (*savedPrefix)+"NET", "nSensors", QString::number(nSensors) );
00492         savedConfigurationParameters->createParameter( (*savedPrefix)+"NET", "nMotors", QString::number(nMotors) );
00493     }
00494     // Now creating the neural network. We first set ourself as the resouce manager, then we lock resources (because during configuration
00495     // evonet could use resources, but the resource user it will use is not thread safe (SimpleResourceUser))
00496     ResourcesLocker locker(this);
00497     savedConfigurationParameters->setResourcesUser(this);
00498     evonet = savedConfigurationParameters->getObjectFromGroup<Evonet>( (*savedPrefix)+"NET" );
00499     locker.unlock();
00500 
00501     declareResource("evonet", static_cast<farsa::ParameterSettable*>(evonet));
00502 
00503     // Here we have to take the lock again because we are going to change neuronsIterator
00504     locker.lock();
00505     delete old_evonet;
00506     neuronsIterator->setEvonet( evonet );
00507     // create the blocks associated to the network
00508     int startIndex = 0;
00509     foreach( Sensor* sensor, sensors ) {
00510         neuronsIterator->defineBlock( sensor->name(), EvonetIterator::InputLayer, startIndex, sensor->size() );
00511         startIndex += sensor->size();
00512     }
00513     startIndex = 0;
00514     foreach( Motor* motor, motors ) {
00515         neuronsIterator->defineBlock( motor->name(), EvonetIterator::OutputLayer, startIndex, motor->size() );
00516         startIndex += motor->size();
00517     }
00518 }
00519 
00520 } // end namespace farsa
00521 
00522 // All the suff below is to restore the warning state on Windows
00523 #if defined(_MSC_VER)
00524     #pragma warning(pop)
00525 #endif