aegis_sim.bioreactor
1import numpy as np 2import logging 3 4from aegis_sim import variables 5from aegis_sim import submodels 6from aegis_sim.constants import VALID_CAUSES_OF_DEATH 7from aegis_sim.dataclasses.population import Population 8from aegis_sim.recording import recordingmanager 9from aegis_sim.parameterization import parametermanager 10from aegis_sim.submodels.resources.starvation import starvation 11from aegis_sim.submodels.resources.resources import resources 12 13 14class Bioreactor: 15 def __init__(self, population: Population): 16 self.eggs: Population = None 17 self.population: Population = population 18 19 ############## 20 # MAIN LOGIC # 21 ############## 22 23 def run_step(self): 24 """Perform one step of simulation.""" 25 26 # If extinct (no living individuals nor eggs left), do nothing 27 if len(self) == 0: 28 logging.debug("Population went extinct.") 29 recordingmanager.summaryrecorder.extinct = True 30 return 31 # Mortality sources 32 self.mortalities() 33 resources.replenish() 34 35 recordingmanager.popsizerecorder.write_before_reproduction(self.population) 36 self.growth() # size increase 37 self.reproduction() # reproduction 38 self.age() # age increment and potentially death 39 self.hatch() 40 submodels.architect.envdrift.evolve(step=variables.steps) 41 42 # Record data 43 recordingmanager.popsizerecorder.write_after_reproduction(self.population) 44 recordingmanager.popsizerecorder.write_egg_num_after_reproduction(self.eggs) 45 recordingmanager.envdriftmaprecorder.write(step=variables.steps) 46 recordingmanager.flushrecorder.collect("additive_age_structure", self.population.ages) # population census 47 recordingmanager.picklerecorder.write(self.population) 48 recordingmanager.featherrecorder.write(self.population) 49 recordingmanager.guirecorder.record(self.population) 50 recordingmanager.flushrecorder.flush() 51 recordingmanager.popgenstatsrecorder.write( 52 self.population.genomes, self.population.phenotypes.extract(ages=self.population.ages, trait_name="muta") 53 ) # TODO defers calculation of mutation rates; hacky 54 recordingmanager.summaryrecorder.record_memuse() 55 recordingmanager.terecorder.record(self.population.ages, "alive") 56 recordingmanager.checkpointrecorder.write(self.population, self.eggs) 57 58 ############### 59 # STEP LOGIC # 60 ############### 61 62 def mortalities(self): 63 for source in parametermanager.parameters.MORTALITY_ORDER: 64 if source == "intrinsic": 65 self.mortality_intrinsic() 66 elif source == "abiotic": 67 self.mortality_abiotic() 68 elif source == "infection": 69 self.mortality_infection() 70 elif source == "predation": 71 self.mortality_predation() 72 elif source == "starvation": 73 self.mortality_starvation() 74 else: 75 raise ValueError(f"Invalid source of mortality '{source}'") 76 77 def mortality_intrinsic(self): 78 probs_surv = self.population.phenotypes.extract(ages=self.population.ages, trait_name="surv") 79 age_hazard = submodels.frailty.modify(hazard=1 - probs_surv, ages=self.population.ages) 80 mask_kill = variables.rng.random(len(probs_surv)) < age_hazard 81 self._kill(mask_kill=mask_kill, causeofdeath="intrinsic") 82 83 def mortality_abiotic(self): 84 hazard = submodels.abiotic(variables.steps) 85 age_hazard = submodels.frailty.modify(hazard=hazard, ages=self.population.ages) 86 mask_kill = variables.rng.random(len(self.population)) < age_hazard 87 self._kill(mask_kill=mask_kill, causeofdeath="abiotic") 88 89 def mortality_infection(self): 90 submodels.infection(self.population) 91 # TODO add age hazard 92 mask_kill = self.population.infection == -1 93 self._kill(mask_kill=mask_kill, causeofdeath="infection") 94 95 def mortality_predation(self): 96 probs_kill = submodels.predation(len(self)) 97 # TODO add age hazard 98 mask_kill = variables.rng.random(len(self)) < probs_kill 99 self._kill(mask_kill=mask_kill, causeofdeath="predation") 100 101 def mortality_starvation(self): 102 recordingmanager.resourcerecorder.write_before_scavenging() 103 resources_scavenged = resources.scavenge(np.ones(len(self.population))) 104 recordingmanager.resourcerecorder.write_after_scavenging() 105 # mask_kill = starvation.get_mask_kill( 106 # n=len(self.population), 107 # resources_scavenged=resources_scavenged.sum(), 108 # ) 109 mask_kill = starvation.get_mask_kill(ages=self.population.ages, resources_scavenged=resources_scavenged.sum()) 110 self._kill(mask_kill=mask_kill, causeofdeath="starvation") 111 112 def reproduction(self): 113 """Generate offspring of reproducing individuals. 114 Initial is set to 0. 115 """ 116 117 # Check if fertile 118 mask_fertile = ( 119 self.population.ages >= parametermanager.parameters.MATURATION_AGE 120 ) # Check if mature; mature if survived MATURATION_AGE full cycles 121 if parametermanager.parameters.REPRODUCTION_ENDPOINT > 0: 122 mask_menopausal = ( 123 self.population.ages >= parametermanager.parameters.REPRODUCTION_ENDPOINT 124 ) # Check if menopausal; menopausal when lived through REPRODUCTION_ENDPOINT full cycles 125 mask_fertile = (mask_fertile) & (~mask_menopausal) 126 127 if not any(mask_fertile): 128 return 129 130 # Check if reproducing 131 probs_repr = self.population.phenotypes.extract(ages=self.population.ages, trait_name="repr", part=mask_fertile) 132 133 # Binomial calculation 134 n = parametermanager.parameters.MAX_OFFSPRING_NUMBER 135 p = probs_repr 136 137 assert np.all(p <= 1) 138 assert np.all(p >= 0) 139 num_repr = variables.rng.binomial(n=n, p=p) 140 mask_repr = num_repr > 0 141 142 if sum(num_repr) == 0: 143 return 144 145 # Indices of reproducing individuals 146 who = np.repeat(np.arange(len(self.population)), num_repr) 147 148 # Count ages at reproduction 149 ages_repr = self.population.ages[who] 150 recordingmanager.flushrecorder.collect("age_at_birth", ages_repr) 151 152 # Increase births statistics 153 self.population.births += num_repr 154 155 # Generate offspring genomes 156 parental_genomes = self.population.genomes.get(individuals=who) 157 parental_sexes = self.population.sexes[who] 158 159 muta_prob = self.population.phenotypes.extract(ages=self.population.ages, trait_name="muta", part=mask_repr)[ 160 mask_repr 161 ] 162 muta_prob = np.repeat(muta_prob, num_repr[mask_repr]) 163 164 offspring_genomes = submodels.reproduction.generate_offspring_genomes( 165 genomes=parental_genomes, 166 muta_prob=muta_prob, 167 ages=ages_repr, 168 parental_sexes=parental_sexes, 169 ) 170 offspring_sexes = submodels.sexsystem.get_sex(len(offspring_genomes)) 171 172 # Randomize order of newly laid egg attributes .. 173 # .. because the order will affect their probability to be removed because of limited carrying capacity 174 order = np.arange(len(offspring_sexes)) 175 variables.rng.shuffle(order) 176 offspring_genomes = offspring_genomes[order] 177 offspring_sexes = offspring_sexes[order] 178 179 # Make eggs 180 eggs = Population.make_eggs( 181 offspring_genomes=offspring_genomes, 182 step=variables.steps, 183 offspring_sexes=offspring_sexes, 184 parental_generations=np.zeros(len(offspring_sexes)), # TODO replace with working calculation 185 ) 186 if self.eggs is None: 187 self.eggs = eggs 188 else: 189 self.eggs += eggs 190 191 if parametermanager.parameters.CARRYING_CAPACITY_EGGS is not None and len(self.eggs) > parametermanager.parameters.CARRYING_CAPACITY_EGGS: 192 indices = np.arange(len(self.eggs))[-parametermanager.parameters.CARRYING_CAPACITY_EGGS :] 193 # TODO biased 194 self.eggs *= indices 195 196 def growth(self): 197 # TODO use already scavenged resources to determine growth 198 # max_growth_potential = self.population.phenotypes.extract(ages=self.population.ages, trait_name="grow") 199 # gathered_resources = submodels.resources.scavenge(max_growth_potential) 200 # self.population.sizes += gathered_resources 201 self.population.sizes += 1 202 203 def age(self): 204 """Increase age of all by one and kill those that surpass age limit. 205 Age denotes the number of full cycles that an individual survived and reproduced. 206 AGE_LIMIT is the maximum number of full cycles an individual can go through. 207 """ 208 self.population.ages += 1 209 mask_kill = self.population.ages >= parametermanager.parameters.AGE_LIMIT 210 self._kill(mask_kill=mask_kill, causeofdeath="age_limit") 211 212 def hatch(self): 213 """Turn eggs into living individuals""" 214 215 # If nothing to hatch 216 if self.eggs is None or len(self.eggs) == 0: 217 return 218 219 # If REPRODUCTION_REGULATION is True, only reproduce until MAX_POPULATION_SIZE 220 if parametermanager.parameters.REPRODUCTION_REGULATION: 221 current_population_size = len(self.population) 222 remaining_capacity = resources.capacity - current_population_size 223 # If no remaining capacity, do not reproduce 224 if remaining_capacity < 1: 225 self.eggs = None 226 return 227 elif remaining_capacity < len(self.eggs): 228 indices = variables.rng.choice(len(self.eggs), size=int(remaining_capacity), replace=False) 229 self.eggs *= indices 230 231 # If something to hatch 232 if ( 233 ( 234 parametermanager.parameters.INCUBATION_PERIOD == -1 and len(self.population) == 0 235 ) # hatch when everyone dead 236 or (parametermanager.parameters.INCUBATION_PERIOD == 0) # hatch immediately 237 or ( 238 parametermanager.parameters.INCUBATION_PERIOD > 0 239 and variables.steps % parametermanager.parameters.INCUBATION_PERIOD == 0 240 ) # hatch with delay 241 ): 242 243 self.eggs.phenotypes = submodels.architect.__call__(self.eggs.genomes) 244 self.population += self.eggs 245 self.eggs = None 246 247 ################ 248 # HELPER FUNCS # 249 ################ 250 251 def _kill(self, mask_kill, causeofdeath): 252 """Kill individuals and record their data.""" 253 254 assert causeofdeath in VALID_CAUSES_OF_DEATH 255 256 # Skip if no one to kill 257 if not any(mask_kill): 258 return 259 260 # Count ages at death 261 # if causeofdeath != "age_limit": 262 ages_death = self.population.ages[mask_kill] 263 recordingmanager.flushrecorder.collect(f"age_at_{causeofdeath}", ages_death) 264 recordingmanager.terecorder.record(ages_death, "dead") 265 266 # Retain survivors 267 self.population *= ~mask_kill 268 269 def __len__(self): 270 """Return the number of living individuals and saved eggs.""" 271 return len(self.population) + len(self.eggs) if self.eggs is not None else len(self.population)
class
Bioreactor:
15class Bioreactor: 16 def __init__(self, population: Population): 17 self.eggs: Population = None 18 self.population: Population = population 19 20 ############## 21 # MAIN LOGIC # 22 ############## 23 24 def run_step(self): 25 """Perform one step of simulation.""" 26 27 # If extinct (no living individuals nor eggs left), do nothing 28 if len(self) == 0: 29 logging.debug("Population went extinct.") 30 recordingmanager.summaryrecorder.extinct = True 31 return 32 # Mortality sources 33 self.mortalities() 34 resources.replenish() 35 36 recordingmanager.popsizerecorder.write_before_reproduction(self.population) 37 self.growth() # size increase 38 self.reproduction() # reproduction 39 self.age() # age increment and potentially death 40 self.hatch() 41 submodels.architect.envdrift.evolve(step=variables.steps) 42 43 # Record data 44 recordingmanager.popsizerecorder.write_after_reproduction(self.population) 45 recordingmanager.popsizerecorder.write_egg_num_after_reproduction(self.eggs) 46 recordingmanager.envdriftmaprecorder.write(step=variables.steps) 47 recordingmanager.flushrecorder.collect("additive_age_structure", self.population.ages) # population census 48 recordingmanager.picklerecorder.write(self.population) 49 recordingmanager.featherrecorder.write(self.population) 50 recordingmanager.guirecorder.record(self.population) 51 recordingmanager.flushrecorder.flush() 52 recordingmanager.popgenstatsrecorder.write( 53 self.population.genomes, self.population.phenotypes.extract(ages=self.population.ages, trait_name="muta") 54 ) # TODO defers calculation of mutation rates; hacky 55 recordingmanager.summaryrecorder.record_memuse() 56 recordingmanager.terecorder.record(self.population.ages, "alive") 57 recordingmanager.checkpointrecorder.write(self.population, self.eggs) 58 59 ############### 60 # STEP LOGIC # 61 ############### 62 63 def mortalities(self): 64 for source in parametermanager.parameters.MORTALITY_ORDER: 65 if source == "intrinsic": 66 self.mortality_intrinsic() 67 elif source == "abiotic": 68 self.mortality_abiotic() 69 elif source == "infection": 70 self.mortality_infection() 71 elif source == "predation": 72 self.mortality_predation() 73 elif source == "starvation": 74 self.mortality_starvation() 75 else: 76 raise ValueError(f"Invalid source of mortality '{source}'") 77 78 def mortality_intrinsic(self): 79 probs_surv = self.population.phenotypes.extract(ages=self.population.ages, trait_name="surv") 80 age_hazard = submodels.frailty.modify(hazard=1 - probs_surv, ages=self.population.ages) 81 mask_kill = variables.rng.random(len(probs_surv)) < age_hazard 82 self._kill(mask_kill=mask_kill, causeofdeath="intrinsic") 83 84 def mortality_abiotic(self): 85 hazard = submodels.abiotic(variables.steps) 86 age_hazard = submodels.frailty.modify(hazard=hazard, ages=self.population.ages) 87 mask_kill = variables.rng.random(len(self.population)) < age_hazard 88 self._kill(mask_kill=mask_kill, causeofdeath="abiotic") 89 90 def mortality_infection(self): 91 submodels.infection(self.population) 92 # TODO add age hazard 93 mask_kill = self.population.infection == -1 94 self._kill(mask_kill=mask_kill, causeofdeath="infection") 95 96 def mortality_predation(self): 97 probs_kill = submodels.predation(len(self)) 98 # TODO add age hazard 99 mask_kill = variables.rng.random(len(self)) < probs_kill 100 self._kill(mask_kill=mask_kill, causeofdeath="predation") 101 102 def mortality_starvation(self): 103 recordingmanager.resourcerecorder.write_before_scavenging() 104 resources_scavenged = resources.scavenge(np.ones(len(self.population))) 105 recordingmanager.resourcerecorder.write_after_scavenging() 106 # mask_kill = starvation.get_mask_kill( 107 # n=len(self.population), 108 # resources_scavenged=resources_scavenged.sum(), 109 # ) 110 mask_kill = starvation.get_mask_kill(ages=self.population.ages, resources_scavenged=resources_scavenged.sum()) 111 self._kill(mask_kill=mask_kill, causeofdeath="starvation") 112 113 def reproduction(self): 114 """Generate offspring of reproducing individuals. 115 Initial is set to 0. 116 """ 117 118 # Check if fertile 119 mask_fertile = ( 120 self.population.ages >= parametermanager.parameters.MATURATION_AGE 121 ) # Check if mature; mature if survived MATURATION_AGE full cycles 122 if parametermanager.parameters.REPRODUCTION_ENDPOINT > 0: 123 mask_menopausal = ( 124 self.population.ages >= parametermanager.parameters.REPRODUCTION_ENDPOINT 125 ) # Check if menopausal; menopausal when lived through REPRODUCTION_ENDPOINT full cycles 126 mask_fertile = (mask_fertile) & (~mask_menopausal) 127 128 if not any(mask_fertile): 129 return 130 131 # Check if reproducing 132 probs_repr = self.population.phenotypes.extract(ages=self.population.ages, trait_name="repr", part=mask_fertile) 133 134 # Binomial calculation 135 n = parametermanager.parameters.MAX_OFFSPRING_NUMBER 136 p = probs_repr 137 138 assert np.all(p <= 1) 139 assert np.all(p >= 0) 140 num_repr = variables.rng.binomial(n=n, p=p) 141 mask_repr = num_repr > 0 142 143 if sum(num_repr) == 0: 144 return 145 146 # Indices of reproducing individuals 147 who = np.repeat(np.arange(len(self.population)), num_repr) 148 149 # Count ages at reproduction 150 ages_repr = self.population.ages[who] 151 recordingmanager.flushrecorder.collect("age_at_birth", ages_repr) 152 153 # Increase births statistics 154 self.population.births += num_repr 155 156 # Generate offspring genomes 157 parental_genomes = self.population.genomes.get(individuals=who) 158 parental_sexes = self.population.sexes[who] 159 160 muta_prob = self.population.phenotypes.extract(ages=self.population.ages, trait_name="muta", part=mask_repr)[ 161 mask_repr 162 ] 163 muta_prob = np.repeat(muta_prob, num_repr[mask_repr]) 164 165 offspring_genomes = submodels.reproduction.generate_offspring_genomes( 166 genomes=parental_genomes, 167 muta_prob=muta_prob, 168 ages=ages_repr, 169 parental_sexes=parental_sexes, 170 ) 171 offspring_sexes = submodels.sexsystem.get_sex(len(offspring_genomes)) 172 173 # Randomize order of newly laid egg attributes .. 174 # .. because the order will affect their probability to be removed because of limited carrying capacity 175 order = np.arange(len(offspring_sexes)) 176 variables.rng.shuffle(order) 177 offspring_genomes = offspring_genomes[order] 178 offspring_sexes = offspring_sexes[order] 179 180 # Make eggs 181 eggs = Population.make_eggs( 182 offspring_genomes=offspring_genomes, 183 step=variables.steps, 184 offspring_sexes=offspring_sexes, 185 parental_generations=np.zeros(len(offspring_sexes)), # TODO replace with working calculation 186 ) 187 if self.eggs is None: 188 self.eggs = eggs 189 else: 190 self.eggs += eggs 191 192 if parametermanager.parameters.CARRYING_CAPACITY_EGGS is not None and len(self.eggs) > parametermanager.parameters.CARRYING_CAPACITY_EGGS: 193 indices = np.arange(len(self.eggs))[-parametermanager.parameters.CARRYING_CAPACITY_EGGS :] 194 # TODO biased 195 self.eggs *= indices 196 197 def growth(self): 198 # TODO use already scavenged resources to determine growth 199 # max_growth_potential = self.population.phenotypes.extract(ages=self.population.ages, trait_name="grow") 200 # gathered_resources = submodels.resources.scavenge(max_growth_potential) 201 # self.population.sizes += gathered_resources 202 self.population.sizes += 1 203 204 def age(self): 205 """Increase age of all by one and kill those that surpass age limit. 206 Age denotes the number of full cycles that an individual survived and reproduced. 207 AGE_LIMIT is the maximum number of full cycles an individual can go through. 208 """ 209 self.population.ages += 1 210 mask_kill = self.population.ages >= parametermanager.parameters.AGE_LIMIT 211 self._kill(mask_kill=mask_kill, causeofdeath="age_limit") 212 213 def hatch(self): 214 """Turn eggs into living individuals""" 215 216 # If nothing to hatch 217 if self.eggs is None or len(self.eggs) == 0: 218 return 219 220 # If REPRODUCTION_REGULATION is True, only reproduce until MAX_POPULATION_SIZE 221 if parametermanager.parameters.REPRODUCTION_REGULATION: 222 current_population_size = len(self.population) 223 remaining_capacity = resources.capacity - current_population_size 224 # If no remaining capacity, do not reproduce 225 if remaining_capacity < 1: 226 self.eggs = None 227 return 228 elif remaining_capacity < len(self.eggs): 229 indices = variables.rng.choice(len(self.eggs), size=int(remaining_capacity), replace=False) 230 self.eggs *= indices 231 232 # If something to hatch 233 if ( 234 ( 235 parametermanager.parameters.INCUBATION_PERIOD == -1 and len(self.population) == 0 236 ) # hatch when everyone dead 237 or (parametermanager.parameters.INCUBATION_PERIOD == 0) # hatch immediately 238 or ( 239 parametermanager.parameters.INCUBATION_PERIOD > 0 240 and variables.steps % parametermanager.parameters.INCUBATION_PERIOD == 0 241 ) # hatch with delay 242 ): 243 244 self.eggs.phenotypes = submodels.architect.__call__(self.eggs.genomes) 245 self.population += self.eggs 246 self.eggs = None 247 248 ################ 249 # HELPER FUNCS # 250 ################ 251 252 def _kill(self, mask_kill, causeofdeath): 253 """Kill individuals and record their data.""" 254 255 assert causeofdeath in VALID_CAUSES_OF_DEATH 256 257 # Skip if no one to kill 258 if not any(mask_kill): 259 return 260 261 # Count ages at death 262 # if causeofdeath != "age_limit": 263 ages_death = self.population.ages[mask_kill] 264 recordingmanager.flushrecorder.collect(f"age_at_{causeofdeath}", ages_death) 265 recordingmanager.terecorder.record(ages_death, "dead") 266 267 # Retain survivors 268 self.population *= ~mask_kill 269 270 def __len__(self): 271 """Return the number of living individuals and saved eggs.""" 272 return len(self.population) + len(self.eggs) if self.eggs is not None else len(self.population)
Bioreactor(population: aegis_sim.dataclasses.population.Population)
population: aegis_sim.dataclasses.population.Population
def
run_step(self):
24 def run_step(self): 25 """Perform one step of simulation.""" 26 27 # If extinct (no living individuals nor eggs left), do nothing 28 if len(self) == 0: 29 logging.debug("Population went extinct.") 30 recordingmanager.summaryrecorder.extinct = True 31 return 32 # Mortality sources 33 self.mortalities() 34 resources.replenish() 35 36 recordingmanager.popsizerecorder.write_before_reproduction(self.population) 37 self.growth() # size increase 38 self.reproduction() # reproduction 39 self.age() # age increment and potentially death 40 self.hatch() 41 submodels.architect.envdrift.evolve(step=variables.steps) 42 43 # Record data 44 recordingmanager.popsizerecorder.write_after_reproduction(self.population) 45 recordingmanager.popsizerecorder.write_egg_num_after_reproduction(self.eggs) 46 recordingmanager.envdriftmaprecorder.write(step=variables.steps) 47 recordingmanager.flushrecorder.collect("additive_age_structure", self.population.ages) # population census 48 recordingmanager.picklerecorder.write(self.population) 49 recordingmanager.featherrecorder.write(self.population) 50 recordingmanager.guirecorder.record(self.population) 51 recordingmanager.flushrecorder.flush() 52 recordingmanager.popgenstatsrecorder.write( 53 self.population.genomes, self.population.phenotypes.extract(ages=self.population.ages, trait_name="muta") 54 ) # TODO defers calculation of mutation rates; hacky 55 recordingmanager.summaryrecorder.record_memuse() 56 recordingmanager.terecorder.record(self.population.ages, "alive") 57 recordingmanager.checkpointrecorder.write(self.population, self.eggs)
Perform one step of simulation.
def
mortalities(self):
63 def mortalities(self): 64 for source in parametermanager.parameters.MORTALITY_ORDER: 65 if source == "intrinsic": 66 self.mortality_intrinsic() 67 elif source == "abiotic": 68 self.mortality_abiotic() 69 elif source == "infection": 70 self.mortality_infection() 71 elif source == "predation": 72 self.mortality_predation() 73 elif source == "starvation": 74 self.mortality_starvation() 75 else: 76 raise ValueError(f"Invalid source of mortality '{source}'")
def
mortality_intrinsic(self):
78 def mortality_intrinsic(self): 79 probs_surv = self.population.phenotypes.extract(ages=self.population.ages, trait_name="surv") 80 age_hazard = submodels.frailty.modify(hazard=1 - probs_surv, ages=self.population.ages) 81 mask_kill = variables.rng.random(len(probs_surv)) < age_hazard 82 self._kill(mask_kill=mask_kill, causeofdeath="intrinsic")
def
mortality_starvation(self):
102 def mortality_starvation(self): 103 recordingmanager.resourcerecorder.write_before_scavenging() 104 resources_scavenged = resources.scavenge(np.ones(len(self.population))) 105 recordingmanager.resourcerecorder.write_after_scavenging() 106 # mask_kill = starvation.get_mask_kill( 107 # n=len(self.population), 108 # resources_scavenged=resources_scavenged.sum(), 109 # ) 110 mask_kill = starvation.get_mask_kill(ages=self.population.ages, resources_scavenged=resources_scavenged.sum()) 111 self._kill(mask_kill=mask_kill, causeofdeath="starvation")
def
reproduction(self):
113 def reproduction(self): 114 """Generate offspring of reproducing individuals. 115 Initial is set to 0. 116 """ 117 118 # Check if fertile 119 mask_fertile = ( 120 self.population.ages >= parametermanager.parameters.MATURATION_AGE 121 ) # Check if mature; mature if survived MATURATION_AGE full cycles 122 if parametermanager.parameters.REPRODUCTION_ENDPOINT > 0: 123 mask_menopausal = ( 124 self.population.ages >= parametermanager.parameters.REPRODUCTION_ENDPOINT 125 ) # Check if menopausal; menopausal when lived through REPRODUCTION_ENDPOINT full cycles 126 mask_fertile = (mask_fertile) & (~mask_menopausal) 127 128 if not any(mask_fertile): 129 return 130 131 # Check if reproducing 132 probs_repr = self.population.phenotypes.extract(ages=self.population.ages, trait_name="repr", part=mask_fertile) 133 134 # Binomial calculation 135 n = parametermanager.parameters.MAX_OFFSPRING_NUMBER 136 p = probs_repr 137 138 assert np.all(p <= 1) 139 assert np.all(p >= 0) 140 num_repr = variables.rng.binomial(n=n, p=p) 141 mask_repr = num_repr > 0 142 143 if sum(num_repr) == 0: 144 return 145 146 # Indices of reproducing individuals 147 who = np.repeat(np.arange(len(self.population)), num_repr) 148 149 # Count ages at reproduction 150 ages_repr = self.population.ages[who] 151 recordingmanager.flushrecorder.collect("age_at_birth", ages_repr) 152 153 # Increase births statistics 154 self.population.births += num_repr 155 156 # Generate offspring genomes 157 parental_genomes = self.population.genomes.get(individuals=who) 158 parental_sexes = self.population.sexes[who] 159 160 muta_prob = self.population.phenotypes.extract(ages=self.population.ages, trait_name="muta", part=mask_repr)[ 161 mask_repr 162 ] 163 muta_prob = np.repeat(muta_prob, num_repr[mask_repr]) 164 165 offspring_genomes = submodels.reproduction.generate_offspring_genomes( 166 genomes=parental_genomes, 167 muta_prob=muta_prob, 168 ages=ages_repr, 169 parental_sexes=parental_sexes, 170 ) 171 offspring_sexes = submodels.sexsystem.get_sex(len(offspring_genomes)) 172 173 # Randomize order of newly laid egg attributes .. 174 # .. because the order will affect their probability to be removed because of limited carrying capacity 175 order = np.arange(len(offspring_sexes)) 176 variables.rng.shuffle(order) 177 offspring_genomes = offspring_genomes[order] 178 offspring_sexes = offspring_sexes[order] 179 180 # Make eggs 181 eggs = Population.make_eggs( 182 offspring_genomes=offspring_genomes, 183 step=variables.steps, 184 offspring_sexes=offspring_sexes, 185 parental_generations=np.zeros(len(offspring_sexes)), # TODO replace with working calculation 186 ) 187 if self.eggs is None: 188 self.eggs = eggs 189 else: 190 self.eggs += eggs 191 192 if parametermanager.parameters.CARRYING_CAPACITY_EGGS is not None and len(self.eggs) > parametermanager.parameters.CARRYING_CAPACITY_EGGS: 193 indices = np.arange(len(self.eggs))[-parametermanager.parameters.CARRYING_CAPACITY_EGGS :] 194 # TODO biased 195 self.eggs *= indices
Generate offspring of reproducing individuals. Initial is set to 0.
def
growth(self):
197 def growth(self): 198 # TODO use already scavenged resources to determine growth 199 # max_growth_potential = self.population.phenotypes.extract(ages=self.population.ages, trait_name="grow") 200 # gathered_resources = submodels.resources.scavenge(max_growth_potential) 201 # self.population.sizes += gathered_resources 202 self.population.sizes += 1
def
age(self):
204 def age(self): 205 """Increase age of all by one and kill those that surpass age limit. 206 Age denotes the number of full cycles that an individual survived and reproduced. 207 AGE_LIMIT is the maximum number of full cycles an individual can go through. 208 """ 209 self.population.ages += 1 210 mask_kill = self.population.ages >= parametermanager.parameters.AGE_LIMIT 211 self._kill(mask_kill=mask_kill, causeofdeath="age_limit")
Increase age of all by one and kill those that surpass age limit. Age denotes the number of full cycles that an individual survived and reproduced. AGE_LIMIT is the maximum number of full cycles an individual can go through.
def
hatch(self):
213 def hatch(self): 214 """Turn eggs into living individuals""" 215 216 # If nothing to hatch 217 if self.eggs is None or len(self.eggs) == 0: 218 return 219 220 # If REPRODUCTION_REGULATION is True, only reproduce until MAX_POPULATION_SIZE 221 if parametermanager.parameters.REPRODUCTION_REGULATION: 222 current_population_size = len(self.population) 223 remaining_capacity = resources.capacity - current_population_size 224 # If no remaining capacity, do not reproduce 225 if remaining_capacity < 1: 226 self.eggs = None 227 return 228 elif remaining_capacity < len(self.eggs): 229 indices = variables.rng.choice(len(self.eggs), size=int(remaining_capacity), replace=False) 230 self.eggs *= indices 231 232 # If something to hatch 233 if ( 234 ( 235 parametermanager.parameters.INCUBATION_PERIOD == -1 and len(self.population) == 0 236 ) # hatch when everyone dead 237 or (parametermanager.parameters.INCUBATION_PERIOD == 0) # hatch immediately 238 or ( 239 parametermanager.parameters.INCUBATION_PERIOD > 0 240 and variables.steps % parametermanager.parameters.INCUBATION_PERIOD == 0 241 ) # hatch with delay 242 ): 243 244 self.eggs.phenotypes = submodels.architect.__call__(self.eggs.genomes) 245 self.population += self.eggs 246 self.eggs = None
Turn eggs into living individuals