/* * JABM - Java Agent-Based Modeling Toolkit * Copyright (C) 2013 Steve Phelps * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. */ package net.sourceforge.jabm.evolution; import java.util.Comparator; import net.sourceforge.jabm.agent.Agent; import net.sourceforge.jabm.agent.AgentList; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Required; import cern.jet.random.engine.RandomEngine; /** * <p> * A breeder which implements Fitness-proportionate reproduction. Agents are * selected for inclusion in the next generation with a probability * proportionate to their fitness, as defined by an exogenous fitness function. * When agents reproduce they do so via an {@link ImitationOperator} which * specifies how agents are copied from one generation to the next. * </p> * * @author Steve Phelps */ public class FitnessProportionateBreeder implements Breeder { /** * The fitness function which specifies the fitness of each agent. */ protected FitnessFunction fitnessFunction; protected ImitationOperator imitationFunction = new StrategyImitationOperator(); protected RandomEngine prng; protected double totalFitness; protected double imitationProbability = 1.0; static Logger logger = Logger.getLogger(FitnessProportionateBreeder.class); public AgentList reproduce(AgentList currentGeneration) { int n = currentGeneration.size(); AgentList nextGeneration = new AgentList(currentGeneration); double[] cummulativeFitnesses = cummulativeFitnesses(currentGeneration); if (!Double.isNaN(totalFitness) && !Double.isInfinite(totalFitness)) { for (int i = 0; i < n; i++) { int j = choose(cummulativeFitnesses); reproduce(nextGeneration.get(i), currentGeneration.get(j)); } } else { logger.warn("Not reproducing because fitness is undefined"); } return nextGeneration; } public int choose(double[] cummulativeFitnesses) { double r = prng.nextDouble(); int j = 0; while (j < cummulativeFitnesses.length && cummulativeFitnesses[j] < r) { j++; } return j; } public void reproduce(Agent child, Agent parent) { if (prng.nextDouble() < imitationProbability) { imitationFunction.inheritBehaviour(child, parent); } } public double[] cummulativeFitnesses(AgentList agents) { agents.sortAgents(new Comparator<Agent>() { public int compare(Agent o1, Agent o2) { return new Double(getFitness(o1)).compareTo(new Double(getFitness(o2))); } }); double[] result = new double[agents.size()]; this.totalFitness = 0.0; for(int i=0; i<result.length; i++) { totalFitness += getFitness(agents.get(i)); } double cummulativeTotal = 0.0; for(int i=0; i<result.length; i++) { double fitness = getFitness(agents.get(i)); cummulativeTotal += fitness; result[i] = cummulativeTotal / totalFitness; } return result; } public double getFitness(Agent i) { double result = 0.0; if (fitnessFunction != null) { result = fitnessFunction.getFitness(i); } else { result = i.getPayoff(); } if (result < 0.0) { result = 0.0; } return result; } public FitnessFunction getFitnessFunction() { return fitnessFunction; } public void setFitnessFunction(FitnessFunction fitnessFunction) { this.fitnessFunction = fitnessFunction; } public RandomEngine getPrng() { return prng; } @Required public void setPrng(RandomEngine prng) { this.prng = prng; } public ImitationOperator getImitationOperator() { return imitationFunction; } public void setImitationOperator(ImitationOperator imitationFunction) { this.imitationFunction = imitationFunction; } public double getImitationProbability() { return imitationProbability; } public void setImitationProbability(double imitationProbability) { this.imitationProbability = imitationProbability; } }