aegis_sim.dataclasses.population
1import numpy as np 2import pickle 3import pathlib 4 5from aegis_sim.dataclasses.genomes import Genomes 6from aegis_sim.dataclasses.phenotypes import Phenotypes 7from aegis_sim import submodels 8 9 10class Population: 11 """Population data 12 13 Contains demographic, genetic and phenotypic data of living individuals. 14 """ 15 16 attrs = ( 17 "genomes", 18 "ages", 19 "births", 20 "birthdays", 21 "generations", 22 "phenotypes", 23 "infection", 24 "sizes", 25 "sexes", 26 ) 27 28 def __init__( 29 self, 30 genomes: Genomes, 31 ages, 32 births, 33 birthdays, 34 phenotypes: Phenotypes, 35 infection, 36 sizes, 37 sexes, 38 generations=None, 39 ): 40 self.genomes = genomes 41 self.ages = ages 42 self.births = births 43 self.birthdays = birthdays 44 self.phenotypes = phenotypes 45 self.infection = infection 46 self.sizes = sizes 47 self.sexes = sexes 48 self.generations = generations 49 50 assert isinstance(phenotypes, Phenotypes) 51 52 if not ( 53 len(genomes) 54 == len(ages) 55 == len(births) 56 == len(birthdays) 57 == len(phenotypes) 58 == len(infection) 59 == len(sizes) 60 == len(sexes) 61 # == len(generations) 62 ): 63 raise ValueError("Population attributes must have equal length") 64 65 def __len__(self): 66 """Return the number of living individuals.""" 67 return len(self.genomes) 68 69 def __getitem__(self, index): 70 """Return a subpopulation.""" 71 return Population( 72 genomes=self.genomes.get(individuals=index), 73 ages=self.ages[index], 74 births=self.births[index], 75 birthdays=self.birthdays[index], 76 phenotypes=self.phenotypes.get(individuals=index), 77 infection=self.infection[index], 78 sizes=self.sizes[index], 79 sexes=self.sexes[index], 80 generations=self.generations[index] if self.generations is not None else None, 81 ) 82 83 def __imul__(self, index): 84 """Redefine itself as its own subpopulation.""" 85 for attr in self.attrs: 86 if attr == "genomes": 87 self.genomes.keep(individuals=index) 88 elif attr == "phenotypes": 89 self.phenotypes.keep(individuals=index) 90 elif attr == "generations": 91 self.generations = None 92 else: 93 setattr(self, attr, getattr(self, attr)[index]) 94 return self 95 96 def __iadd__(self, population): 97 """Merge with another population.""" 98 99 for attr in self.attrs: 100 if attr == "genomes": 101 self.genomes.add(population.genomes) 102 elif attr == "phenotypes": 103 assert isinstance(population.phenotypes, Phenotypes) 104 self.phenotypes.add(population.phenotypes) 105 elif attr == "generations": 106 self.generations = None 107 else: 108 val = np.concatenate([getattr(self, attr), getattr(population, attr)]) 109 setattr(self, attr, val) 110 return self 111 112 # def shuffle(self): 113 # order = np.random.arange(len(self)) 114 # np.random.shuffle(order) 115 # self *= order 116 117 @staticmethod 118 def load_pickle_from(path: pathlib.Path): 119 assert path.exists(), f"pickle_path {path} does not exist" 120 with open(path, "rb") as file_: 121 return pickle.load(file_) 122 123 def save_pickle_to(self, path): 124 with open(path, "wb") as file_: 125 pickle.dump(self, file_) 126 127 @staticmethod 128 def initialize(n, AGE_LIMIT): 129 genomes = Genomes(submodels.architect.architecture.init_genome_array(n)) 130 ages = np.random.randint(low=0, high=AGE_LIMIT, size=n, dtype=np.int32) 131 births = np.zeros(n, dtype=np.int32) 132 birthdays = np.zeros(n, dtype=np.int32) 133 # generations = np.zeros(n, dtype=np.int32) 134 generations = None 135 136 phenotypes = submodels.architect.__call__(genomes) 137 assert isinstance(phenotypes, Phenotypes) 138 139 infection = np.zeros(n, dtype=np.int32) 140 sizes = np.zeros(n, dtype=np.float32) 141 sexes = submodels.sexsystem.get_sex(n) 142 return Population( 143 genomes=genomes, 144 ages=ages, 145 births=births, 146 birthdays=birthdays, 147 generations=generations, 148 phenotypes=phenotypes, 149 infection=infection, 150 sizes=sizes, 151 sexes=sexes, 152 ) 153 154 @staticmethod 155 def make_eggs(offspring_genomes: Genomes, step, offspring_sexes, parental_generations): 156 n = len(offspring_genomes) 157 eggs = Population( 158 genomes=offspring_genomes, 159 ages=np.zeros(n, dtype=np.int32), 160 births=np.zeros(n, dtype=np.int32), 161 birthdays=np.zeros(n, dtype=np.int32) + step, 162 # generations=parental_generations + 1, 163 generations=None, 164 # phenotypes=submodels.architect.__call__(offspring_genomes), # Do not compute phenotypes until eggs are laid! Why? Because it is computationally expensive. 165 phenotypes=Phenotypes.init_phenotype_array(n), 166 infection=np.zeros(n, dtype=np.int32), 167 sizes=np.zeros(n, dtype=np.float32), 168 sexes=offspring_sexes, 169 ) 170 return eggs
class
Population:
11class Population: 12 """Population data 13 14 Contains demographic, genetic and phenotypic data of living individuals. 15 """ 16 17 attrs = ( 18 "genomes", 19 "ages", 20 "births", 21 "birthdays", 22 "generations", 23 "phenotypes", 24 "infection", 25 "sizes", 26 "sexes", 27 ) 28 29 def __init__( 30 self, 31 genomes: Genomes, 32 ages, 33 births, 34 birthdays, 35 phenotypes: Phenotypes, 36 infection, 37 sizes, 38 sexes, 39 generations=None, 40 ): 41 self.genomes = genomes 42 self.ages = ages 43 self.births = births 44 self.birthdays = birthdays 45 self.phenotypes = phenotypes 46 self.infection = infection 47 self.sizes = sizes 48 self.sexes = sexes 49 self.generations = generations 50 51 assert isinstance(phenotypes, Phenotypes) 52 53 if not ( 54 len(genomes) 55 == len(ages) 56 == len(births) 57 == len(birthdays) 58 == len(phenotypes) 59 == len(infection) 60 == len(sizes) 61 == len(sexes) 62 # == len(generations) 63 ): 64 raise ValueError("Population attributes must have equal length") 65 66 def __len__(self): 67 """Return the number of living individuals.""" 68 return len(self.genomes) 69 70 def __getitem__(self, index): 71 """Return a subpopulation.""" 72 return Population( 73 genomes=self.genomes.get(individuals=index), 74 ages=self.ages[index], 75 births=self.births[index], 76 birthdays=self.birthdays[index], 77 phenotypes=self.phenotypes.get(individuals=index), 78 infection=self.infection[index], 79 sizes=self.sizes[index], 80 sexes=self.sexes[index], 81 generations=self.generations[index] if self.generations is not None else None, 82 ) 83 84 def __imul__(self, index): 85 """Redefine itself as its own subpopulation.""" 86 for attr in self.attrs: 87 if attr == "genomes": 88 self.genomes.keep(individuals=index) 89 elif attr == "phenotypes": 90 self.phenotypes.keep(individuals=index) 91 elif attr == "generations": 92 self.generations = None 93 else: 94 setattr(self, attr, getattr(self, attr)[index]) 95 return self 96 97 def __iadd__(self, population): 98 """Merge with another population.""" 99 100 for attr in self.attrs: 101 if attr == "genomes": 102 self.genomes.add(population.genomes) 103 elif attr == "phenotypes": 104 assert isinstance(population.phenotypes, Phenotypes) 105 self.phenotypes.add(population.phenotypes) 106 elif attr == "generations": 107 self.generations = None 108 else: 109 val = np.concatenate([getattr(self, attr), getattr(population, attr)]) 110 setattr(self, attr, val) 111 return self 112 113 # def shuffle(self): 114 # order = np.random.arange(len(self)) 115 # np.random.shuffle(order) 116 # self *= order 117 118 @staticmethod 119 def load_pickle_from(path: pathlib.Path): 120 assert path.exists(), f"pickle_path {path} does not exist" 121 with open(path, "rb") as file_: 122 return pickle.load(file_) 123 124 def save_pickle_to(self, path): 125 with open(path, "wb") as file_: 126 pickle.dump(self, file_) 127 128 @staticmethod 129 def initialize(n, AGE_LIMIT): 130 genomes = Genomes(submodels.architect.architecture.init_genome_array(n)) 131 ages = np.random.randint(low=0, high=AGE_LIMIT, size=n, dtype=np.int32) 132 births = np.zeros(n, dtype=np.int32) 133 birthdays = np.zeros(n, dtype=np.int32) 134 # generations = np.zeros(n, dtype=np.int32) 135 generations = None 136 137 phenotypes = submodels.architect.__call__(genomes) 138 assert isinstance(phenotypes, Phenotypes) 139 140 infection = np.zeros(n, dtype=np.int32) 141 sizes = np.zeros(n, dtype=np.float32) 142 sexes = submodels.sexsystem.get_sex(n) 143 return Population( 144 genomes=genomes, 145 ages=ages, 146 births=births, 147 birthdays=birthdays, 148 generations=generations, 149 phenotypes=phenotypes, 150 infection=infection, 151 sizes=sizes, 152 sexes=sexes, 153 ) 154 155 @staticmethod 156 def make_eggs(offspring_genomes: Genomes, step, offspring_sexes, parental_generations): 157 n = len(offspring_genomes) 158 eggs = Population( 159 genomes=offspring_genomes, 160 ages=np.zeros(n, dtype=np.int32), 161 births=np.zeros(n, dtype=np.int32), 162 birthdays=np.zeros(n, dtype=np.int32) + step, 163 # generations=parental_generations + 1, 164 generations=None, 165 # phenotypes=submodels.architect.__call__(offspring_genomes), # Do not compute phenotypes until eggs are laid! Why? Because it is computationally expensive. 166 phenotypes=Phenotypes.init_phenotype_array(n), 167 infection=np.zeros(n, dtype=np.int32), 168 sizes=np.zeros(n, dtype=np.float32), 169 sexes=offspring_sexes, 170 ) 171 return eggs
Population data
Contains demographic, genetic and phenotypic data of living individuals.
Population( genomes: aegis_sim.dataclasses.genomes.Genomes, ages, births, birthdays, phenotypes: aegis_sim.dataclasses.phenotypes.Phenotypes, infection, sizes, sexes, generations=None)
29 def __init__( 30 self, 31 genomes: Genomes, 32 ages, 33 births, 34 birthdays, 35 phenotypes: Phenotypes, 36 infection, 37 sizes, 38 sexes, 39 generations=None, 40 ): 41 self.genomes = genomes 42 self.ages = ages 43 self.births = births 44 self.birthdays = birthdays 45 self.phenotypes = phenotypes 46 self.infection = infection 47 self.sizes = sizes 48 self.sexes = sexes 49 self.generations = generations 50 51 assert isinstance(phenotypes, Phenotypes) 52 53 if not ( 54 len(genomes) 55 == len(ages) 56 == len(births) 57 == len(birthdays) 58 == len(phenotypes) 59 == len(infection) 60 == len(sizes) 61 == len(sexes) 62 # == len(generations) 63 ): 64 raise ValueError("Population attributes must have equal length")
attrs =
('genomes', 'ages', 'births', 'birthdays', 'generations', 'phenotypes', 'infection', 'sizes', 'sexes')
@staticmethod
def
initialize(n, AGE_LIMIT):
128 @staticmethod 129 def initialize(n, AGE_LIMIT): 130 genomes = Genomes(submodels.architect.architecture.init_genome_array(n)) 131 ages = np.random.randint(low=0, high=AGE_LIMIT, size=n, dtype=np.int32) 132 births = np.zeros(n, dtype=np.int32) 133 birthdays = np.zeros(n, dtype=np.int32) 134 # generations = np.zeros(n, dtype=np.int32) 135 generations = None 136 137 phenotypes = submodels.architect.__call__(genomes) 138 assert isinstance(phenotypes, Phenotypes) 139 140 infection = np.zeros(n, dtype=np.int32) 141 sizes = np.zeros(n, dtype=np.float32) 142 sexes = submodels.sexsystem.get_sex(n) 143 return Population( 144 genomes=genomes, 145 ages=ages, 146 births=births, 147 birthdays=birthdays, 148 generations=generations, 149 phenotypes=phenotypes, 150 infection=infection, 151 sizes=sizes, 152 sexes=sexes, 153 )
@staticmethod
def
make_eggs( offspring_genomes: aegis_sim.dataclasses.genomes.Genomes, step, offspring_sexes, parental_generations):
155 @staticmethod 156 def make_eggs(offspring_genomes: Genomes, step, offspring_sexes, parental_generations): 157 n = len(offspring_genomes) 158 eggs = Population( 159 genomes=offspring_genomes, 160 ages=np.zeros(n, dtype=np.int32), 161 births=np.zeros(n, dtype=np.int32), 162 birthdays=np.zeros(n, dtype=np.int32) + step, 163 # generations=parental_generations + 1, 164 generations=None, 165 # phenotypes=submodels.architect.__call__(offspring_genomes), # Do not compute phenotypes until eggs are laid! Why? Because it is computationally expensive. 166 phenotypes=Phenotypes.init_phenotype_array(n), 167 infection=np.zeros(n, dtype=np.int32), 168 sizes=np.zeros(n, dtype=np.float32), 169 sexes=offspring_sexes, 170 ) 171 return eggs