I am experimenting with python about genetic algorithm. In other words, I want to make a program to simulate random process and control come parts (like experiment) to play around with variables and outcomes.
For my program, I am simulating rat reproduction and controlling its population to create superior (in my case, heavier) rats at the end. In specific, I want to have starting population of rats, randomly pair two for reproduction (in which offspring will have average weight of two parent rats) and control offspring population and so on. In simple words, I will have x population of rats (initial and constant), create offsprings, list them by weight, kill from the bottom (lightest rats) to lower the offspring population == initial population and repeat the process to eventually create heavier rats.
I know this experiment does not reflect real-life example but I want to initialize with this program and maybe make modifications or applications to other usages.
Sorry for the long introduction. Now, before writing the actual program, I want to create functions (using def command) to make actual codes easier to write. So far, I've come up with this:
import random
#Variables (Stop at (1) period of time or (2) above standard)
#number_rats = #initial population (standard)
min_offspring = 2
max_offspring = 10
#weight_rat = #Avg. of parents' weight (consider as single gender)
#mutation = #Randomly reduce the weight of small number of offspring (how many and how
much)
def breeding(female, male):
#random # of offspring and random weight for each
offspring = []
number_offspring = random.randint(min_offspring, max_offspring)
for i in range(0, number_offspring, 1):
offspring.append(random.triangular(female, male))
return offspring
def #random pair
def # cut offspring population to number_rats (list from highest weight to lowest and
cut light rats)
def # negative mutation (maybe inside breeding function) 10% chance of mutation that
decreases weight by 10%.
I have tried to complete the function for breeding to take 2 rats (I labeled male and female but I decided that any 2 rats are fine to lessen the complication) and they would produce random numbers of offsprings.
However, I am stuck on creating function for (1) taking random pairs from number_rats, (2) cutting huge number of offsprings to == number_rat, and (3) creating negative mutation when reproducing. It would help me a lot to help me write these functions in concise ways.
I know this is long post with incomplete work but helping me with this initial stage would greatly help me. I am sorry if this is not advanced work.
Anyways, thanks in advance.
CodePudding user response:
Try this, you can experiment on the initial number of rats and generations etc. There is a class Rat that defines a rat with name and weight used to track down the weight.
code
import random
import string
num_rats = 20
min_offspring = 2
max_offspring = 10
init_weight_min = 1
init_weight_max = 10
mutation_chance_rate = 10 # %
mutation_weight_decrease = 10 # %
generations = 4
alphabet = string.ascii_lowercase string.digits
def random_name():
"""Generates random chars for rat names."""
return ''.join(random.choices(alphabet, k=8))
def print_population(population):
for i, r in enumerate(population):
print(f'no. {i 1:02d}, name: {r.name}, weight: {r.weight}')
class Rat:
def __init__(self, name, weight):
self.name = name
self.weight = weight
def breeding(p1, p2):
"""
Create offsprings from parents p1 and p2.
Randomize number of offsprings and weight is based on parents weight.
"""
offspring = []
w1 = p1.weight
w2 = p2.weight
meanw = (w1 w2)/2
number_offspring = random.randint(min_offspring, max_offspring)
for _ in range(1, number_offspring):
name = random_name()
weight = meanw
offspring.append(Rat(name, weight)) # Create Rat object as new pop
return offspring
def random_pair():
pass
def cut_offspring_population(population):
"""
population is a list of rats.
Cut offsprings to orignal number of rats, preserve heavier rats.
"""
new_population = sorted(population, key=lambda x: x.weight, reverse=True) # sort rats weights descending
return new_population[0:num_rats] # Cutoff
def negative_mutation(population):
"""
10% chance of mutation that decreases weight by 10%.
"""
new_population = []
for p in population:
current_name = p.name
current_weight = p.weight
if random.randint(1, 100) <= mutation_chance_rate:
current_weight = current_weight * (100 - mutation_weight_decrease) / 100
new_population.append(Rat(current_name, current_weight))
else:
new_population.append(Rat(current_name, current_weight))
return new_population
def main():
# (1) Create rats
orig_rats = []
for _ in range(num_rats):
orig_rats.append(Rat(random_name(), random.randint(init_weight_min, init_weight_max)))
random.shuffle(orig_rats)
tmp_rats = orig_rats.copy()
for i in range(1, generations 1):
# (2) Breeding
new_offs = []
while tmp_rats:
# Select 2 rats as parents.
a = tmp_rats.pop()
b = tmp_rats.pop()
offs = breeding(a, b)
new_offs = new_offs offs # save all new offsprings in a list.
# (3) Reduce population.
reduced_pop = cut_offspring_population(new_offs)
# (4) Mutation
mutated_pop = negative_mutation(reduced_pop)
print(f'gen: {i}')
print_population(mutated_pop)
print()
tmp_rats = mutated_pop.copy() # for next gen
if __name__ == "__main__":
main()
Output after 4 generations
Offsprings weight is the average of parents weight. Rat name is just random.
gen: 1
no. 01, name: gzqru5c7, weight: 10.0
no. 02, name: ngpqx75q, weight: 10.0
no. 03, name: 3f2f8ua9, weight: 10.0
no. 04, name: uitaftbs, weight: 9.0
no. 05, name: dkr2dmyg, weight: 9.0
no. 06, name: lq350zck, weight: 8.0
no. 07, name: 4l08ks0t, weight: 8.0
no. 08, name: 1sl64mzl, weight: 7.5
no. 09, name: 88umsinn, weight: 7.5
no. 10, name: 3f30jp8m, weight: 7.5
no. 11, name: y1gbmbyn, weight: 7.5
no. 12, name: j7w7fr9y, weight: 7.5
no. 13, name: 3x5gl7zt, weight: 7.5
no. 14, name: 7mus480j, weight: 7.5
no. 15, name: 8yaifbuf, weight: 7.5
no. 16, name: t14n1qyq, weight: 7.5
no. 17, name: pqtieh8h, weight: 7.0
no. 18, name: 2eb1rhax, weight: 7.0
no. 19, name: ekfhcwye, weight: 7.0
no. 20, name: gdmeu1td, weight: 7.0
gen: 2
no. 01, name: nod75vx3, weight: 10.0
no. 02, name: pbe2z04b, weight: 10.0
no. 03, name: 1gn30dch, weight: 9.0
no. 04, name: txj11vza, weight: 10.0
no. 05, name: eonla5xu, weight: 9.0
no. 06, name: kwh5uffh, weight: 10.0
no. 07, name: pcvw8djm, weight: 10.0
no. 08, name: 7upmw4bu, weight: 9.0
no. 09, name: 3yb36bfr, weight: 10.0
no. 10, name: sjp0m8n8, weight: 10.0
no. 11, name: yj5oyuwd, weight: 9.5
no. 12, name: hsnbhyy7, weight: 9.5
no. 13, name: 40bpj2jw, weight: 9.5
no. 14, name: 4cdsgb4l, weight: 9.5
no. 15, name: 4lutoxh7, weight: 9.5
no. 16, name: s1111jrc, weight: 9.5
no. 17, name: le2m1x6w, weight: 7.65
no. 18, name: m2t9tfas, weight: 8.5
no. 19, name: r1gzn6a7, weight: 8.5
no. 20, name: lmvntp28, weight: 8.5
gen: 3
no. 01, name: 6g42b95g, weight: 10.0
no. 02, name: end7366f, weight: 10.0
no. 03, name: ccivuw0g, weight: 9.0
no. 04, name: rpf9pd51, weight: 10.0
no. 05, name: 94qkveea, weight: 10.0
no. 06, name: x1p9rd00, weight: 10.0
no. 07, name: v4d39x6t, weight: 10.0
no. 08, name: z3miwqoy, weight: 10.0
no. 09, name: vmkkrkqt, weight: 10.0
no. 10, name: ii8is1xp, weight: 10.0
no. 11, name: uadfjnng, weight: 10.0
no. 12, name: 5349eie7, weight: 10.0
no. 13, name: ikpoyce6, weight: 10.0
no. 14, name: yqqgsm9p, weight: 10.0
no. 15, name: ykkq03jv, weight: 10.0
no. 16, name: i3zzdab2, weight: 10.0
no. 17, name: 1m7kjzom, weight: 9.0
no. 18, name: vqatmrar, weight: 10.0
no. 19, name: 6ddudyf7, weight: 9.5
no. 20, name: 5b9dhzwp, weight: 9.5
gen: 4
no. 01, name: mm0iggdw, weight: 10.0
no. 02, name: u6evuhn8, weight: 9.0
no. 03, name: e0jo12tu, weight: 9.0
no. 04, name: wbage11q, weight: 10.0
no. 05, name: 4zlf1gvx, weight: 10.0
no. 06, name: 1c2hr5dd, weight: 10.0
no. 07, name: hbyzhpfn, weight: 10.0
no. 08, name: avf5ptk5, weight: 10.0
no. 09, name: hgurh5l0, weight: 10.0
no. 10, name: crqyuao0, weight: 10.0
no. 11, name: vjxkf3qf, weight: 10.0
no. 12, name: myzdj95e, weight: 9.0
no. 13, name: 8v4g3wxz, weight: 10.0
no. 14, name: l0z17ijw, weight: 10.0
no. 15, name: 1z3brmra, weight: 10.0
no. 16, name: r261q7pr, weight: 10.0
no. 17, name: ovl7vla5, weight: 10.0
no. 18, name: f2mvcvyw, weight: 10.0
no. 19, name: u1x8b7il, weight: 9.0
no. 20, name: l5k43dut, weight: 10.0