def set_gender(self, gender: int): """ Set the gender of the individual as male (1) or female (0). """ gender = lib.check_data("gender", gender, int, set_val=[0, 1]) self._gender = gender
def set_offsprings(self, offsprings: list): """ Allows to specify the offsprings of the individual. :param [Individual] offsprings : The offsprings of the individual. """ for offspring in offsprings: _ = lib.check_data("offspring", offspring, Individual) self._offsprings = offsprings
def set_parents(self, parents: list): """ Allows to specify the parents of the individual. :param [Individual] parents : The parents of the individual. """ for parent in parents: _ = lib.check_data("parent", parent, Individual) self._parents = parents
def breed(self, mate, n=1, offspring_gender=None, offspring_id=None): """ Given a mate Individual, a new offspring Individual is created. :param Individual mate : The partner with whom the offspring is generated. :param int, optional n : The number of offsprings that will be generated. :param [int], optional offspring_gender : The gender for the offsprings to be generated. :param str or [str], optional offspring_id : The ID(s) of the offspring(s). :return: Individual : A single Individual if n=1 or a list of Individual for n>1 """ # verify consistency of the parameters n = lib.check_data("n", n, int, min_val=1) if n == 1: if type(offspring_id) is str: tmp_id = offspring_id else: tmp_id = None offspring = Individual(ind_id=tmp_id, parents=[self, mate]) if offspring_gender is not None: offspring.set_gender(offspring_gender) offspring.x_breed() # add the offspring to the list of each parent self._offsprings.append(offspring) mate._offsprings.append(offspring) else: offspring = [] for idx in range(n): if offspring_id is not None and type(offspring_id) is list: tmp_id = offspring_id[idx] else: tmp_id = None if offspring_gender is None: tmp_gender = None else: tmp_gender = offspring_gender[idx] offspring.append( self.breed(mate=mate, offspring_gender=tmp_gender, offspring_id=tmp_id)) return offspring
def strand(self, idx: int) -> [[int]]: """ Returns the selected strand for each segment of the chromosomes given a specific parent. :param int idx : identify the parent (0 or 1). :return: [[int]] : The selected strands for each segment of the chromosomes. """ # evaluate consistency of the parameters idx = lib.check_data("parent idx", idx, int, set_val=[0, 1]) parent = ["parent1", "parent2"] strand = [] if self._strand: strand = self._strand[parent[idx]] return strand
def crossover(self, idx: int) -> [[int]]: """ Returns the crossover points for a specific parent. :param int idx : select the parent (0 or 1). :return: [[int]] : The crossover points pertaining to the specified parent. """ # evaluate consistency of the parameters idx = lib.check_data("parent idx", idx, int, set_val=[0, 1]) parent = ["parent1", "parent2"] crs = [] if self._crossover: crs = self._crossover[parent[idx]] return crs
def is_sibling(self, individual) -> bool: """ Return True if the passed individual is a sibling, False otherwise. :param Individual individual : The individual to be verified as sibling. :return: bool : True if the individual is a sibling, False otherwise. """ sibling = False if self.has_parents() and individual.has_parents( ): # both individuals must have parents individual = lib.check_data( "individual", individual, Individual) # check correctness of the parameter parent_ids = [self._parents[0].id, self._parents[1].id] # list of parent IDs # both parents are shared if individual.parents[0].id in parent_ids and individual.parents[ 1].id in parent_ids: sibling = True return sibling
def chromosomes_reproduction(chromosomes1: [[str]], chromosomes2: [[str]], div_snp="|") -> dict: """ Generate the chromosomes of an offspring given the chromosomes of the parents. :param [[str]] chromosomes1 : The chromosomes of the first parent. :param [[str]] chromosomes2 : The chromosomes of the second parent. :param str, optional div_snp : The separator of the strands. :return: A dictionary containing the chromosomes of the offspring (chromosomes) the crossover points from each parent (crossover["parent1"], crossover["parent2"]) and the selected segments (strand["parent1"], strand["parent2"]). """ output = {} n_chr = len(chromosomes1) # number of chromosomes if not n_chr == len(chromosomes2): msg = "Individuals have different number of chromosomes (Found {} and {}).".format( n_chr, len(chromosomes2)) raise ValueError(msg) offspring_chromosome = [ [] ] * n_chr # initialize the sequence of the chromosomes offspring_crossover = { "parent1": [[0, 0, 0]] * n_chr, "parent2": [[0, 0, 0]] * n_chr } offspring_strand = { "parent1": [[0, 0, 0]] * n_chr, "parent2": [[0, 0, 0]] * n_chr } for idx_chromosome in range(len(offspring_chromosome)): # the considered chromosome (from each parent) chr1 = chromosomes1[idx_chromosome] chr2 = chromosomes2[idx_chromosome] chr_length = len(chr1) if not (chr_length == len(chr2)): msg = "Individuals have different chromosome length (Found {} and {} for index {}).".format( chr_length, len(chr2), idx_chromosome) raise ValueError(msg) # evaluate consistency of the length chr_length = lib.check_data( "chromosome length (idx = {})".format(idx_chromosome), chr_length, int, min_val=4) # define the homologue chromosomes for parent1 chr1_homo1 = [] chr1_homo2 = [] for val in chr1: chr1_homo1.append(val.split(div_snp)[0]) chr1_homo2.append(val.split(div_snp)[1]) # define the homologue chromosomes for parent2 chr2_homo1 = [] chr2_homo2 = [] for val in chr2: chr2_homo1.append(val.split(div_snp)[0]) chr2_homo2.append(val.split(div_snp)[1]) # first segment: [0, chr1_rec_pts[0]) # second segment: [chr1_rec_pts[0], chr1_rec_pts[1]) # third segment: [chr1_rec_pts[1], chr1_rec_pts[2]) # third segment: [chr1_rec_pts[2], end] # # range(1, chr_length) generate random values between 1 and chr_length-1 chr1_rec_pts = sorted(random.sample(range(1, chr_length), 3)) chr2_rec_pts = sorted(random.sample(range(1, chr_length), 3)) offspring_crossover["parent1"][idx_chromosome] = chr1_rec_pts offspring_crossover["parent2"][idx_chromosome] = chr2_rec_pts # split the homologue chromosomes according to recombination points chr1_homo1 = [ chr1_homo1[i:j] for i, j in zip([0] + chr1_rec_pts, chr1_rec_pts + [None]) ] chr1_homo2 = [ chr1_homo2[i:j] for i, j in zip([0] + chr1_rec_pts, chr1_rec_pts + [None]) ] chr2_homo1 = [ chr2_homo1[i:j] for i, j in zip([0] + chr2_rec_pts, chr2_rec_pts + [None]) ] chr2_homo2 = [ chr2_homo2[i:j] for i, j in zip([0] + chr2_rec_pts, chr2_rec_pts + [None]) ] chrs1_homos = [chr1_homo1, chr1_homo2] # merge the two strands of parent 1 chrs2_homos = [chr2_homo1, chr2_homo2] # merge the two strands of parent 2 # choose one homologue randomly from each segment chr1_segs = random.choices([0, 1], k=4) chr2_segs = random.choices([0, 1], k=4) offspring_strand["parent1"][idx_chromosome] = chr1_segs offspring_strand["parent2"][idx_chromosome] = chr2_segs # assemble the selected segments together to form gametes chr1_gamete = [] chr2_gamete = [] for i in range(4): chr1_gamete = chr1_gamete + chrs1_homos[chr1_segs[i]][i] chr2_gamete = chr2_gamete + chrs2_homos[chr2_segs[i]][i] # join the gametes to form the offspring chromosome single_chr = [] for i in range(len(chr1_gamete)): offspring_gt = str(chr1_gamete[i]) + div_snp + str( chr2_gamete[i]) single_chr.append(offspring_gt) offspring_chromosome[idx_chromosome] = single_chr output["chromosomes"] = offspring_chromosome output["crossover"] = offspring_crossover output["strand"] = offspring_strand return output