Implementation of a Simple Binary Genome hill climber. The fitness is determined simply by how many 1's are turned on in the genome of the child. So a perfect fitness is 100.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"math/rand" | |
"time" | |
"runtime" | |
) | |
/* HillClimber | |
* By Ethan J Eldridge. | |
* Keep the population size great than 25 or you might have issues. | |
*/ | |
var populationSize int; //How many there are | |
var genomeSize int; //How big their genome is | |
var generations int; //How many generations to run | |
type Child struct{ | |
genome []int | |
resultChan chan int | |
fitness int | |
} | |
func (c* Child) init(resChan * chan int) { | |
c.resultChan = *resChan | |
c.genome = make([]int, genomeSize) | |
initilizeChannel := make(chan int, genomeSize) | |
for i := 0; i < genomeSize; i++ { | |
i := i //overshadow local | |
go func(){ | |
c.genome[i] = rand.Intn(2) | |
initilizeChannel <- c.genome[i] | |
}() | |
} | |
//Compute initial fitness from initalization | |
c.fitness = 0 | |
for i := 0; i < genomeSize; i++ { | |
c.fitness += <- initilizeChannel | |
} | |
} | |
func (c* Child) CopyTo(o * Child){ | |
o.genome = c.genome | |
o.fitness = c.fitness | |
o.resultChan = c.resultChan | |
} | |
func (c* Child) Mutate() { | |
doneChannel := make(chan int, genomeSize) | |
for i := 0; i < genomeSize; i++ { | |
i := i | |
go func(){ | |
if rand.Intn(2) == 1 { | |
c.genome[i] = rand.Intn(2) | |
} | |
doneChannel <- c.genome[i] | |
}() | |
} | |
c.fitness = 0 | |
for i := 0; i < genomeSize; i++ { | |
c.fitness += <- doneChannel | |
} | |
//Send fitness out | |
c.resultChan <- c.fitness | |
} | |
func main() { | |
fmt.Printf("Simple HillClimber by Ethan J Eldridge\n SettingUsing %v CPU's\n", runtime.NumCPU()) | |
runtime.GOMAXPROCS(runtime.NumCPU()) //use all the cpus! | |
populationSize = 100 | |
genomeSize = 100 | |
generations = 50 | |
rand.Seed(time.Now().Unix()) | |
children := make([]Child, populationSize) | |
resultsChan := make(chan int, populationSize) | |
fmt.Printf(" Initializing Population...\n") | |
for i := 0; i < populationSize; i++ { | |
i := i | |
go func(){ | |
children[i].init(&resultsChan) | |
resultsChan <- children[i].fitness | |
}() | |
} | |
bestFitness := 0 | |
bestIdx := 0 | |
for i := 0; i < populationSize; i++ { | |
r := <-resultsChan | |
if r > bestFitness { | |
bestIdx = i | |
bestFitness = r | |
} | |
} | |
fmt.Printf(" Population Ready, best child fitness: %v\n", bestFitness) | |
for i := 0; i < generations; i++ { | |
fmt.Printf(" Running Generation %v\n", i) | |
for j := 0; j < populationSize; j++ { | |
j := j | |
go func(){ | |
children[j].Mutate() | |
}() | |
} | |
//Collect results of population mutation | |
for j := 0; j < populationSize; j++ { | |
tmpFitness := <- resultsChan | |
if tmpFitness > bestFitness { | |
bestFitness = tmpFitness | |
bestIdx = j | |
} | |
} | |
fmt.Printf(" Best Of Generation %v : Child: %v, fitness: %v\n", i, bestIdx,bestFitness) | |
//'Breed' new results from the best child | |
if bestIdx < populationSize/2 { | |
for i := bestIdx+1; i < bestIdx + 11; i++ { | |
children[bestIdx].CopyTo( &children[i] ) | |
children[i].Mutate() | |
<- resultsChan //discard the mutated value | |
} | |
} else { | |
for i := bestIdx-11; i < bestIdx; i++ { | |
children[bestIdx].CopyTo( &children[i] ) | |
children[i].Mutate() | |
<- resultsChan //discard the mutated value | |
} | |
} | |
} | |
fmt.Printf(" Overall Best: %v with %v fitness score\n", bestIdx, bestFitness) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment