Skip to content

Commit be9a822

Browse files
author
Jared Smith
committed
fully working version
1 parent 4b443cd commit be9a822

21 files changed

+66805
-64
lines changed

‎genetic/ffs.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010

1111

1212
deffitness_func_1(bit_sum, l):
13-
return (pow(((bit_sum/pow(2, l)), 10)))
13+
return (pow((bit_sum/pow(2, l)), 10))
1414

1515

1616
deffitness_func_2(bit_sum, l):
17-
return (pow((((1-bit_sum) /pow(2, l)), 10)))
17+
return (pow(((1-bit_sum) /pow(2, l)), 10))

‎genetic/genetic_algorithms.py‎

Lines changed: 129 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
importrandom
1616

1717
importbitstringasbs
18-
importscipyassp
18+
fromscipyimportstats
1919
importnumpyasnp
2020

2121
importffs
@@ -50,38 +50,38 @@ def __init__(self, args, logger):
5050
self.l=args.l
5151
self.N=args.N
5252
self.G=args.G
53-
self.pr_mutation=args.pr_mutation
54-
self.pr_crossover=args.pr_crossover
53+
self.pr_mutation=args.pm
54+
self.pr_crossover=args.pc
5555
self.population= []
5656
self.current_offspring= []
5757
self.nruns=args.nruns
5858
self.NG=args.NG
5959
self.learn=args.learn
6060
self.ff=args.ff
6161
self.ce=args.ce
62+
self.max_recovery=100
6263

6364
# Helper objects
6465
self.logger=logger
65-
self.orig_fitness_vals=np.zeros((self.G, self.N))
66-
self.norm_fitness_vals=np.zeros((self.G, self.N))
67-
self.total_fitness_vals=np.zeros((self.G, self.N))
68-
self.parents=np.zeros((self.G, 2))
66+
self.orig_fitness_vals=np.zeros((self.G+self.max_recovery, self.N))
67+
self.norm_fitness_vals=np.zeros((self.G+self.max_recovery, self.N))
68+
self.total_fitness_vals=np.zeros((self.G+self.max_recovery, self.N))
69+
self.parents=[[None, None] forxinxrange(self.G+self.max_recovery)]
6970
self.pr_mut_dist=None
7071
self.pr_cr_dist=None
7172
self.env_state=0
7273

7374
# Statistics Objects
74-
self.avg_fitness_vals=np.zeros((self.nruns, 2, self.G))
75-
self.best_fitness_vals=np.zeros((self.nruns, 2, self.G))
76-
self.num_correct_bits=np.zeros((self.nruns, 2, self.G))
75+
self.avg_fitness_vals=np.zeros((self.nruns, 2, self.G+self.max_recovery))
76+
self.best_fitness_vals=np.zeros((self.nruns, 2, self.G+self.max_recovery))
77+
self.num_correct_bits=np.zeros((self.nruns, 2, self.G+self.max_recovery))
7778
self.recovery_time=0
7879

7980

8081
definitialize_algorithm(self):
8182
# Initialize the population
82-
self.logger.info("Initializing population and genetic environment...")
83+
self.logger.info("Initializing population...")
8384
self.initialize_pop()
84-
self.initialize_env()
8585
self.logger.info("Initialization Complete.")
8686

8787
# Generate probablility distributions for mutation and crossover
@@ -94,6 +94,8 @@ def initialize_algorithm(self):
9494
# Initialize the Population
9595
definitialize_pop(self):
9696
# Generate N random bitstrings
97+
self.population= []
98+
self.current_offspring= []
9799
foriinrange(self.N):
98100
tmp_bitstring=''.join(random.choice('01') for_inrange(self.N))
99101
tmp_bitstring=bs.BitArray(bin=tmp_bitstring)
@@ -102,9 +104,10 @@ def initialize_pop(self):
102104
# Initialize the genetic environment
103105
definitialize_env(self):
104106
# Get the appropriate fitness function
107+
self.logger.info("Initializing environment...")
105108
ifself.env_state!=0:
106109
self.recovery_time=0
107-
self.ff=random.choice(['fitness_func_2'])
110+
self.logger.info("Initialized environment.")
108111

109112
# Generate probability distributions for mutation and crossover
110113
defgenerate_prob_distributions(self):
@@ -119,52 +122,118 @@ def generate_prob_distributions(self):
119122

120123
# Generate the object that will be used to get random numbers
121124
# according to each distribution.
122-
self.pr_mut_dist=sp.stats.rv_discrete(name='pr_mut_dist',
125+
self.pr_mut_dist=stats.rv_discrete(name='pr_mut_dist',
123126
values=(xk, pk1))
124-
self.pr_cr_dist=sp.stats.rv_discrete(name='pr_cr_dist',
127+
self.pr_cr_dist=stats.rv_discrete(name='pr_cr_dist',
125128
values=(xk, pk2))
126129

127130
# Calculate the fitness of each individual of the population
128131
deffitness(self, g, nrun, ff=ffs.fitness_func_1):
129132
total_fitness=0
130133

134+
self.logger.debug("Getting fitness of generation %d"%g)
135+
131136
# Step through each bitstring in the population
132137
fori, bitstringinenumerate(self.population):
133138
# Get the integer value of the string
134139
bit_sum=bitstring.uint
135-
fitness_val=ff(bit_sum, self.l)
140+
self.logger.debug("Sum of bitstring at %d of population: %d"% (i, bit_sum))
141+
fitness_val=self.ff(bit_sum, self.l)
136142
self.orig_fitness_vals[g][i] =fitness_val
143+
self.logger.debug("Fitness Value at index %d in population: %lf"% (i, fitness_val))
137144
total_fitness+=fitness_val
138145

146+
self.logger.debug("Total fitness from step 1: %lf"%total_fitness)
147+
148+
self.norm_fitness_vals[g] =self.orig_fitness_vals[g] /total_fitness
149+
self.logger.debug("Sum of norm fitness vals: %lf"%np.sum(self.norm_fitness_vals[g]))
150+
151+
prev_norm_fitness_val=0
139152
foriinrange(self.N):
140-
norm_fitness_val= (self.orig_fitness_vals[g][i] /total_fitness)
141-
self.norm_fitness_vals[g][i] =norm_fitness_val
142-
ifi!=0:
143-
self.total_fitness_vals[g][i] = (
144-
self.norm_fitness_vals[g][i-1] +norm_fitness_val)
145-
else:
146-
self.total_fitness_vals[g][i] =norm_fitness_val
153+
self.logger.debug("Normalized Fitness Value at index %d in population: %lf"% (i, self.norm_fitness_vals[g][i]))
154+
self.total_fitness_vals[g][i] = (
155+
self.norm_fitness_vals[g][i] +prev_norm_fitness_val)
156+
prev_norm_fitness_val=self.total_fitness_vals[g][i]
157+
self.logger.debug("Total Fitness Value at index %d in population: %lf"% (i, self.total_fitness_vals[g][i]))
147158

148159
# Select parents from population
149160
defselect(self, g):
150161
rand_nums=np.random.uniform(0, 1, 2)
151162

152163
# Select the first parent
164+
self.logger.info("Selecting the first parent...")
153165
prev_individual_fit=0
154-
forj, individual_fitinenumerate(self.total_fitness_vals[g]):
166+
j=0
167+
whileTrue:
168+
ifj>=self.N:
169+
j=0
170+
rand_nums=np.random.uniform(0, 1, 2)
171+
172+
individual_fit=self.total_fitness_vals[g][j]
173+
ifrand_nums[0] <self.total_fitness_vals[g][0]:
174+
self.logger.debug("1: Prev_Individual Fit: %lf"%prev_individual_fit)
175+
self.logger.debug("1: Individual Fit: %lf"%individual_fit)
176+
self.logger.debug("1: Rand Num: %lf"%rand_nums[0])
177+
self.logger.debug("1: Population at %d: %s"% (j, self.population[j].bin))
178+
self.parents[g][0] =self.population[0]
179+
break
180+
155181
ifj!=0:
182+
self.logger.debug("1: Prev_Individual Fit: %lf"%prev_individual_fit)
183+
self.logger.debug("1: Individual Fit: %lf"%individual_fit)
184+
self.logger.debug("1: Rand Num: %lf"%rand_nums[0])
185+
self.logger.debug("1: Population at %d: %s"% (j, self.population[j].bin))
186+
156187
if (prev_individual_fit<=rand_nums[0] <=individual_fit):
157-
self.parents[g][0] =individual_fit
188+
self.logger.debug("1: selected individuval from population at %d: %s"% (j, self.population[j].bin))
189+
self.parents[g][0] =self.population[j]
190+
self.logger.debug("1: parents[%d][0]: %s"% (g, str(self.parents[g][0])))
191+
break
158192
prev_individual_fit=individual_fit
193+
j+=1
194+
self.logger.info("First parent has been selected.")
159195

160-
# Select the second parents
196+
# Select the second parent
197+
self.logger.info("Selecting the second parent...")
161198
prev_individual_fit=0
162-
forj, individual_fitinenumerate(self.total_fitness_vals[g]):
199+
j=0
200+
cycles=0
201+
whileTrue:
202+
ifj>=self.N:
203+
cycles+=j
204+
ifcycles>=100:
205+
self.parents[g][1] =self.parents[g][0]
206+
break
207+
else:
208+
j=0
209+
rand_nums=np.random.uniform(0, 1, 2)
210+
211+
individual_fit=self.total_fitness_vals[g][j]
212+
ifrand_nums[1] <self.total_fitness_vals[g][0]:
213+
self.logger.debug("2: prev_individual fit: %lf"%prev_individual_fit)
214+
self.logger.debug("2: individual fit: %lf"%individual_fit)
215+
self.logger.debug("2: rand num: %lf"%rand_nums[1])
216+
self.logger.debug("2: population at %d: %s"% (j, self.population[j].bin))
217+
self.parents[g][1] =self.population[0]
218+
break
219+
163220
ifj!=0:
221+
self.logger.debug("2: prev_individual fit: %lf"%prev_individual_fit)
222+
self.logger.debug("2: individual fit: %lf"%individual_fit)
223+
self.logger.debug("2: rand num: %lf"%rand_nums[1])
224+
self.logger.debug("2: population at %d: %s"% (j, self.population[j].bin))
225+
164226
if (prev_individual_fit<=rand_nums[1] <=individual_fit):
165-
if (individual_fit!=self.parents[g][0]):
166-
self.parents[g][1] =individual_fit
227+
if (self.population[j] !=self.parents[g][0]):
228+
self.logger.debug("2: selected individuval from population at %d: %s"% (j, self.population[j].bin))
229+
self.parents[g][1] =self.population[j]
230+
self.logger.debug("1: parents[%d][0]: %s"% (g, str(self.parents[g][1])))
231+
break
232+
167233
prev_individual_fit=individual_fit
234+
j+=1
235+
236+
self.logger.info("Second parent has been selected.")
168237

169238
# Mutate the parents
170239
defmutate(self, g):
@@ -178,9 +247,10 @@ def mutate(self, g):
178247
ifto_mutate:
179248
parent.invert(index_bit)
180249

250+
181251
# Crossover the parents
182252
defcrossover(self, g):
183-
to_crossover=self.pr_cross_dist.rvs(size=1)
253+
to_crossover=self.pr_cr_dist.rvs(size=1)
184254

185255
# Crossover the parents if to_crossover is 1, otherwise copy the
186256
# parents exactly into the children
@@ -190,13 +260,13 @@ def crossover(self, g):
190260
c2=bs.BitArray(length=self.N)
191261

192262
# Select the bit at which to crossover the parents
193-
crossover_bit=random.randint(0, self.N)
263+
crossover_bit=random.randint(0, self.l)
194264

195265
# Perform the crossover
196266
c1.overwrite(self.parents[g][0][:crossover_bit], 0)
197-
c1.overwrite(self.parents[g][1][:crossover_bit], crossover_bit)
267+
c1.overwrite(self.parents[g][1][:(self.l-crossover_bit)], crossover_bit)
198268
c2.overwrite(self.parents[g][1][:crossover_bit], 0)
199-
c2.overwrite(self.parents[g][0][:crossover_bit], crossover_bit)
269+
c1.overwrite(self.parents[g][0][:(self.l-crossover_bit)], crossover_bit)
200270

201271
self.current_offspring.append(c1)
202272
self.current_offspring.append(c2)
@@ -229,7 +299,7 @@ def learn_offspring(self, g, ff=ffs.fitness_func_1):
229299
ifcurrent_fitness==max_fitness:
230300
child=current_child
231301

232-
defcompute_statistics(self, g, nrun, env_state):
302+
defcompute_statistics(self, g, nrun):
233303
# Get the number of correct bits in the best individual
234304
index_bi=self.orig_fitness_vals[g].argmax()
235305
bi_bitstring=self.population[index_bi]
@@ -253,8 +323,10 @@ def compute_statistics(self, g, nrun, env_state):
253323
# Check if the population has recovered from an environment change
254324
defcheck_population_recovery(self, g, nrun):
255325
checks= []
256-
checks.append(self.best_fitness_vals[nrun][self.env_state][g] >self.best_fitness_vals[nrun][self.env_state-1][self.G-1])
257-
checks.append(self.avg_fitness_vals[nrun][self.env_state][g] >self.avg_fitness_vals[nrun][self.env_state-1][self.G-1])
326+
checks.append((self.best_fitness_vals[nrun][1][g] >self.best_fitness_vals[nrun][0][self.G-1]))
327+
checks.append((self.avg_fitness_vals[nrun][1][g] >self.avg_fitness_vals[nrun][0][self.G-1]))
328+
forcheckinchecks:
329+
printcheck
258330
ifall(checks):
259331
returnTrue
260332

@@ -291,38 +363,48 @@ def reproduce(self, nrun, g):
291363
" of generation %d finished."%g)
292364

293365
# Compute statistics for this generation
294-
self.logger.info("Computing statistics for Run %d, "+
295-
"Generation %d..."%nrun, g)
366+
self.logger.info("Computing statistics for Run %d, Generation %d..."% (nrun, g))
296367
self.compute_statistics(g, nrun)
297-
self.logger.info("Computing statistics for Run %d, "+
298-
"Generation %d."%nrun, g)
368+
self.logger.info("Computation of statistics finished for Run %d, Generation %d."% (nrun, g))
299369

300370
# Replace the old population with the new population
301371
self.population=self.current_offspring
302-
self.logger.info("Generation %d finished."%g)
372+
self.current_offspring= []
303373

304374
# Run through the total runs specified
305375
defrun(self):
306-
fornruninxrange(0, self.nruns):
376+
fornruninrange(self.nruns):
307377
self.logger.info("Starting run %d..."%nrun)
308378
self.initialize_algorithm()
309379

310-
forginxrange(0, self.G):
380+
self.env_state=0
381+
self.ff=ffs.fitness_func_1
382+
self.logger.info("Fitness Function before normal generation run: %s"%str(self.ff.__name__))
383+
384+
forginrange(self.G):
385+
self.logger.info("Generation %d running..."%g)
311386
self.reproduce(nrun, g)
387+
self.logger.info("Generation %d finished."%g)
312388

313389
ifself.ce:
314-
self.logger.info("Running Sudden change in environment test...")
390+
self.logger.info("Running Sudden change in environment test starting at generation %d..."%g)
315391
self.env_state=1
316392
self.initialize_env()
393+
self.ff=ffs.fitness_func_2
394+
self.logger.info("Fitness Function before changed environment generation run: %s"%str(self.ff.__name__))
317395

318396
whileTrue:
397+
g+=1
398+
self.recovery_time+=1
399+
self.logger.info("Recovery Time: %d, Max Recovery: %d, Generation %d In Changed Environment"% (self.recovery_time, self.max_recovery, g))
400+
ifself.recovery_time>=self.max_recovery:
401+
self.logger.info("Population has not recovered after %d iterations. Quitting now."%self.recovery_time)
402+
break
319403
self.reproduce(nrun, g)
320404
ifself.check_population_recovery(g, nrun):
321-
self.logger.info("Population has recovered after %d "+
322-
"iterations."%self.recovery_time)
405+
self.logger.info("Population has recovered after %d iterations."%self.recovery_time)
323406
break
324407
self.logger.info("Population has not recovered...continuing generation.")
325-
self.recovery_time+=1
326408

327409
self.logger.info("Finished run %d."%nrun)
328410

0 commit comments

Comments
(0)