[Avida-SVN] r3432 - in development/source: actions main tools

dk at myxo.css.msu.edu dk at myxo.css.msu.edu
Wed Sep 30 07:37:46 PDT 2009


Author: dk
Date: 2009-09-30 10:37:46 -0400 (Wed, 30 Sep 2009)
New Revision: 3432

Modified:
   development/source/actions/PopulationActions.cc
   development/source/main/cAvidaConfig.h
   development/source/main/cPopulation.cc
   development/source/tools/cRandom.h
Log:
merging deme tournaments, generic opinion-related deme actions.

Modified: development/source/actions/PopulationActions.cc
===================================================================
--- development/source/actions/PopulationActions.cc	2009-09-30 13:46:32 UTC (rev 3431)
+++ development/source/actions/PopulationActions.cc	2009-09-30 14:37:46 UTC (rev 3432)
@@ -2300,6 +2300,33 @@
 		}
 		return std::make_pair(support, have_opinion);
 	}
+	
+	/*! Returns the number of organisms that have set an opinion. */
+	int count_opinions(const cDeme& deme) { 
+		int have_opinion=0;
+		// For each organism in the deme:
+		for(int i=0; i<deme.GetSize(); ++i) {
+			cOrganism* org = deme.GetOrganism(i);
+			// if the org has an opinion, and it's the right value, count it:
+			if((org != 0) && org->HasOpinion()) {
+				++have_opinion;
+			}
+		}
+		return have_opinion;
+	}
+	
+	/*! Returns the set of organism opinions */
+	std::set<cOrganism::Opinion> unique_opinions(const cDeme& deme) {
+		std::set<cOrganism::Opinion> opinions; 
+		for(int i=0; i<deme.GetSize(); ++i) {
+			cOrganism* org = deme.GetOrganism(i);
+			// if the org has an opinion, and it's the right value, count it:
+			if((org != 0) && org->HasOpinion()) {
+				opinions.insert(org->GetOpinion().first);
+			}
+		}
+		return opinions;
+	}	
 };
 
 
@@ -2532,26 +2559,36 @@
 
 /*! This class rewards a deme based on the number of opinions that have been set
  to a given value.
+ 
+ There are three parameters that control this event:
+ desired: the opinion we're counting.
+ multiplicity: the number of times we want this opinion to be in the deme
+ side: the "sidedness" of the fitness function, that is, "to which side of mult is
+ there a fitness cliff?"  If side=-1, the cliff is to the left.  If side=1, the cliff
+ is to the right.  If side=0, there is no cliff.
  */
 class cActionCountOpinions : public cAbstractCompeteDemes, ConsensusSupport {
 public:
 	
 	//! Constructor.
 	cActionCountOpinions(cWorld* world, const cString& args) : cAbstractCompeteDemes(world, args),
-	_desired(0), _mult(1) {
+	_desired(0), _mult(1), _side(0) {
 		if(args.GetSize()) {
 			cString largs(args);
 			_desired = largs.PopWord().AsInt();
 			if(largs.GetSize()) {
 				_mult = largs.PopWord().AsInt();
-			}			
+				if(largs.GetSize()) {
+					_side = largs.PopWord().AsInt();
+				}
+			}
 		}		
 	}
 	
 	//! Destructor.
 	virtual ~cActionCountOpinions() { }
 	
-	static const cString GetDescription() { return "Arguments: [int desired_opinion=0 [int multiplicity=1]]"; }
+	static const cString GetDescription() { return "Arguments: [int desired_opinion=0 [int multiplicity=1 [int side=0]]]"; }
 	
   /*! Fitness function.
 	 
@@ -2562,7 +2599,7 @@
   virtual double Fitness(cDeme& deme) {
 		// the number of individuals that have set their opinion to _desired:
 		// s.first == support, s.second == have_opinion
-		std::pair<unsigned int, int> s = support(deme, _desired);
+		std::pair<int, int> s = support(deme, _desired);
 		
 		// encourage opinion setting:
 		if(s.second < deme.GetSize()) {
@@ -2584,15 +2621,243 @@
 		//		}
 		//		return pow(deme.GetSize() - scaled + 1, 2);
 		
-		return pow(deme.GetSize() - std::abs(static_cast<double>(_mult - s.first)) + 1, 2);
+		switch(_side) {
+			case -1: {
+				// fitness cliff to the left of _mult.
+				if(s.first >= _mult) {
+					return pow((double)deme.GetSize() - (s.first - _mult) + 1, 2);					
+				}
+				break;
+			}
+			case 0: {
+				// no fitness cliff.
+				return pow(deme.GetSize() - std::abs(static_cast<double>(_mult - s.first)) + 1, 2);
+			}
+			case 1: {
+				// fitness cliff to the right of _mult.
+				if(s.first <= _mult) {
+					return pow((double)deme.GetSize() - (_mult - s.first) + 1, 2);
+				}
+				break;
+			}
+			default: {
+				assert(false);
+			}
+		}
+		
+		// we only get down here if we fell off the fitness cliff to either side of _mult.
+		// we reward by the size of the deme 'cause all organisms did set their opinion.
+		return deme.GetSize();
 	}
 	
 private:
 	cOrganism::Opinion _desired; //!< Opinion that will be rewarded.
 	int _mult; //!< Desired multiplicity of the opinion that will be rewarded.
+	int _side; //!< "sidedness" of the fitness function (two-sided==0, one-sided left==-1, one-sided right==1).
 };
 
 
+/*! This class rewards a deme based on the number of opinions the deme has. 
+ Specifically, the user defines how many opinions it wants the deme to have (e.g., 5)
+ and the deme then receives rewards for having at least one of those opinions (e.g., 1-5).
+ 
+ There is one parameter that controls this event:
+ opinion_count: number of opinions we want the deme to have. We assume the opinions 
+ start at 1 and continue until the desired count is reached.
+ */
+class cActionCountMultipleOpinions : public cAbstractCompeteDemes, ConsensusSupport {
+public:
+	
+	typedef std::set<cOrganism::Opinion> opinion_set;
+	
+	//! Constructor.
+	cActionCountMultipleOpinions(cWorld* world, const cString& args) : cAbstractCompeteDemes(world, args) {
+		if(args.GetSize()) {
+			cString largs(args);
+			// Build the set of opinions that we will reward... does not include 0.
+			for(int i=largs.PopWord().AsInt(); i>0; --i) {
+				_rewarded.insert(i);
+			}
+		}		
+	}
+	
+	//! Destructor.
+	virtual ~cActionCountMultipleOpinions() { }
+	
+	static const cString GetDescription() { return "Arguments: [int opinion_count=0]"; }
+	
+  /*! Fitness function.
+	 
+	 The idea here is that we want to reward each deme based the number of opinions
+	 expressed out of the desired set (e.g., for a count of 5 opinions, we want
+	 at least one of 1, 2, 3, 4, 5 -- the others don't count.  
+	 
+	 We're going to set fitness == number of organisms that have an opinion to get 
+	 things jumpstarted.
+   */
+  virtual double Fitness(cDeme& deme) {
+		
+		// How many opinions are set?
+		int ocount = count_opinions(deme);
+		
+		// If not all opinions have been set, just return the number that have.
+		// We want to encourage opinion-setting.
+		if(ocount < deme.GetSize()) {
+			return ocount + 1;
+		}
+		
+		// Ok, great.  Everyone has set an opinion.  Now, reward based on
+		// the size of the intersection between the rewarded opinions and the
+		// unique opinions expressed in the deme.
+		opinion_set intersection;
+		opinion_set unique = unique_opinions(deme);
+		std::set_intersection(unique.begin(), unique.end(), _rewarded.begin(), _rewarded.end(), 
+													std::insert_iterator<opinion_set>(intersection, intersection.begin()));
+		
+		return intersection.size() + ocount + 1;
+	}
+	
+private:
+	opinion_set _rewarded; //!< Set of opinions that are rewarded.
+};
+
+
+
+/*! Compete demes event that evaluates based on 2 tasks (t1 & t2) as follows. 
+ If min <= t1 <= max, then f = 1 + 1 + t2
+ Else, f = 1
+ */
+class cActionDemeBalanceTwoTasks : public cAbstractCompeteDemes {
+public:
+	
+	//! Constructor.
+	cActionDemeBalanceTwoTasks(cWorld* world, const cString& args) : cAbstractCompeteDemes(world, args), _min(0), _max(0) {
+		if(args.GetSize()) {
+			cString largs(args);
+			_min = largs.PopWord().AsInt();
+			_max = largs.PopWord().AsInt();
+		}		
+	}
+	
+	//! Destructor.
+	virtual ~cActionDemeBalanceTwoTasks() { }
+	
+	static const cString GetDescription() { return "Two arguments: min and max for not"; }
+	
+  /*! Fitness function.
+	 Functions as described in the comments above.
+	 */
+  virtual double Fitness(cDeme& deme) {
+		
+		float val = 0.0;
+		double performed_t1=0.0;
+		double performed_t2=0.0;
+		for(int i=0; i<deme.GetSize(); ++i) {
+			cOrganism* org = deme.GetOrganism(i);
+			if(org != 0) {
+				tArray<int> reactions = org->GetPhenotype().GetCurReactionCount();
+				assert(reactions.GetSize() > 1);
+				
+				if (reactions[0]) performed_t1++;
+				if (reactions[1]) performed_t2++;
+				
+			}
+		}
+		
+		// return something
+		if ((_min <= performed_t1) && (performed_t1 <= _max)) {
+			val = 2 + performed_t2;
+		} else {
+			val = 1;
+		}
+		return val;
+	}
+	
+protected:
+	int _min; //!< min for task 1 (not)
+	int _max; //!< max for task 1 (not)
+};
+
+/*! Compete demes event that rewards for diversity of reactions that have been
+ performed by the currently living individuals in each deme.
+ */
+class cActionDemeReactionDiversity : public cAbstractCompeteDemes {
+public:
+	
+	//! Constructor.
+	cActionDemeReactionDiversity(cWorld* world, const cString& args) : cAbstractCompeteDemes(world, args), _uniq_only(false) {
+		if(args.GetSize()) {
+			cString largs(args);
+			_uniq_only = largs.PopWord().AsInt();
+		}		
+	}
+	
+	//! Destructor.
+	virtual ~cActionDemeReactionDiversity() { }
+	
+	static const cString GetDescription() { return "No arguments."; }
+	
+  /*! Fitness function.
+	 Here we reward the deme based on the number of unique reactions that have been
+	 performed by the current individuals.
+	 */
+  virtual double Fitness(cDeme& deme) {
+		std::set<int> uniq_reactions;
+		int performed=0;
+		for(int i=0; i<deme.GetSize(); ++i) {
+			cOrganism* org = deme.GetOrganism(i);
+			if(org != 0) {
+				bool performed_rx=false;
+				//				tArray<int> reactions = org->GetPhenotype().GetLastReactionCount();
+				tArray<int> reactions = org->GetPhenotype().GetCurReactionCount();
+				for(int j=0; j<reactions.GetSize(); ++j) {
+					if(reactions[j] > 0) {
+						uniq_reactions.insert(j);
+						performed_rx = true;
+					}
+				}
+				if(performed_rx) {
+					++performed;
+				}
+			}
+		}
+		
+		if(_uniq_only) {
+			return uniq_reactions.size() + 1.0;
+		} else {
+			if(performed < deme.GetSize()) {
+				return performed + 1.0;
+			} else {
+				return uniq_reactions.size() + deme.GetSize() + 1.0;
+			}
+		} 
+	}
+	
+protected:
+	bool _uniq_only; //!< Whether to reward for uniqueness of reaction only.
+};
+
+
+/*! Unit-fitness compete demes method (use for control runs).
+ */
+class cActionUnitFitness : public cAbstractCompeteDemes {
+public:
+	
+	//! Constructor.
+	cActionUnitFitness(cWorld* world, const cString& args) : cAbstractCompeteDemes(world, args) {
+	}
+	
+	//! Destructor.
+	virtual ~cActionUnitFitness() { }
+	
+	static const cString GetDescription() { return "No arguments."; }
+	
+	virtual double Fitness(cDeme& deme) {
+		return 1.0;
+	}
+};
+
+
 /*!	This class competes demes based on the total number of times that a
  *	given task has been completed by an organism in the deme since the
  *  deme was initialized. This action takes one integer parameter representing
@@ -2724,7 +2989,6 @@
 };
 
 
-
 /*! Send an artificial flash to a single organism in each deme in the population
  at a specified period.
  
@@ -4010,8 +4274,12 @@
 	action_lib->Register<cActionCompeteDemesByNetwork>("CompeteDemesByNetwork");
   action_lib->Register<cActionIteratedConsensus>("IteratedConsensus");
 	action_lib->Register<cActionCountOpinions>("CountOpinions");
+	action_lib->Register<cActionCountMultipleOpinions>("CountMultipleOpinions");
+	action_lib->Register<cActionDemeReactionDiversity>("DemeReactionDiversity");
+	action_lib->Register<cActionDemeBalanceTwoTasks>("DemeBalanceTwoTasks");
 	action_lib->Register<cActionSynchronization>("Synchronization");
 	action_lib->Register<cActionDesynchronization>("Desynchronization");
+	action_lib->Register<cActionUnitFitness>("UnitFitness");
 	
   action_lib->Register<cActionNewTrial>("NewTrial");
   action_lib->Register<cActionCompeteOrganisms>("CompeteOrganisms");

Modified: development/source/main/cAvidaConfig.h
===================================================================
--- development/source/main/cAvidaConfig.h	2009-09-30 13:46:32 UTC (rev 3431)
+++ development/source/main/cAvidaConfig.h	2009-09-30 14:37:46 UTC (rev 3432)
@@ -304,6 +304,8 @@
   
   CONFIG_ADD_GROUP(DEME_GROUP, "Demes and Germlines");
   CONFIG_ADD_VAR(NUM_DEMES, int, 1, "Number of independent groups in the\npopulation (default=1).");
+	CONFIG_ADD_VAR(DEMES_COMPETITION_STYLE, int, 0, "Select how the demes compete\n0=Fitness proportional\n1=Tournament");
+  CONFIG_ADD_VAR(DEMES_TOURNAMENT_SIZE, int, 0, "Number of demes that participate in a tournament");
   CONFIG_ADD_VAR(DEMES_USE_GERMLINE, int, 0, "Whether demes use a distinct germline (default=0).");
   CONFIG_ADD_VAR(DEMES_PREVENT_STERILE, int, 0, "Whether to prevent sterile demes from\nreplicating (default=0 or no).");
   CONFIG_ADD_VAR(DEMES_RESET_RESOURCES, int, 0, "Reset resources in demes on replication. \n0 = reset both demes \n1 = reset target deme \n2 = deme resources remain unchanged\n");

Modified: development/source/main/cPopulation.cc
===================================================================
--- development/source/main/cPopulation.cc	2009-09-30 13:46:32 UTC (rev 3431)
+++ development/source/main/cPopulation.cc	2009-09-30 14:37:46 UTC (rev 3432)
@@ -1266,65 +1266,112 @@
 	// only one deme, but we do want the stat-tracking.
 	if(fitness.size()==1) {
 		return; 
-	}	
+	}
 	
-  // Now, select the demes to live.  Each deme has a probability to replicate that is
-  // equal to its fitness / total fitness.
-  const double total_fitness = std::accumulate(fitness.begin(), fitness.end(), 0.0);
-  assert(total_fitness > 0.0); // Must have *some* positive fitnesses...
-  std::vector<unsigned int> deme_counts(deme_array.GetSize(), 0); // Number of demes (at index) which should wind up in the next generation.
-  
-  // What we're doing here is summing up the fitnesses until we reach or exceed the target fitness.
-  // Then we're marking that deme as being part of the next generation.
-  for(int i=0; i<deme_array.GetSize(); ++i) {
-    double running_sum = 0.0;
-    double target_sum = m_world->GetRandom().GetDouble(total_fitness);
-    for(int j=0; j<deme_array.GetSize(); ++j) {
-      running_sum += fitness[j];
-      if(running_sum >= target_sum) {
-        // j'th deme will be replicated.
-        ++deme_counts[j];
-        break;
-      }
-    }
-  }
-  
-  //re-inject demes with count of 1 back into self
-  for(int i = 0; i < (int)deme_counts.size(); i++) {
-    if(deme_counts[i] == 1)
-      ReplaceDeme(deme_array[i], deme_array[i]);
-  }
+	// Number of demes (at index) which should wind up in the next generation.
+	std::vector<unsigned int> deme_counts(deme_array.GetSize(), 0);						
 	
-  // Now, while we can find both a source deme (one with a count greater than 1)
-  // and a target deme (one with a count of 0), replace the target with the source.
-  while(true) {
-    int source_id=0;
-    for(; source_id<(int)deme_counts.size(); ++source_id) {
-      if(deme_counts[source_id] > 1) {
-        --deme_counts[source_id];
-        break;
-      }
-    }
-    
-    if(source_id == (int)deme_counts.size()) {
-      break; // All done.
-    }
-    
-    int target_id=0;
-    for(; target_id<(int)deme_counts.size(); ++target_id) {
-      if(deme_counts[target_id] == 0) {
-        ++deme_counts[target_id];
-        break;
-      }
-    }
-    
-    assert(source_id < deme_array.GetSize());
-    assert(target_id < deme_array.GetSize());
-    assert(source_id != target_id);
-    
-    // Replace the target with a copy of the source:
-    ReplaceDeme(deme_array[source_id], deme_array[target_id]);
-  }  
+	// Now, compete all demes based on the competition style.
+	switch(m_world->GetConfig().DEMES_COMPETITION_STYLE.Get()) {
+		case 0: {
+			// Fitness-proportional selection.
+			//
+			// Each deme has a probability equal to its fitness / sum(deme fitnesses)
+			// of proceeding to the next generation.
+			
+			const double total_fitness = std::accumulate(fitness.begin(), fitness.end(), 0.0);
+			assert(total_fitness > 0.0); // Must have *some* positive fitnesses...
+			// What we're doing here is summing up the fitnesses until we reach or exceed the target fitness.
+			// Then we're marking that deme as being part of the next generation.
+			for(int i=0; i<deme_array.GetSize(); ++i) {
+				double running_sum = 0.0;
+				double target_sum = m_world->GetRandom().GetDouble(total_fitness);
+				for(int j=0; j<deme_array.GetSize(); ++j) {
+					running_sum += fitness[j];
+					if(running_sum >= target_sum) {
+						// j'th deme will be replicated.
+						++deme_counts[j];
+						break;
+					}
+				}
+			}			
+			break;
+		}
+		case 1: {
+			// Tournament selection.
+			//
+			// We run NUM_DEMES tournaments of size DEME_TOURNAMENT_SIZE, and select the
+			// **single** winner of the tournament to proceed to the next generation.
+			// Losers of tournaments will be replaced, with no guarantees as to which
+			// deme will ultimately end up replacing them.  (Yes, this means that K==1.)
+						
+			// We need a list of all possible deme_ids so that we can pull samples from it.
+			std::vector<int> deme_ids(deme_array.GetSize());
+			for(int i=0; i<(int)deme_ids.size(); ++i) { deme_ids[i] = i; }
+			
+			// Run the tournaments.
+			for(int i=0; i<m_world->GetConfig().NUM_DEMES.Get(); ++i) {
+				// Which demes are in this tournament?
+				std::vector<int> tournament(m_world->GetConfig().DEMES_TOURNAMENT_SIZE.Get());
+				sample(deme_ids.begin(), deme_ids.end(), tournament.begin(), tournament.end(), cRandomStdAdaptor(m_world->GetRandom()));
+				
+				// Now, iterate through the fitnesses of each of the tournament players,
+				// capturing the winner's index and fitness.
+				std::pair<int, double> winner(i, 0.0);
+				for(std::vector<int>::iterator j=tournament.begin(); j!=tournament.end(); ++j) {
+					if(fitness[*j] > winner.second) { 
+						winner = std::make_pair(*j, fitness[*j]);
+					}
+				}
+				
+				// We have a winner!  Increment his replication count.
+				++deme_counts[winner.first];				
+			}
+			break;
+		}
+		default: {
+			// should never get here.
+			assert(false);
+		}
+	}
+	
+	// Housekeeping: re-inject demes with count of 1 back into self (energy-related).
+	for(int i = 0; i < (int)deme_counts.size(); i++) {
+		if(deme_counts[i] == 1)
+			ReplaceDeme(deme_array[i], deme_array[i]);
+	}
+	
+	// Ok, the below algorithm relies upon the fact that we have a strict weak ordering
+	// of fitness values for all demes.  We're going to loop through, find demes with a
+	// count greater than one, and insert them into demes with a count of zero.
+	while(true) {
+		int source_id=0;
+		for(; source_id<(int)deme_counts.size(); ++source_id) {
+			if(deme_counts[source_id] > 1) {
+				--deme_counts[source_id];
+				break;
+			}
+		}
+		
+		if(source_id == (int)deme_counts.size()) {
+			break; // All done; we looped through the whole list of counts, and didn't find any > 1.
+		}
+		
+		int target_id=0;
+		for(; target_id<(int)deme_counts.size(); ++target_id) {
+			if(deme_counts[target_id] == 0) {
+				++deme_counts[target_id];
+				break;
+			}
+		}
+		
+		assert(source_id < deme_array.GetSize());
+		assert(target_id < deme_array.GetSize());
+		assert(source_id != target_id);
+		
+		// Replace the target with a copy of the source:
+		ReplaceDeme(deme_array[source_id], deme_array[target_id]);
+	}
 }
 
 

Modified: development/source/tools/cRandom.h
===================================================================
--- development/source/tools/cRandom.h	2009-09-30 13:46:32 UTC (rev 3431)
+++ development/source/tools/cRandom.h	2009-09-30 14:37:46 UTC (rev 3432)
@@ -256,7 +256,18 @@
 };
 
 
+/*! Draw a sample (with replacement) from an input range, copying to the output range.
+ */
+template <typename ForwardIterator, typename OutputIterator, typename RNG>
+void sample(ForwardIterator first, ForwardIterator last, OutputIterator ofirst, OutputIterator olast, RNG rng) {
+	std::size_t range = std::distance(first, last);
+	while(ofirst != olast) {
+		*ofirst = *(first+rng(range));
+		++ofirst;
+	}
+}
 
+
 #ifdef ENABLE_UNIT_TESTS
 namespace nRandom {
   /**




More information about the Avida-cvs mailing list