From 84c84a44e2161d71bd56fd4b007fe63d9bbe59f1 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Sun, 28 Jul 2024 21:55:26 -0700 Subject: [PATCH 01/46] Adds metrics for diseases in a separate log file --- agent.py | 3 ++ config.json | 1 + disease.py | 6 ++++ sugarscape.py | 94 ++++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 96 insertions(+), 8 deletions(-) diff --git a/agent.py b/agent.py index 2d2d76a8..92a1ebae 100644 --- a/agent.py +++ b/agent.py @@ -187,6 +187,9 @@ def catchDisease(self, disease, infector=None): caughtDisease = {"disease": disease, "startIndex": startIndex, "endIndex": endIndex} if infector != None: caughtDisease["infector"] = infector + if infector not in disease.infectors: + disease.infectors.append(infector.ID) + disease.infected += 1 self.diseases.append(caughtDisease) self.updateDiseaseEffects(disease) self.findCellsInRange() diff --git a/config.json b/config.json index ce01b054..98253739 100644 --- a/config.json +++ b/config.json @@ -88,6 +88,7 @@ "keepAlivePostExtinction": false, "logfile": "log.json", "logfileFormat": "json", + "logfileDisease": "diseaselog.json", "neighborhoodMode": "vonNeumann", "profileMode": false, "screenshots": false, diff --git a/disease.py b/disease.py index 269b85af..f52a21c6 100644 --- a/disease.py +++ b/disease.py @@ -12,6 +12,12 @@ def __init__(self, diseaseID, configuration): self.aggressionPenalty = configuration["aggressionPenalty"] self.tags = configuration["tags"] self.configuration = configuration + self.infectors = [] + self.infected = 0 + + def resetRStats(self): + self.infectors = [] + self.infected = 0 def __str__(self): return f"{self.ID}" diff --git a/sugarscape.py b/sugarscape.py index 59c08a94..0c6463d2 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -67,6 +67,9 @@ def __init__(self, configuration): "maxSugar": 0, "maxSpice": 0, "maxWealth": 0} self.log = open(configuration["logfile"], 'a') if configuration["logfile"] != None else None self.logFormat = configuration["logfileFormat"] + self.diseaselog = open(configuration["logfileDisease"], 'w') + self.diseasesStats = [] + self.diseasesStats.append({"disease": None, "timestep": None, "totalInfected": None, "newlyInfected": None, "infectors": None, "r": None}) def addAgent(self, agent): self.agentsBorn += 1 @@ -100,6 +103,12 @@ def addSugarPeak(self, startX, startY, radius, maxSugar): self.environment.findCell(i, j).maxSugar = cellMaxCapacity self.environment.findCell(i, j).sugar = cellMaxCapacity + def checkActiveDiseases(self): + for agent in self.agents: + if len(agent.diseases) > 0: + return True + return False + def configureAgents(self, numAgents): if self.environment == None: return @@ -219,10 +228,23 @@ def configureEnvironment(self, maxSugar, maxSpice, sugarPeaks, spicePeaks): self.environment.findCellNeighbors() self.environment.findCellRanges() + def countInfectedAgents(self, disease): + totalInfected = 0 + for agent in self.agents: + for agentDisease in agent.diseases: + if disease == agentDisease["disease"]: + totalInfected += 1 + return totalInfected + def doTimestep(self): if self.timestep >= self.maxTimestep: self.toggleEnd() return + if self.checkActiveDiseases() == True: + for disease in self.diseases: + disease.resetRStats() + else: + return if "all" in self.debug or "sugarscape" in self.debug: print(f"Timestep: {self.timestep}\nLiving Agents: {len(self.agents)}") self.timestep += 1 @@ -258,6 +280,7 @@ def endLog(self): self.runtimeStats["environmentWealthCreated"] = environmentWealthCreated self.runtimeStats["environmentWealthTotal"] = environmentWealthTotal logString = '\t' + json.dumps(self.runtimeStats) + "\n]" + self.diseasesStats.sort(key=lambda d: d["disease"]) if self.logFormat == "csv": logString = "" # Ensure consistent ordering for CSV format @@ -267,16 +290,24 @@ def endLog(self): else: logString += f",{self.runtimeStats[stat]}" logString += "\n" + # Entire disease log written at the end of the simulation to sort it by disease instead of timestep + for disease in self.diseasesStats: + diseaselogString = "" + for stat in disease.values(): + if diseaselogString == "": + diseaselogString += f"{stat}" + else: + diseaselogString += f",{stat}" + diseaselogString += "\n" + self.diseaselog.write(diseaselogString) + else: + groupedDiseases = self.groupDiseases() + self.diseaselog.write(self.formatDiseaseJSON(groupedDiseases)) self.log.write(logString) self.log.flush() self.log.close() - - def endSimulation(self): - self.removeDeadAgents() - self.endLog() - if "all" in self.debug or "sugarscape" in self.debug: - print(str(self)) - exit(0) + self.diseaselog.flush() + self.diseaselog.close() def findActiveQuadrants(self): quadrants = self.configuration["environmentStartingQuadrants"] @@ -302,6 +333,15 @@ def findActiveQuadrants(self): cellRange.append(quadrantFour) return cellRange + def formatDiseaseJSON(self, data): + formattedData = json.dumps(data, indent=4) + formattedData = formattedData.replace(", \"", ",\t\"") + formattedData = formattedData.replace("{\n ", "{") + formattedData = formattedData.replace(",\n ", ", ") + formattedData = formattedData.replace("\n },", "},") + formattedData = formattedData.replace("\n }\n ]", "}\n ]") + return formattedData + def generateAgentID(self): agentID = self.nextAgentID self.nextAgentID += 1 @@ -338,6 +378,23 @@ def generateTribeTags(self, tribe): random.shuffle(tags) return tags + def groupDiseases(self): + groupedDiseases = {} + for timestep in self.diseasesStats: + disease = timestep["disease"] + if disease not in groupedDiseases: + groupedDiseases[disease] = [] + del timestep["disease"] + groupedDiseases[disease].append(timestep) + return groupedDiseases + + def endSimulation(self): + self.removeDeadAgents() + self.endLog() + if "all" in self.debug or "sugarscape" in self.debug: + print(str(self)) + exit(0) + def pauseSimulation(self): while self.run == False: if self.gui != None and self.end == False: @@ -674,8 +731,18 @@ def startLog(self): header += f",{stat}" header += "\n" self.log.write(header) + + diseaseHeader = "" + for stat in (self.diseasesStats[0]): + if diseaseHeader == "": + diseaseHeader += f"{stat}" + else: + diseaseHeader += f",{stat}" + diseaseHeader += "\n" + self.diseaselog.write(diseaseHeader) else: self.log.write("[\n") + self.diseasesStats.clear() self.updateRuntimeStats() self.writeToLog() @@ -896,7 +963,6 @@ def updateRuntimeStats(self): meanAgeAtDeath = round(meanAgeAtDeath / numDeadAgents, 2) if numDeadAgents > 0 else 0 self.deadAgents = [] - self.runtimeStats["timestep"] = self.timestep self.runtimeStats["population"] = numAgents self.runtimeStats["meanMetabolism"] = meanMetabolism self.runtimeStats["meanMovement"] = meanMovement @@ -938,6 +1004,17 @@ def updateRuntimeStats(self): self.runtimeStats["agentTotalMetabolism"] = agentTotalMetabolism self.runtimeStats["totalSickAgents"] = totalSickAgents + if self.checkActiveDiseases == True: + for disease in self.diseases: + infectors = len(disease.infectors) + newlyInfected = disease.infected + r = 0 + totalInfected = self.countInfectedAgents(disease) + if infectors > 0: + r = round(float(newlyInfected / infectors), 2) + diseaseInfo = {"disease": disease.ID, "timestep": self.timestep, "totalInfected": totalInfected, "newlyInfected": newlyInfected, "infectors": infectors, "r": r} + self.diseasesStats.append(diseaseInfo) + def writeToLog(self): if self.log == None: return @@ -1273,6 +1350,7 @@ def verifyConfiguration(configuration): "keepAlivePostExtinction": False, "logfile": None, "logfileFormat": "json", + "logfileDisease": "disease.json", "neighborhoodMode": "vonNeumann", "profileMode": False, "screenshots": False, From b732c1c194edbaafc6b0535dc56e91ab4d1ac573 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Mon, 29 Jul 2024 20:36:53 -0700 Subject: [PATCH 02/46] Removes checkActiveDiseases function --- sugarscape.py | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/sugarscape.py b/sugarscape.py index 0c6463d2..8a5d887e 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -103,12 +103,6 @@ def addSugarPeak(self, startX, startY, radius, maxSugar): self.environment.findCell(i, j).maxSugar = cellMaxCapacity self.environment.findCell(i, j).sugar = cellMaxCapacity - def checkActiveDiseases(self): - for agent in self.agents: - if len(agent.diseases) > 0: - return True - return False - def configureAgents(self, numAgents): if self.environment == None: return @@ -240,11 +234,8 @@ def doTimestep(self): if self.timestep >= self.maxTimestep: self.toggleEnd() return - if self.checkActiveDiseases() == True: - for disease in self.diseases: - disease.resetRStats() - else: - return + for disease in self.diseases: + disease.resetRStats() if "all" in self.debug or "sugarscape" in self.debug: print(f"Timestep: {self.timestep}\nLiving Agents: {len(self.agents)}") self.timestep += 1 @@ -1004,16 +995,15 @@ def updateRuntimeStats(self): self.runtimeStats["agentTotalMetabolism"] = agentTotalMetabolism self.runtimeStats["totalSickAgents"] = totalSickAgents - if self.checkActiveDiseases == True: - for disease in self.diseases: - infectors = len(disease.infectors) - newlyInfected = disease.infected - r = 0 - totalInfected = self.countInfectedAgents(disease) - if infectors > 0: - r = round(float(newlyInfected / infectors), 2) - diseaseInfo = {"disease": disease.ID, "timestep": self.timestep, "totalInfected": totalInfected, "newlyInfected": newlyInfected, "infectors": infectors, "r": r} - self.diseasesStats.append(diseaseInfo) + for disease in self.diseases: + infectors = len(disease.infectors) + newlyInfected = disease.infected + r = 0 + totalInfected = self.countInfectedAgents(disease) + if infectors > 0: + r = round(float(newlyInfected / infectors), 2) + diseaseInfo = {"disease": disease.ID, "timestep": self.timestep, "totalInfected": totalInfected, "newlyInfected": newlyInfected, "infectors": infectors, "r": r} + self.diseasesStats.append(diseaseInfo) def writeToLog(self): if self.log == None: From 6374aa40e58622ab0358758f198e13eed9620c83 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Mon, 29 Jul 2024 20:53:08 -0700 Subject: [PATCH 03/46] Adds function to check for infected agents --- sugarscape.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/sugarscape.py b/sugarscape.py index 8a5d887e..fb0a8098 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -103,6 +103,12 @@ def addSugarPeak(self, startX, startY, radius, maxSugar): self.environment.findCell(i, j).maxSugar = cellMaxCapacity self.environment.findCell(i, j).sugar = cellMaxCapacity + def checkActiveDiseases(self): + for agent in self.agents: + if len(agent.diseases) > 0: + return True + return False + def configureAgents(self, numAgents): if self.environment == None: return @@ -234,8 +240,9 @@ def doTimestep(self): if self.timestep >= self.maxTimestep: self.toggleEnd() return - for disease in self.diseases: - disease.resetRStats() + if self.checkActiveDiseases() == True: + for disease in self.diseases: + disease.resetRStats() if "all" in self.debug or "sugarscape" in self.debug: print(f"Timestep: {self.timestep}\nLiving Agents: {len(self.agents)}") self.timestep += 1 @@ -995,15 +1002,16 @@ def updateRuntimeStats(self): self.runtimeStats["agentTotalMetabolism"] = agentTotalMetabolism self.runtimeStats["totalSickAgents"] = totalSickAgents - for disease in self.diseases: - infectors = len(disease.infectors) - newlyInfected = disease.infected - r = 0 - totalInfected = self.countInfectedAgents(disease) - if infectors > 0: - r = round(float(newlyInfected / infectors), 2) - diseaseInfo = {"disease": disease.ID, "timestep": self.timestep, "totalInfected": totalInfected, "newlyInfected": newlyInfected, "infectors": infectors, "r": r} - self.diseasesStats.append(diseaseInfo) + if self.checkActiveDiseases() == True: + for disease in self.diseases: + infectors = len(disease.infectors) + newlyInfected = disease.infected + r = 0 + totalInfected = self.countInfectedAgents(disease) + if infectors > 0: + r = round(float(newlyInfected / infectors), 2) + diseaseInfo = {"disease": disease.ID, "timestep": self.timestep, "totalInfected": totalInfected, "newlyInfected": newlyInfected, "infectors": infectors, "r": r} + self.diseasesStats.append(diseaseInfo) def writeToLog(self): if self.log == None: From b3d9ca83d2d31bdce8336f18cc74ef0d6608e3b6 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Thu, 1 Aug 2024 22:03:59 -0700 Subject: [PATCH 04/46] Adds disease r metrics to logfile --- config.json | 5 ++--- sugarscape.py | 53 ++++++++++++--------------------------------------- 2 files changed, 14 insertions(+), 44 deletions(-) diff --git a/config.json b/config.json index 98253739..4befacae 100644 --- a/config.json +++ b/config.json @@ -86,9 +86,8 @@ "interfaceHeight": 1000, "interfaceWidth": 900, "keepAlivePostExtinction": false, - "logfile": "log.json", - "logfileFormat": "json", - "logfileDisease": "diseaselog.json", + "logfile": "log.csv", + "logfileFormat": "csv", "neighborhoodMode": "vonNeumann", "profileMode": false, "screenshots": false, diff --git a/sugarscape.py b/sugarscape.py index fb0a8098..fecf49d0 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -63,13 +63,12 @@ def __init__(self, configuration): "totalMetabolismCost": 0, "agentsReplaced": 0, "agentsBorn": 0, "agentStarvationDeaths": 0, "agentDiseaseDeaths": 0, "environmentWealthCreated": 0, "agentWealthTotal": 0, "environmentWealthTotal": 0, "agentWealthCollected": 0, "agentWealthBurnRate": 0, "agentMeanTimeToLive": 0, "agentTotalMetabolism": 0, "agentCombatDeaths": 0, "agentAgingDeaths": 0, "totalSickAgents": 0} + diseaseStats = {f"disease{disease.ID}RValue": 0 for disease in self.diseases} + self.runtimeStats.update(diseaseStats) self.graphStats = {"ageBins": [], "sugarBins": [], "spiceBins": [], "lorenzCurvePoints": [], "meanTribeTags": [], "maxSugar": 0, "maxSpice": 0, "maxWealth": 0} self.log = open(configuration["logfile"], 'a') if configuration["logfile"] != None else None self.logFormat = configuration["logfileFormat"] - self.diseaselog = open(configuration["logfileDisease"], 'w') - self.diseasesStats = [] - self.diseasesStats.append({"disease": None, "timestep": None, "totalInfected": None, "newlyInfected": None, "infectors": None, "r": None}) def addAgent(self, agent): self.agentsBorn += 1 @@ -204,7 +203,7 @@ def configureDiseases(self, numDiseases): break if currStartingDiseases > maxStartingDiseases: currStartingDiseases = minStartingDiseases - + self.diseases.sort(key=lambda d: d.ID) if startingDiseases == [0, 0] and len(diseases) > 0 and ("all" in self.debug or "sugarscape" in self.debug): print(f"Could not place {len(diseases)} diseases.") @@ -278,7 +277,6 @@ def endLog(self): self.runtimeStats["environmentWealthCreated"] = environmentWealthCreated self.runtimeStats["environmentWealthTotal"] = environmentWealthTotal logString = '\t' + json.dumps(self.runtimeStats) + "\n]" - self.diseasesStats.sort(key=lambda d: d["disease"]) if self.logFormat == "csv": logString = "" # Ensure consistent ordering for CSV format @@ -288,24 +286,9 @@ def endLog(self): else: logString += f",{self.runtimeStats[stat]}" logString += "\n" - # Entire disease log written at the end of the simulation to sort it by disease instead of timestep - for disease in self.diseasesStats: - diseaselogString = "" - for stat in disease.values(): - if diseaselogString == "": - diseaselogString += f"{stat}" - else: - diseaselogString += f",{stat}" - diseaselogString += "\n" - self.diseaselog.write(diseaselogString) - else: - groupedDiseases = self.groupDiseases() - self.diseaselog.write(self.formatDiseaseJSON(groupedDiseases)) self.log.write(logString) self.log.flush() self.log.close() - self.diseaselog.flush() - self.diseaselog.close() def findActiveQuadrants(self): quadrants = self.configuration["environmentStartingQuadrants"] @@ -729,18 +712,8 @@ def startLog(self): header += f",{stat}" header += "\n" self.log.write(header) - - diseaseHeader = "" - for stat in (self.diseasesStats[0]): - if diseaseHeader == "": - diseaseHeader += f"{stat}" - else: - diseaseHeader += f",{stat}" - diseaseHeader += "\n" - self.diseaselog.write(diseaseHeader) else: self.log.write("[\n") - self.diseasesStats.clear() self.updateRuntimeStats() self.writeToLog() @@ -961,6 +934,7 @@ def updateRuntimeStats(self): meanAgeAtDeath = round(meanAgeAtDeath / numDeadAgents, 2) if numDeadAgents > 0 else 0 self.deadAgents = [] + self.runtimeStats["timestep"] = self.timestep self.runtimeStats["population"] = numAgents self.runtimeStats["meanMetabolism"] = meanMetabolism self.runtimeStats["meanMovement"] = meanMovement @@ -1002,16 +976,14 @@ def updateRuntimeStats(self): self.runtimeStats["agentTotalMetabolism"] = agentTotalMetabolism self.runtimeStats["totalSickAgents"] = totalSickAgents - if self.checkActiveDiseases() == True: - for disease in self.diseases: - infectors = len(disease.infectors) - newlyInfected = disease.infected - r = 0 - totalInfected = self.countInfectedAgents(disease) - if infectors > 0: - r = round(float(newlyInfected / infectors), 2) - diseaseInfo = {"disease": disease.ID, "timestep": self.timestep, "totalInfected": totalInfected, "newlyInfected": newlyInfected, "infectors": infectors, "r": r} - self.diseasesStats.append(diseaseInfo) + for disease in self.diseases: + infectors = len(disease.infectors) + newlyInfected = disease.infected + r = 0 + totalInfected = self.countInfectedAgents(disease) + if infectors > 0: + r = round(float(newlyInfected / infectors), 2) + self.runtimeStats[f"disease{disease.ID}RValue"] = r def writeToLog(self): if self.log == None: @@ -1348,7 +1320,6 @@ def verifyConfiguration(configuration): "keepAlivePostExtinction": False, "logfile": None, "logfileFormat": "json", - "logfileDisease": "disease.json", "neighborhoodMode": "vonNeumann", "profileMode": False, "screenshots": False, From 63af41275f946c46f17a79288c3bbf48ed1c4747 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Thu, 1 Aug 2024 22:46:34 -0700 Subject: [PATCH 05/46] Modifies disease naming scheme for runtime stats --- sugarscape.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sugarscape.py b/sugarscape.py index fecf49d0..1ff663df 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -63,7 +63,8 @@ def __init__(self, configuration): "totalMetabolismCost": 0, "agentsReplaced": 0, "agentsBorn": 0, "agentStarvationDeaths": 0, "agentDiseaseDeaths": 0, "environmentWealthCreated": 0, "agentWealthTotal": 0, "environmentWealthTotal": 0, "agentWealthCollected": 0, "agentWealthBurnRate": 0, "agentMeanTimeToLive": 0, "agentTotalMetabolism": 0, "agentCombatDeaths": 0, "agentAgingDeaths": 0, "totalSickAgents": 0} - diseaseStats = {f"disease{disease.ID}RValue": 0 for disease in self.diseases} + diseaseStats = {f"disease0{disease.ID}RValue": 0 for disease in self.diseases if disease.ID < 10} + diseaseStats.update({f"disease{disease.ID}RValue": 0 for disease in self.diseases}) self.runtimeStats.update(diseaseStats) self.graphStats = {"ageBins": [], "sugarBins": [], "spiceBins": [], "lorenzCurvePoints": [], "meanTribeTags": [], "maxSugar": 0, "maxSpice": 0, "maxWealth": 0} @@ -980,10 +981,12 @@ def updateRuntimeStats(self): infectors = len(disease.infectors) newlyInfected = disease.infected r = 0 - totalInfected = self.countInfectedAgents(disease) if infectors > 0: r = round(float(newlyInfected / infectors), 2) - self.runtimeStats[f"disease{disease.ID}RValue"] = r + if disease.ID < 10: + self.runtimeStats[f"disease0{disease.ID}RValue"] = r + else: + self.runtimeStats[f"disease{disease.ID}RValue"] = r def writeToLog(self): if self.log == None: From fa5f20de531982bf38fa7bc58868c80eb27cb6d8 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Fri, 2 Aug 2024 15:54:30 -0700 Subject: [PATCH 06/46] Adds metrics for incidence and prevalence --- sugarscape.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/sugarscape.py b/sugarscape.py index 1ff663df..4e54f21f 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -63,9 +63,12 @@ def __init__(self, configuration): "totalMetabolismCost": 0, "agentsReplaced": 0, "agentsBorn": 0, "agentStarvationDeaths": 0, "agentDiseaseDeaths": 0, "environmentWealthCreated": 0, "agentWealthTotal": 0, "environmentWealthTotal": 0, "agentWealthCollected": 0, "agentWealthBurnRate": 0, "agentMeanTimeToLive": 0, "agentTotalMetabolism": 0, "agentCombatDeaths": 0, "agentAgingDeaths": 0, "totalSickAgents": 0} - diseaseStats = {f"disease0{disease.ID}RValue": 0 for disease in self.diseases if disease.ID < 10} - diseaseStats.update({f"disease{disease.ID}RValue": 0 for disease in self.diseases}) - self.runtimeStats.update(diseaseStats) + self.diseaseStats = {} + for disease in self.diseases: + self.diseaseStats[f"disease{disease.ID}Incidence"] = 0 + self.diseaseStats[f"disease{disease.ID}Prevalence"] = 0 + self.diseaseStats[f"disease{disease.ID}RValue"] = 0 + self.runtimeStats.update(self.diseaseStats) self.graphStats = {"ageBins": [], "sugarBins": [], "spiceBins": [], "lorenzCurvePoints": [], "meanTribeTags": [], "maxSugar": 0, "maxSpice": 0, "maxWealth": 0} self.log = open(configuration["logfile"], 'a') if configuration["logfile"] != None else None @@ -979,14 +982,14 @@ def updateRuntimeStats(self): for disease in self.diseases: infectors = len(disease.infectors) - newlyInfected = disease.infected + incidence = disease.infected + prevalence = self.countInfectedAgents(disease) r = 0 if infectors > 0: - r = round(float(newlyInfected / infectors), 2) - if disease.ID < 10: - self.runtimeStats[f"disease0{disease.ID}RValue"] = r - else: - self.runtimeStats[f"disease{disease.ID}RValue"] = r + r = round(float(incidence / infectors), 2) + self.runtimeStats[f"disease{disease.ID}Incidence"] = incidence + self.runtimeStats[f"disease{disease.ID}Prevalence"] = prevalence + self.runtimeStats[f"disease{disease.ID}RValue"] = r def writeToLog(self): if self.log == None: From c9405d34edb5852527d321594aba35c67f7f49fc Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Wed, 7 Aug 2024 21:31:59 -0700 Subject: [PATCH 07/46] Adds feature to initialize diseases at different timesteps --- agent.py | 3 ++ config.json | 5 +- disease.py | 3 ++ sugarscape.py | 125 +++++++++++++++++++++++++++++++++++++------------- 4 files changed, 102 insertions(+), 34 deletions(-) diff --git a/agent.py b/agent.py index 92a1ebae..4651f3c3 100644 --- a/agent.py +++ b/agent.py @@ -81,6 +81,7 @@ def __init__(self, agentID, birthday, cell, configuration): self.spiceMeanIncome = 1 self.spiceMetabolismModifier = 0 self.spicePrice = 0 + self.startingDiseases = 0 self.sugarMeanIncome = 1 self.sugarMetabolismModifier = 0 self.sugarPrice = 0 @@ -181,6 +182,8 @@ def catchDisease(self, disease, infector=None): hammingDistance = diseaseInImmuneSystem["distance"] # If immune to disease, do not contract it if hammingDistance == 0: + if self not in disease.immuneAgents: + disease.immuneAgents.append(self) return startIndex = diseaseInImmuneSystem["start"] endIndex = diseaseInImmuneSystem["end"] diff --git a/config.json b/config.json index 4befacae..a58c5963 100644 --- a/config.json +++ b/config.json @@ -54,6 +54,7 @@ "diseaseMovementPenalty": [0, 0], "diseaseSpiceMetabolismPenalty": [1, 3], "diseaseSugarMetabolismPenalty": [1, 3], + "diseaseStartTimeframe": [1, 1], "diseaseTagStringLength": [11, 21], "diseaseVisionPenalty": [-1, 1], "environmentEquator": -1, @@ -86,8 +87,8 @@ "interfaceHeight": 1000, "interfaceWidth": 900, "keepAlivePostExtinction": false, - "logfile": "log.csv", - "logfileFormat": "csv", + "logfile": "log.json", + "logfileFormat": "json", "neighborhoodMode": "vonNeumann", "profileMode": false, "screenshots": false, diff --git a/disease.py b/disease.py index f52a21c6..81b30b79 100644 --- a/disease.py +++ b/disease.py @@ -10,10 +10,13 @@ def __init__(self, diseaseID, configuration): self.movementPenalty = configuration["movementPenalty"] self.fertilityPenalty = configuration["fertilityPenalty"] self.aggressionPenalty = configuration["aggressionPenalty"] + self.startTimestep = configuration["startTimestep"] self.tags = configuration["tags"] self.configuration = configuration + self.startingInfectedAgents = 0 self.infectors = [] self.infected = 0 + self.immuneAgents = [] def resetRStats(self): self.infectors = [] diff --git a/sugarscape.py b/sugarscape.py index 4e54f21f..22f11614 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -49,6 +49,7 @@ def __init__(self, configuration): self.agentsBorn = 0 self.deadAgents = [] self.diseases = [] + self.diseasesCount = [[] for i in range(configuration["diseaseStartTimeframe"][1] + 1)] self.activeQuadrants = self.findActiveQuadrants() self.configureAgents(configuration["startingAgents"]) self.configureDiseases(configuration["startingDiseases"]) @@ -63,12 +64,19 @@ def __init__(self, configuration): "totalMetabolismCost": 0, "agentsReplaced": 0, "agentsBorn": 0, "agentStarvationDeaths": 0, "agentDiseaseDeaths": 0, "environmentWealthCreated": 0, "agentWealthTotal": 0, "environmentWealthTotal": 0, "agentWealthCollected": 0, "agentWealthBurnRate": 0, "agentMeanTimeToLive": 0, "agentTotalMetabolism": 0, "agentCombatDeaths": 0, "agentAgingDeaths": 0, "totalSickAgents": 0} + """ self.diseaseStats = {} for disease in self.diseases: self.diseaseStats[f"disease{disease.ID}Incidence"] = 0 self.diseaseStats[f"disease{disease.ID}Prevalence"] = 0 self.diseaseStats[f"disease{disease.ID}RValue"] = 0 self.runtimeStats.update(self.diseaseStats) + """ + # temporary, to be deleted + self.diseaselog = open("diseaselog.csv", 'w') + self.diseaseStats = [] + self.diseaseStats.append({"disease": None, "timestep": None, "incidence": None, "prevalence": None, "RValue": None}) + self.graphStats = {"ageBins": [], "sugarBins": [], "spiceBins": [], "lorenzCurvePoints": [], "meanTribeTags": [], "maxSugar": 0, "maxSpice": 0, "maxWealth": 0} self.log = open(configuration["logfile"], 'a') if configuration["logfile"] != None else None @@ -179,37 +187,51 @@ def configureDiseases(self, numDiseases): numDiseases = numAgents diseaseEndowments = self.randomizeDiseaseEndowments(numDiseases) - random.shuffle(self.agents) - diseases = [] for i in range(numDiseases): diseaseID = self.generateDiseaseID() diseaseConfiguration = diseaseEndowments[i] newDisease = disease.Disease(diseaseID, diseaseConfiguration) - diseases.append(newDisease) + timestep = newDisease.startTimestep + self.diseases.append(newDisease) + self.diseasesCount[timestep].append(newDisease) + self.diseaseStats.sort(key=lambda d: d["disease"]) + self.infectAgents() + + def infectAgents(self): + timestep = self.timestep + if timestep > self.configuration["diseaseStartTimeframe"][1]: + return + diseases = self.diseasesCount[timestep] + if len(diseases) == 0: + return startingDiseases = self.configuration["startingDiseasesPerAgent"] minStartingDiseases = startingDiseases[0] maxStartingDiseases = startingDiseases[1] currStartingDiseases = minStartingDiseases + random.shuffle(self.agents) for agent in self.agents: random.shuffle(diseases) for newDisease in diseases: - if len(agent.diseases) >= currStartingDiseases and startingDiseases != [0, 0]: + if newDisease.startingInfectedAgents == 1 and startingDiseases == [0, 0]: + continue + if agent.startingDiseases >= currStartingDiseases and startingDiseases != [0, 0]: currStartingDiseases += 1 break hammingDistance = agent.findNearestHammingDistanceInDisease(newDisease)["distance"] if hammingDistance == 0: continue agent.catchDisease(newDisease) - self.diseases.append(newDisease) + agent.startingDiseases += 1 + newDisease.startingInfectedAgents += 1 if startingDiseases == [0, 0]: diseases.remove(newDisease) break if currStartingDiseases > maxStartingDiseases: currStartingDiseases = minStartingDiseases - self.diseases.sort(key=lambda d: d.ID) - if startingDiseases == [0, 0] and len(diseases) > 0 and ("all" in self.debug or "sugarscape" in self.debug): - print(f"Could not place {len(diseases)} diseases.") + if startingDiseases == [0, 0] and self.timestep == self.configuration["diseaseStartTimeframe"][1] and len(diseases) > 0: + if "all" in self.debug or "sugarscape" in self.debug: + print(f"Could not place {len(diseases)} diseases.") def configureEnvironment(self, maxSugar, maxSpice, sugarPeaks, spicePeaks): height = self.environment.height @@ -243,9 +265,8 @@ def doTimestep(self): if self.timestep >= self.maxTimestep: self.toggleEnd() return - if self.checkActiveDiseases() == True: - for disease in self.diseases: - disease.resetRStats() + for disease in self.diseases: + disease.resetRStats() if "all" in self.debug or "sugarscape" in self.debug: print(f"Timestep: {self.timestep}\nLiving Agents: {len(self.agents)}") self.timestep += 1 @@ -290,9 +311,22 @@ def endLog(self): else: logString += f",{self.runtimeStats[stat]}" logString += "\n" + # temporary, to be deleted + for disease in self.diseaseStats: + diseaselogString = "" + for stat in disease.values(): + if diseaselogString == "": + diseaselogString += f"{stat}" + else: + diseaselogString += f",{stat}" + diseaselogString += "\n" + self.diseaselog.write(diseaselogString) self.log.write(logString) self.log.flush() self.log.close() + # to be deleted + self.diseaselog.flush() + self.diseaselog.close() def findActiveQuadrants(self): quadrants = self.configuration["environmentStartingQuadrants"] @@ -318,15 +352,6 @@ def findActiveQuadrants(self): cellRange.append(quadrantFour) return cellRange - def formatDiseaseJSON(self, data): - formattedData = json.dumps(data, indent=4) - formattedData = formattedData.replace(", \"", ",\t\"") - formattedData = formattedData.replace("{\n ", "{") - formattedData = formattedData.replace(",\n ", ", ") - formattedData = formattedData.replace("\n },", "},") - formattedData = formattedData.replace("\n }\n ]", "}\n ]") - return formattedData - def generateAgentID(self): agentID = self.nextAgentID self.nextAgentID += 1 @@ -396,6 +421,7 @@ def randomizeDiseaseEndowments(self, numDiseases): fertilityPenalty = configs["diseaseFertilityPenalty"] aggressionPenalty = configs["diseaseAggressionPenalty"] tagLengths = configs["diseaseTagStringLength"] + startTimeframe = configs["diseaseStartTimeframe"] minSugarMetabolismPenalty = sugarMetabolismPenalty[0] minSpiceMetabolismPenalty = spiceMetabolismPenalty[0] @@ -404,6 +430,7 @@ def randomizeDiseaseEndowments(self, numDiseases): minFertilityPenalty = fertilityPenalty[0] minAggressionPenalty = aggressionPenalty[0] minTagLength = tagLengths[0] + minStartTimeframe = startTimeframe[0] maxSugarMetabolismPenalty = sugarMetabolismPenalty[1] maxSpiceMetabolismPenalty = spiceMetabolismPenalty[1] @@ -412,6 +439,7 @@ def randomizeDiseaseEndowments(self, numDiseases): maxFertilityPenalty = fertilityPenalty[1] maxAggressionPenalty = aggressionPenalty[1] maxTagLength = tagLengths[1] + maxStartTimeframe = startTimeframe[1] endowments = [] sugarMetabolismPenalties = [] @@ -421,6 +449,7 @@ def randomizeDiseaseEndowments(self, numDiseases): fertilityPenalties = [] aggressionPenalties = [] diseaseTags = [] + startTimesteps = [] currSugarMetabolismPenalty = minSugarMetabolismPenalty currSpiceMetabolismPenalty = minSpiceMetabolismPenalty @@ -429,6 +458,7 @@ def randomizeDiseaseEndowments(self, numDiseases): currFertilityPenalty = minFertilityPenalty currAggressionPenalty = minAggressionPenalty currTagLength = minTagLength + currStartTimestep = minStartTimeframe for i in range(numDiseases): sugarMetabolismPenalties.append(currSugarMetabolismPenalty) @@ -438,6 +468,7 @@ def randomizeDiseaseEndowments(self, numDiseases): fertilityPenalties.append(currFertilityPenalty) aggressionPenalties.append(currAggressionPenalty) diseaseTags.append([random.randrange(2) for i in range(currTagLength)]) + startTimesteps.append(currStartTimestep) currSugarMetabolismPenalty += 1 currSpiceMetabolismPenalty += 1 @@ -446,6 +477,7 @@ def randomizeDiseaseEndowments(self, numDiseases): currFertilityPenalty += 1 currAggressionPenalty += 1 currTagLength += 1 + currStartTimestep += 1 if currSugarMetabolismPenalty > maxSugarMetabolismPenalty: currSugarMetabolismPenalty = minSugarMetabolismPenalty @@ -461,6 +493,8 @@ def randomizeDiseaseEndowments(self, numDiseases): currAggressionPenalty = minAggressionPenalty if currTagLength > maxTagLength: currTagLength = minTagLength + if currStartTimestep > maxStartTimeframe: + currStartTimestep = minStartTimeframe randomDiseaseEndowment = {"sugarMetabolismPenalties": sugarMetabolismPenalties, "spiceMetabolismPenalties": spiceMetabolismPenalties, @@ -468,7 +502,8 @@ def randomizeDiseaseEndowments(self, numDiseases): "visionPenalties": visionPenalties, "fertilityPenalties": fertilityPenalties, "aggressionPenalties": aggressionPenalties, - "diseaseTags": diseaseTags} + "diseaseTags": diseaseTags, + "startTimesteps": startTimesteps} # Map configuration to a random number via hash to make random number generation independent of iteration order if (self.diseaseConfigHashes == None): @@ -491,7 +526,8 @@ def randomizeDiseaseEndowments(self, numDiseases): "sugarMetabolismPenalty": sugarMetabolismPenalties.pop(), "spiceMetabolismPenalty": spiceMetabolismPenalties.pop(), "tags": diseaseTags.pop(), - "visionPenalty": visionPenalties.pop()} + "visionPenalty": visionPenalties.pop(), + "startTimestep": startTimesteps.pop()} endowments.append(diseaseEndowment) return endowments @@ -697,6 +733,7 @@ def runSimulation(self, timesteps=5): if self.configuration["screenshots"] == True and self.configuration["headlessMode"] == False: self.gui.canvas.postscript(file=f"screenshot{screenshots}.ps", colormode="color") screenshots += 1 + self.infectAgents() self.doTimestep() t += 1 if self.gui != None and self.run == False: @@ -718,6 +755,16 @@ def startLog(self): self.log.write(header) else: self.log.write("[\n") + # temporary for disease testing + diseaseHeader = "" + for stat in self.diseaseStats[0]: + if diseaseHeader == "": + diseaseHeader += f"{stat}" + else: + diseaseHeader += f",{stat}" + diseaseHeader += "\n" + self.diseaselog.write(diseaseHeader) + self.diseaseStats.clear() self.updateRuntimeStats() self.writeToLog() @@ -980,16 +1027,29 @@ def updateRuntimeStats(self): self.runtimeStats["agentTotalMetabolism"] = agentTotalMetabolism self.runtimeStats["totalSickAgents"] = totalSickAgents - for disease in self.diseases: - infectors = len(disease.infectors) - incidence = disease.infected - prevalence = self.countInfectedAgents(disease) - r = 0 - if infectors > 0: - r = round(float(incidence / infectors), 2) - self.runtimeStats[f"disease{disease.ID}Incidence"] = incidence - self.runtimeStats[f"disease{disease.ID}Prevalence"] = prevalence - self.runtimeStats[f"disease{disease.ID}RValue"] = r + + + if self.checkActiveDiseases() == True: + for disease in self.diseases: + """ + prevalence = self.countInfectedAgents(disease) + r = 0 + if infectors > 0: + r = round(float(incidence / infectors), 2) + self.runtimeStats[f"disease{disease.ID}Incidence"] = incidence + self.runtimeStats[f"disease{disease.ID}Prevalence"] = prevalence + self.runtimeStats[f"disease{disease.ID}RValue"] = r + """ + # below is temporary, the commented code above is to write to the main log + infectors = len(disease.infectors) + incidence = disease.infected + prevalence = self.countInfectedAgents(disease) + r = 0 + if infectors > 0: + r = round(float(incidence / infectors), 2) + diseaseInfo = {"disease": disease.ID, "timestep": self.timestep, "incidence": incidence, + "prevalence": prevalence, "rValue": r} + self.diseaseStats.append(diseaseInfo) def writeToLog(self): if self.log == None: @@ -1291,6 +1351,7 @@ def verifyConfiguration(configuration): "diseaseFertilityPenalty": [0, 0], "diseaseMovementPenalty": [0, 0], "diseaseSpiceMetabolismPenalty": [0, 0], + "diseaseStartTimeframe": [0, 0], "diseaseSugarMetabolismPenalty": [0, 0], "diseaseTagStringLength": [0, 0], "diseaseVisionPenalty": [0, 0], From 2b210fb1ef0bb08d71571829bae14b4cc7700fb2 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Wed, 7 Aug 2024 21:47:29 -0700 Subject: [PATCH 08/46] Fixes sort line error --- sugarscape.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sugarscape.py b/sugarscape.py index 22f11614..5b3e1264 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -194,7 +194,6 @@ def configureDiseases(self, numDiseases): timestep = newDisease.startTimestep self.diseases.append(newDisease) self.diseasesCount[timestep].append(newDisease) - self.diseaseStats.sort(key=lambda d: d["disease"]) self.infectAgents() @@ -312,6 +311,7 @@ def endLog(self): logString += f",{self.runtimeStats[stat]}" logString += "\n" # temporary, to be deleted + self.diseaseStats.sort(key=lambda d: d["disease"]) for disease in self.diseaseStats: diseaselogString = "" for stat in disease.values(): From 82c478ed8d816c17c83ac14241ce54110a8dc745 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Thu, 8 Aug 2024 16:55:47 -0700 Subject: [PATCH 09/46] Adds disease stats to main log --- README | 5 ++++ config.json | 12 ++++----- sugarscape.py | 68 +++++++++------------------------------------------ 3 files changed, 23 insertions(+), 62 deletions(-) diff --git a/README b/README index b600c011..d2987fd4 100644 --- a/README +++ b/README @@ -311,6 +311,11 @@ diseaseSpiceMetabolismPenalty: [float, float] Note: Negative values constitute a decrease in agent spice metabolism. Default: [0, 0] +diseaseStartTimeframe: [int, int] + Set the timestep a disease can be initialized. + Default: [0, 0] + + diseaseSugarMetabolismPenalty: [float, float] Set the impact a disease will have on an agent's sugar metabolism rate. Note: Negative values constitute a decrease in agent sugar metabolism. diff --git a/config.json b/config.json index a58c5963..40099884 100644 --- a/config.json +++ b/config.json @@ -7,7 +7,7 @@ "numSeeds": 100, "plots": ["deaths", "meanAgeAtDeath", "meanttl", "meanWealth", "population", "wealth"], "plotTimesteps": 1000, - "pythonAlias": "python" + "pythonAlias": "python3" }, "sugarscapeOptions": { "__README__": "Default values for Sugarscape simulation provided here. Details can be found in the README.", @@ -54,7 +54,7 @@ "diseaseMovementPenalty": [0, 0], "diseaseSpiceMetabolismPenalty": [1, 3], "diseaseSugarMetabolismPenalty": [1, 3], - "diseaseStartTimeframe": [1, 1], + "diseaseStartTimeframe": [50, 52], "diseaseTagStringLength": [11, 21], "diseaseVisionPenalty": [-1, 1], "environmentEquator": -1, @@ -87,15 +87,15 @@ "interfaceHeight": 1000, "interfaceWidth": 900, "keepAlivePostExtinction": false, - "logfile": "log.json", - "logfileFormat": "json", + "logfile": "log.csv", + "logfileFormat": "csv", "neighborhoodMode": "vonNeumann", "profileMode": false, "screenshots": false, "seed": -1, "startingAgents": 250, "startingDiseases": 50, - "startingDiseasesPerAgent": [0, 0], - "timesteps": 1000 + "startingDiseasesPerAgent": [0, 5], + "timesteps": 500 } } diff --git a/sugarscape.py b/sugarscape.py index 5b3e1264..40169288 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -64,19 +64,12 @@ def __init__(self, configuration): "totalMetabolismCost": 0, "agentsReplaced": 0, "agentsBorn": 0, "agentStarvationDeaths": 0, "agentDiseaseDeaths": 0, "environmentWealthCreated": 0, "agentWealthTotal": 0, "environmentWealthTotal": 0, "agentWealthCollected": 0, "agentWealthBurnRate": 0, "agentMeanTimeToLive": 0, "agentTotalMetabolism": 0, "agentCombatDeaths": 0, "agentAgingDeaths": 0, "totalSickAgents": 0} - """ self.diseaseStats = {} for disease in self.diseases: self.diseaseStats[f"disease{disease.ID}Incidence"] = 0 self.diseaseStats[f"disease{disease.ID}Prevalence"] = 0 self.diseaseStats[f"disease{disease.ID}RValue"] = 0 self.runtimeStats.update(self.diseaseStats) - """ - # temporary, to be deleted - self.diseaselog = open("diseaselog.csv", 'w') - self.diseaseStats = [] - self.diseaseStats.append({"disease": None, "timestep": None, "incidence": None, "prevalence": None, "RValue": None}) - self.graphStats = {"ageBins": [], "sugarBins": [], "spiceBins": [], "lorenzCurvePoints": [], "meanTribeTags": [], "maxSugar": 0, "maxSpice": 0, "maxWealth": 0} self.log = open(configuration["logfile"], 'a') if configuration["logfile"] != None else None @@ -194,9 +187,9 @@ def configureDiseases(self, numDiseases): timestep = newDisease.startTimestep self.diseases.append(newDisease) self.diseasesCount[timestep].append(newDisease) + self.diseases.sort(key=lambda d: d.ID) self.infectAgents() - def infectAgents(self): timestep = self.timestep if timestep > self.configuration["diseaseStartTimeframe"][1]: @@ -269,6 +262,7 @@ def doTimestep(self): if "all" in self.debug or "sugarscape" in self.debug: print(f"Timestep: {self.timestep}\nLiving Agents: {len(self.agents)}") self.timestep += 1 + self.infectAgents() if self.end == True or (len(self.agents) == 0 and self.keepAlive == False): self.toggleEnd() else: @@ -310,23 +304,9 @@ def endLog(self): else: logString += f",{self.runtimeStats[stat]}" logString += "\n" - # temporary, to be deleted - self.diseaseStats.sort(key=lambda d: d["disease"]) - for disease in self.diseaseStats: - diseaselogString = "" - for stat in disease.values(): - if diseaselogString == "": - diseaselogString += f"{stat}" - else: - diseaselogString += f",{stat}" - diseaselogString += "\n" - self.diseaselog.write(diseaselogString) self.log.write(logString) self.log.flush() self.log.close() - # to be deleted - self.diseaselog.flush() - self.diseaselog.close() def findActiveQuadrants(self): quadrants = self.configuration["environmentStartingQuadrants"] @@ -733,7 +713,6 @@ def runSimulation(self, timesteps=5): if self.configuration["screenshots"] == True and self.configuration["headlessMode"] == False: self.gui.canvas.postscript(file=f"screenshot{screenshots}.ps", colormode="color") screenshots += 1 - self.infectAgents() self.doTimestep() t += 1 if self.gui != None and self.run == False: @@ -755,16 +734,6 @@ def startLog(self): self.log.write(header) else: self.log.write("[\n") - # temporary for disease testing - diseaseHeader = "" - for stat in self.diseaseStats[0]: - if diseaseHeader == "": - diseaseHeader += f"{stat}" - else: - diseaseHeader += f",{stat}" - diseaseHeader += "\n" - self.diseaselog.write(diseaseHeader) - self.diseaseStats.clear() self.updateRuntimeStats() self.writeToLog() @@ -1027,29 +996,16 @@ def updateRuntimeStats(self): self.runtimeStats["agentTotalMetabolism"] = agentTotalMetabolism self.runtimeStats["totalSickAgents"] = totalSickAgents - - - if self.checkActiveDiseases() == True: - for disease in self.diseases: - """ - prevalence = self.countInfectedAgents(disease) - r = 0 - if infectors > 0: - r = round(float(incidence / infectors), 2) - self.runtimeStats[f"disease{disease.ID}Incidence"] = incidence - self.runtimeStats[f"disease{disease.ID}Prevalence"] = prevalence - self.runtimeStats[f"disease{disease.ID}RValue"] = r - """ - # below is temporary, the commented code above is to write to the main log - infectors = len(disease.infectors) - incidence = disease.infected - prevalence = self.countInfectedAgents(disease) - r = 0 - if infectors > 0: - r = round(float(incidence / infectors), 2) - diseaseInfo = {"disease": disease.ID, "timestep": self.timestep, "incidence": incidence, - "prevalence": prevalence, "rValue": r} - self.diseaseStats.append(diseaseInfo) + for disease in self.diseases: + infectors = len(disease.infectors) + incidence = disease.infected + prevalence = self.countInfectedAgents(disease) + r = 0 + if infectors > 0: + r = round(float(incidence / infectors), 2) + self.runtimeStats[f"disease{disease.ID}Incidence"] = incidence + self.runtimeStats[f"disease{disease.ID}Prevalence"] = prevalence + self.runtimeStats[f"disease{disease.ID}RValue"] = r def writeToLog(self): if self.log == None: From b6f5c8c4f312f19328e112667fc81bf87b3834bb Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Fri, 9 Aug 2024 11:43:13 -0700 Subject: [PATCH 10/46] Adds immune agents disease stat to log --- agent.py | 10 ++++++++-- disease.py | 1 - sugarscape.py | 21 ++++++++++++++++----- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/agent.py b/agent.py index 4651f3c3..0bff7759 100644 --- a/agent.py +++ b/agent.py @@ -182,9 +182,8 @@ def catchDisease(self, disease, infector=None): hammingDistance = diseaseInImmuneSystem["distance"] # If immune to disease, do not contract it if hammingDistance == 0: - if self not in disease.immuneAgents: - disease.immuneAgents.append(self) return + startIndex = diseaseInImmuneSystem["start"] endIndex = diseaseInImmuneSystem["end"] caughtDisease = {"disease": disease, "startIndex": startIndex, "endIndex": endIndex} @@ -197,6 +196,13 @@ def catchDisease(self, disease, infector=None): self.updateDiseaseEffects(disease) self.findCellsInRange() + def checkDiseaseImmunity(self, disease): + hammingDistance = self.findNearestHammingDistanceInDisease(disease)["distance"] + if hammingDistance == 0: + return True + else: + return False + def collectResourcesAtCell(self): sugarCollected = self.cell.sugar spiceCollected = self.cell.spice diff --git a/disease.py b/disease.py index 81b30b79..8b544d40 100644 --- a/disease.py +++ b/disease.py @@ -16,7 +16,6 @@ def __init__(self, diseaseID, configuration): self.startingInfectedAgents = 0 self.infectors = [] self.infected = 0 - self.immuneAgents = [] def resetRStats(self): self.infectors = [] diff --git a/sugarscape.py b/sugarscape.py index 40169288..976ba6da 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -66,6 +66,7 @@ def __init__(self, configuration): "agentTotalMetabolism": 0, "agentCombatDeaths": 0, "agentAgingDeaths": 0, "totalSickAgents": 0} self.diseaseStats = {} for disease in self.diseases: + self.diseaseStats[f"disease{disease.ID}ImmunePercentage"] = 0.0 self.diseaseStats[f"disease{disease.ID}Incidence"] = 0 self.diseaseStats[f"disease{disease.ID}Prevalence"] = 0 self.diseaseStats[f"disease{disease.ID}RValue"] = 0 @@ -210,8 +211,8 @@ def infectAgents(self): if agent.startingDiseases >= currStartingDiseases and startingDiseases != [0, 0]: currStartingDiseases += 1 break - hammingDistance = agent.findNearestHammingDistanceInDisease(newDisease)["distance"] - if hammingDistance == 0: + agentImmunity = agent.checkDiseaseImmunity(newDisease) + if agentImmunity == True: continue agent.catchDisease(newDisease) agent.startingDiseases += 1 @@ -997,14 +998,24 @@ def updateRuntimeStats(self): self.runtimeStats["totalSickAgents"] = totalSickAgents for disease in self.diseases: + immuneAgents = 0 + if numAgents > 0: + for agent in self.agents: + agentImmunity = agent.checkDiseaseImmunity(disease) + if agentImmunity == True: + immuneAgents += 1 + immunePercentage = 0.0 + if numAgents > 0: + immunePercentage = round(float(immuneAgents / numAgents), 2) infectors = len(disease.infectors) incidence = disease.infected prevalence = self.countInfectedAgents(disease) - r = 0 + r = 0.0 if infectors > 0: r = round(float(incidence / infectors), 2) - self.runtimeStats[f"disease{disease.ID}Incidence"] = incidence - self.runtimeStats[f"disease{disease.ID}Prevalence"] = prevalence + self.runtimeStats[f"disease{disease.ID}ImmunePercentage"] = immunePercentage + self.runtimeStats[f"disease{disease.ID}Incidence"] = incidence + self.runtimeStats[f"disease{disease.ID}Prevalence"] = prevalence self.runtimeStats[f"disease{disease.ID}RValue"] = r def writeToLog(self): From 0770f59f5045030cf7515d4f67bf6c090a5ea522 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Fri, 9 Aug 2024 20:24:09 -0700 Subject: [PATCH 11/46] Fixes config.json to be the standard config --- config.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config.json b/config.json index 40099884..32ae4584 100644 --- a/config.json +++ b/config.json @@ -7,7 +7,7 @@ "numSeeds": 100, "plots": ["deaths", "meanAgeAtDeath", "meanttl", "meanWealth", "population", "wealth"], "plotTimesteps": 1000, - "pythonAlias": "python3" + "pythonAlias": "python" }, "sugarscapeOptions": { "__README__": "Default values for Sugarscape simulation provided here. Details can be found in the README.", @@ -54,7 +54,7 @@ "diseaseMovementPenalty": [0, 0], "diseaseSpiceMetabolismPenalty": [1, 3], "diseaseSugarMetabolismPenalty": [1, 3], - "diseaseStartTimeframe": [50, 52], + "diseaseStartTimeframe": [0, 0], "diseaseTagStringLength": [11, 21], "diseaseVisionPenalty": [-1, 1], "environmentEquator": -1, @@ -95,7 +95,7 @@ "seed": -1, "startingAgents": 250, "startingDiseases": 50, - "startingDiseasesPerAgent": [0, 5], - "timesteps": 500 + "startingDiseasesPerAgent": [0, 0], + "timesteps": 1000 } } From 61af5e28efd8d3c93fb321cd17cca5de6358a33f Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Sun, 11 Aug 2024 18:16:55 -0700 Subject: [PATCH 12/46] Adds agentDiseaseProtectionChance to reduce disease infections --- README | 5 +++++ agent.py | 12 ++++++++++-- config.json | 1 + sugarscape.py | 5 ++++- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/README b/README index d2987fd4..b45e2c25 100644 --- a/README +++ b/README @@ -159,6 +159,11 @@ agentDepressionPercentage: float Note: The starting agent population will have this same percentage of depressed agents. Default: 0.0 +agentDiseaseProtectionChance: [int, int] + Sets the initial chance an agent can get infected by another agent. + Note: a value of 0 mean no protection, while a value of 10 means complete protection + Default: [0, 0] + agentFemaleInfertilityAge: [int, int] Set the timestep age at which female agents become infertile. Default: [0, 0] diff --git a/agent.py b/agent.py index 0bff7759..eb234625 100644 --- a/agent.py +++ b/agent.py @@ -18,6 +18,7 @@ def __init__(self, agentID, birthday, cell, configuration): self.decisionModelLookaheadFactor = configuration["decisionModelLookaheadFactor"] self.decisionModelTribalFactor = configuration["decisionModelTribalFactor"] self.depressionFactor = configuration["depressionFactor"] + self.diseaseProtectionChance = configuration["diseaseProtectionChance"] self.fertilityAge = configuration["fertilityAge"] self.fertilityFactor = configuration["fertilityFactor"] self.immuneSystem = configuration["immuneSystem"] @@ -172,18 +173,23 @@ def canTradeWithNeighbor(self, neighbor): return False def catchDisease(self, disease, infector=None): + if self.diseaseProtectionChance == 10: + return diseaseID = disease.ID for currDisease in self.diseases: currDiseaseID = currDisease["disease"].ID # If currently sick with this disease, do not contract it again if diseaseID == currDiseaseID: return + # Random number determines if agent gets sick or not + randomInfectionRate = random.randint(1,10) + if randomInfectionRate <= self.diseaseProtectionChance and self.diseaseProtectionChance > 0: + return diseaseInImmuneSystem = self.findNearestHammingDistanceInDisease(disease) hammingDistance = diseaseInImmuneSystem["distance"] # If immune to disease, do not contract it if hammingDistance == 0: return - startIndex = diseaseInImmuneSystem["start"] endIndex = diseaseInImmuneSystem["end"] caughtDisease = {"disease": disease, "startIndex": startIndex, "endIndex": endIndex} @@ -192,6 +198,9 @@ def catchDisease(self, disease, infector=None): if infector not in disease.infectors: disease.infectors.append(infector.ID) disease.infected += 1 + self.diseaseProtectionChance += 1 + if self.diseaseProtectionChance > 10: + self.diseaseProtectionChance = 10 self.diseases.append(caughtDisease) self.updateDiseaseEffects(disease) self.findCellsInRange() @@ -717,7 +726,6 @@ def findChildEndowment(self, mate): parentEndowments = { "aggressionFactor": [self.aggressionFactor, mate.aggressionFactor], "baseInterestRate": [self.baseInterestRate, mate.baseInterestRate], - "depressionFactor": [self.depressionFactor, mate.depressionFactor], "fertilityAge": [self.fertilityAge, mate.fertilityAge], "fertilityFactor": [self.fertilityFactor, mate.fertilityFactor], "infertilityAge": [self.infertilityAge, mate.infertilityAge], diff --git a/config.json b/config.json index 32ae4584..ccafb26f 100644 --- a/config.json +++ b/config.json @@ -19,6 +19,7 @@ "agentDecisionModelLookaheadFactor": 0, "agentDecisionModelTribalFactor": [-1, -1], "agentDepressionPercentage": 0, + "agentDiseaseProtectionChance": [0, 10], "agentFemaleInfertilityAge": [40, 50], "agentFemaleFertilityAge": [12, 15], "agentFertilityFactor": [1, 1], diff --git a/sugarscape.py b/sugarscape.py index 976ba6da..ce545a74 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -548,6 +548,7 @@ def randomizeAgentEndowments(self, numAgents): movementMode = configs["agentMovementMode"] neighborhoodMode = configs["neighborhoodMode"] visionMode = configs["agentVisionMode"] + diseaseProtectionChance = configs["agentDiseaseProtectionChance"] numDepressedAgents = int(math.ceil(numAgents * configs["agentDepressionPercentage"])) depressionFactors = [1 for i in range(numDepressedAgents)] + [0 for i in range(numAgents - numDepressedAgents)] @@ -577,7 +578,8 @@ def randomizeAgentEndowments(self, numAgents): "tradeFactor": {"endowments": [], "curr": tradeFactor[0], "min": tradeFactor[0], "max": tradeFactor[1]}, "vision": {"endowments": [], "curr": vision[0], "min": vision[0], "max": vision[1]}, "universalSpice": {"endowments": [], "curr": universalSpice[0], "min": universalSpice[0], "max": universalSugar[1]}, - "universalSugar": {"endowments": [], "curr": universalSugar[0], "min": universalSugar[0], "max": universalSugar[1]} + "universalSugar": {"endowments": [], "curr": universalSugar[0], "min": universalSugar[0], "max": universalSugar[1]}, + "diseaseProtectionChance": {"endowments": [], "curr": diseaseProtectionChance[0], "min": diseaseProtectionChance[0], "max": diseaseProtectionChance[1]} } if self.agentConfigHashes == None: @@ -1284,6 +1286,7 @@ def verifyConfiguration(configuration): "agentDecisionModelLookaheadFactor": [0], "agentDecisionModelTribalFactor": [-1, -1], "agentDepressionPercentage": 0, + "agentDiseaseProtectionChance": [0, 0], "agentFemaleInfertilityAge": [0, 0], "agentFemaleFertilityAge": [0, 0], "agentFertilityFactor": [0, 0], From d1ba81940ed4bc18e41e6deb2e638a02f5b289c0 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Mon, 12 Aug 2024 18:41:05 -0700 Subject: [PATCH 13/46] Fixes initial disease so it can still infect an agent with complete immunity --- agent.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/agent.py b/agent.py index eb234625..72984548 100644 --- a/agent.py +++ b/agent.py @@ -173,7 +173,7 @@ def canTradeWithNeighbor(self, neighbor): return False def catchDisease(self, disease, infector=None): - if self.diseaseProtectionChance == 10: + if self.diseaseProtectionChance == 10 and infector != None: return diseaseID = disease.ID for currDisease in self.diseases: @@ -182,9 +182,10 @@ def catchDisease(self, disease, infector=None): if diseaseID == currDiseaseID: return # Random number determines if agent gets sick or not - randomInfectionRate = random.randint(1,10) - if randomInfectionRate <= self.diseaseProtectionChance and self.diseaseProtectionChance > 0: - return + if infector != None: + randomInfectionRate = random.randint(1,10) + if randomInfectionRate <= self.diseaseProtectionChance and self.diseaseProtectionChance > 0: + return diseaseInImmuneSystem = self.findNearestHammingDistanceInDisease(disease) hammingDistance = diseaseInImmuneSystem["distance"] # If immune to disease, do not contract it From 3afe38c92e8f2f10e49f3b62b0c0b277b482243d Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Tue, 13 Aug 2024 09:42:50 -0700 Subject: [PATCH 14/46] Removes function to check for active diseases --- sugarscape.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sugarscape.py b/sugarscape.py index ce545a74..7f4ed8fc 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -108,12 +108,6 @@ def addSugarPeak(self, startX, startY, radius, maxSugar): self.environment.findCell(i, j).maxSugar = cellMaxCapacity self.environment.findCell(i, j).sugar = cellMaxCapacity - def checkActiveDiseases(self): - for agent in self.agents: - if len(agent.diseases) > 0: - return True - return False - def configureAgents(self, numAgents): if self.environment == None: return From 9725dd981053af965b0e9cdf1d7ac3caa38504ad Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Wed, 14 Aug 2024 14:16:22 -0700 Subject: [PATCH 15/46] Adds agent behavior to avoid infected agents --- agent.py | 10 ++++++++++ sugarscape.py | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/agent.py b/agent.py index 72984548..c6a65254 100644 --- a/agent.py +++ b/agent.py @@ -607,6 +607,14 @@ def doTrading(self): def findAggression(self): return max(0, self.aggressionFactor + self.aggressionFactorModifier) + def isPreyInfected(self, prey): + if prey == None: + return True + if len(prey.diseases) > 0 and len(self.diseases) == 0: + return False + if len(self.diseases) > 0 and len(prey.diseases) == 0: + return False + def findBestCell(self): self.findNeighborhood() if len(self.cellsInRange) == 0: @@ -628,6 +636,8 @@ def findBestCell(self): prey = cell.agent if cell.isOccupied() and self.isNeighborValidPrey(prey) == False: continue + if self.isPreyInfected(prey) == False: + continue preyTribe = prey.tribe if prey != None else "empty" preySugar = prey.sugar if prey != None else 0 preySpice = prey.spice if prey != None else 0 diff --git a/sugarscape.py b/sugarscape.py index 7f4ed8fc..02d6f49e 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -1149,6 +1149,11 @@ def verifyConfiguration(configuration): print(f"Cannot have agent maximum tribal factor of {configuration['agentDecisionModelTribalFactor'][1]}. Setting agent maximum tribal factor to 1.0.") configuration["agentDecisionModelTribalFactor"][1] = 1 + if configuration["agentDiseaseProtectionChance"][1] > 10: + if "all" in configuration["debugMode"] or "agent" in configuration["debugMode"]: + print(f"Cannot have agent maximum disease protection chance of {configuration['agentDiseaseProtectionChance'][1]}. Setting agent maximum disease protection chance to 10.") + configuration["agentDiseaseProtectionChance"][1] = 10 + if configuration["agentTagStringLength"] < 0: if "all" in configuration["debugMode"] or "agent" in configuration["debugMode"]: print(f"Cannot have a negative agent tag string length. Setting agent tag string length to 0.") From 1fa3e8f197f9ff60f5f14bdad4bc8a431025f35c Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Wed, 14 Aug 2024 15:00:09 -0700 Subject: [PATCH 16/46] Modifies disease initialization so it doesn't infect completely immune agents --- agent.py | 2 +- sugarscape.py | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/agent.py b/agent.py index c6a65254..1abe49e9 100644 --- a/agent.py +++ b/agent.py @@ -173,7 +173,7 @@ def canTradeWithNeighbor(self, neighbor): return False def catchDisease(self, disease, infector=None): - if self.diseaseProtectionChance == 10 and infector != None: + if self.diseaseProtectionChance == 10: return diseaseID = disease.ID for currDisease in self.diseases: diff --git a/sugarscape.py b/sugarscape.py index 02d6f49e..4a9015e3 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -209,11 +209,12 @@ def infectAgents(self): if agentImmunity == True: continue agent.catchDisease(newDisease) - agent.startingDiseases += 1 - newDisease.startingInfectedAgents += 1 - if startingDiseases == [0, 0]: - diseases.remove(newDisease) - break + if len(agent.diseases) > 0: + agent.startingDiseases += 1 + newDisease.startingInfectedAgents += 1 + if startingDiseases == [0, 0]: + diseases.remove(newDisease) + break if currStartingDiseases > maxStartingDiseases: currStartingDiseases = minStartingDiseases if startingDiseases == [0, 0] and self.timestep == self.configuration["diseaseStartTimeframe"][1] and len(diseases) > 0: From 4c35a91207abe275fb0e3b516a95aae10d1461fb Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Thu, 15 Aug 2024 09:46:43 -0700 Subject: [PATCH 17/46] Modifies disease death stat to count any diseased agents upon death --- agent.py | 3 +++ sugarscape.py | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/agent.py b/agent.py index 1abe49e9..9ca5fbfe 100644 --- a/agent.py +++ b/agent.py @@ -60,6 +60,7 @@ def __init__(self, agentID, birthday, cell, configuration): self.conflictHappiness = 0 self.depressed = False self.diseases = [] + self.diseaseDeath = False self.familyHappiness = 0 self.fertile = False self.fertilityFactorModifier = 0 @@ -257,6 +258,8 @@ def doCombat(self, cell): def doDeath(self, causeOfDeath): self.alive = False self.causeOfDeath = causeOfDeath + if len(self.diseases) > 0: + self.diseaseDeath = True self.resetCell() self.doInheritance() diff --git a/sugarscape.py b/sugarscape.py index 4a9015e3..ecde7879 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -946,9 +946,11 @@ def updateRuntimeStats(self): agentWealthCollected += agentWealth - (agent.lastSugar + agent.lastSpice) totalWealthLost += agentWealth agentStarvationDeaths += 1 if agent.causeOfDeath == "starvation" else 0 - agentDiseaseDeaths += 1 if agent.causeOfDeath == "disease" else 0 + agentDiseaseDeaths += 1 if agent.diseaseDeath == True else 0 agentCombatDeaths += 1 if agent.causeOfDeath == "combat" else 0 agentAgingDeaths += 1 if agent.causeOfDeath == "aging" else 0 + #if agent.diseaseDeath == True: + # agentDiseaseDeaths += 1 meanAgeAtDeath = round(meanAgeAtDeath / numDeadAgents, 2) if numDeadAgents > 0 else 0 self.deadAgents = [] From 4056917dc1351317f8269f7122f9787a362eba19 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Fri, 16 Aug 2024 16:46:30 -0700 Subject: [PATCH 18/46] Adds disease transmission chance feature --- agent.py | 18 ++++++++++++------ disease.py | 1 + sugarscape.py | 16 ++++++++++++++-- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/agent.py b/agent.py index 9ca5fbfe..4e998eef 100644 --- a/agent.py +++ b/agent.py @@ -59,6 +59,7 @@ def __init__(self, agentID, birthday, cell, configuration): self.childEndowmentHashes = None self.conflictHappiness = 0 self.depressed = False + self.diseaseTransmissionChance = 0 self.diseases = [] self.diseaseDeath = False self.familyHappiness = 0 @@ -182,16 +183,16 @@ def catchDisease(self, disease, infector=None): # If currently sick with this disease, do not contract it again if diseaseID == currDiseaseID: return - # Random number determines if agent gets sick or not - if infector != None: - randomInfectionRate = random.randint(1,10) - if randomInfectionRate <= self.diseaseProtectionChance and self.diseaseProtectionChance > 0: - return diseaseInImmuneSystem = self.findNearestHammingDistanceInDisease(disease) hammingDistance = diseaseInImmuneSystem["distance"] # If immune to disease, do not contract it if hammingDistance == 0: return + # Random number determines if agent gets sick or not + if infector != None: + randomInfectionRate = random.randint(1,10) + if randomInfectionRate <= self.diseaseProtectionChance and self.diseaseProtectionChance > 0: + return startIndex = diseaseInImmuneSystem["start"] endIndex = diseaseInImmuneSystem["end"] caughtDisease = {"disease": disease, "startIndex": startIndex, "endIndex": endIndex} @@ -201,6 +202,9 @@ def catchDisease(self, disease, infector=None): disease.infectors.append(infector.ID) disease.infected += 1 self.diseaseProtectionChance += 1 + self.diseaseTransmissionChance += disease.transmissionChance + if self.diseaseTransmissionChance > 10: + self.diseaseTransmissionChance = 10 if self.diseaseProtectionChance > 10: self.diseaseProtectionChance = 10 self.diseases.append(caughtDisease) @@ -295,7 +299,9 @@ def doDisease(self): neighbors.append(neighbor) random.shuffle(neighbors) for neighbor in neighbors: - neighbor.catchDisease(self.diseases[random.randrange(diseaseCount)]["disease"], self) + randomTransmissionRate = random.randint(1, 10) + if randomTransmissionRate <= self.diseaseTransmissionChance: + neighbor.catchDisease(self.diseases[random.randrange(diseaseCount)]["disease"], self) def doInheritance(self): if self.inheritancePolicy == "none": diff --git a/disease.py b/disease.py index 8b544d40..4ba8e78b 100644 --- a/disease.py +++ b/disease.py @@ -11,6 +11,7 @@ def __init__(self, diseaseID, configuration): self.fertilityPenalty = configuration["fertilityPenalty"] self.aggressionPenalty = configuration["aggressionPenalty"] self.startTimestep = configuration["startTimestep"] + self.transmissionChance = configuration["transmissionChance"] self.tags = configuration["tags"] self.configuration = configuration self.startingInfectedAgents = 0 diff --git a/sugarscape.py b/sugarscape.py index ecde7879..0f48fc73 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -398,6 +398,7 @@ def randomizeDiseaseEndowments(self, numDiseases): aggressionPenalty = configs["diseaseAggressionPenalty"] tagLengths = configs["diseaseTagStringLength"] startTimeframe = configs["diseaseStartTimeframe"] + transmissionChance = configs["diseaseTransmissionChance"] minSugarMetabolismPenalty = sugarMetabolismPenalty[0] minSpiceMetabolismPenalty = spiceMetabolismPenalty[0] @@ -407,6 +408,7 @@ def randomizeDiseaseEndowments(self, numDiseases): minAggressionPenalty = aggressionPenalty[0] minTagLength = tagLengths[0] minStartTimeframe = startTimeframe[0] + minTransmissionChance = transmissionChance[0] maxSugarMetabolismPenalty = sugarMetabolismPenalty[1] maxSpiceMetabolismPenalty = spiceMetabolismPenalty[1] @@ -416,6 +418,7 @@ def randomizeDiseaseEndowments(self, numDiseases): maxAggressionPenalty = aggressionPenalty[1] maxTagLength = tagLengths[1] maxStartTimeframe = startTimeframe[1] + maxTransmissionChance = transmissionChance[1] endowments = [] sugarMetabolismPenalties = [] @@ -426,6 +429,7 @@ def randomizeDiseaseEndowments(self, numDiseases): aggressionPenalties = [] diseaseTags = [] startTimesteps = [] + transmissionChances = [] currSugarMetabolismPenalty = minSugarMetabolismPenalty currSpiceMetabolismPenalty = minSpiceMetabolismPenalty @@ -435,6 +439,7 @@ def randomizeDiseaseEndowments(self, numDiseases): currAggressionPenalty = minAggressionPenalty currTagLength = minTagLength currStartTimestep = minStartTimeframe + currTransmissionChance = minTransmissionChance for i in range(numDiseases): sugarMetabolismPenalties.append(currSugarMetabolismPenalty) @@ -445,6 +450,7 @@ def randomizeDiseaseEndowments(self, numDiseases): aggressionPenalties.append(currAggressionPenalty) diseaseTags.append([random.randrange(2) for i in range(currTagLength)]) startTimesteps.append(currStartTimestep) + transmissionChances.append(currTransmissionChance) currSugarMetabolismPenalty += 1 currSpiceMetabolismPenalty += 1 @@ -454,6 +460,7 @@ def randomizeDiseaseEndowments(self, numDiseases): currAggressionPenalty += 1 currTagLength += 1 currStartTimestep += 1 + currTransmissionChance += 1 if currSugarMetabolismPenalty > maxSugarMetabolismPenalty: currSugarMetabolismPenalty = minSugarMetabolismPenalty @@ -471,6 +478,8 @@ def randomizeDiseaseEndowments(self, numDiseases): currTagLength = minTagLength if currStartTimestep > maxStartTimeframe: currStartTimestep = minStartTimeframe + if currTransmissionChance > maxTransmissionChance: + currTransmissionChance = minTransmissionChance randomDiseaseEndowment = {"sugarMetabolismPenalties": sugarMetabolismPenalties, "spiceMetabolismPenalties": spiceMetabolismPenalties, @@ -479,7 +488,8 @@ def randomizeDiseaseEndowments(self, numDiseases): "fertilityPenalties": fertilityPenalties, "aggressionPenalties": aggressionPenalties, "diseaseTags": diseaseTags, - "startTimesteps": startTimesteps} + "startTimesteps": startTimesteps, + "transmissionChances": transmissionChances} # Map configuration to a random number via hash to make random number generation independent of iteration order if (self.diseaseConfigHashes == None): @@ -503,7 +513,8 @@ def randomizeDiseaseEndowments(self, numDiseases): "spiceMetabolismPenalty": spiceMetabolismPenalties.pop(), "tags": diseaseTags.pop(), "visionPenalty": visionPenalties.pop(), - "startTimestep": startTimesteps.pop()} + "startTimestep": startTimesteps.pop(), + "transmissionChance": transmissionChances.pop()} endowments.append(diseaseEndowment) return endowments @@ -1326,6 +1337,7 @@ def verifyConfiguration(configuration): "diseaseStartTimeframe": [0, 0], "diseaseSugarMetabolismPenalty": [0, 0], "diseaseTagStringLength": [0, 0], + "diseaseTransmissionChance": [0, 0], "diseaseVisionPenalty": [0, 0], "environmentEquator": -1, "environmentHeight": 50, From 7e0faeed715f680ff46e821f34d690fdb63637c5 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Fri, 16 Aug 2024 21:17:12 -0700 Subject: [PATCH 19/46] Adds disease incubation period --- agent.py | 11 ++-- config.json | 2 + disease.py | 22 ++++---- sugarscape.py | 138 +++++++++++++++++++++++++++----------------------- 4 files changed, 95 insertions(+), 78 deletions(-) diff --git a/agent.py b/agent.py index 4e998eef..d57b1cf1 100644 --- a/agent.py +++ b/agent.py @@ -188,10 +188,13 @@ def catchDisease(self, disease, infector=None): # If immune to disease, do not contract it if hammingDistance == 0: return + randomTransmissionRate = random.randint(1, 10) + if randomTransmissionRate > disease.transmissionChance: + return # Random number determines if agent gets sick or not if infector != None: - randomInfectionRate = random.randint(1,10) - if randomInfectionRate <= self.diseaseProtectionChance and self.diseaseProtectionChance > 0: + randomProtectionRate = random.randint(1,10) + if randomProtectionRate <= self.diseaseProtectionChance and self.diseaseProtectionChance > 0: return startIndex = diseaseInImmuneSystem["start"] endIndex = diseaseInImmuneSystem["end"] @@ -299,9 +302,7 @@ def doDisease(self): neighbors.append(neighbor) random.shuffle(neighbors) for neighbor in neighbors: - randomTransmissionRate = random.randint(1, 10) - if randomTransmissionRate <= self.diseaseTransmissionChance: - neighbor.catchDisease(self.diseases[random.randrange(diseaseCount)]["disease"], self) + neighbor.catchDisease(self.diseases[random.randrange(diseaseCount)]["disease"], self) def doInheritance(self): if self.inheritancePolicy == "none": diff --git a/config.json b/config.json index ccafb26f..40b1511b 100644 --- a/config.json +++ b/config.json @@ -52,11 +52,13 @@ "debugMode": ["none"], "diseaseAggressionPenalty": [-1, 1], "diseaseFertilityPenalty": [-1, 1], + "diseaseIncubationPeriod": [0,4], "diseaseMovementPenalty": [0, 0], "diseaseSpiceMetabolismPenalty": [1, 3], "diseaseSugarMetabolismPenalty": [1, 3], "diseaseStartTimeframe": [0, 0], "diseaseTagStringLength": [11, 21], + "diseaseTransmissionChance": [0, 10], "diseaseVisionPenalty": [-1, 1], "environmentEquator": -1, "environmentHeight": 50, diff --git a/disease.py b/disease.py index 4ba8e78b..d40377da 100644 --- a/disease.py +++ b/disease.py @@ -4,23 +4,25 @@ class Disease: def __init__(self, diseaseID, configuration): self.ID = diseaseID - self.sugarMetabolismPenalty = configuration["sugarMetabolismPenalty"] - self.spiceMetabolismPenalty = configuration["spiceMetabolismPenalty"] - self.visionPenalty = configuration["visionPenalty"] - self.movementPenalty = configuration["movementPenalty"] - self.fertilityPenalty = configuration["fertilityPenalty"] + self.configuration = configuration self.aggressionPenalty = configuration["aggressionPenalty"] + self.fertilityPenalty = configuration["fertilityPenalty"] + self.incubationPeriod = configuration["incubationPeriod"] + self.movementPenalty = configuration["movementPenalty"] + self.spiceMetabolismPenalty = configuration["spiceMetabolismPenalty"] self.startTimestep = configuration["startTimestep"] - self.transmissionChance = configuration["transmissionChance"] + self.sugarMetabolismPenalty = configuration["sugarMetabolismPenalty"] self.tags = configuration["tags"] - self.configuration = configuration - self.startingInfectedAgents = 0 - self.infectors = [] + self.transmissionChance = configuration["transmissionChance"] + self.visionPenalty = configuration["visionPenalty"] + self.infected = 0 + self.infectors = [] + self.startingInfectedAgents = 0 def resetRStats(self): - self.infectors = [] self.infected = 0 + self.infectors = [] def __str__(self): return f"{self.ID}" diff --git a/sugarscape.py b/sugarscape.py index 0f48fc73..92cacae7 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -390,106 +390,116 @@ def pauseSimulation(self): def randomizeDiseaseEndowments(self, numDiseases): configs = self.configuration - sugarMetabolismPenalty = configs["diseaseSugarMetabolismPenalty"] - spiceMetabolismPenalty = configs["diseaseSpiceMetabolismPenalty"] - movementPenalty = configs["diseaseMovementPenalty"] - visionPenalty = configs["diseaseVisionPenalty"] - fertilityPenalty = configs["diseaseFertilityPenalty"] aggressionPenalty = configs["diseaseAggressionPenalty"] - tagLengths = configs["diseaseTagStringLength"] + fertilityPenalty = configs["diseaseFertilityPenalty"] + incubationPeriod = configs["diseaseIncubationPeriod"] + movementPenalty = configs["diseaseMovementPenalty"] + spiceMetabolismPenalty = configs["diseaseSpiceMetabolismPenalty"] startTimeframe = configs["diseaseStartTimeframe"] + sugarMetabolismPenalty = configs["diseaseSugarMetabolismPenalty"] + tagLengths = configs["diseaseTagStringLength"] transmissionChance = configs["diseaseTransmissionChance"] + visionPenalty = configs["diseaseVisionPenalty"] - minSugarMetabolismPenalty = sugarMetabolismPenalty[0] - minSpiceMetabolismPenalty = spiceMetabolismPenalty[0] - minMovementPenalty = movementPenalty[0] - minVisionPenalty = visionPenalty[0] - minFertilityPenalty = fertilityPenalty[0] minAggressionPenalty = aggressionPenalty[0] - minTagLength = tagLengths[0] + minFertilityPenalty = fertilityPenalty[0] + minIncubationPeriod = incubationPeriod[0] + minMovementPenalty = movementPenalty[0] + minSpiceMetabolismPenalty = spiceMetabolismPenalty[0] minStartTimeframe = startTimeframe[0] + minSugarMetabolismPenalty = sugarMetabolismPenalty[0] + minTagLength = tagLengths[0] minTransmissionChance = transmissionChance[0] + minVisionPenalty = visionPenalty[0] - maxSugarMetabolismPenalty = sugarMetabolismPenalty[1] - maxSpiceMetabolismPenalty = spiceMetabolismPenalty[1] - maxMovementPenalty = movementPenalty[1] - maxVisionPenalty = visionPenalty[1] - maxFertilityPenalty = fertilityPenalty[1] maxAggressionPenalty = aggressionPenalty[1] - maxTagLength = tagLengths[1] + maxFertilityPenalty = fertilityPenalty[1] + maxIncubationPeriod = incubationPeriod[1] + maxMovementPenalty = movementPenalty[1] + maxSpiceMetabolismPenalty = spiceMetabolismPenalty[1] maxStartTimeframe = startTimeframe[1] + maxSugarMetabolismPenalty = sugarMetabolismPenalty[1] + maxTagLength = tagLengths[1] maxTransmissionChance = transmissionChance[1] + maxVisionPenalty = visionPenalty[1] - endowments = [] - sugarMetabolismPenalties = [] - spiceMetabolismPenalties = [] - movementPenalties = [] - visionPenalties = [] - fertilityPenalties = [] aggressionPenalties = [] diseaseTags = [] + endowments = [] + fertilityPenalties = [] + incubationPeriods = [] + movementPenalties = [] + spiceMetabolismPenalties = [] startTimesteps = [] + sugarMetabolismPenalties = [] transmissionChances = [] + visionPenalties = [] - currSugarMetabolismPenalty = minSugarMetabolismPenalty - currSpiceMetabolismPenalty = minSpiceMetabolismPenalty - currMovementPenalty = minMovementPenalty - currVisionPenalty = minVisionPenalty - currFertilityPenalty = minFertilityPenalty currAggressionPenalty = minAggressionPenalty - currTagLength = minTagLength + currFertilityPenalty = minFertilityPenalty + currIncubationPeriod = minIncubationPeriod + currMovementPenalty = minMovementPenalty + currSpiceMetabolismPenalty = minSpiceMetabolismPenalty currStartTimestep = minStartTimeframe + currSugarMetabolismPenalty = minSugarMetabolismPenalty + currTagLength = minTagLength currTransmissionChance = minTransmissionChance + currVisionPenalty = minVisionPenalty for i in range(numDiseases): - sugarMetabolismPenalties.append(currSugarMetabolismPenalty) - spiceMetabolismPenalties.append(currSpiceMetabolismPenalty) - movementPenalties.append(currMovementPenalty) - visionPenalties.append(currVisionPenalty) - fertilityPenalties.append(currFertilityPenalty) aggressionPenalties.append(currAggressionPenalty) diseaseTags.append([random.randrange(2) for i in range(currTagLength)]) + fertilityPenalties.append(currFertilityPenalty) + incubationPeriods.append(currIncubationPeriod) + movementPenalties.append(currMovementPenalty) + spiceMetabolismPenalties.append(currSpiceMetabolismPenalty) startTimesteps.append(currStartTimestep) + sugarMetabolismPenalties.append(currSugarMetabolismPenalty) transmissionChances.append(currTransmissionChance) + visionPenalties.append(currVisionPenalty) - currSugarMetabolismPenalty += 1 - currSpiceMetabolismPenalty += 1 - currMovementPenalty += 1 - currVisionPenalty += 1 - currFertilityPenalty += 1 currAggressionPenalty += 1 - currTagLength += 1 + currFertilityPenalty += 1 + currIncubationPeriod += 1 + currMovementPenalty += 1 + currSpiceMetabolismPenalty += 1 currStartTimestep += 1 + currSugarMetabolismPenalty += 1 + currTagLength += 1 currTransmissionChance += 1 + currVisionPenalty += 1 - if currSugarMetabolismPenalty > maxSugarMetabolismPenalty: - currSugarMetabolismPenalty = minSugarMetabolismPenalty - if currSpiceMetabolismPenalty > maxSpiceMetabolismPenalty: - currSpiceMetabolismPenalty = minSpiceMetabolismPenalty - if currMovementPenalty > maxMovementPenalty: - currMovementPenalty = minMovementPenalty - if currVisionPenalty > maxVisionPenalty: - currVisionPenalty = minVisionPenalty - if currFertilityPenalty > maxFertilityPenalty: - currFertilityPenalty = minFertilityPenalty if currAggressionPenalty > maxAggressionPenalty: currAggressionPenalty = minAggressionPenalty - if currTagLength > maxTagLength: - currTagLength = minTagLength + if currFertilityPenalty > maxFertilityPenalty: + currFertilityPenalty = minFertilityPenalty + if currIncubationPeriod > maxIncubationPeriod: + currIncubationPeriod = minIncubationPeriod + if currMovementPenalty > maxMovementPenalty: + currMovementPenalty = minMovementPenalty + if currSpiceMetabolismPenalty > maxSpiceMetabolismPenalty: + currSpiceMetabolismPenalty = minSpiceMetabolismPenalty if currStartTimestep > maxStartTimeframe: currStartTimestep = minStartTimeframe + if currSugarMetabolismPenalty > maxSugarMetabolismPenalty: + currSugarMetabolismPenalty = minSugarMetabolismPenalty + if currTagLength > maxTagLength: + currTagLength = minTagLength if currTransmissionChance > maxTransmissionChance: currTransmissionChance = minTransmissionChance + if currVisionPenalty > maxVisionPenalty: + currVisionPenalty = minVisionPenalty - randomDiseaseEndowment = {"sugarMetabolismPenalties": sugarMetabolismPenalties, - "spiceMetabolismPenalties": spiceMetabolismPenalties, - "movementPenalties": movementPenalties, - "visionPenalties": visionPenalties, - "fertilityPenalties": fertilityPenalties, - "aggressionPenalties": aggressionPenalties, + randomDiseaseEndowment = {"aggressionPenalties": aggressionPenalties, "diseaseTags": diseaseTags, + "fertilityPenalties": fertilityPenalties, + "incubationPeriods": incubationPeriods, + "movementPenalties": movementPenalties, + "spiceMetabolismPenalties": spiceMetabolismPenalties, "startTimesteps": startTimesteps, - "transmissionChances": transmissionChances} + "sugarMetabolismPenalties": sugarMetabolismPenalties, + "transmissionChances": transmissionChances, + "visionPenalties": visionPenalties} # Map configuration to a random number via hash to make random number generation independent of iteration order if (self.diseaseConfigHashes == None): @@ -508,13 +518,14 @@ def randomizeDiseaseEndowments(self, numDiseases): for i in range(numDiseases): diseaseEndowment = {"aggressionPenalty": aggressionPenalties.pop(), "fertilityPenalty": fertilityPenalties.pop(), + "incubationPeriod": incubationPeriods.pop(), "movementPenalty": movementPenalties.pop(), "sugarMetabolismPenalty": sugarMetabolismPenalties.pop(), + "startTimestep": startTimesteps.pop(), "spiceMetabolismPenalty": spiceMetabolismPenalties.pop(), "tags": diseaseTags.pop(), - "visionPenalty": visionPenalties.pop(), - "startTimestep": startTimesteps.pop(), - "transmissionChance": transmissionChances.pop()} + "transmissionChance": transmissionChances.pop(), + "visionPenalty": visionPenalties.pop()} endowments.append(diseaseEndowment) return endowments @@ -1332,6 +1343,7 @@ def verifyConfiguration(configuration): "debugMode": ["none"], "diseaseAggressionPenalty": [0, 0], "diseaseFertilityPenalty": [0, 0], + "diseaseIncubationPeriod": [0, 0], "diseaseMovementPenalty": [0, 0], "diseaseSpiceMetabolismPenalty": [0, 0], "diseaseStartTimeframe": [0, 0], From bfe9c9f38c96352d473ec8ca3c4b8ce724f95178 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Sat, 17 Aug 2024 10:26:28 -0700 Subject: [PATCH 20/46] Adds agent reactions to incubation periods --- agent.py | 45 ++++++++++++++++++++++++++++----------------- config.json | 2 +- gui.py | 4 ++-- sugarscape.py | 5 +++-- 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/agent.py b/agent.py index d57b1cf1..de561623 100644 --- a/agent.py +++ b/agent.py @@ -62,6 +62,9 @@ def __init__(self, agentID, birthday, cell, configuration): self.diseaseTransmissionChance = 0 self.diseases = [] self.diseaseDeath = False + self.symptomaticDiseases = [] + self.asymptomaticDiseases = [] + self.diseaseTransmissionChance = 0 self.familyHappiness = 0 self.fertile = False self.fertilityFactorModifier = 0 @@ -178,7 +181,7 @@ def catchDisease(self, disease, infector=None): if self.diseaseProtectionChance == 10: return diseaseID = disease.ID - for currDisease in self.diseases: + for currDisease in self.symptomaticDiseases: currDiseaseID = currDisease["disease"].ID # If currently sick with this disease, do not contract it again if diseaseID == currDiseaseID: @@ -198,22 +201,28 @@ def catchDisease(self, disease, infector=None): return startIndex = diseaseInImmuneSystem["start"] endIndex = diseaseInImmuneSystem["end"] - caughtDisease = {"disease": disease, "startIndex": startIndex, "endIndex": endIndex} + caughtDisease = {"disease": disease, "startIndex": startIndex, "endIndex": endIndex, "startIncubation": self.timestep, "endIncubation": self.timestep + disease.incubationPeriod} if infector != None: caughtDisease["infector"] = infector if infector not in disease.infectors: disease.infectors.append(infector.ID) disease.infected += 1 self.diseaseProtectionChance += 1 + if self.diseaseProtectionChance > 10: + self.diseaseProtectionChance = 10 self.diseaseTransmissionChance += disease.transmissionChance if self.diseaseTransmissionChance > 10: self.diseaseTransmissionChance = 10 - if self.diseaseProtectionChance > 10: - self.diseaseProtectionChance = 10 - self.diseases.append(caughtDisease) + self.asymptomaticDiseases.append(caughtDisease) self.updateDiseaseEffects(disease) self.findCellsInRange() + def showSymptoms(self): + for disease in self.asymptomaticDiseases: + if self.timestep >= disease["endIncubation"]: + diseaseIndex = self.asymptomaticDiseases.index(disease) + self.symptomaticDiseases.append(self.asymptomaticDiseases.pop(diseaseIndex)) + def checkDiseaseImmunity(self, disease): hammingDistance = self.findNearestHammingDistanceInDisease(disease)["distance"] if hammingDistance == 0: @@ -265,7 +274,7 @@ def doCombat(self, cell): def doDeath(self, causeOfDeath): self.alive = False self.causeOfDeath = causeOfDeath - if len(self.diseases) > 0: + if self.isSick(): self.diseaseDeath = True self.resetCell() self.doInheritance() @@ -274,11 +283,12 @@ def doDeath(self, causeOfDeath): self.socialNetwork = {"debtors": self.socialNetwork["debtors"], "children": self.socialNetwork["children"]} self.neighbors = [] self.neighborhood = [] - self.diseases = [] + self.symptomaticDiseases = [] def doDisease(self): - random.shuffle(self.diseases) - for diseaseRecord in self.diseases: + self.showSymptoms() + random.shuffle(self.symptomaticDiseases) + for diseaseRecord in self.symptomaticDiseases: diseaseTags = diseaseRecord["disease"].tags immuneResponseStart = diseaseRecord["startIndex"] immuneResponseEnd = min(diseaseRecord["endIndex"] + 1, len(self.immuneSystem)) @@ -288,10 +298,9 @@ def doDisease(self): self.immuneSystem[immuneResponseStart + i] = diseaseTags[i] break if diseaseTags == immuneResponse: - self.diseases.remove(diseaseRecord) + self.symptomaticDiseases.remove(diseaseRecord) self.updateDiseaseEffects(diseaseRecord["disease"]) - - diseaseCount = len(self.diseases) + diseaseCount = len(self.symptomaticDiseases) if diseaseCount == 0: return neighborCells = self.cell.neighbors.values() @@ -302,7 +311,8 @@ def doDisease(self): neighbors.append(neighbor) random.shuffle(neighbors) for neighbor in neighbors: - neighbor.catchDisease(self.diseases[random.randrange(diseaseCount)]["disease"], self) + diseases = self.asymptomaticDiseases + self.symptomaticDiseases + neighbor.catchDisease(diseases[random.randrange(diseaseCount)]["disease"], self) def doInheritance(self): if self.inheritancePolicy == "none": @@ -620,9 +630,9 @@ def findAggression(self): def isPreyInfected(self, prey): if prey == None: return True - if len(prey.diseases) > 0 and len(self.diseases) == 0: + if len(prey.symptomaticDiseases) > 0 and len(self.symptomaticDiseases) == 0: return False - if len(self.diseases) > 0 and len(prey.diseases) == 0: + if len(self.symptomaticDiseases) > 0 and len(prey.symptomaticDiseases) == 0: return False def findBestCell(self): @@ -1167,7 +1177,8 @@ def isNeighborValidPrey(self, neighbor): return False def isSick(self): - if len(self.diseases) > 0: + combinedDiseases = self.asymptomaticDiseases + self.symptomaticDiseases + if len(combinedDiseases) > 0: return True return False @@ -1288,7 +1299,7 @@ def sortCellsByWealth(self, cells): def updateDiseaseEffects(self, disease): # If disease not in list of diseases, agent has recovered and undo its effects recoveryCheck = -1 - for diseaseRecord in self.diseases: + for diseaseRecord in self.symptomaticDiseases: if disease == diseaseRecord["disease"]: recoveryCheck = 1 break diff --git a/config.json b/config.json index 40b1511b..f3a37164 100644 --- a/config.json +++ b/config.json @@ -52,7 +52,7 @@ "debugMode": ["none"], "diseaseAggressionPenalty": [-1, 1], "diseaseFertilityPenalty": [-1, 1], - "diseaseIncubationPeriod": [0,4], + "diseaseIncubationPeriod": [0,5], "diseaseMovementPenalty": [0, 0], "diseaseSpiceMetabolismPenalty": [1, 3], "diseaseSugarMetabolismPenalty": [1, 3], diff --git a/gui.py b/gui.py index c666ea25..ce90988f 100644 --- a/gui.py +++ b/gui.py @@ -464,7 +464,7 @@ def drawLines(self): elif self.activeNetwork.get() == "Disease": for agent in self.sugarscape.agents: if agent.isSick() == True: - for diseaseRecord in agent.diseases: + for diseaseRecord in agent.symptomaticDiseases: # Starting diseases without an infector are not considered if "infector" not in diseaseRecord: continue @@ -584,7 +584,7 @@ def lookupFillColor(self, cell): elif self.activeColorOptions["agent"] == "Depression": return self.colors["sick"] if agent.depressed == True else self.colors["healthy"] elif self.activeColorOptions["agent"] == "Disease": - return self.colors["sick"] if len(agent.diseases) > 0 else self.colors["healthy"] + return self.colors["sick"] if len(agent.symptomaticDiseases) > 0 else self.colors["healthy"] elif self.activeColorOptions["agent"] == "Metabolism": return self.colors["metabolism"][agent.sugarMetabolism + agent.spiceMetabolism] elif self.activeColorOptions["agent"] == "Movement": diff --git a/sugarscape.py b/sugarscape.py index 92cacae7..bbf175ff 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -209,7 +209,8 @@ def infectAgents(self): if agentImmunity == True: continue agent.catchDisease(newDisease) - if len(agent.diseases) > 0: + if agent.isSick(): + agent.showSymptoms() agent.startingDiseases += 1 newDisease.startingInfectedAgents += 1 if startingDiseases == [0, 0]: @@ -244,7 +245,7 @@ def configureEnvironment(self, maxSugar, maxSpice, sugarPeaks, spicePeaks): def countInfectedAgents(self, disease): totalInfected = 0 for agent in self.agents: - for agentDisease in agent.diseases: + for agentDisease in agent.symptomaticDiseases: if disease == agentDisease["disease"]: totalInfected += 1 return totalInfected From 50b3206972fd41ed3c40b27cdc5e6cf6abd2ecb8 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Sat, 17 Aug 2024 21:40:22 -0700 Subject: [PATCH 21/46] Adds new config options to the README --- README | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README b/README index b45e2c25..60f502e8 100644 --- a/README +++ b/README @@ -160,7 +160,7 @@ agentDepressionPercentage: float Default: 0.0 agentDiseaseProtectionChance: [int, int] - Sets the initial chance an agent can get infected by another agent. + Sets the chance an agent can get infected by another agent. Note: a value of 0 mean no protection, while a value of 10 means complete protection Default: [0, 0] @@ -306,6 +306,10 @@ diseaseFertilityPenalty: [float, float] Note: Negative values constitute a fertility decrease. Default: [0, 0] +diseaseIncubationPeriod: [int, int] + Set the number of timesteps a disease can remain hidden while still infecting other agents. + Default: [0, 0] + diseaseMovementPenalty: [int, int] Set the impact a disease will have on an agent's movement distance. Note: Negative values constitute a decrease in movement range. @@ -320,7 +324,6 @@ diseaseStartTimeframe: [int, int] Set the timestep a disease can be initialized. Default: [0, 0] - diseaseSugarMetabolismPenalty: [float, float] Set the impact a disease will have on an agent's sugar metabolism rate. Note: Negative values constitute a decrease in agent sugar metabolism. @@ -331,6 +334,10 @@ diseaseTagStringLength: [int, int] The longer the length, the longer an agent will have the disease. Default: [0, 0] +diseaseTransmissionChance: [int, int] + Set the chance a disease can transmit between agents. + Default: [0, 0] + diseaseVisionPenalty: [int, int] Set the impact a disease will have on an agent's vision. Note: Negative values constitute a decrease in agent vision. From 55a92c16599463267d91c4c8fc3ab3dcaa9a1fba Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Sun, 18 Aug 2024 10:23:57 -0700 Subject: [PATCH 22/46] Modified isSick so it only sees symptomatic diseases --- agent.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/agent.py b/agent.py index de561623..83c03f41 100644 --- a/agent.py +++ b/agent.py @@ -1177,8 +1177,7 @@ def isNeighborValidPrey(self, neighbor): return False def isSick(self): - combinedDiseases = self.asymptomaticDiseases + self.symptomaticDiseases - if len(combinedDiseases) > 0: + if len(self.symptomaticDiseases) > 0: return True return False From a10b51f1afdeeb12b65387334b1d71b23463b743 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Tue, 20 Aug 2024 01:26:23 -0700 Subject: [PATCH 23/46] Adds boolean function to see if an agent can get infected --- agent.py | 80 +++++++++++++++++++++++++++------------------------ gui.py | 7 +++-- sugarscape.py | 15 ++++------ 3 files changed, 52 insertions(+), 50 deletions(-) diff --git a/agent.py b/agent.py index 83c03f41..ecbfff54 100644 --- a/agent.py +++ b/agent.py @@ -54,6 +54,7 @@ def __init__(self, agentID, birthday, cell, configuration): self.alive = True self.age = 0 self.aggressionFactorModifier = 0 + self.asymptomaticDiseases = [] self.causeOfDeath = None self.cellsInRange = [] self.childEndowmentHashes = None @@ -62,9 +63,9 @@ def __init__(self, agentID, birthday, cell, configuration): self.diseaseTransmissionChance = 0 self.diseases = [] self.diseaseDeath = False + self.depressed = False self.symptomaticDiseases = [] self.asymptomaticDiseases = [] - self.diseaseTransmissionChance = 0 self.familyHappiness = 0 self.fertile = False self.fertilityFactorModifier = 0 @@ -91,6 +92,7 @@ def __init__(self, agentID, birthday, cell, configuration): self.sugarMeanIncome = 1 self.sugarMetabolismModifier = 0 self.sugarPrice = 0 + self.symptomaticDiseases = [] self.tribe = self.findTribe() self.timestep = birthday self.tagZeroes = 0 @@ -163,6 +165,27 @@ def addLoanFromAgent(self, agent, timestep, sugarLoan, spiceLoan, duration): "loanOrigin": timestep} self.socialNetwork["creditors"].append(loan) + def canCatchDisease(self, disease, infector): + if self.diseaseProtectionChance == 10: + return False + if self.checkDiseaseImmunity == True: + return False + if self.diseaseProtectionChance == 0 or infector == None: + return True + diseaseID = disease.ID + for currDisease in self.asymptomaticDiseases: + currDiseaseID = currDisease["disease"].ID + if diseaseID == currDiseaseID: + return False + for currDisease in self.symptomaticDiseases: + currDiseaseID = currDisease["disease"].ID + if diseaseID == currDiseaseID: + return False + randomTransmission = random.randint(1, 10) + randomProtection = random.randint(1, 10) + if randomTransmission > disease.transmissionChance and randomProtection <= self.diseaseProtectionChance: + return False + def canReachCell(self, cell): if cell == self.cell or cell in self.cellsInRange: return True @@ -178,27 +201,9 @@ def canTradeWithNeighbor(self, neighbor): return False def catchDisease(self, disease, infector=None): - if self.diseaseProtectionChance == 10: + if self.canCatchDisease(disease, infector) == False: return - diseaseID = disease.ID - for currDisease in self.symptomaticDiseases: - currDiseaseID = currDisease["disease"].ID - # If currently sick with this disease, do not contract it again - if diseaseID == currDiseaseID: - return diseaseInImmuneSystem = self.findNearestHammingDistanceInDisease(disease) - hammingDistance = diseaseInImmuneSystem["distance"] - # If immune to disease, do not contract it - if hammingDistance == 0: - return - randomTransmissionRate = random.randint(1, 10) - if randomTransmissionRate > disease.transmissionChance: - return - # Random number determines if agent gets sick or not - if infector != None: - randomProtectionRate = random.randint(1,10) - if randomProtectionRate <= self.diseaseProtectionChance and self.diseaseProtectionChance > 0: - return startIndex = diseaseInImmuneSystem["start"] endIndex = diseaseInImmuneSystem["end"] caughtDisease = {"disease": disease, "startIndex": startIndex, "endIndex": endIndex, "startIncubation": self.timestep, "endIncubation": self.timestep + disease.incubationPeriod} @@ -206,23 +211,17 @@ def catchDisease(self, disease, infector=None): caughtDisease["infector"] = infector if infector not in disease.infectors: disease.infectors.append(infector.ID) + else: + self.startingDiseases += 1 + disease.startingInfectedAgents += 1 disease.infected += 1 self.diseaseProtectionChance += 1 if self.diseaseProtectionChance > 10: self.diseaseProtectionChance = 10 - self.diseaseTransmissionChance += disease.transmissionChance - if self.diseaseTransmissionChance > 10: - self.diseaseTransmissionChance = 10 self.asymptomaticDiseases.append(caughtDisease) self.updateDiseaseEffects(disease) self.findCellsInRange() - def showSymptoms(self): - for disease in self.asymptomaticDiseases: - if self.timestep >= disease["endIncubation"]: - diseaseIndex = self.asymptomaticDiseases.index(disease) - self.symptomaticDiseases.append(self.asymptomaticDiseases.pop(diseaseIndex)) - def checkDiseaseImmunity(self, disease): hammingDistance = self.findNearestHammingDistanceInDisease(disease)["distance"] if hammingDistance == 0: @@ -1279,6 +1278,12 @@ def setMother(self, mother): self.addAgentToSocialNetwork(mother) self.socialNetwork["mother"] = mother + def showSymptoms(self): + for disease in self.asymptomaticDiseases: + if self.timestep >= disease["endIncubation"]: + diseaseIndex = self.asymptomaticDiseases.index(disease) + self.symptomaticDiseases.append(self.asymptomaticDiseases.pop(diseaseIndex)) + def spawnChild(self, childID, birthday, cell, configuration): return Agent(childID, birthday, cell, configuration) @@ -1302,20 +1307,19 @@ def updateDiseaseEffects(self, disease): if disease == diseaseRecord["disease"]: recoveryCheck = 1 break - - sugarMetabolismPenalty = disease.sugarMetabolismPenalty * recoveryCheck + aggressionPenalty = disease.aggressionPenalty * recoveryCheck + fertilityPenalty = disease.fertilityPenalty * recoveryCheck + movementPenalty = disease.movementPenalty * recoveryCheck spiceMetabolismPenalty = disease.spiceMetabolismPenalty * recoveryCheck + sugarMetabolismPenalty = disease.sugarMetabolismPenalty * recoveryCheck visionPenalty = disease.visionPenalty * recoveryCheck - movementPenalty = disease.movementPenalty * recoveryCheck - fertilityPenalty = disease.fertilityPenalty * recoveryCheck - aggressionPenalty = disease.aggressionPenalty * recoveryCheck - self.sugarMetabolismModifier += sugarMetabolismPenalty + self.aggressionFactorModifier += aggressionPenalty + self.fertilityFactorModifier += fertilityPenalty + self.movementModifier += movementPenalty self.spiceMetabolismModifier += spiceMetabolismPenalty + self.sugarMetabolismModifier += sugarMetabolismPenalty self.visionModifier += visionPenalty - self.movementModifier += movementPenalty - self.fertilityFactorModifier += fertilityPenalty - self.aggressionFactorModifier += aggressionPenalty def updateFriends(self, neighbor): neighborID = neighbor.ID diff --git a/gui.py b/gui.py index ce90988f..f7c1769a 100644 --- a/gui.py +++ b/gui.py @@ -666,12 +666,13 @@ def updateHighlightedCellStats(self): if agent != None: agentStats = f"Agent: {str(agent)} | Age: {agent.age} | Vision: {round(agent.findVision(), 2)} | Movement: {round(agent.findMovement(), 2)} | " agentStats += f"Sugar: {round(agent.sugar, 2)} | Spice: {round(agent.spice, 2)} | " - agentStats += f"Metabolism: {round(((agent.findSugarMetabolism() + agent.findSpiceMetabolism()) / 2), 2)} | Decision Model: {agent.decisionModel}" + agentStats += f"Metabolism: {round(((agent.findSugarMetabolism() + agent.findSpiceMetabolism()) / 2), 2)} | Decision Model: {agent.decisionModel} | " + agentStats += f"Diseases: {len(agent.symptomaticDiseases)}" else: - agentStats = "Agent: - | Age: - | Vision: - | Movement: - | Sugar: - | Spice: - | Metabolism: - | Decision Model: -" + agentStats = "Agent: - | Age: - | Vision: - | Movement: - | Sugar: - | Spice: - | Metabolism: - | Decision Model: - | Diseases: - " cellStats += f"\n{agentStats}" else: - cellStats = "Cell: - | Sugar: - | Spice: - | Pollution: - | Season: -\nAgent: - | Age: - | Sugar: - | Spice: - " + cellStats = "Cell: - | Sugar: - | Spice: - | Pollution: - | Season: -\nAgent: - | Age: - | Sugar: - | Spice: -" label = self.widgets["cellLabel"] label.config(text=cellStats) diff --git a/sugarscape.py b/sugarscape.py index bbf175ff..28946a8e 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -205,17 +205,14 @@ def infectAgents(self): if agent.startingDiseases >= currStartingDiseases and startingDiseases != [0, 0]: currStartingDiseases += 1 break - agentImmunity = agent.checkDiseaseImmunity(newDisease) - if agentImmunity == True: + agentCanCatchDisease = agent.canCatchDisease(newDisease, None) + if agentCanCatchDisease == False: continue agent.catchDisease(newDisease) - if agent.isSick(): - agent.showSymptoms() - agent.startingDiseases += 1 - newDisease.startingInfectedAgents += 1 - if startingDiseases == [0, 0]: - diseases.remove(newDisease) - break + agent.showSymptoms() + if startingDiseases == [0, 0]: + diseases.remove(newDisease) + break if currStartingDiseases > maxStartingDiseases: currStartingDiseases = minStartingDiseases if startingDiseases == [0, 0] and self.timestep == self.configuration["diseaseStartTimeframe"][1] and len(diseases) > 0: From b2c06a8143b482a362089f890f4e31a9bd218bd9 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Tue, 20 Aug 2024 01:41:04 -0700 Subject: [PATCH 24/46] Modifies agent fields --- agent.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/agent.py b/agent.py index ecbfff54..cebdc9e0 100644 --- a/agent.py +++ b/agent.py @@ -61,11 +61,8 @@ def __init__(self, agentID, birthday, cell, configuration): self.conflictHappiness = 0 self.depressed = False self.diseaseTransmissionChance = 0 - self.diseases = [] self.diseaseDeath = False self.depressed = False - self.symptomaticDiseases = [] - self.asymptomaticDiseases = [] self.familyHappiness = 0 self.fertile = False self.fertilityFactorModifier = 0 From a93bdc51b91d86bccc17b1e3a5d6351b83f3aa83 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Tue, 20 Aug 2024 01:48:05 -0700 Subject: [PATCH 25/46] Adds disease protection to child endowment --- agent.py | 1 + 1 file changed, 1 insertion(+) diff --git a/agent.py b/agent.py index cebdc9e0..3519d861 100644 --- a/agent.py +++ b/agent.py @@ -753,6 +753,7 @@ def findChildEndowment(self, mate): parentEndowments = { "aggressionFactor": [self.aggressionFactor, mate.aggressionFactor], "baseInterestRate": [self.baseInterestRate, mate.baseInterestRate], + "diseaseProtectionChance": [self.diseaseProtectionChance, mate.diseaseProtectionChance], "fertilityAge": [self.fertilityAge, mate.fertilityAge], "fertilityFactor": [self.fertilityFactor, mate.fertilityFactor], "infertilityAge": [self.infertilityAge, mate.infertilityAge], From e9ce0bf121f0dbec0b6a494ef84caca03922a106 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Tue, 20 Aug 2024 01:51:54 -0700 Subject: [PATCH 26/46] Modifies checking if agent is already infected --- agent.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/agent.py b/agent.py index 3519d861..e25a10e5 100644 --- a/agent.py +++ b/agent.py @@ -170,11 +170,8 @@ def canCatchDisease(self, disease, infector): if self.diseaseProtectionChance == 0 or infector == None: return True diseaseID = disease.ID - for currDisease in self.asymptomaticDiseases: - currDiseaseID = currDisease["disease"].ID - if diseaseID == currDiseaseID: - return False - for currDisease in self.symptomaticDiseases: + combinedDiseases = self.asymptomaticDiseases + self.symptomaticDiseases + for currDisease in combinedDiseases: currDiseaseID = currDisease["disease"].ID if diseaseID == currDiseaseID: return False From 0abc5c0a4dd4cce373b3a84eaf3a834e907ee60e Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Tue, 20 Aug 2024 14:25:23 -0700 Subject: [PATCH 27/46] Removes extra lines of code --- sugarscape.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/sugarscape.py b/sugarscape.py index 28946a8e..724e6580 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -969,8 +969,6 @@ def updateRuntimeStats(self): agentDiseaseDeaths += 1 if agent.diseaseDeath == True else 0 agentCombatDeaths += 1 if agent.causeOfDeath == "combat" else 0 agentAgingDeaths += 1 if agent.causeOfDeath == "aging" else 0 - #if agent.diseaseDeath == True: - # agentDiseaseDeaths += 1 meanAgeAtDeath = round(meanAgeAtDeath / numDeadAgents, 2) if numDeadAgents > 0 else 0 self.deadAgents = [] From cf479395e71897288078922a05a2cbea2c6fbd96 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Tue, 20 Aug 2024 19:47:08 -0700 Subject: [PATCH 28/46] Fixes disease effects modify agent after incubation --- agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent.py b/agent.py index e25a10e5..d39851fd 100644 --- a/agent.py +++ b/agent.py @@ -213,7 +213,6 @@ def catchDisease(self, disease, infector=None): if self.diseaseProtectionChance > 10: self.diseaseProtectionChance = 10 self.asymptomaticDiseases.append(caughtDisease) - self.updateDiseaseEffects(disease) self.findCellsInRange() def checkDiseaseImmunity(self, disease): @@ -1278,6 +1277,7 @@ def showSymptoms(self): if self.timestep >= disease["endIncubation"]: diseaseIndex = self.asymptomaticDiseases.index(disease) self.symptomaticDiseases.append(self.asymptomaticDiseases.pop(diseaseIndex)) + self.updateDiseaseEffects(disease["disease"]) def spawnChild(self, childID, birthday, cell, configuration): return Agent(childID, birthday, cell, configuration) From 780ee4e2bdba6f256f063622f636f53c1c1cfcd7 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Wed, 21 Aug 2024 21:43:06 -0700 Subject: [PATCH 29/46] Modfies code for correctness and readability --- agent.py | 63 ++++++++++++++++++++++++++------------------------- cell.py | 2 +- disease.py | 5 ++-- gui.py | 2 +- sugarscape.py | 16 ++++--------- 5 files changed, 40 insertions(+), 48 deletions(-) diff --git a/agent.py b/agent.py index d39851fd..a47c820a 100644 --- a/agent.py +++ b/agent.py @@ -54,7 +54,6 @@ def __init__(self, agentID, birthday, cell, configuration): self.alive = True self.age = 0 self.aggressionFactorModifier = 0 - self.asymptomaticDiseases = [] self.causeOfDeath = None self.cellsInRange = [] self.childEndowmentHashes = None @@ -68,6 +67,7 @@ def __init__(self, agentID, birthday, cell, configuration): self.fertilityFactorModifier = 0 self.happiness = 0 self.healthHappiness = 0 + self.incubatingDiseases = [] self.lastDoneCombat = -1 self.lastMoved = -1 self.lastReproduced = -1 @@ -162,23 +162,25 @@ def addLoanFromAgent(self, agent, timestep, sugarLoan, spiceLoan, duration): "loanOrigin": timestep} self.socialNetwork["creditors"].append(loan) - def canCatchDisease(self, disease, infector): + def canCatchDisease(self, disease, infector=None): if self.diseaseProtectionChance == 10: return False - if self.checkDiseaseImmunity == True: + if self.checkDiseaseImmunity(disease) == True: return False if self.diseaseProtectionChance == 0 or infector == None: return True diseaseID = disease.ID - combinedDiseases = self.asymptomaticDiseases + self.symptomaticDiseases + combinedDiseases = self.incubatingDiseases + self.symptomaticDiseases for currDisease in combinedDiseases: currDiseaseID = currDisease["disease"].ID if diseaseID == currDiseaseID: return False + # TODO: need to change protection and transmission to 0->1 use random.random() randomTransmission = random.randint(1, 10) randomProtection = random.randint(1, 10) if randomTransmission > disease.transmissionChance and randomProtection <= self.diseaseProtectionChance: return False + return True def canReachCell(self, cell): if cell == self.cell or cell in self.cellsInRange: @@ -195,25 +197,24 @@ def canTradeWithNeighbor(self, neighbor): return False def catchDisease(self, disease, infector=None): - if self.canCatchDisease(disease, infector) == False: - return - diseaseInImmuneSystem = self.findNearestHammingDistanceInDisease(disease) - startIndex = diseaseInImmuneSystem["start"] - endIndex = diseaseInImmuneSystem["end"] - caughtDisease = {"disease": disease, "startIndex": startIndex, "endIndex": endIndex, "startIncubation": self.timestep, "endIncubation": self.timestep + disease.incubationPeriod} - if infector != None: - caughtDisease["infector"] = infector - if infector not in disease.infectors: - disease.infectors.append(infector.ID) - else: - self.startingDiseases += 1 - disease.startingInfectedAgents += 1 - disease.infected += 1 - self.diseaseProtectionChance += 1 - if self.diseaseProtectionChance > 10: - self.diseaseProtectionChance = 10 - self.asymptomaticDiseases.append(caughtDisease) - self.findCellsInRange() + if self.canCatchDisease(disease, infector) == True: + diseaseInImmuneSystem = self.findNearestHammingDistanceInDisease(disease) + startIndex = diseaseInImmuneSystem["start"] + endIndex = diseaseInImmuneSystem["end"] + caughtDisease = {"disease": disease, "startIndex": startIndex, "endIndex": endIndex, "startIncubation": self.timestep, "endIncubation": self.timestep + disease.incubationPeriod} + if infector != None: + caughtDisease["infector"] = infector + disease.infectors.add(infector.ID) + else: + self.startingDiseases += 1 + disease.startingInfectedAgents += 1 + disease.infected += 1 + self.diseaseProtectionChance += 1 + if self.diseaseProtectionChance > 10: + self.diseaseProtectionChance = 10 + self.incubatingDiseases.append(caughtDisease) + self.showSymptoms() + self.findCellsInRange() def checkDiseaseImmunity(self, disease): hammingDistance = self.findNearestHammingDistanceInDisease(disease)["distance"] @@ -303,7 +304,7 @@ def doDisease(self): neighbors.append(neighbor) random.shuffle(neighbors) for neighbor in neighbors: - diseases = self.asymptomaticDiseases + self.symptomaticDiseases + diseases = self.incubatingDiseases + self.symptomaticDiseases neighbor.catchDisease(diseases[random.randrange(diseaseCount)]["disease"], self) def doInheritance(self): @@ -621,11 +622,11 @@ def findAggression(self): def isPreyInfected(self, prey): if prey == None: - return True - if len(prey.symptomaticDiseases) > 0 and len(self.symptomaticDiseases) == 0: return False + if len(prey.symptomaticDiseases) > 0 and len(self.symptomaticDiseases) == 0: + return True if len(self.symptomaticDiseases) > 0 and len(prey.symptomaticDiseases) == 0: - return False + return True def findBestCell(self): self.findNeighborhood() @@ -648,7 +649,7 @@ def findBestCell(self): prey = cell.agent if cell.isOccupied() and self.isNeighborValidPrey(prey) == False: continue - if self.isPreyInfected(prey) == False: + if self.isPreyInfected(prey) == True: continue preyTribe = prey.tribe if prey != None else "empty" preySugar = prey.sugar if prey != None else 0 @@ -1273,10 +1274,10 @@ def setMother(self, mother): self.socialNetwork["mother"] = mother def showSymptoms(self): - for disease in self.asymptomaticDiseases: + for disease in self.incubatingDiseases: if self.timestep >= disease["endIncubation"]: - diseaseIndex = self.asymptomaticDiseases.index(disease) - self.symptomaticDiseases.append(self.asymptomaticDiseases.pop(diseaseIndex)) + diseaseIndex = self.incubatingDiseases.index(disease) + self.symptomaticDiseases.append(self.incubatingDiseases.pop(diseaseIndex)) self.updateDiseaseEffects(disease["disease"]) def spawnChild(self, childID, birthday, cell, configuration): diff --git a/cell.py b/cell.py index fd235dcc..a91b8d07 100644 --- a/cell.py +++ b/cell.py @@ -1,7 +1,7 @@ import math class Cell: - def __init__(self, x, y, environment, maxSugar=0, maxSpice=0, growbackRate=0): + def __init__(self, x, y, environment, maxSugar=0, maxSpice=0): self.x = x self.y = y self.environment = environment diff --git a/disease.py b/disease.py index d40377da..524f8763 100644 --- a/disease.py +++ b/disease.py @@ -10,19 +10,18 @@ def __init__(self, diseaseID, configuration): self.incubationPeriod = configuration["incubationPeriod"] self.movementPenalty = configuration["movementPenalty"] self.spiceMetabolismPenalty = configuration["spiceMetabolismPenalty"] - self.startTimestep = configuration["startTimestep"] self.sugarMetabolismPenalty = configuration["sugarMetabolismPenalty"] self.tags = configuration["tags"] self.transmissionChance = configuration["transmissionChance"] self.visionPenalty = configuration["visionPenalty"] self.infected = 0 - self.infectors = [] + self.infectors = set() self.startingInfectedAgents = 0 def resetRStats(self): self.infected = 0 - self.infectors = [] + self.infectors = set() def __str__(self): return f"{self.ID}" diff --git a/gui.py b/gui.py index f7c1769a..f35348e0 100644 --- a/gui.py +++ b/gui.py @@ -584,7 +584,7 @@ def lookupFillColor(self, cell): elif self.activeColorOptions["agent"] == "Depression": return self.colors["sick"] if agent.depressed == True else self.colors["healthy"] elif self.activeColorOptions["agent"] == "Disease": - return self.colors["sick"] if len(agent.symptomaticDiseases) > 0 else self.colors["healthy"] + return self.colors["sick"] if agent.isSick() else self.colors["healthy"] elif self.activeColorOptions["agent"] == "Metabolism": return self.colors["metabolism"][agent.sugarMetabolism + agent.spiceMetabolism] elif self.activeColorOptions["agent"] == "Movement": diff --git a/sugarscape.py b/sugarscape.py index 724e6580..37a720f7 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -66,10 +66,9 @@ def __init__(self, configuration): "agentTotalMetabolism": 0, "agentCombatDeaths": 0, "agentAgingDeaths": 0, "totalSickAgents": 0} self.diseaseStats = {} for disease in self.diseases: - self.diseaseStats[f"disease{disease.ID}ImmunePercentage"] = 0.0 self.diseaseStats[f"disease{disease.ID}Incidence"] = 0 self.diseaseStats[f"disease{disease.ID}Prevalence"] = 0 - self.diseaseStats[f"disease{disease.ID}RValue"] = 0 + self.diseaseStats[f"disease{disease.ID}RValue"] = 0.0 self.runtimeStats.update(self.diseaseStats) self.graphStats = {"ageBins": [], "sugarBins": [], "spiceBins": [], "lorenzCurvePoints": [], "meanTribeTags": [], "maxSugar": 0, "maxSpice": 0, "maxWealth": 0} @@ -179,10 +178,9 @@ def configureDiseases(self, numDiseases): diseaseID = self.generateDiseaseID() diseaseConfiguration = diseaseEndowments[i] newDisease = disease.Disease(diseaseID, diseaseConfiguration) - timestep = newDisease.startTimestep + timestep = diseaseConfiguration["startTimestep"] self.diseases.append(newDisease) self.diseasesCount[timestep].append(newDisease) - self.diseases.sort(key=lambda d: d.ID) self.infectAgents() def infectAgents(self): @@ -197,19 +195,17 @@ def infectAgents(self): maxStartingDiseases = startingDiseases[1] currStartingDiseases = minStartingDiseases random.shuffle(self.agents) + random.shuffle(diseases) for agent in self.agents: - random.shuffle(diseases) for newDisease in diseases: if newDisease.startingInfectedAgents == 1 and startingDiseases == [0, 0]: continue if agent.startingDiseases >= currStartingDiseases and startingDiseases != [0, 0]: currStartingDiseases += 1 break - agentCanCatchDisease = agent.canCatchDisease(newDisease, None) - if agentCanCatchDisease == False: + if agent.canCatchDisease(newDisease) == False: continue agent.catchDisease(newDisease) - agent.showSymptoms() if startingDiseases == [0, 0]: diseases.remove(newDisease) break @@ -1021,16 +1017,12 @@ def updateRuntimeStats(self): agentImmunity = agent.checkDiseaseImmunity(disease) if agentImmunity == True: immuneAgents += 1 - immunePercentage = 0.0 - if numAgents > 0: - immunePercentage = round(float(immuneAgents / numAgents), 2) infectors = len(disease.infectors) incidence = disease.infected prevalence = self.countInfectedAgents(disease) r = 0.0 if infectors > 0: r = round(float(incidence / infectors), 2) - self.runtimeStats[f"disease{disease.ID}ImmunePercentage"] = immunePercentage self.runtimeStats[f"disease{disease.ID}Incidence"] = incidence self.runtimeStats[f"disease{disease.ID}Prevalence"] = prevalence self.runtimeStats[f"disease{disease.ID}RValue"] = r From 5be7c08738eb2b9db38a3ace48070dec32e3daba Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Thu, 22 Aug 2024 18:16:12 -0700 Subject: [PATCH 30/46] Modifies disease transmission and protection to be between 0 and 1 --- agent.py | 21 ++++++++-------- config.json | 6 ++--- sugarscape.py | 68 ++++++++++++++++++++++++++------------------------- 3 files changed, 49 insertions(+), 46 deletions(-) diff --git a/agent.py b/agent.py index a47c820a..5c84924a 100644 --- a/agent.py +++ b/agent.py @@ -59,7 +59,6 @@ def __init__(self, agentID, birthday, cell, configuration): self.childEndowmentHashes = None self.conflictHappiness = 0 self.depressed = False - self.diseaseTransmissionChance = 0 self.diseaseDeath = False self.depressed = False self.familyHappiness = 0 @@ -80,6 +79,7 @@ def __init__(self, agentID, birthday, cell, configuration): self.neighborhood = [] self.neighbors = [] self.nice = 0 + self.recoveredDiseases = [] self.socialHappiness = 0 self.socialNetwork = {"father": None, "mother": None, "children": [], "friends": [], "creditors": [], "debtors": [], "mates": []} self.spiceMeanIncome = 1 @@ -163,7 +163,7 @@ def addLoanFromAgent(self, agent, timestep, sugarLoan, spiceLoan, duration): self.socialNetwork["creditors"].append(loan) def canCatchDisease(self, disease, infector=None): - if self.diseaseProtectionChance == 10: + if self.diseaseProtectionChance == 1: return False if self.checkDiseaseImmunity(disease) == True: return False @@ -175,9 +175,8 @@ def canCatchDisease(self, disease, infector=None): currDiseaseID = currDisease["disease"].ID if diseaseID == currDiseaseID: return False - # TODO: need to change protection and transmission to 0->1 use random.random() - randomTransmission = random.randint(1, 10) - randomProtection = random.randint(1, 10) + randomTransmission = random.random() + randomProtection = random.random() if randomTransmission > disease.transmissionChance and randomProtection <= self.diseaseProtectionChance: return False return True @@ -209,9 +208,6 @@ def catchDisease(self, disease, infector=None): self.startingDiseases += 1 disease.startingInfectedAgents += 1 disease.infected += 1 - self.diseaseProtectionChance += 1 - if self.diseaseProtectionChance > 10: - self.diseaseProtectionChance = 10 self.incubatingDiseases.append(caughtDisease) self.showSymptoms() self.findCellsInRange() @@ -291,8 +287,7 @@ def doDisease(self): self.immuneSystem[immuneResponseStart + i] = diseaseTags[i] break if diseaseTags == immuneResponse: - self.symptomaticDiseases.remove(diseaseRecord) - self.updateDiseaseEffects(diseaseRecord["disease"]) + self.recoverFromDisease(diseaseRecord) diseaseCount = len(self.symptomaticDiseases) if diseaseCount == 0: return @@ -307,6 +302,12 @@ def doDisease(self): diseases = self.incubatingDiseases + self.symptomaticDiseases neighbor.catchDisease(diseases[random.randrange(diseaseCount)]["disease"], self) + def recoverFromDisease(self, disease): + index = self.symptomaticDiseases.index(disease) + recoveredDisease = self.symptomaticDiseases.pop(index) + self.recoveredDiseases.append(recoveredDisease) + self.updateDiseaseEffects(recoveredDisease["disease"]) + def doInheritance(self): if self.inheritancePolicy == "none": return diff --git a/config.json b/config.json index f3a37164..14183440 100644 --- a/config.json +++ b/config.json @@ -19,7 +19,7 @@ "agentDecisionModelLookaheadFactor": 0, "agentDecisionModelTribalFactor": [-1, -1], "agentDepressionPercentage": 0, - "agentDiseaseProtectionChance": [0, 10], + "agentDiseaseProtectionChance": [0.0, 1.0], "agentFemaleInfertilityAge": [40, 50], "agentFemaleFertilityAge": [12, 15], "agentFertilityFactor": [1, 1], @@ -52,13 +52,13 @@ "debugMode": ["none"], "diseaseAggressionPenalty": [-1, 1], "diseaseFertilityPenalty": [-1, 1], - "diseaseIncubationPeriod": [0,5], + "diseaseIncubationPeriod": [0,10], "diseaseMovementPenalty": [0, 0], "diseaseSpiceMetabolismPenalty": [1, 3], "diseaseSugarMetabolismPenalty": [1, 3], "diseaseStartTimeframe": [0, 0], "diseaseTagStringLength": [11, 21], - "diseaseTransmissionChance": [0, 10], + "diseaseTransmissionChance": [0.0, 1.0], "diseaseVisionPenalty": [-1, 1], "environmentEquator": -1, "environmentHeight": 50, diff --git a/sugarscape.py b/sugarscape.py index 37a720f7..e4aa684d 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -64,12 +64,12 @@ def __init__(self, configuration): "totalMetabolismCost": 0, "agentsReplaced": 0, "agentsBorn": 0, "agentStarvationDeaths": 0, "agentDiseaseDeaths": 0, "environmentWealthCreated": 0, "agentWealthTotal": 0, "environmentWealthTotal": 0, "agentWealthCollected": 0, "agentWealthBurnRate": 0, "agentMeanTimeToLive": 0, "agentTotalMetabolism": 0, "agentCombatDeaths": 0, "agentAgingDeaths": 0, "totalSickAgents": 0} - self.diseaseStats = {} + diseaseStats = {} for disease in self.diseases: - self.diseaseStats[f"disease{disease.ID}Incidence"] = 0 - self.diseaseStats[f"disease{disease.ID}Prevalence"] = 0 - self.diseaseStats[f"disease{disease.ID}RValue"] = 0.0 - self.runtimeStats.update(self.diseaseStats) + diseaseStats[f"disease{disease.ID}Incidence"] = 0 + diseaseStats[f"disease{disease.ID}Prevalence"] = 0 + diseaseStats[f"disease{disease.ID}RValue"] = 0.0 + self.runtimeStats.update(diseaseStats) self.graphStats = {"ageBins": [], "sugarBins": [], "spiceBins": [], "lorenzCurvePoints": [], "meanTribeTags": [], "maxSugar": 0, "maxSpice": 0, "maxWealth": 0} self.log = open(configuration["logfile"], 'a') if configuration["logfile"] != None else None @@ -252,11 +252,11 @@ def doTimestep(self): if "all" in self.debug or "sugarscape" in self.debug: print(f"Timestep: {self.timestep}\nLiving Agents: {len(self.agents)}") self.timestep += 1 - self.infectAgents() if self.end == True or (len(self.agents) == 0 and self.keepAlive == False): self.toggleEnd() else: self.environment.doTimestep(self.timestep) + self.infectAgents() random.shuffle(self.agents) for agent in self.agents: agent.doTimestep(self.timestep) @@ -358,16 +358,6 @@ def generateTribeTags(self, tribe): random.shuffle(tags) return tags - def groupDiseases(self): - groupedDiseases = {} - for timestep in self.diseasesStats: - disease = timestep["disease"] - if disease not in groupedDiseases: - groupedDiseases[disease] = [] - del timestep["disease"] - groupedDiseases[disease].append(timestep) - return groupedDiseases - def endSimulation(self): self.removeDeadAgents() self.endLog() @@ -460,7 +450,8 @@ def randomizeDiseaseEndowments(self, numDiseases): currStartTimestep += 1 currSugarMetabolismPenalty += 1 currTagLength += 1 - currTransmissionChance += 1 + # To make sure the disease's transmissionChance is a properly-formatted decimal + currTransmissionChance = round(currTransmissionChance + (10 ** -1), 1) currVisionPenalty += 1 if currAggressionPenalty > maxAggressionPenalty: @@ -1011,18 +1002,16 @@ def updateRuntimeStats(self): self.runtimeStats["totalSickAgents"] = totalSickAgents for disease in self.diseases: - immuneAgents = 0 - if numAgents > 0: - for agent in self.agents: - agentImmunity = agent.checkDiseaseImmunity(disease) - if agentImmunity == True: - immuneAgents += 1 - infectors = len(disease.infectors) - incidence = disease.infected - prevalence = self.countInfectedAgents(disease) + incidence = 0 + prevalence = 0 r = 0.0 - if infectors > 0: - r = round(float(incidence / infectors), 2) + if numAgents > 0: + infectors = len(disease.infectors) + incidence = disease.infected + prevalence = self.countInfectedAgents(disease) + r = 0.0 + if infectors > 0: + r = round(float(incidence / infectors), 2) self.runtimeStats[f"disease{disease.ID}Incidence"] = incidence self.runtimeStats[f"disease{disease.ID}Prevalence"] = prevalence self.runtimeStats[f"disease{disease.ID}RValue"] = r @@ -1162,10 +1151,14 @@ def verifyConfiguration(configuration): print(f"Cannot have agent maximum tribal factor of {configuration['agentDecisionModelTribalFactor'][1]}. Setting agent maximum tribal factor to 1.0.") configuration["agentDecisionModelTribalFactor"][1] = 1 - if configuration["agentDiseaseProtectionChance"][1] > 10: + if configuration["agentDiseaseProtectionChance"][0] < 0: + if "all" in configuration["debugMode"] or "agent" in configuration["debugMode"]: + print(f"Cannot have agent minimum disease protection chance of {configuration['agentDiseaseProtectionChance'][0]}. Setting agent minimum disease protection chance to 0.0.") + configuration["agentDiseaseProtectionChance"][0] = 0.0 + if configuration["agentDiseaseProtectionChance"][1] > 1: if "all" in configuration["debugMode"] or "agent" in configuration["debugMode"]: - print(f"Cannot have agent maximum disease protection chance of {configuration['agentDiseaseProtectionChance'][1]}. Setting agent maximum disease protection chance to 10.") - configuration["agentDiseaseProtectionChance"][1] = 10 + print(f"Cannot have agent maximum disease protection chance of {configuration['agentDiseaseProtectionChance'][1]}. Setting agent maximum disease protection chance to 1.0.") + configuration["agentDiseaseProtectionChance"][1] = 1.0 if configuration["agentTagStringLength"] < 0: if "all" in configuration["debugMode"] or "agent" in configuration["debugMode"]: @@ -1182,6 +1175,15 @@ def verifyConfiguration(configuration): print(f"Cannot have a longer agent tag string length than maximum number of tribes. Setting the number of tribes to {configuration['agentTagStringLength']}.") configuration["environmentMaxTribes"] = configuration["agentTagStringLength"] + if configuration["diseaseTransmissionChance"][0] < 0.0: + if "all" in configuration["debugMode"] or "disease" in configuration["debugMode"]: + print(f"Cannot have a minimum disease transmission chance of {configuration['diseaseTransmissionChance'][0]}. Setting disease transmission chance to 0.0.") + configuration["diseaseTransmissionChance"][0] = 0.0 + if configuration["diseaseTransmissionChance"][1] > 1.0: + if "all" in configuration["debugMode"] or "disease" in configuration["debugMode"]: + print(f"Cannot have a maximum disease transmission chance of {configuration['diseaseTransmissionChance'][1]}. Setting disease transmission chance to 1.0.") + configuration["diseaseTransmissionChance"][1] = 1.0 + # Ensure at most number of tribes and decision models are equal to the number of colors in the GUI maxColors = 25 if configuration["environmentMaxTribes"] > maxColors: @@ -1298,7 +1300,7 @@ def verifyConfiguration(configuration): "agentDecisionModelLookaheadFactor": [0], "agentDecisionModelTribalFactor": [-1, -1], "agentDepressionPercentage": 0, - "agentDiseaseProtectionChance": [0, 0], + "agentDiseaseProtectionChance": [0.0, 0.0], "agentFemaleInfertilityAge": [0, 0], "agentFemaleFertilityAge": [0, 0], "agentFertilityFactor": [0, 0], @@ -1337,7 +1339,7 @@ def verifyConfiguration(configuration): "diseaseStartTimeframe": [0, 0], "diseaseSugarMetabolismPenalty": [0, 0], "diseaseTagStringLength": [0, 0], - "diseaseTransmissionChance": [0, 0], + "diseaseTransmissionChance": [0.0, 0.0], "diseaseVisionPenalty": [0, 0], "environmentEquator": -1, "environmentHeight": 50, From afd1d1a56820b32d014b5a5b11b64e539173b337 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Thu, 22 Aug 2024 18:24:06 -0700 Subject: [PATCH 31/46] Updates README entries --- README | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README b/README index 60f502e8..12528103 100644 --- a/README +++ b/README @@ -159,10 +159,10 @@ agentDepressionPercentage: float Note: The starting agent population will have this same percentage of depressed agents. Default: 0.0 -agentDiseaseProtectionChance: [int, int] +agentDiseaseProtectionChance: [float, float] Sets the chance an agent can get infected by another agent. - Note: a value of 0 mean no protection, while a value of 10 means complete protection - Default: [0, 0] + Note: a value of 0.0 mean no protection, while a value of 1.0 means complete protection + Default: [0.0, 0.0] agentFemaleInfertilityAge: [int, int] Set the timestep age at which female agents become infertile. @@ -334,9 +334,9 @@ diseaseTagStringLength: [int, int] The longer the length, the longer an agent will have the disease. Default: [0, 0] -diseaseTransmissionChance: [int, int] +diseaseTransmissionChance: [float, float] Set the chance a disease can transmit between agents. - Default: [0, 0] + Default: [0.0, 0.0] diseaseVisionPenalty: [int, int] Set the impact a disease will have on an agent's vision. From b5d87db101f9b1c1974c8d910f5ed9cec6923fca Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Thu, 22 Aug 2024 18:31:39 -0700 Subject: [PATCH 32/46] Adds feature where agent protection gradually lowers after infertility age --- agent.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/agent.py b/agent.py index 5c84924a..0d6dbca3 100644 --- a/agent.py +++ b/agent.py @@ -276,6 +276,10 @@ def doDeath(self, causeOfDeath): def doDisease(self): self.showSymptoms() + if self.age >= self.infertilityAge: + self.diseaseProtectionChance = round(self.diseaseProtectionChance - 0.01, 2) + if self.diseaseProtectionChance < 0: + self.diseaseProtectionChance = 0 random.shuffle(self.symptomaticDiseases) for diseaseRecord in self.symptomaticDiseases: diseaseTags = diseaseRecord["disease"].tags From a5a34205e8350bfa33f5051d720bebd430c59989 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Fri, 23 Aug 2024 15:24:56 -0700 Subject: [PATCH 33/46] Modifies agent behavior to avoid sick agents --- agent.py | 50 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/agent.py b/agent.py index 0d6dbca3..f4ea7311 100644 --- a/agent.py +++ b/agent.py @@ -112,6 +112,13 @@ def __init__(self, agentID, birthday, cell, configuration): # Depressed agents have a smaller friend network due to social withdrawal self.maxFriends = math.ceil(self.maxFriends - (self.maxFriends * 0.3667)) + def addAgentToSocialNetwork(self, agent): + agentID = agent.ID + if agentID in self.socialNetwork: + return + self.socialNetwork[agentID] = {"agent": agent, "lastSeen": self.lastMoved, "timesVisited": 1, "timesReproduced": 0, + "timesTraded": 0, "timesLoaned": 0, "marginalRateOfSubstitution": 0} + def addChildToCell(self, mate, cell, childConfiguration): sugarscape = self.cell.environment.sugarscape childID = sugarscape.generateAgentID() @@ -132,13 +139,6 @@ def addChildToCell(self, mate, cell, childConfiguration): child.setMother(mate) return child - def addAgentToSocialNetwork(self, agent): - agentID = agent.ID - if agentID in self.socialNetwork: - return - self.socialNetwork[agentID] = {"agent": agent, "lastSeen": self.lastMoved, "timesVisited": 1, "timesReproduced": 0, - "timesTraded": 0, "timesLoaned": 0, "marginalRateOfSubstitution": 0} - def addLoanToAgent(self, agent, timestep, sugarPrincipal, sugarLoan, spicePrincipal, spiceLoan, duration): agentID = agent.ID if agentID not in self.socialNetwork: @@ -219,6 +219,26 @@ def checkDiseaseImmunity(self, disease): else: return False + def checkInfectedArea(self, targetCell): + infectedAgentArea = False + checkAgents = targetCell.findNeighborAgents() + if targetCell.isOccupied() == True: + checkAgents.append(targetCell.agent) + for agent in checkAgents: + if agent.isSick() == True: + infectedAgentArea = True + return infectedAgentArea + + def checkTribeArea(self, targetCell): + sameTribe = False + checkAgents = targetCell.findNeighborAgents() + if targetCell.isOccupied() == True: + checkAgents.append(targetCell.agent) + for agent in checkAgents: + if self.tribe == agent.tribe: + sameTribe = True + return sameTribe + def collectResourcesAtCell(self): sugarCollected = self.cell.sugar spiceCollected = self.cell.spice @@ -306,12 +326,6 @@ def doDisease(self): diseases = self.incubatingDiseases + self.symptomaticDiseases neighbor.catchDisease(diseases[random.randrange(diseaseCount)]["disease"], self) - def recoverFromDisease(self, disease): - index = self.symptomaticDiseases.index(disease) - recoveredDisease = self.symptomaticDiseases.pop(index) - self.recoveredDiseases.append(recoveredDisease) - self.updateDiseaseEffects(recoveredDisease["disease"]) - def doInheritance(self): if self.inheritancePolicy == "none": return @@ -654,7 +668,9 @@ def findBestCell(self): prey = cell.agent if cell.isOccupied() and self.isNeighborValidPrey(prey) == False: continue - if self.isPreyInfected(prey) == True: + if self.isSick() == False and self.checkInfectedArea(cell) == True: + continue + if self.isSick() == True and self.checkTribeArea(cell) == True: continue preyTribe = prey.tribe if prey != None else "empty" preySugar = prey.sugar if prey != None else 0 @@ -1256,6 +1272,12 @@ def printEthicalCellScores(self, cells): print(f"Ethical cell {i + 1}/{len(cells)}: {cellString}") i += 1 + def recoverFromDisease(self, disease): + index = self.symptomaticDiseases.index(disease) + recoveredDisease = self.symptomaticDiseases.pop(index) + self.recoveredDiseases.append(recoveredDisease) + self.updateDiseaseEffects(recoveredDisease["disease"]) + def removeDebt(self, loan): for debtor in self.socialNetwork["debtors"]: if debtor == loan: From 90c30c873a22eb8be0e4d649aa925f7b5d6e0dcb Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Fri, 23 Aug 2024 15:51:08 -0700 Subject: [PATCH 34/46] Adds 'diseases' label to agent data gui --- gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui.py b/gui.py index f35348e0..ea4eda4c 100644 --- a/gui.py +++ b/gui.py @@ -115,7 +115,7 @@ def configureButtons(self, window): statsLabel = tkinter.Label(window, text="Timestep: - | Population: - | Metabolism: - | Movement: - | Vision: - | Gini: - | Trade Price: - | Trade Volume: -", font="Roboto 10", justify=tkinter.CENTER) statsLabel.grid(row=1, column=0, columnspan=self.menuTrayColumns, sticky="nsew") - cellLabel = tkinter.Label(window, text="Cell: - | Sugar: - | Spice: - | Pollution: - | Season: -\nAgent: - | Age: - | Vision: - | Movement: - | Sugar: - | Spice: - | Metabolism: - | Decision Model: -", font="Roboto 10", justify=tkinter.CENTER) + cellLabel = tkinter.Label(window, text="Cell: - | Sugar: - | Spice: - | Pollution: - | Season: -\nAgent: - | Age: - | Vision: - | Movement: - | Sugar: - | Spice: - | Metabolism: - | Decision Model: - | Diseases: -", font="Roboto 10", justify=tkinter.CENTER) cellLabel.grid(row=2, column=0, columnspan=self.menuTrayColumns, sticky="nsew") self.widgets["playButton"] = playButton From bb5b7c15ef21993f7719f0602834e89b6a7d48b7 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Fri, 23 Aug 2024 20:29:50 -0700 Subject: [PATCH 35/46] Modifies default protection and transmission values --- README | 4 ++-- agent.py | 4 ++++ sugarscape.py | 6 +++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README b/README index 502d7fd9..bc30a52d 100644 --- a/README +++ b/README @@ -162,7 +162,7 @@ agentDepressionPercentage: float agentDiseaseProtectionChance: [float, float] Sets the chance an agent can get infected by another agent. Note: a value of 0.0 mean no protection, while a value of 1.0 means complete protection - Default: [0.0, 0.0] + Default: [0.0, 1.0] agentFemaleInfertilityAge: [int, int] Set the timestep age at which female agents become infertile. @@ -336,7 +336,7 @@ diseaseTagStringLength: [int, int] diseaseTransmissionChance: [float, float] Set the chance a disease can transmit between agents. - Default: [0.0, 0.0] + Default: [0.0, 1.0] diseaseVisionPenalty: [int, int] Set the impact a disease will have on an agent's vision. diff --git a/agent.py b/agent.py index eea566d6..970a744d 100644 --- a/agent.py +++ b/agent.py @@ -225,6 +225,8 @@ def checkInfectedArea(self, targetCell): if targetCell.isOccupied() == True: checkAgents.append(targetCell.agent) for agent in checkAgents: + if agent == self: + continue if agent.isSick() == True: infectedAgentArea = True return infectedAgentArea @@ -235,6 +237,8 @@ def checkTribeArea(self, targetCell): if targetCell.isOccupied() == True: checkAgents.append(targetCell.agent) for agent in checkAgents: + if agent == self: + continue if self.tribe == agent.tribe: sameTribe = True return sameTribe diff --git a/sugarscape.py b/sugarscape.py index 9e84529c..b05ee453 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -83,7 +83,7 @@ def __init__(self, configuration): experimentalGroupKey = self.experimentalGroup + key[0].upper() + key[1:] groupRuntimeStats[controlGroupKey] = 0 groupRuntimeStats[experimentalGroupKey] = 0 - self.runtimeStats.update(groupRuntimeStats) + self.runtimeStats.update(groupRuntimeStats) def addAgent(self, agent): self.bornAgents.append(agent) @@ -1328,7 +1328,7 @@ def verifyConfiguration(configuration): "agentDecisionModelLookaheadFactor": [0], "agentDecisionModelTribalFactor": [-1, -1], "agentDepressionPercentage": 0, - "agentDiseaseProtectionChance": [0.0, 0.0], + "agentDiseaseProtectionChance": [0.0, 1.0], "agentFemaleInfertilityAge": [0, 0], "agentFemaleFertilityAge": [0, 0], "agentFertilityFactor": [0, 0], @@ -1367,7 +1367,7 @@ def verifyConfiguration(configuration): "diseaseStartTimeframe": [0, 0], "diseaseSugarMetabolismPenalty": [0, 0], "diseaseTagStringLength": [0, 0], - "diseaseTransmissionChance": [0.0, 0.0], + "diseaseTransmissionChance": [0.0, 1.0], "diseaseVisionPenalty": [0, 0], "environmentEquator": -1, "environmentHeight": 50, From 212d3d9dca7dca7b063282c558981c29eee9b0a3 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Sat, 24 Aug 2024 15:41:28 -0700 Subject: [PATCH 36/46] Fixes merge with master --- agent.py | 19 +++++++++++++++++++ sugarscape.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/agent.py b/agent.py index 5716b3b1..f089a911 100644 --- a/agent.py +++ b/agent.py @@ -153,6 +153,25 @@ def addLoanToAgent(self, agent, timestep, sugarPrincipal, sugarLoan, spicePrinci agent.sugar = agent.sugar + sugarPrincipal agent.spice = agent.spice + spicePrincipal + def canCatchDisease(self, disease, infector=None): + if self.diseaseProtectionChance == 1: + return False + if self.checkDiseaseImmunity(disease) == True: + return False + if self.diseaseProtectionChance == 0 or infector == None: + return True + diseaseID = disease.ID + combinedDiseases = self.incubatingDiseases + self.symptomaticDiseases + for currDisease in combinedDiseases: + currDiseaseID = currDisease["disease"].ID + if diseaseID == currDiseaseID: + return False + randomTransmission = random.random() + randomProtection = random.random() + if randomTransmission > disease.transmissionChance and randomProtection <= self.diseaseProtectionChance: + return False + return True + def canReachCell(self, cell): if cell == self.cell or cell in self.cellsInRange: return True diff --git a/sugarscape.py b/sugarscape.py index adeabded..27b6534c 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -576,7 +576,7 @@ def randomizeAgentEndowments(self, numAgents): "decisionModelFactor": {"endowments": [], "curr": decisionModelFactor[0], "min": decisionModelFactor[0], "max": decisionModelFactor[1]}, "decisionModelLookaheadDiscount": {"endowments": [], "curr": decisionModelLookaheadDiscount[0], "min": decisionModelLookaheadDiscount[0], "max": decisionModelLookaheadDiscount[1]}, "decisionModelTribalFactor": {"endowments": [], "curr": decisionModelTribalFactor[0], "min": decisionModelTribalFactor[0], "max": decisionModelTribalFactor[1]}, - "diseaseProtectionChance": {"endowments": [], "curr": diseaseProtectionChance[0], "min": diseaseProtectionChance[0], "max": diseaseProtectionChance[1]} + "diseaseProtectionChance": {"endowments": [], "curr": diseaseProtectionChance[0], "min": diseaseProtectionChance[0], "max": diseaseProtectionChance[1]}, "femaleFertilityAge": {"endowments": [], "curr": femaleFertilityAge[0], "min": femaleFertilityAge[0], "max": femaleFertilityAge[1]}, "femaleInfertilityAge": {"endowments": [], "curr": femaleInfertilityAge[0], "min": femaleInfertilityAge[0], "max": femaleInfertilityAge[1]}, "fertilityFactor": {"endowments": [], "curr": fertilityFactor[0], "min": fertilityFactor[0], "max": fertilityFactor[1]}, From 1e382d67805b2499a19cc7e0edb8a36f2d907633 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Fri, 6 Sep 2024 15:49:13 -0700 Subject: [PATCH 37/46] Fixes merge that removed an agent function --- agent.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/agent.py b/agent.py index f089a911..ac6a7aca 100644 --- a/agent.py +++ b/agent.py @@ -112,6 +112,15 @@ def __init__(self, agentID, birthday, cell, configuration): # Depressed agents have a smaller friend network due to social withdrawal self.maxFriends = math.ceil(self.maxFriends - (self.maxFriends * 0.3667)) + def addLoanFromAgent(self, agent, timestep, sugarLoan, spiceLoan, duration): + agentID = agent.ID + if agentID not in self.socialNetwork: + self.addAgentToSocialNetwork(agent) + self.socialNetwork[agentID]["timesLoaned"] += 1 + loan = {"creditor": agentID, "debtor": self.ID, "sugarLoan": sugarLoan, "spiceLoan": spiceLoan, "loanDuration": duration, + "loanOrigin": timestep} + self.socialNetwork["creditors"].append(loan) + def addAgentToSocialNetwork(self, agent): agentID = agent.ID if agentID in self.socialNetwork: From 8f51bf50f382a7e3eac76c245834b1b95e133a9f Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Wed, 25 Sep 2024 10:43:12 -0700 Subject: [PATCH 38/46] Fixes randomizeDiseaseEndowments duplication --- sugarscape.py | 108 -------------------------------------------------- 1 file changed, 108 deletions(-) diff --git a/sugarscape.py b/sugarscape.py index 27b6534c..a62c787b 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -699,114 +699,6 @@ def randomizeAgentEndowments(self, numAgents): endowments.append(agentEndowment) return endowments - def randomizeDiseaseEndowments(self, numDiseases): - configs = self.configuration - sugarMetabolismPenalty = configs["diseaseSugarMetabolismPenalty"] - spiceMetabolismPenalty = configs["diseaseSpiceMetabolismPenalty"] - movementPenalty = configs["diseaseMovementPenalty"] - visionPenalty = configs["diseaseVisionPenalty"] - fertilityPenalty = configs["diseaseFertilityPenalty"] - aggressionPenalty = configs["diseaseAggressionPenalty"] - tagLengths = configs["diseaseTagStringLength"] - - minSugarMetabolismPenalty = sugarMetabolismPenalty[0] - minSpiceMetabolismPenalty = spiceMetabolismPenalty[0] - minMovementPenalty = movementPenalty[0] - minVisionPenalty = visionPenalty[0] - minFertilityPenalty = fertilityPenalty[0] - minAggressionPenalty = aggressionPenalty[0] - minTagLength = tagLengths[0] - - maxSugarMetabolismPenalty = sugarMetabolismPenalty[1] - maxSpiceMetabolismPenalty = spiceMetabolismPenalty[1] - maxMovementPenalty = movementPenalty[1] - maxVisionPenalty = visionPenalty[1] - maxFertilityPenalty = fertilityPenalty[1] - maxAggressionPenalty = aggressionPenalty[1] - maxTagLength = tagLengths[1] - - endowments = [] - sugarMetabolismPenalties = [] - spiceMetabolismPenalties = [] - movementPenalties = [] - visionPenalties = [] - fertilityPenalties = [] - aggressionPenalties = [] - diseaseTags = [] - - currSugarMetabolismPenalty = minSugarMetabolismPenalty - currSpiceMetabolismPenalty = minSpiceMetabolismPenalty - currMovementPenalty = minMovementPenalty - currVisionPenalty = minVisionPenalty - currFertilityPenalty = minFertilityPenalty - currAggressionPenalty = minAggressionPenalty - currTagLength = minTagLength - - for i in range(numDiseases): - sugarMetabolismPenalties.append(currSugarMetabolismPenalty) - spiceMetabolismPenalties.append(currSpiceMetabolismPenalty) - movementPenalties.append(currMovementPenalty) - visionPenalties.append(currVisionPenalty) - fertilityPenalties.append(currFertilityPenalty) - aggressionPenalties.append(currAggressionPenalty) - diseaseTags.append([random.randrange(2) for i in range(currTagLength)]) - - currSugarMetabolismPenalty += 1 - currSpiceMetabolismPenalty += 1 - currMovementPenalty += 1 - currVisionPenalty += 1 - currFertilityPenalty += 1 - currAggressionPenalty += 1 - currTagLength += 1 - - if currSugarMetabolismPenalty > maxSugarMetabolismPenalty: - currSugarMetabolismPenalty = minSugarMetabolismPenalty - if currSpiceMetabolismPenalty > maxSpiceMetabolismPenalty: - currSpiceMetabolismPenalty = minSpiceMetabolismPenalty - if currMovementPenalty > maxMovementPenalty: - currMovementPenalty = minMovementPenalty - if currVisionPenalty > maxVisionPenalty: - currVisionPenalty = minVisionPenalty - if currFertilityPenalty > maxFertilityPenalty: - currFertilityPenalty = minFertilityPenalty - if currAggressionPenalty > maxAggressionPenalty: - currAggressionPenalty = minAggressionPenalty - if currTagLength > maxTagLength: - currTagLength = minTagLength - - randomDiseaseEndowment = {"sugarMetabolismPenalties": sugarMetabolismPenalties, - "spiceMetabolismPenalties": spiceMetabolismPenalties, - "movementPenalties": movementPenalties, - "visionPenalties": visionPenalties, - "fertilityPenalties": fertilityPenalties, - "aggressionPenalties": aggressionPenalties, - "diseaseTags": diseaseTags} - - # Map configuration to a random number via hash to make random number generation independent of iteration order - if (self.diseaseConfigHashes == None): - self.diseaseConfigHashes = {} - for penalty in randomDiseaseEndowment: - hashed = hashlib.md5(penalty.encode()) - self.diseaseConfigHashes[penalty] = int(hashed.hexdigest(), 16) - - # Keep state of random numbers to allow extending agent endowments without altering original random object state - randomNumberReset = random.getstate() - for endowment in randomDiseaseEndowment.keys(): - random.seed(self.diseaseConfigHashes[endowment] + self.timestep) - random.shuffle(randomDiseaseEndowment[endowment]) - random.setstate(randomNumberReset) - - for i in range(numDiseases): - diseaseEndowment = {"aggressionPenalty": aggressionPenalties.pop(), - "fertilityPenalty": fertilityPenalties.pop(), - "movementPenalty": movementPenalties.pop(), - "sugarMetabolismPenalty": sugarMetabolismPenalties.pop(), - "spiceMetabolismPenalty": spiceMetabolismPenalties.pop(), - "tags": diseaseTags.pop(), - "visionPenalty": visionPenalties.pop()} - endowments.append(diseaseEndowment) - return endowments - def removeDeadAgents(self): deadAgents = [] for agent in self.agents: From 6a9332da7f0d079089b9e2b9f6a5776ce740823c Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Thu, 26 Sep 2024 22:15:33 -0700 Subject: [PATCH 39/46] Comments out agent quarantine behavior --- agent.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/agent.py b/agent.py index ac6a7aca..4f0ea6e6 100644 --- a/agent.py +++ b/agent.py @@ -670,12 +670,14 @@ def findBestCell(self): for cell, travelDistance in cellsInRange: # Avoid attacking agents ineligible to attack prey = cell.agent + """ if cell.isOccupied() and self.isNeighborValidPrey(prey) == False: continue if self.isSick() == False and self.checkInfectedArea(cell) == True: continue if self.isSick() == True and self.checkTribeArea(cell) == True: continue + """ preyTribe = prey.tribe if prey != None else "empty" preySugar = prey.sugar if prey != None else 0 preySpice = prey.spice if prey != None else 0 From 78b435ac7798201d0a554a35426a07589843f038 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Tue, 1 Oct 2024 22:20:13 -0700 Subject: [PATCH 40/46] Removes agent quarantine feature --- agent.py | 33 +-------------------------------- 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/agent.py b/agent.py index 4f0ea6e6..7fee6f6e 100644 --- a/agent.py +++ b/agent.py @@ -79,7 +79,6 @@ def __init__(self, agentID, birthday, cell, configuration): self.neighborhood = [] self.neighbors = [] self.nice = 0 - self.recoveredDiseases = [] self.socialHappiness = 0 self.socialNetwork = {"father": None, "mother": None, "children": [], "friends": [], "creditors": [], "debtors": [], "mates": []} self.spiceMeanIncome = 1 @@ -219,30 +218,6 @@ def checkDiseaseImmunity(self, disease): else: return False - def checkInfectedArea(self, targetCell): - infectedAgentArea = False - checkAgents = targetCell.findNeighborAgents() - if targetCell.isOccupied() == True: - checkAgents.append(targetCell.agent) - for agent in checkAgents: - if agent == self: - continue - if agent.isSick() == True: - infectedAgentArea = True - return infectedAgentArea - - def checkTribeArea(self, targetCell): - sameTribe = False - checkAgents = targetCell.findNeighborAgents() - if targetCell.isOccupied() == True: - checkAgents.append(targetCell.agent) - for agent in checkAgents: - if agent == self: - continue - if self.tribe == agent.tribe: - sameTribe = True - return sameTribe - def collectResourcesAtCell(self): sugarCollected = self.cell.sugar spiceCollected = self.cell.spice @@ -670,14 +645,9 @@ def findBestCell(self): for cell, travelDistance in cellsInRange: # Avoid attacking agents ineligible to attack prey = cell.agent - """ if cell.isOccupied() and self.isNeighborValidPrey(prey) == False: continue - if self.isSick() == False and self.checkInfectedArea(cell) == True: - continue - if self.isSick() == True and self.checkTribeArea(cell) == True: - continue - """ + preyTribe = prey.tribe if prey != None else "empty" preySugar = prey.sugar if prey != None else 0 preySpice = prey.spice if prey != None else 0 @@ -1305,7 +1275,6 @@ def printEthicalCellScores(self, cells): def recoverFromDisease(self, disease): index = self.symptomaticDiseases.index(disease) recoveredDisease = self.symptomaticDiseases.pop(index) - self.recoveredDiseases.append(recoveredDisease) self.updateDiseaseEffects(recoveredDisease["disease"]) def removeDebt(self, loan): From 1bd7b17bdc0893bc0ef7723d698f14c5040d9078 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Wed, 2 Oct 2024 11:06:44 -0700 Subject: [PATCH 41/46] Modifies disease prevalence metric to include incubating diseases --- sugarscape.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sugarscape.py b/sugarscape.py index ca4b36b2..478686d1 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -255,7 +255,8 @@ def configureEnvironment(self, maxSugar, maxSpice, sugarPeaks, spicePeaks): def countInfectedAgents(self, disease): totalInfected = 0 for agent in self.agents: - for agentDisease in agent.symptomaticDiseases: + combinedDiseases = agent.incubatingDiseases + agent.symptomaticDiseases + for agentDisease in combinedDiseases: if disease == agentDisease["disease"]: totalInfected += 1 return totalInfected From b35399c39a3c6fd0c2552c6197e6284f7f1b691a Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Mon, 14 Oct 2024 12:24:55 -0700 Subject: [PATCH 42/46] Removes isPreyInfected function --- agent.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/agent.py b/agent.py index 7fee6f6e..d81ba2ca 100644 --- a/agent.py +++ b/agent.py @@ -618,14 +618,6 @@ def doUniversalIncome(self): def findAggression(self): return max(0, self.aggressionFactor + self.aggressionFactorModifier) - def isPreyInfected(self, prey): - if prey == None: - return False - if len(prey.symptomaticDiseases) > 0 and len(self.symptomaticDiseases) == 0: - return True - if len(self.symptomaticDiseases) > 0 and len(prey.symptomaticDiseases) == 0: - return True - def findBestCell(self): self.findNeighborhood() if len(self.cellsInRange) == 0: From 2feb3ee79fc9974f17a25b0dedd9cb59d3dba1b4 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Fri, 8 Nov 2024 13:18:34 -0800 Subject: [PATCH 43/46] Adds agent.recoveredDiseases list --- agent.py | 9 ++++++--- disease.py | 2 +- sugarscape.py | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/agent.py b/agent.py index d81ba2ca..d59b888f 100644 --- a/agent.py +++ b/agent.py @@ -79,6 +79,7 @@ def __init__(self, agentID, birthday, cell, configuration): self.neighborhood = [] self.neighbors = [] self.nice = 0 + self.recoveredDiseases = [] self.socialHappiness = 0 self.socialNetwork = {"father": None, "mother": None, "children": [], "friends": [], "creditors": [], "debtors": [], "mates": []} self.spiceMeanIncome = 1 @@ -206,7 +207,7 @@ def catchDisease(self, disease, infector=None): else: self.startingDiseases += 1 disease.startingInfectedAgents += 1 - disease.infected += 1 + disease.newInfection += 1 self.incubatingDiseases.append(caughtDisease) self.showSymptoms() self.findCellsInRange() @@ -1266,8 +1267,10 @@ def printEthicalCellScores(self, cells): def recoverFromDisease(self, disease): index = self.symptomaticDiseases.index(disease) - recoveredDisease = self.symptomaticDiseases.pop(index) - self.updateDiseaseEffects(recoveredDisease["disease"]) + recoveredDiseaseRecord = self.symptomaticDiseases.pop(index) + recoveredDisease = recoveredDiseaseRecord["disease"] + self.recoveredDiseases.append({"disease": recoveredDisease, "timestep": self.timestep}) + self.updateDiseaseEffects(recoveredDisease) def removeDebt(self, loan): for debtor in self.socialNetwork["debtors"]: diff --git a/disease.py b/disease.py index 524f8763..ed774099 100644 --- a/disease.py +++ b/disease.py @@ -15,7 +15,7 @@ def __init__(self, diseaseID, configuration): self.transmissionChance = configuration["transmissionChance"] self.visionPenalty = configuration["visionPenalty"] - self.infected = 0 + self.newInfection = 0 self.infectors = set() self.startingInfectedAgents = 0 diff --git a/sugarscape.py b/sugarscape.py index 478686d1..25a55989 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -1048,7 +1048,7 @@ def updateRuntimeStatsPerGroup(self, group=None, notInGroup=False): r = 0.0 if numAgents > 0: infectors = len(disease.infectors) - incidence = disease.infected + incidence = disease.newInfected prevalence = self.countInfectedAgents(disease) r = 0.0 if infectors > 0: From df9443b85db4584a25e9b7f770d84daa549fd7aa Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Wed, 13 Nov 2024 11:38:11 -0800 Subject: [PATCH 44/46] Modifies infected variable to newInfections --- agent.py | 2 +- disease.py | 4 ++-- sugarscape.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/agent.py b/agent.py index d59b888f..368fa134 100644 --- a/agent.py +++ b/agent.py @@ -207,7 +207,7 @@ def catchDisease(self, disease, infector=None): else: self.startingDiseases += 1 disease.startingInfectedAgents += 1 - disease.newInfection += 1 + disease.newInfections += 1 self.incubatingDiseases.append(caughtDisease) self.showSymptoms() self.findCellsInRange() diff --git a/disease.py b/disease.py index ed774099..b41cf1a1 100644 --- a/disease.py +++ b/disease.py @@ -15,12 +15,12 @@ def __init__(self, diseaseID, configuration): self.transmissionChance = configuration["transmissionChance"] self.visionPenalty = configuration["visionPenalty"] - self.newInfection = 0 + self.newInfections = 0 self.infectors = set() self.startingInfectedAgents = 0 def resetRStats(self): - self.infected = 0 + self.newInfections = 0 self.infectors = set() def __str__(self): diff --git a/sugarscape.py b/sugarscape.py index 25a55989..6f1f6c8e 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -1048,7 +1048,7 @@ def updateRuntimeStatsPerGroup(self, group=None, notInGroup=False): r = 0.0 if numAgents > 0: infectors = len(disease.infectors) - incidence = disease.newInfected + incidence = disease.newInfections prevalence = self.countInfectedAgents(disease) r = 0.0 if infectors > 0: From 8fca89e1f40110f2f9ed29316d66b2b08ecaec37 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Fri, 15 Nov 2024 15:50:40 -0800 Subject: [PATCH 45/46] Adds disease groups to agents --- agent.py | 64 +++++++++++++++++++++++++++++---------- sugarscape.py | 84 +++++++++++++++++++++++++++------------------------ 2 files changed, 93 insertions(+), 55 deletions(-) diff --git a/agent.py b/agent.py index 368fa134..6b6d8ad0 100644 --- a/agent.py +++ b/agent.py @@ -66,7 +66,6 @@ def __init__(self, agentID, birthday, cell, configuration): self.fertilityFactorModifier = 0 self.happiness = 0 self.healthHappiness = 0 - self.incubatingDiseases = [] self.lastDoneCombat = -1 self.lastMoved = -1 self.lastReproduced = -1 @@ -79,7 +78,6 @@ def __init__(self, agentID, birthday, cell, configuration): self.neighborhood = [] self.neighbors = [] self.nice = 0 - self.recoveredDiseases = [] self.socialHappiness = 0 self.socialNetwork = {"father": None, "mother": None, "children": [], "friends": [], "creditors": [], "debtors": [], "mates": []} self.spiceMeanIncome = 1 @@ -89,7 +87,6 @@ def __init__(self, agentID, birthday, cell, configuration): self.sugarMeanIncome = 1 self.sugarMetabolismModifier = 0 self.sugarPrice = 0 - self.symptomaticDiseases = [] self.tagZeroes = 0 self.timestep = birthday self.tradeVolume = 0 @@ -97,6 +94,12 @@ def __init__(self, agentID, birthday, cell, configuration): self.visionModifier = 0 self.wealthHappiness = 0 + self.immuneDiseases = [] # disease only + self.susceptibleDiseases = [] # disease only + self.incubatingDiseases = [] # disease record + self.symptomaticDiseases = [] # disease record + self.recoveredDiseases = [] # different disease record + # Change metrics for depressed agents if self.depressionFactor == 1: self.depressed = True @@ -166,15 +169,23 @@ def canCatchDisease(self, disease, infector=None): if self.diseaseProtectionChance == 1: return False if self.checkDiseaseImmunity(disease) == True: + if disease in self.susceptibleDiseases: + # susceptible to immune + self.updateDiseaseGroups(disease, disease, self.susceptibleDiseases, self.immuneDiseases) return False - if self.diseaseProtectionChance == 0 or infector == None: - return True + else: + # check if disease is in immune[] - this is to cover the case that the neighbor hasn't had their turn to doTimestep() + if disease in self.immuneDiseases: + # immune to susceptible + self.updateDiseaseGroups(disease, disease, self.immuneDiseases, self.susceptibleDiseases) diseaseID = disease.ID combinedDiseases = self.incubatingDiseases + self.symptomaticDiseases for currDisease in combinedDiseases: currDiseaseID = currDisease["disease"].ID if diseaseID == currDiseaseID: return False + if self.diseaseProtectionChance == 0 or infector == None: + return True randomTransmission = random.random() randomProtection = random.random() if randomTransmission > disease.transmissionChance and randomProtection <= self.diseaseProtectionChance: @@ -208,7 +219,8 @@ def catchDisease(self, disease, infector=None): self.startingDiseases += 1 disease.startingInfectedAgents += 1 disease.newInfections += 1 - self.incubatingDiseases.append(caughtDisease) + # susceptible to infected + self.updateDiseaseGroups(disease, caughtDisease, self.susceptibleDiseases, self.incubatingDiseases) self.showSymptoms() self.findCellsInRange() @@ -275,6 +287,10 @@ def doDeath(self, causeOfDeath): self.symptomaticDiseases = [] def doDisease(self): + for immuneDisease in self.immuneDiseases: + if self.checkDiseaseImmunity(immuneDisease) == False: + # immune to susceptible + self.updateDiseaseGroups(immuneDisease, immuneDisease, self.immuneDiseases, self.susceptibleDiseases) self.showSymptoms() if self.age >= self.infertilityAge: self.diseaseProtectionChance = round(self.diseaseProtectionChance - 0.01, 2) @@ -292,6 +308,9 @@ def doDisease(self): break if diseaseTags == immuneResponse: self.recoverFromDisease(diseaseRecord) + for recoveredDisease in self.recoveredDiseases: + if self.checkDiseaseImmunity(recoveredDisease["disease"]) == False and recoveredDisease["disease"] not in self.susceptibleDiseases: + self.susceptibleDiseases.append(recoveredDisease["disease"]) diseaseCount = len(self.symptomaticDiseases) if diseaseCount == 0: return @@ -423,7 +442,7 @@ def doMetabolism(self): elif (self.sugar <= 0 and sugarMetabolism > 0) or (self.spice <= 0 and spiceMetabolism > 0): self.doDeath("starvation") - def doReproduction(self): + def doReproduction(self, diseasesList): # Agent marked for removal or not interested in reproduction should not reproduce if self.isAlive() == False or self.isFertile() == False: return @@ -459,6 +478,14 @@ def doReproduction(self): self.updateTimesReproducedWithAgent(neighbor, self.lastMoved) self.lastReproduced += 1 + # traced sugarscape.diseases just for this line + for potentialDisease in diseasesList: + if child.checkDiseaseImmunity(potentialDisease) == True: + child.immuneDiseases.append(potentialDisease) + + else: + child.susceptibleDiseases.append(potentialDisease) + sugarCost = self.startingSugar / (self.fertilityFactor * 2) spiceCost = self.startingSpice / (self.fertilityFactor * 2) mateSugarCost = neighbor.startingSugar / (neighbor.fertilityFactor * 2) @@ -483,7 +510,7 @@ def doTagging(self): neighbor.flipTag(position, self.tags[position]) neighbor.tribe = neighbor.findTribe() - def doTimestep(self, timestep): + def doTimestep(self, timestep, diseasesList): self.timestep = timestep # Prevent dead or already moved agent from moving if self.isAlive() == True and self.lastMoved != self.timestep: @@ -500,7 +527,7 @@ def doTimestep(self, timestep): return self.doTagging() self.doTrading() - self.doReproduction() + self.doReproduction(diseasesList) self.doLending() self.doDisease() self.doAging() @@ -1265,11 +1292,11 @@ def printEthicalCellScores(self, cells): print(f"Ethical cell {i + 1}/{len(cells)}: {cellString}") i += 1 - def recoverFromDisease(self, disease): - index = self.symptomaticDiseases.index(disease) - recoveredDiseaseRecord = self.symptomaticDiseases.pop(index) - recoveredDisease = recoveredDiseaseRecord["disease"] - self.recoveredDiseases.append({"disease": recoveredDisease, "timestep": self.timestep}) + def recoverFromDisease(self, diseaseRecord): + recoveredDisease = diseaseRecord["disease"] + recoveredDiseaseRecord = {"disease": recoveredDisease, "timestep": self.timestep} + # symptomatic to recovered + self.updateDiseaseGroups(diseaseRecord, recoveredDiseaseRecord, self.symptomaticDiseases, self.recoveredDiseases) self.updateDiseaseEffects(recoveredDisease) def removeDebt(self, loan): @@ -1297,8 +1324,8 @@ def setMother(self, mother): def showSymptoms(self): for disease in self.incubatingDiseases: if self.timestep >= disease["endIncubation"]: - diseaseIndex = self.incubatingDiseases.index(disease) - self.symptomaticDiseases.append(self.incubatingDiseases.pop(diseaseIndex)) + # incubating to symptomatic + self.updateDiseaseGroups(disease, disease, self.incubatingDiseases, self.symptomaticDiseases) self.updateDiseaseEffects(disease["disease"]) def sortCellsByWealth(self, cells): @@ -1338,6 +1365,11 @@ def updateDiseaseEffects(self, disease): self.sugarMetabolismModifier += sugarMetabolismPenalty self.visionModifier += visionPenalty + def updateDiseaseGroups(self, oldDisease, newDisease, oldGroup, newGroup): + # old disease and new disease for the case of disease records (incubating, symptomatatic, recovered) + oldGroup.remove(oldDisease) + newGroup.append(newDisease) + def updateFriends(self, neighbor): neighborID = neighbor.ID neighborHammingDistance = self.findHammingDistanceInTags(neighbor) diff --git a/sugarscape.py b/sugarscape.py index 6f1f6c8e..0926d80b 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -198,40 +198,14 @@ def configureDiseases(self, numDiseases): timestep = diseaseConfiguration["startTimestep"] self.diseases.append(newDisease) self.diseasesCount[timestep].append(newDisease) + # agents start either immune or susceptible - this part should be fine + for agent in self.agents: + if agent.checkDiseaseImmunity(newDisease) == True: + agent.immuneDiseases.append(newDisease) + else: + agent.susceptibleDiseases.append(newDisease) self.infectAgents() - def infectAgents(self): - timestep = self.timestep - if timestep > self.configuration["diseaseStartTimeframe"][1]: - return - diseases = self.diseasesCount[timestep] - if len(diseases) == 0: - return - startingDiseases = self.configuration["startingDiseasesPerAgent"] - minStartingDiseases = startingDiseases[0] - maxStartingDiseases = startingDiseases[1] - currStartingDiseases = minStartingDiseases - random.shuffle(self.agents) - random.shuffle(diseases) - for agent in self.agents: - for newDisease in diseases: - if newDisease.startingInfectedAgents == 1 and startingDiseases == [0, 0]: - continue - if agent.startingDiseases >= currStartingDiseases and startingDiseases != [0, 0]: - currStartingDiseases += 1 - break - if agent.canCatchDisease(newDisease) == False: - continue - agent.catchDisease(newDisease) - if startingDiseases == [0, 0]: - diseases.remove(newDisease) - break - if currStartingDiseases > maxStartingDiseases: - currStartingDiseases = minStartingDiseases - if startingDiseases == [0, 0] and self.timestep == self.configuration["diseaseStartTimeframe"][1] and len(diseases) > 0: - if "all" in self.debug or "sugarscape" in self.debug: - print(f"Could not place {len(diseases)} diseases.") - def configureEnvironment(self, maxSugar, maxSpice, sugarPeaks, spicePeaks): height = self.environment.height width = self.environment.width @@ -277,7 +251,7 @@ def doTimestep(self): self.infectAgents() random.shuffle(self.agents) for agent in self.agents: - agent.doTimestep(self.timestep) + agent.doTimestep(self.timestep, self.diseases) self.removeDeadAgents() self.replaceDeadAgents() self.updateRuntimeStats() @@ -314,6 +288,13 @@ def endLog(self): self.log.flush() self.log.close() + def endSimulation(self): + self.removeDeadAgents() + self.endLog() + if "all" in self.debug or "sugarscape" in self.debug: + print(str(self)) + exit(0) + def findActiveQuadrants(self): quadrants = self.configuration["environmentStartingQuadrants"] cellRange = [] @@ -374,12 +355,37 @@ def generateTribeTags(self, tribe): random.shuffle(tags) return tags - def endSimulation(self): - self.removeDeadAgents() - self.endLog() - if "all" in self.debug or "sugarscape" in self.debug: - print(str(self)) - exit(0) + def infectAgents(self): + timestep = self.timestep + if timestep > self.configuration["diseaseStartTimeframe"][1]: + return + diseases = self.diseasesCount[timestep] + if len(diseases) == 0: + return + startingDiseases = self.configuration["startingDiseasesPerAgent"] + minStartingDiseases = startingDiseases[0] + maxStartingDiseases = startingDiseases[1] + currStartingDiseases = minStartingDiseases + random.shuffle(self.agents) + random.shuffle(diseases) + for agent in self.agents: + for newDisease in diseases: + if newDisease.startingInfectedAgents == 1 and startingDiseases == [0, 0]: + continue + if agent.startingDiseases >= currStartingDiseases and startingDiseases != [0, 0]: + currStartingDiseases += 1 + break + if agent.canCatchDisease(newDisease) == False: + continue + agent.catchDisease(newDisease) + if startingDiseases == [0, 0]: + diseases.remove(newDisease) + break + if currStartingDiseases > maxStartingDiseases: + currStartingDiseases = minStartingDiseases + if startingDiseases == [0, 0] and self.timestep == self.configuration["diseaseStartTimeframe"][1] and len(diseases) > 0: + if "all" in self.debug or "sugarscape" in self.debug: + print(f"Could not place {len(diseases)} diseases.") def pauseSimulation(self): while self.run == False: From 32bb035d070cf6d0be327679fab4e62d0e7383e5 Mon Sep 17 00:00:00 2001 From: Anna Muller Date: Mon, 18 Nov 2024 10:25:07 -0800 Subject: [PATCH 46/46] Updated log with new metrics --- sugarscape.py | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/sugarscape.py b/sugarscape.py index 0926d80b..cb93f12c 100644 --- a/sugarscape.py +++ b/sugarscape.py @@ -69,11 +69,13 @@ def __init__(self, configuration): "agentsBorn": 0, "agentStarvationDeaths": 0, "agentDiseaseDeaths": 0, "environmentWealthCreated": 0, "agentWealthTotal": 0, "environmentWealthTotal": 0, "agentWealthCollected": 0, "agentWealthBurnRate": 0, "agentMeanTimeToLive": 0, "agentTotalMetabolism": 0, "agentCombatDeaths": 0, "agentAgingDeaths": 0, "agentDeaths": 0, "largestTribe": 0, "largestTribeSize": 0, - "remainingTribes": self.configuration["environmentMaxTribes"], "sickAgents": 0} + "remainingTribes": self.configuration["environmentMaxTribes"], "sickAgents": 0, "carryingCapacity": 0} diseaseStats = {} for disease in self.diseases: - diseaseStats[f"disease{disease.ID}Incidence"] = 0 - diseaseStats[f"disease{disease.ID}Prevalence"] = 0 + diseaseStats[f"disease{disease.ID}Immune"] = 0 + diseaseStats[f"disease{disease.ID}Susceptible"] = 0 + diseaseStats[f"disease{disease.ID}Infected"] = 0 + diseaseStats[f"disease{disease.ID}Recovered"] = 0 diseaseStats[f"disease{disease.ID}RValue"] = 0.0 self.runtimeStats.update(diseaseStats) self.graphStats = {"ageBins": [], "sugarBins": [], "spiceBins": [], "lorenzCurvePoints": [], "meanTribeTags": [], @@ -226,15 +228,6 @@ def configureEnvironment(self, maxSugar, maxSpice, sugarPeaks, spicePeaks): self.environment.findCellNeighbors() self.environment.findCellRanges() - def countInfectedAgents(self, disease): - totalInfected = 0 - for agent in self.agents: - combinedDiseases = agent.incubatingDiseases + agent.symptomaticDiseases - for agentDisease in combinedDiseases: - if disease == agentDisease["disease"]: - totalInfected += 1 - return totalInfected - def doTimestep(self): if self.timestep >= self.maxTimestep: self.toggleEnd() @@ -907,6 +900,11 @@ def updateRuntimeStatsPerGroup(self, group=None, notInGroup=False): agentsReplaced = 0 tribes = {} + immuneDiseaseStats = [0 for i in range(len(self.diseases))] + susceptibleDiseaseStats = [0 for i in range(len(self.diseases))] + infectedDiseaseStats = [0 for i in range(len(self.diseases))] + recoveredDiseaseStats = [0 for i in range(len(self.diseases))] + for agent in self.agents: if group != None and agent.isInGroup(group, notInGroup) == False: continue @@ -946,6 +944,17 @@ def updateRuntimeStatsPerGroup(self, group=None, notInGroup=False): tribes[agent.tribe] += 1 numAgents += 1 + for immuneDisease in agent.immuneDiseases: + immuneDiseaseStats[immuneDisease.ID] += 1 + for susceptibleDisease in agent.susceptibleDiseases: + susceptibleDiseaseStats[susceptibleDisease.ID] += 1 + for incubatingDisease in agent.incubatingDiseases: + infectedDiseaseStats[incubatingDisease["disease"].ID] += 1 + for symptomaticDisease in agent.symptomaticDiseases: + infectedDiseaseStats[symptomaticDisease["disease"].ID] += 1 + for recoveredDisease in agent.recoveredDiseases: + recoveredDiseaseStats[recoveredDisease["disease"].ID] += 1 + if numAgents > 0: agentMeanTimeToLive = round(agentMeanTimeToLive / numAgents, 2) agentWealthBurnRate = round(agentWealthBurnRate / numAgents, 2) @@ -1049,19 +1058,18 @@ def updateRuntimeStatsPerGroup(self, group=None, notInGroup=False): self.runtimeStats[key] = runtimeStats[key] for disease in self.diseases: - incidence = 0 - prevalence = 0 r = 0.0 if numAgents > 0: infectors = len(disease.infectors) incidence = disease.newInfections - prevalence = self.countInfectedAgents(disease) r = 0.0 if infectors > 0: r = round(float(incidence / infectors), 2) - self.runtimeStats[f"disease{disease.ID}Incidence"] = incidence - self.runtimeStats[f"disease{disease.ID}Prevalence"] = prevalence self.runtimeStats[f"disease{disease.ID}RValue"] = r + self.runtimeStats[f"disease{disease.ID}Immune"] = immuneDiseaseStats[disease.ID] + self.runtimeStats[f"disease{disease.ID}Susceptible"] = susceptibleDiseaseStats[disease.ID] + self.runtimeStats[f"disease{disease.ID}Infected"] = infectedDiseaseStats[disease.ID] + self.runtimeStats[f"disease{disease.ID}Recovered"] = recoveredDiseaseStats[disease.ID] def writeToLog(self): if self.log == None: