Genetic algorithm help

BlitzPlus Forums/BlitzPlus Programming/Genetic algorithm help

schilcote(Posted 2009) [#1]
A while ago, I got interested in advanced artificial intelligence. I read a tutorial on neural networks, and that tutorial told me to learn genetic algorithms first. So I looked up the same guy's tutorial on genetic algorithms, and wrote this.

AppTitle "Genetic algorithm test"

Const NUMGENES=100
Const ANSWER=42
Const MUTATIONRATE=50
Const DEATHFITNESS=100
Const DEATHAGE=1000


Type gene
Field number1
Field operator1
Field number2
Field operator2
Field number3
Field operator3
Field number4
Field fitness
Field age
End Type


SeedRnd MilliSecs()

For t=1 To NUMGENES
	phenotype.gene=New gene
	phenotype\number1=Rnd(1,100)
	phenotype\operator1=Rnd(1,4)
	phenotype\number2=Rnd(1,100)
	phenotype\operator2=Rnd(1,4)
	phenotype\number3=Rnd(1,100)
	phenotype\operator3=Rnd(1,4)
	phenotype\number4=Rnd(1,100)
	phenotype\fitness=0
	phenotype\age=0
Next


While Not KeyDown(1)
	
	For phenotype.gene=Each gene
		
		Select phenotype\operator1
		
			Case 1
			newnumber1=phenotype\number1+phenotype\number2
			
			Case 2
			newnumber1=phenotype\number1-phenotype\number2
			
			Case 3
			newnumber1=phenotype\number1*phenotype\number2
			
			Case 4
			If phenotype\number1>0 And phenotype\number2>0 Then
				newnumber1=phenotype\number1/phenotype\number2
			EndIf
			
		End Select
		
		Select phenotype\operator2
		
			Case 1
			newnumber2=newumber1+phenotype\number3
			
			Case 2
			newnumber2=newnumber1-phenotype\number3
			
			Case 3
			newnumber2=newnumber1*phenotype\number3
			
			Case 4
			If newnumber1>0 And phenotype\number3>0 Then
				newnumber2=newnumber1/phenotype\number3
			EndIf
			
		End Select
		
		Select phenotype\operator3
		
			Case 1
			newnumber3=newumber2+phenotype\number4
			
			Case 2
			newnumber3=newnumber2-phenotype\number4
			
			Case 3
			newnumber3=newnumber2*phenotype\number4
			
			Case 4
			If newnumber2>0 And phenotype\number4>0 Then
				newnumber3=newnumber2/phenotype\number4
			EndIf
		
		End Select
		
		phenotype\fitness=ANSWER-newnumber3
		
		phenotype\age=phenotype\age+1
		
		If phenotype\fitness=0 Then
			Exit
		EndIf
		
		If Abs(phenotype\fitness) > DEATHFITNESS Then
			Delete phenotype.gene
		ElseIf phenotype\age > DEATHAGE Then
			Delete phenotype.gene
		EndIf
		

		
	Next
	
	For phenotype.gene=Each gene
		
		
		If Rnd(1,Abs(phenotype\fitness))=1 Then
			geneA.gene=phenotype.gene
			Exit
		EndIf
	Next
	
	For phenotype.gene=Each gene
		
		If Rnd(1,Abs(phenotype\fitness))=1 Then
			geneB.gene=phenotype.gene
			Exit
		EndIf
	Next
	
	If geneA <> Null And geneB <> Null Then
		
		phenotype.gene=New gene
		
		If Rand(1,2)=1 Then
			phenotype\number1=geneA\number1
		Else
			phenotype\number1=geneB\number1
		EndIf
	
		If Rand(1,2)=1 Then
			phenotype\operator1=geneA\operator1
		Else
			phenotype\operator1=geneB\operator1
		EndIf
		
		If Rand(1,2)=1 Then
			phenotype\number2=geneA\number2
		Else
			phenotype\number2=geneB\number2
		EndIf
	
		If Rand(1,2)=1 Then
			phenotype\operator2=geneA\operator2
		Else
			phenotype\operator2=geneB\operator2
		EndIf
		
		If Rand(1,2)=1 Then
			phenotype\number3=geneA\number3
		Else
			phenotype\number3=geneB\number3
		EndIf
	
		If Rand(1,2)=1 Then
			phenotype\operator3=geneA\operator3
		Else
			phenotype\operator3=geneB\operator3
		EndIf
		
		If Rand(1,2)=1 Then
			phenotype\number4=geneA\number4
		Else
			phenotype\number4=geneB\number4
		EndIf
		
		
		
		If Rand(1,MUTATIONRATE)=1 Then
			If Rand(1,2)=1 Then
				phenotype\number1=phenotype\number1+Rand(1,20)
			Else
				phenotype\number1=phenotype\number1-Rand(1,20)
			EndIf
		EndIf
		
		If Rand(1,MUTATIONRATE)=1 Then
				phenotype\operator1=Rand(1,4)
		EndIf
		
		If Rand(1,MUTATIONRATE)=1 Then
			If Rand(1,2)=1 Then
				phenotype\number2=phenotype\number2+Rand(1,20)
			Else
				phenotype\number2=phenotype\number2-Rand(1,20)
			EndIf
		EndIf
		
		If Rand(1,MUTATIONRATE)=1 Then
				phenotype\operator2=Rand(1,4)
		EndIf
		
		If Rand(1,MUTATIONRATE)=1 Then
			If Rand(1,2)=1 Then
				phenotype\number3=phenotype\number3+Rand(1,20)
			Else
				phenotype\number3=phenotype\number3-Rand(1,20)
			EndIf
		EndIf
		
		If Rand(1,MUTATIONRATE)=1 Then
				phenotype\operator3=Rand(1,4)
		EndIf
		
		If Rand(1,MUTATIONRATE)=1 Then
			If Rand(1,2)=1 Then
				phenotype\number4=phenotype\number4+Rand(1,20)
			Else
				phenotype\number4=phenotype\number4-Rand(1,20)
			EndIf
		EndIf
	EndIf
		
Wend
		
				
				
If phenotype\fitness <> 0 Then
	End
EndIf

Print "Solution found!"

stri$=stri$+phenotype\number1

Select phenotype\operator1
	
	Case 1
	stri$=stri$+"+"	
	
	Case 2
	stri$=stri$+"-"	
	
	Case 3
	stri$=stri$+"*"
	
	Case 4
	stri$=stri$+"/"	
	
End Select				
				
stri$=stri$+phenotype\number2

Select phenotype\operator2
	
	Case 1
	stri$=stri$+"+"	
	
	Case 2
	stri$=stri$+"-"	
	
	Case 3
	stri$=stri$+"*"
	
	Case 4
	stri$=stri$+"/"	
	
End Select

stri$=stri$+phenotype\number3

Select phenotype\operator3
	
	Case 1
	stri$=stri$+"+"	
	
	Case 2
	stri$=stri$+"-"	
	
	Case 3
	stri$=stri$+"*"
	
	Case 4
	stri$=stri$+"/"	
	
End Select	

stri$=stri$+phenotype\number4

stri$=stri$+"="+ANSWER+"!"

WaitKey 
End	




(I know waitkey won't work unless I set the graphics mode, I just have it in there so I can read my stuff and click the close button, habit probably has something to do with it too.)

It compiles and goes about its job, but I always end up with either of two end results, both undesirable.

Either A: All "genes" die (which I can fix, but B is more important)
or B: I end up with a couple of "genes" that are one digit away from being right and never go any farther (which I can't seem to fix).

The goal is to find four numbers and operators that when combined make up the number ANSWER, in my code 42. It never gets to 42, it just stays one digit away until the "genes" get too old (DEATHAGE, 1000 computations) and die and I am left with nothing.

The tutorial only explained the mathematical principles behind a genetic algorithm (and some C code). The code I have here is completely written by scratch from me.

Does anyone know what is wrong with it?


Sauer(Posted 2009) [#2]
I don't know anything about this sort of algorithm, but I'll do a little research myself and get back to you if I find an answer.

Never hurts to learn something new.

EDIT: Can you supply a link to the tutorial you were working from? I'm struggling to see a relationship between thing's I'm seeing on the internet and your code.

Something you might want to try is printing out values of each phenotype on each iteration of the loop, and slow it down or pause it for each iteration. You might find that some of the values are not what you'd expect them to be.

When it comes to debugging, print statements are your best friends. That is, unless you're writing shaders, where they don't exist ;)


schilcote(Posted 2009) [#3]
Excuse me for saying this, but if you couldn't find this tutorial, you weren't looking very hard :P. It is the first google result on the subject.

http://www.ai-junkie.com/ga/intro/gat1.html

I wrote the code about a year ago, I did some very extensive debugging but I don't remember it all. I'll try working on it some more and see what I get.

EDIT:

Hmm... Weird crap is happening. I ran the program, waited a few seconds, and clicked the stop button. My computer can theoretically do 2.7 billion computations in a second, so I considered this a relatively long time in computer math terms. I saw the usual pattern of a couple genes with a fitness of one. But then, I let it go for a few seconds more and clicked stop again. This time, there were genes with a fitness of 0. A bug in the mutation code seems to have frozen the program at that point (it kept doing rand(0,1) and waited for it to return 1...) but when I computed the gene myself, they were way, way off. Usually around positive or negative 200.

I'll try fixing the bug that stops the program, but I don't know how they got fitnesses of 0 when they were more than 100 degrees off...

EDIT:

Solution found!
56-75*99+42=42!

Huh. It turns out that the code that detects a fitness value of 0 didn't actually do anything after detecting it except exit(), which merely skipped over the code that increments age and kills off genes that are too old or unfit. However, fixing this problem results in the above result, which is quite obviously incorrect. I'll work on it some more...

EDIT:

I see a pattern. It either reaches extinction or finds an answer ending in 42. I guess I forgot an operation or something.

EDIT:

Okay, I fixed all bugs mentioned up till now, including extinction. Now it always produces an answer. However, the answer is only right around 1/4 of the time.

66-40/99+42=107.59595959595959595959595959596
58+56-86/75=112.85333333333333333333333333333
32-79/55+28=58.563636363636363636363636363636
79-46-29+38=42
71-44/8*14=-6
2-68+23+85=42
11*3-16+25=42
37*91-30/79=3366.6202531645569620253164556962
80+21+25/3=109.33333333333333333333333333333
72+28/48*21=84.25

Those are the numbers it gives me (checked with windows calc, it turns out you can actually copy and paste right from the program into calc).

AppTitle "Genetic algorithm test"

Const NUMGENES=100
Const ANSWER=42
Const MUTATIONRATE=50
Const DEATHFITNESS=100
Const DEATHAGE=1000


Type gene
Field number1
Field operator1
Field number2
Field operator2
Field number3
Field operator3
Field number4
Field fitness
Field age
End Type


SeedRnd MilliSecs()



While Not SolutionFound

	If extinction Then
			For t=1 To NUMGENES
			phenotype.gene=New gene
			phenotype\number1=Rnd(1,100)
			phenotype\operator1=Rnd(1,4)
			phenotype\number2=Rnd(1,100)
			phenotype\operator2=Rnd(1,4)
			phenotype\number3=Rnd(1,100)
			phenotype\operator3=Rnd(1,4)
			phenotype\number4=Rnd(1,100)
			phenotype\fitness=0
			phenotype\age=0
		Next
	
		extinction=1
		
	EndIf
	
	For phenotype.gene=Each gene
		
		
		Select phenotype\operator1
		
			Case 1
			newnumber1=phenotype\number1+phenotype\number2
			
			Case 2
			newnumber1=phenotype\number1-phenotype\number2
			
			Case 3
			newnumber1=phenotype\number1*phenotype\number2
			
			Case 4
			If phenotype\number1>0 And phenotype\number2>0 Then
				newnumber1=phenotype\number1/phenotype\number2
			EndIf
			
		End Select
		
		Select phenotype\operator2
		
			Case 1
			newnumber2=newnumber1+phenotype\number3
			
			Case 2
			newnumber2=newnumber1-phenotype\number3
			
			Case 3
			newnumber2=newnumber1*phenotype\number3
			
			Case 4
			If newnumber1>0 And phenotype\number3>0 Then
				newnumber2=newnumber1/phenotype\number3
			EndIf
			
		End Select
		
		Select phenotype\operator3
		
			Case 1
			newnumber3=newnumber2+phenotype\number4
			
			Case 2
			newnumber3=newnumber2-phenotype\number4
			
			Case 3
			newnumber3=newnumber2*phenotype\number4
			
			Case 4
			If newnumber2>0 And phenotype\number4>0 Then
				newnumber3=newnumber2/phenotype\number4
			EndIf
		
		End Select
		
		phenotype\fitness=ANSWER-newnumber3
		
		phenotype\age=phenotype\age+1
		
		If phenotype\fitness=0 Then
			;Goto SolutionFound
			SolutionFound=True
		EndIf
		
		If Abs(phenotype\fitness) > DEATHFITNESS Then
			Delete phenotype.gene
		ElseIf phenotype\age > DEATHAGE Then
			Delete phenotype.gene
		EndIf
		

		
	Next
	
	For phenotype.gene=Each gene
		
		
		If Rnd(0,Abs(phenotype\fitness))=0 Then
			geneA.gene=phenotype.gene
			Exit
		EndIf
	Next
	
	For phenotype.gene=Each gene
		
		If Rnd(0,Abs(phenotype\fitness))=0 Then
			geneB.gene=phenotype.gene
			Exit
		EndIf
	Next
	
	If geneA <> Null And geneB <> Null Then
		
		phenotype.gene=New gene
		
		If Rand(1,2)=1 Then
			phenotype\number1=geneA\number1
		Else
			phenotype\number1=geneB\number1
		EndIf
	
		If Rand(1,2)=1 Then
			phenotype\operator1=geneA\operator1
		Else
			phenotype\operator1=geneB\operator1
		EndIf
		
		If Rand(1,2)=1 Then
			phenotype\number2=geneA\number2
		Else
			phenotype\number2=geneB\number2
		EndIf
	
		If Rand(1,2)=1 Then
			phenotype\operator2=geneA\operator2
		Else
			phenotype\operator2=geneB\operator2
		EndIf
		
		If Rand(1,2)=1 Then
			phenotype\number3=geneA\number3
		Else
			phenotype\number3=geneB\number3
		EndIf
	
		If Rand(1,2)=1 Then
			phenotype\operator3=geneA\operator3
		Else
			phenotype\operator3=geneB\operator3
		EndIf
		
		If Rand(1,2)=1 Then
			phenotype\number4=geneA\number4
		Else
			phenotype\number4=geneB\number4
		EndIf
		
		
		
		If Rand(1,MUTATIONRATE)=1 Then
			If Rand(1,2)=1 Then
				phenotype\number1=phenotype\number1+Rand(1,20)
			Else
				phenotype\number1=phenotype\number1-Rand(1,20)
			EndIf
		EndIf
		
		If Rand(1,MUTATIONRATE)=1 Then
				phenotype\operator1=Rand(1,4)
		EndIf
		
		If Rand(1,MUTATIONRATE)=1 Then
			If Rand(1,2)=1 Then
				phenotype\number2=phenotype\number2+Rand(1,20)
			Else
				phenotype\number2=phenotype\number2-Rand(1,20)
			EndIf
		EndIf
		
		If Rand(1,MUTATIONRATE)=1 Then
				phenotype\operator2=Rand(1,4)
		EndIf
		
		If Rand(1,MUTATIONRATE)=1 Then
			If Rand(1,2)=1 Then
				phenotype\number3=phenotype\number3+Rand(1,20)
			Else
				phenotype\number3=phenotype\number3-Rand(1,20)
			EndIf
		EndIf
		
		If Rand(1,MUTATIONRATE)=1 Then
				phenotype\operator3=Rand(1,4)
		EndIf
		
		If Rand(1,MUTATIONRATE)=1 Then
			If Rand(1,2)=1 Then
				phenotype\number4=phenotype\number4+Rand(1,20)
			Else
				phenotype\number4=phenotype\number4-Rand(1,20)
			EndIf
		EndIf
	EndIf
		
If phenotype=Null Then
	extinction=1
EndIf

Wend
		
.SolutionFound				
				
If phenotype\fitness <> 0 Then
	End
EndIf

Print "Solution found!"

stri$=stri$+phenotype\number1

Select phenotype\operator1
	
	Case 1
	stri$=stri$+"+"	
	
	Case 2
	stri$=stri$+"-"	
	
	Case 3
	stri$=stri$+"*"
	
	Case 4
	stri$=stri$+"/"	
	
End Select				
				
stri$=stri$+phenotype\number2

Select phenotype\operator2
	
	Case 1
	stri$=stri$+"+"	
	
	Case 2
	stri$=stri$+"-"	
	
	Case 3
	stri$=stri$+"*"
	
	Case 4
	stri$=stri$+"/"	
	
End Select

stri$=stri$+phenotype\number3

Select phenotype\operator3
	
	Case 1
	stri$=stri$+"+"	
	
	Case 2
	stri$=stri$+"-"	
	
	Case 3
	stri$=stri$+"*"
	
	Case 4
	stri$=stri$+"/"	
	
End Select	

stri$=stri$+phenotype\number4

stri$=stri$+"="+ANSWER+"!"

Print Stri$

WaitKey 
End	


		



Sauer(Posted 2009) [#4]
Excuse me for saying this, but if you couldn't find this tutorial, you weren't looking very hard :P. It is the first google result on the subject.


Well, since I don't know the exact keywords you used in your search, it's difficult to make that accusation. It appears third when searching "genetic algorithm" on Google.

It seems you're well on your way, keep at it.


schilcote(Posted 2009) [#5]
Schilcote won the battle!
Schilcote gained 1337 EXP
Schilcote leveled up!
Schilcote learned a new skill: Genetic Algorithms!

Yay, I actually learned something new from debugging this program. When you're dividing two uncontrolled numbers, use a float.

There's still the occasional wrong answer, but it occurs very rarely now. It seems to find the answer pretty fast. Now I'm going to try changing the answer value and see how it performs.