# Project: Socail Sensitivity Model # File name: SensitivityModel.py # Description: Model of social sensitivity and migration # # @author Sean Roberts: sean.g.roberts@gmail.com # # # @see The GNU Public License (GPL) # / # / # 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 2 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. # # You should have received a copy of the GNU General Public License along # with this program; if not, see . # / # Explanation at http://wp.me/pZBGv-zD # uses ffmpeg - http://ffmpeg.org/ # and matplotlib - http://matplotlib.sourceforge.net/ # it assumes there is a folder called 'images' in the same directory as this file (to store temporary images) # there are three classes: # Agent - an agent # Area - a local area that contains many agents has a certain amount of resources # World - a collection of Areas import math import random from matplotlib import pyplot as P import numpy as np import itertools class Agent: def __init__(self,g): self.sensitive = g class Area: def __init__(self): self.agents = [] self.resource = 0.0 def addAgent(self,a): self.agents.append(a) def removeAgent(self,a): try: self.remove(a) except: pass def removeRandomAgent(self): del self.agents[random.randint(0,len(self.agents))] def removeAgentProb(self,probSensitive,probNonSensitive,support,deathThreshold,maxCapacity,resourceThresh): # remove an agent based on their probability of if len(self.agents)>0: thresholds = self.getThresholds(probSensitive,probNonSensitive,support,resourceThresh) # print thresholds while len([x for x in thresholds if x < deathThreshold])>0 or len(self.agents)>maxCapacity: # need to remove agents # remove one at with probability proportionate to their thresholds if len(self.agents)0 & len(self.agents)>0: resourceShare = self.resource/float(len(self.agents)) if resourceShare > resourceThresh: # plenty of food, everyone has the same coping level thresholds = [probNonSensitive for x in self.agents] else: # when things are tough, difference comes out. thresholds = [] for x in self.agents: if x.sensitive: thresholds.append(probSensitive) else: thresholds.append(probNonSensitive) # threshold is likelihood of surviving given gene, resources and support thresholds = [(x * resourceShare * float(support)) for x in thresholds] return thresholds def setResource(self,x): self.resource = x def mate(self,driftProb): a = [] for x in self.agents: a.append(x) pairs = zip(a[:len(a)/2],a[len(a)/2:]) for p in pairs: inherit = random.choice([p[0].sensitive,p[1].sensitive]) if random.random() float(maxCapacity)*res: # #print "DEATH\t",x,"\t",y,"\t",res,"\t",support # self.map[x][y].removeAgentProb(probS,probNonS,support,deathThreshold) def getSupport(self,x,y,maxCapacity): # number of people in surrounding area numOthers = 0 for i in itertools.product([1,0,-1],repeat=2): try: numOthers += len(self.map[x+i[0]][y+i[1]].agents) except: pass ret = float(numOthers) / float(maxCapacity * 9) if ret >0.99: ret = 0.99 return ret # return numOthers def mate(self,driftProb): for x in range(self.size): for y in range(self.size): self.map[x][y].mate(driftProb) def migrate(self,migProb,maxMigDist): for x in range(self.size): for y in range(self.size): for a in self.map[x][y].agents: acopy = a if random.random()=self.size or newY<0 or newY>=self.size: newY = y+random.choice([i-maxMigDist for i in range((2*maxMigDist)+1)]) newX = x+random.choice([i-maxMigDist for i in range((2*maxMigDist)+1)]) # print "MIGRATE ",x,y,newX,newY self.map[newX][newY].addAgent(acopy) def resources(self, x,y): # random resources #return 0.7 + (0.3*random.random()) # an area's resources are based on latitude and longitude (x,y) return math.pow((float(x+1)/float(self.size)),2) * math.pow((float(y+1)/float(self.size)),2) ################### # Model Parameters ################### size = 60 # length of x axis world = World(size,10) maxCapacity = 10 # capacity of single area years = 200# number of years to run driftProb = 0.01 # chance of mutation migProb = 0.05 # chance of migration maxMigrationDistance = 1 # maximum distance an agent can migrate sensitiveThresh = 0.6 # probability sensitive agent will cope under stress nonSensitiveThresh = 0.9 # probability non-sensitive agent will cope under stress, #must be <1 resourceThresh = 0.5/maxCapacity deathThreshold = 0.003 # this has to be fiddled a bit! ticks = 2 # report every ticks years tickNumber = 0 # Run the model! for year in range(years): # if year == 10: # deathThreshold = 0.01 # raise deathThreshold # death world.death(maxCapacity,sensitiveThresh,nonSensitiveThresh,resourceThresh) # mate world.mate(driftProb) # migrate world.migrate(migProb,maxMigrationDistance) sLevels = [] for x in range(size): # print "\n", for y in range(size): slevel = -1 if len(world.map[x][y].agents)>0: # slevel is percentage of area that is sensitive slevel = float(sum([aa.sensitive for aa in world.map[x][y].agents]))/float(len(world.map[x][y].agents)) # slevel is num of senstive / maxCapacity # this ensures small numbers in a small pop don't have a big effect #slevel = float(sum([aa.sensitive for aa in world.map[x][y].agents]))/float(len(world.map[x][y].agents)) #print year,x,y,slevel # print slevel,"\t", sLevels.append(slevel) # print "\n\n\n" if year % ticks == 0: print year,sum([sum([len(y.agents) for y in x]) for x in world.map]) data = np.array(sLevels).reshape(size,size) P.clf() # clear the figure so we're not redrawing on top # levels so non-populated areas are white, # red = sensitive # blue = non-sensitive # green = mix cs = P.contourf(data,levels = [0,0.33,0.66,1]) fname = 'images/tmp%04d'%tickNumber tickNumber +=1 P.savefig(fname, dpi=100) # P.show() import os os.system('ffmpeg -f image2 -i images/tmp%04d.png video.mpg') os.system('rm images/*.png')