evorobotexperiment.cpp
1 /********************************************************************************
2  * FARSA Experiments Library *
3  * Copyright (C) 2007-2012 *
4  * Stefano Nolfi <stefano.nolfi@istc.cnr.it> *
5  * Onofrio Gigliotta <onofrio.gigliotta@istc.cnr.it> *
6  * Gianluca Massera <emmegian@yahoo.it> *
7  * Tomassino Ferrauto <tomassino.ferrauto@istc.cnr.it> *
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  * This program is distributed in the hope that it will be useful, *
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17  * GNU General Public License for more details. *
18  * *
19  * You should have received a copy of the GNU General Public License *
20  * along with this program; if not, write to the Free Software *
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
22  ********************************************************************************/
23 
24 #include "evorobotexperiment.h"
25 #include "sensors.h"
26 #include "motors.h"
27 #include "evoga.h"
28 #include "configurationhelper.h"
29 #include "factory.h"
30 #include "logger.h"
31 
32 #include <QFile>
33 #include <QTextStream>
34 #include <QString>
35 #include <QTime>
36 #include <QThread>
37 #include <iostream>
38 #include <cstdlib>
39 
40 // All the suff below is to avoid warnings on Windows about the use of unsafe
41 // functions. This should be only a temporary workaround, the solution is stop
42 // using C string and file functions...
43 #if defined(_MSC_VER)
44  #pragma warning(push)
45  #pragma warning(disable:4996)
46 #endif
47 
48 namespace farsa {
49 
50 /*
51  * Experiment constructor
52  */
54 {
55  ga = NULL;
56  world = NULL;
57  savedConfigurationParameters = NULL;
58  savedPrefix = NULL;
59  ntrial = 0;
60  stopCurrentTrial = false;
61  skipCurrentTrial = false;
62  restartCurrentTrial = false;
63  endCurrentIndividualLife = false;
64  arena = NULL;
65  agentIdSelected = 0;
66  timestep = 0.05;
67  // initializing the stepDelay at the same amount of timestep
68  // will slow down the simulation at real-time pace when the GUI is on
69  stepDelay = timestep*1000;
70 
71  gaPhase = NONE;
72 
73  // fitness and additional fitness components are not displayed by default
74 
75  // By default we don't run in batch mode
76  batchRunning = false;
77 
78  // Stating which resources we use here. This is here in the constructor so that we are sure to
79  // be the first to declare resources (if we did this later we should have used addUsableResources
80  // because child classes could declare they will use resources before us)
81  usableResources( QStringList() << "world" << "arena" << "experiment" << "robot" << "evonet" << "neuronsIterator" );
82 }
83 
85 {
86  // Removing resources
87  try {
88  deleteResource("experiment");
89  deleteResource("arena");
90  deleteResource("world");
91  deleteResource("robot");
92  deleteResource("evonet");
93  deleteResource("neuronsIterator");
94  } catch (...) {
95  // Doing nothing, this is here just to prevent throwing an exception from the destructor
96  }
97 
98  foreach( EmbodiedAgent* agent, eagents ) {
99  delete agent;
100  }
101 
102  delete savedConfigurationParameters;
103  delete savedPrefix;
104  delete arena;
105  delete world;
106 }
107 
109 {
110  // Saving configuration parameters and prefix for cloning
111  delete savedConfigurationParameters;
112  delete savedPrefix;
113  savedConfigurationParameters = new ConfigurationParameters(params);
114  savedConfigurationParameters->shareObserversWith(params);
115  savedPrefix = new QString(prefix);
116  // Setting ourself as resource manager in the configuration parameters object
117  params.setResourcesUser(this);
118  savedConfigurationParameters->setResourcesUser(this);
119 
120  ntrials = 1;
121  nsteps = 1;
122 
123  batchRunning = ConfigurationHelper::getBool(params, "__INTERNAL__/BatchRunning", batchRunning); // If we are running in batch or not
124  ntrials = ConfigurationHelper::getInt(params, prefix + "ntrials", ntrials); // number of trials to do
125  notifyChangesToParam( "ntrials" );
126  nsteps = ConfigurationHelper::getInt(params, prefix + "nsteps", nsteps); // number of step for each trial
127 
128  // Reading world parameters. We need to do this before calling recreateWorld because that function uses the parameters
129  timestep = ConfigurationHelper::getDouble(params, prefix + "World/timestep", timestep);
130  // initializing the stepDelay at the same amount of timestep
131  // will slow down the simulation at real-time pace when the GUI is on
132  stepDelay = timestep*1000;
133 
134  // create a World by default in order to exit from here with all configured properly
135  // if they are already created it will not destroy and recreate
136  recreateWorld();
137  // Creates the arena (if the group Arena is present)
138  recreateArena();
139  // Creates the Embodied Agents
140  // number of agents to create
141  int nagents = ConfigurationHelper::getInt(params, prefix + "nagents", 1);
142  if ( nagents > 1 ) {
143  // refactor the configuration parameters and create a subgroup foreach agent
144  for( int i=0; i<nagents; i++ ) {
145  QString agentPrefix = prefix + "AGENT:" + QString::number(i) + "/";
146  savedConfigurationParameters->createGroup( agentPrefix );
147  savedConfigurationParameters->copyGroupTree( prefix+"ROBOT", agentPrefix+"ROBOT" );
148  savedConfigurationParameters->copyGroupTree( prefix+"NET", agentPrefix+"NET" );
149  QStringList sensorsList = savedConfigurationParameters->getGroupsWithPrefixList(prefix, "Sensor:");
150  foreach( QString sensorGroup, sensorsList ) {
151  savedConfigurationParameters->copyGroupTree( prefix+sensorGroup, agentPrefix+sensorGroup );
152  }
153  QStringList motorsList = savedConfigurationParameters->getGroupsWithPrefixList(prefix, "Motor:");
154  foreach( QString motorGroup, motorsList ) {
155  savedConfigurationParameters->copyGroupTree( prefix+motorGroup, agentPrefix+motorGroup );
156  }
157  eagents.append( new EmbodiedAgent(i, agentPrefix, this) );
158  eagents.last()->configure();
159  }
160  } else {
161  eagents.append( new EmbodiedAgent(0, prefix, this) );
162  eagents.last()->configure();
163  }
164  selectAgent(0);
165 
166  // Adding robots to the arena (if the arena exists)
167  if (arena != NULL) {
168  QStringList robots;
169  foreach(EmbodiedAgent* e, eagents) {
170  robots.append(e->resourcePrefix+"robot");
171  }
172  arena->addRobots(robots);
173  }
174 
175  // declaring other resources
176  declareResource( "experiment", static_cast<ParameterSettableWithConfigureFunction*>(this) );
177 
178  Logger::info( params.getValue(prefix+"type") + " Configured" );
179 }
180 
182 {
183  Logger::error("NOT IMPLEMENTED (EvoRobotExperiment::save)");
184  abort();
185 }
186 
187 void EvoRobotExperiment::describe( QString type ) {
188  Descriptor d = addTypeDescription( type, "The experimental setup that defines the conditions and the fitness function of the evolutionary experiment" );
189  d.describeInt( "ntrials" ).def(1).limits(1,MaxInteger).runtime( &EvoRobotExperiment::setNTrials, &EvoRobotExperiment::getNTrials ).help("The number of trials the individual will be tested to calculate its fitness");
190  d.describeInt( "nsteps" ).def(1).limits(1,MaxInteger).help("The number of step a trials will last");
191  d.describeInt( "nagents" ).def(1).limits(1,MaxInteger).help("The number of embodied agents to create", "This parameter allow to setup experiments with more than one robot; all agents are clones");
192  d.describeSubgroup( "NET" ).props( IsMandatory ).type( "Evonet" ).help( "The Neural Network controlling the robot");
193  d.describeSubgroup( "ROBOT" ).props( IsMandatory ).type( "Robot" ).help( "The robot");
194  d.describeSubgroup( "Sensor" ).props( AllowMultiple ).type( "Sensor" ).help( "One of the Sensors from which the neural network will receive information about the environment" );
195  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" );
196  d.describeSubgroup( "Arena" ).type( "Arena" ).help( "The arena where robots live");
197 
198  SubgroupDescriptor world = d.describeSubgroup( "World" ).help( "Parameters affecting the simulated World" );
199  world.describeReal( "timestep" ).def(0.05).runtime( &EvoRobotExperiment::setWorldTimestep, &EvoRobotExperiment::getWorldTimeStep ).help( "The time in seconds corresponding to one simulated step of the World" );
200 }
201 
203 {
204  if (!batchRunning) {
205  // preventing gas from using multithread, which is not supported if the GUI is present
207  }
208  // Doing evolution by default
209  gaPhase=EvoRobotExperiment::INEVOLUTION;
210 }
211 
212 void EvoRobotExperiment::doTrial()
213 {
214  restartCurrentTrial = false;
215  stopCurrentTrial = false;
216  trialFitnessValue = 0.0;
217  for(nstep = 0; nstep < nsteps; nstep++) {
218  initStep( nstep );
219  if ( ga->commitStep() || restartCurrentTrial ) {
220  break;
221  }
222  doStep();
223  if ( ga->commitStep() ) break;
224  endStep( nstep );
225  if (ga->commitStep() || stopCurrentTrial || restartCurrentTrial) {
226  break;
227  }
228  }
229 }
230 
232 {
233  endCurrentIndividualLife = false;
234  totalFitnessValue = 0.0;
235 
236  initIndividual(individual);
237  if ( ga->commitStep() ) return;
238 
239  for (ntrial = 0; ntrial < ntrials; ntrial++) {
240  skipCurrentTrial = false;
241 
242  initTrial(ntrial);
243  if ( ga->commitStep() ) break;
244  if (skipCurrentTrial) { // && !ga->commitStep()) {
245  continue;
246  }
247  if (!endCurrentIndividualLife) {
248  doTrial();
249  }
250  if ( ga->isStopped() ) break;
251  if (restartCurrentTrial) {
252  ntrial--;
253  continue;
254  }
255  endTrial(ntrial);
256 
257  if (gaPhase == INTEST) {
258  Logger::info("Fitness for trial: " + QString::number(trialFitnessValue));
259  }
260 
261  if ( ga->commitStep() || endCurrentIndividualLife ) {
262  break;
263  }
264 
265  }
266 
267  endIndividual(individual);
268  ga->commitStep();
269 }
270 
272 {
273 }
274 
276 {
277 }
278 
280 {
281  // reset the neural controller
282  ResourcesLocker locker(this);
283  foreach( EmbodiedAgent* agent, eagents ) {
284  agent->evonet->resetNet();
285  }
286 }
287 
289 {
290 }
291 
293 {
294  return totalFitnessValue;
295 }
296 
298 {
299 }
300 
302 {
303 }
304 
306 {
307 }
308 
310 {
312 }
313 
315 {
316 }
317 
319 {
320 }
321 
322 void EvoRobotExperiment::doStep()
323 {
324  // There is no getResource below, but we are actually using resources so we must take the lock.
325  // We don't acquire the lock here, but lock and unlock when needed in the body of the function
326  ResourcesLocker locker(this, false);
327 
328  if (!batchRunning && stepDelay>0) {
329  // To use platform-independent sleep functions we have to do this...
330  class T : public QThread
331  {
332  public:
333  using QThread::sleep;
334  using QThread::msleep;
335  using QThread::usleep;
336  };
337  T::msleep( stepDelay );
338  }
339 
340  // update sensors
341  foreach( EmbodiedAgent* agent, eagents ) {
342  for (int s = 0; s < agent->sensors.size(); s++) {
343  agent->sensors[s]->update();
344  }
345  }
347  // update the neural controller
348  locker.lock();
349  foreach( EmbodiedAgent* agent, eagents ) {
350  agent->evonet->updateNet();
351  }
352  locker.unlock();
354  // setting motors
355  foreach( EmbodiedAgent* agent, eagents ) {
356  for (int m = 0; m < agent->motors.size(); m++) {
357  agent->motors[m]->update();
358  }
359  }
361  // advance the world simulation
362  locker.lock();
363  if (arena != NULL) {
365  }
366  world->advance();
367  if (arena != NULL) {
369  }
370  locker.unlock();
371 }
372 
374 {
375  stopCurrentTrial = true;
376 }
377 
379 {
380  skipCurrentTrial = true;
381 }
382 
384 {
385  restartCurrentTrial = true;
386 }
387 
389 {
390  endCurrentIndividualLife = true;
391  stopCurrentTrial = true;
392 }
393 
395 {
396  ResourcesLocker locker(this);
397 
398  return eagents[0]->evonet->freeParameters();
399 }
400 
401 Sensor* EvoRobotExperiment::getSensor( QString name, int id ) {
402  if ( eagents[id]->sensorsMap.contains( name ) ) {
403  return eagents[id]->sensorsMap[name];
404  } else {
405  Logger::error( "getSensor returned NULL pointer because there is no sensor named "+name+" in the agent "+QString::number(id) );
406  return NULL;
407  }
408 }
409 
410 Motor* EvoRobotExperiment::getMotor( QString name, int id ) {
411  if ( eagents[id]->motorsMap.contains( name ) ) {
412  return eagents[id]->motorsMap[name];
413  } else {
414  Logger::error( "getMotor returned NULL pointer because there is no motor named "+name+" in the agent "+QString::number(id) );
415  return NULL;
416  }
417 }
418 
420 {
421  return batchRunning;
422 }
423 
425  return eagents.size();
426 }
427 
429  agentIdSelected = id;
430  declareResource( "robot",
431  eagents[agentIdSelected]->robot,
432  eagents[agentIdSelected]->resourcePrefix+"robot" );
433  declareResource( "evonet",
434  static_cast<farsa::ParameterSettable*>(eagents[agentIdSelected]->evonet),
435  eagents[agentIdSelected]->resourcePrefix+"evonet" );
436  declareResource( "neuronsIterator",
437  eagents[agentIdSelected]->neuronsIterator,
438  eagents[agentIdSelected]->resourcePrefix+"neuronsIterator" );
439 }
440 
442 {
443  return eagents[id]->evonet;
444 }
445 
447 {
448  this->ga = ga;
449 }
450 
452  return ga;
453 }
454 
456 {
457  ResourcesLocker locker(this);
458  foreach( EmbodiedAgent* agent, eagents ) {
459  agent->evonet->setParameters(genes);
460  }
461 }
462 
464 {
465  Logger::error("EvoRobotExperiment::setTestingAgentAndSeed() not yet implemented");
466 }
467 
469  // Saving the old robot, the old arena and world to delete them after the new world has been
470  // created (as world is a resource, we need the old instance to exists during notifications.
471  // It can be safely deleted afterward)
472  World* const old_world = world;
473 
474  // TODO: parametrize the name and the dontUseYarp and all other parameters
475  world = new World( "World", true );
476  world->setTimeStep( timestep );
477  world->setSize( wVector( -2.0f, -2.0f, -0.50f ), wVector( +2.0f, +2.0f, +2.0f ) );
478  world->setFrictionModel( "exact" );
479  world->setSolverModel( "exact" );
480  world->setMultiThread( 1 );
481  world->setIsRealTime( false );
482 
483  // Removing deleted resources (if they existed) and then re-declaring world
484  if ( arena != NULL ) {
485  deleteResource( "arena" );
486  delete arena;
487  arena = NULL;
488  }
489 
490  if ( eagents.size() > 0 ) {
491  deleteResource( "robot" );
492  for( int i=0; i<eagents.size(); i++ ) {
493  deleteResource( eagents[i]->resourcePrefix+"robot" );
494  delete eagents[i]->robot;
495  eagents[i]->robot = NULL;
496  }
497  }
498 
499  declareResource( "world", world );
500 
501  // Now we can actually free memory
502  delete old_world;
503 }
504 
506  eagents[id]->recreateRobot();
507  if ( id == agentIdSelected ) {
508  // rebind the resource of the robot
509  declareResource( "robot",
510  eagents[agentIdSelected]->robot,
511  eagents[agentIdSelected]->resourcePrefix+"robot" );
512  }
513 }
514 
516  for (int i = 0; i < eagents.size(); i++ ) {
517  recreateRobot(i);
518  }
519 }
520 
522  // First of all we need to check whether there is an Arena group or not
523  if (!ConfigurationHelper::hasGroup( *savedConfigurationParameters, (*savedPrefix) + "Arena" ) ) {
524  // This is just to be sure...
525  arena = NULL;
526  return;
527  }
528 
529  // to be sure that a World exist
530  if ( !world ) {
531  recreateWorld();
532  }
533 
534  // Taking lock because we need to use world
535  ResourcesLocker locker(this);
536  // Saving the old arena to delete it after the new arena has been created
537  Arena* const old_arena = arena;
538 
539  // Now creating the arena. We first set ourself as the resouce manager
540  savedConfigurationParameters->setResourcesUser(this);
541  arena = savedConfigurationParameters->getObjectFromGroup<Arena>((*savedPrefix) + "Arena");
542  arena->shareResourcesWith(this);
543  QStringList robots;
544  foreach(EmbodiedAgent* e, eagents) {
545  robots.append(e->resourcePrefix+"robot");
546  }
547  arena->addRobots(robots);
548 
549  // Unlocking before redeclaring the arena resource
550  locker.unlock();
551 
552  declareResource("arena", static_cast<Resource*>(arena), "world");
553  delete old_arena;
554 }
555 
557  eagents[id]->recreateNeuralNetwork();
558  if ( id == agentIdSelected ) {
559  // rebind the resource of the evonet
560  declareResource( "evonet",
561  eagents[agentIdSelected]->robot,
562  eagents[agentIdSelected]->resourcePrefix+"evonet" );
563  }
564 }
565 
567  for (int i = 0; i < eagents.size(); i++ ) {
569  }
570 }
571 
572 void EvoRobotExperiment::setWorldTimestep( float timestep ) {
573  ResourcesLocker locker(this);
574  this->timestep = timestep;
575  if ( !world ) return;
576  world->setTimeStep( timestep );
577 }
578 
580  return timestep;
581 }
582 
584  stepDelay = delay;
585 }
586 
588  return stepDelay;
589 }
590 
591 EvoRobotExperiment::EmbodiedAgent::EmbodiedAgent( int id, QString agentPath, EvoRobotExperiment* exp ) {
592  this->id = id;
593  this->agentPath = agentPath;
594  this->exp = exp;
595  evonet = NULL;
596  neuronsIterator = new EvonetIterator();
597  robot = NULL;
598  resourcePrefix = QString("agent[%1]:").arg(id);
599 }
600 
601 void EvoRobotExperiment::EmbodiedAgent::configure() {
602  exp->savedConfigurationParameters->setResourcesUser(exp);
603  // add to the experiment the resources will create here
604  exp->addUsableResource( resourcePrefix+"evonet" );
605  exp->addUsableResource( resourcePrefix+"robot" );
606  exp->addUsableResource( resourcePrefix+"neuronsIterator" );
607  recreateRobot();
608 
609  // Reading the sensors parameters. For each sensor there must be a subgroup Sensor:NN where NN is a progressive number
610  // (needed to specify the sensors order). Here we also actually create sensors
611  QStringList sensorsList = exp->savedConfigurationParameters->getGroupsWithPrefixList(agentPath, "Sensor:");
612  sensorsList.sort();
613  foreach( QString sensorGroup, sensorsList ) {
614  // Setting the prefix for resources that depend on the robot.
615  // !! WARNING !! this should only be a temporary implementation, a better implementation is needed
616  exp->savedConfigurationParameters->createParameter(
617  agentPath+sensorGroup, "__resourcePrefix__", resourcePrefix );
618  // !! END OF TRICK !!
619  Sensor* sensor = exp->savedConfigurationParameters->getObjectFromGroup<Sensor>(agentPath + sensorGroup);
620  if ( sensor == NULL ) {
621  Logger::error("Cannot create the Sensor from group " + *(exp->savedPrefix) + sensorGroup + ". Aborting");
622  abort();
623  }
624  // in order to avoid name clash when using more than one Sensor,
625  // the Sensors are renamed using the same name of the Group when they don't have a name assigned
626  if ( sensor->name() == QString("unnamed") ) {
627  sensor->setName( sensorGroup );
628  }
629  sensors.append( sensor );
630  sensors.last()->shareResourcesWith( exp );
631  Logger::info( "Created a Sensor named "+sensor->name() );
632  // if the user manually set the name and create a name clash, it is only reported as error in Logger
633  if ( sensorsMap.contains( sensor->name() ) ) {
634  Logger::error( "More than one sensor has name "+sensor->name()+" !! The name has to be unique !!" );
635  } else {
636  // add to the map
637  sensorsMap[sensor->name()] = sensor;
638  }
639  }
640 
641  // Now we do for motors what we did for sensors. Motor groups are in the form Motor:NN
642  QStringList motorsList = exp->savedConfigurationParameters->getGroupsWithPrefixList(agentPath, "Motor:");
643  motorsList.sort();
644  foreach( QString motorGroup, motorsList ) {
645  // Setting the prefix for resources that depend on the robot.
646  // !! WARNING !! this should only be a temporary implementation, a better implementation is needed
647  exp->savedConfigurationParameters->createParameter(
648  agentPath+motorGroup, "__resourcePrefix__", resourcePrefix );
649  // !! END OF TRICK !!
650  Motor* motor = exp->savedConfigurationParameters->getObjectFromGroup<Motor>(agentPath + motorGroup);
651  if (motor == NULL) {
652  Logger::error("Cannot create the Motor from group " + *(exp->savedPrefix) + motorGroup + ". Aborting");
653  abort();
654  }
655  // in order to avoid name clash when using more than one Motor,
656  // the Motors are renamed using the same name of the Group when they don't have a name assigned
657  if ( motor->name() == QString("unnamed") ) {
658  motor->setName( motorGroup );
659  }
660  motors.append( motor );
661  motors.last()->shareResourcesWith( exp );
662  Logger::info( "Created a Motor named "+motor->name() );
663  // if the user manually set the name and create a name clash, it is only reported as error in Logger
664  if ( motorsMap.contains( motor->name() ) ) {
665  Logger::error( "More than one motor has name "+motor->name()+" !! The name has to be unique !!" );
666  } else {
667  // add to the map
668  motorsMap[motor->name()] = motor;
669  }
670  }
671 
672  recreateNeuralNetwork();
673 
674  exp->declareResource( resourcePrefix+"neuronsIterator", neuronsIterator, resourcePrefix+"evonet" );
675 }
676 
677 EvoRobotExperiment::EmbodiedAgent::~EmbodiedAgent() {
678  delete robot;
679  delete evonet;
680  delete neuronsIterator;
681  for (int i = 0; i < sensors.size(); i++) {
682  delete sensors[i];
683  }
684  for (int i = 0; i < motors.size(); i++) {
685  delete motors[i];
686  }
687 }
688 
689 void EvoRobotExperiment::EmbodiedAgent::recreateRobot() {
690  // to be sure that a World exist
691  if ( !(exp->world) ) {
692  exp->recreateWorld();
693  }
694 
695  // Taking lock because we need to use world
696  ResourcesLocker locker(exp);
697  // Saving the old robot to delete it after the new robot has been created
698  Robot* const old_robot = robot;
699 
700  // Now creating the robot
701  exp->savedConfigurationParameters->setResourcesUser(exp);
702  robot = exp->savedConfigurationParameters->getObjectFromGroup<Robot>(agentPath + "ROBOT");
703 
704  // Unlocking before redeclaring the robot resource
705  locker.unlock();
706 
707  exp->declareResource( resourcePrefix+"robot", robot, "world" );
708  delete old_robot;
709 }
710 
711 void EvoRobotExperiment::EmbodiedAgent::recreateNeuralNetwork() {
712  // Saving the old evonet to delete it after the new evonet has been created
713  Evonet* const old_evonet = evonet;
714 
715  // Check the subgroup [NET]
716  if ( exp->savedConfigurationParameters->getValue( agentPath+"NET/netFile" ).isEmpty() ) {
717  // calculate the number of sensors and motors neurons
718  int nSensors = 0;
719  foreach( Sensor* sensor, sensors ) {
720  nSensors += sensor->size();
721  }
722  int nMotors = 0;
723  foreach( Motor* motor, motors ) {
724  nMotors += motor->size();
725  }
726  // it inject the calculated nSensor and nMotors
727  exp->savedConfigurationParameters->createParameter( agentPath+"NET", "nSensors", QString::number(nSensors) );
728  exp->savedConfigurationParameters->createParameter( agentPath+"NET", "nMotors", QString::number(nMotors) );
729  }
730  // Now creating the neural network. We first set ourself as the resouce manager, then we lock resources (because during configuration
731  // evonet could use resources, but the resource user it will use is not thread safe (SimpleResourceUser))
732  ResourcesLocker locker(exp);
733  exp->savedConfigurationParameters->setResourcesUser(exp);
734  evonet = exp->savedConfigurationParameters->getObjectFromGroup<Evonet>( agentPath+"NET" );
735  locker.unlock();
736 
737  exp->declareResource( resourcePrefix+"evonet", static_cast<farsa::ParameterSettable*>(evonet) );
738 
739  // Here we have to take the lock again because we are going to change neuronsIterator
740  locker.lock();
741  delete old_evonet;
742  neuronsIterator->setEvonet( evonet );
743  // create the blocks associated to the network
744  int startIndex = 0;
745  foreach( Sensor* sensor, sensors ) {
746  neuronsIterator->defineBlock( sensor->name(), EvonetIterator::InputLayer, startIndex, sensor->size() );
747  startIndex += sensor->size();
748  }
749  startIndex = 0;
750  foreach( Motor* motor, motors ) {
751  neuronsIterator->defineBlock( motor->name(), EvonetIterator::OutputLayer, startIndex, motor->size() );
752  startIndex += motor->size();
753  }
754 }
755 
756 } // end namespace farsa
757 
758 // All the suff below is to restore the warning state on Windows
759 #if defined(_MSC_VER)
760  #pragma warning(pop)
761 #endif