Skip to content

Commit

Permalink
Model testing + updated Readme
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxHalford committed Aug 13, 2016
1 parent 162a0c8 commit a886433
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 28 deletions.
37 changes: 31 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ import (
)

// Sphere function minimum is 0 reached in (0, ..., 0).
// Any search domain is fine.
func sphere(X []float64) float64 {
sum := 0.0
for _, x := range X {
Expand All @@ -80,18 +81,26 @@ func sphere(X []float64) float64 {

func main() {
// Instantiate a GA with 2 variables and the fitness function
var ga = presets.Float(2, sphere)
var ga = presets.Float64(2, sphere)
ga.Initialize()
// Enhancement
for i := 0; i < 1000; i++ {
ga.Enhance()
for i := 0; i < 10; i++ {
ga.Enhance()
// Display the current best solution
fmt.Printf("The best obtained solution is %f\n", ga.Best.Fitness)
}
// Display the best obtained solution
fmt.Printf("The best obtained solution is %f\n", ga.Best.Fitness)
}
```

A preset is simply a genetic algorithm configuration. It's unlikely that a preset will find an optimal solution as is. Presets should be considered as starting points and should be tuned for specific problems.
<br/>

<div align="center">
<img src="docs/img/command-line.png" alt="command_line" />
</div>

<br/>

A preset is simply a genetic algorithm configuration. It's unlikely that a preset will find an optimal solution as is. Presets should be considered as starting points and should be tuned for specific problems. The following is the preset that was used for the previous example.

```go
// Float returns a configuration for minimizing continuous mathematical
Expand Down Expand Up @@ -125,6 +134,20 @@ func Float(n int, function func([]float64) float64) gago.GA {
}
```

## Why use gago?

- It's architectured in a modular way.
- It allows using different evolutionary models.
- It's ambitious in the sense that it wants to implement every kind of operator possible.
- It allows implementing custom genetic operators.
- It makes it possible to use speciation.
- It makes it possible to run multiple populations in parallel (and to add migration).
- It's open to suggestions and to improvements.
- It's heavily commented.
- It has no external dependencies.
- It's got a high test coverage.
- It's actively maintained and will remain one my priorities for a very long time.

## Alternatives

- [GeneticGo](https://github.com/handcraftsman/GeneticGo)
Expand All @@ -134,3 +157,5 @@ func Float(n int, function func([]float64) float64) gago.GA {
## Contact

Feel free to contact me at **maxhalford25@gmail.com** for any enquiries.

You can also ask questions on [codewake](https://www.codewake.com/p/gago).
2 changes: 1 addition & 1 deletion ga_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func TestValidationSuccess(t *testing.T) {
}
}

func TestValidationNbrCluster(t *testing.T) {
func TestValidationNbrClusters(t *testing.T) {
// Check invalid number of clusters
ga.NbrClusters = -1
if ga.Validate() == nil {
Expand Down
38 changes: 19 additions & 19 deletions individual.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ type Individual struct {
Name string
}

// Generate a new individual.
func makeIndividual(nbGenes int, rng *rand.Rand) Individual {
return Individual{
Genome: make([]interface{}, nbGenes),
Fitness: math.Inf(1),
Evaluated: false,
Name: randomString(6, rng),
}
}

// Evaluate the fitness of an individual.
func (indi *Individual) Evaluate(ff FitnessFunction) {
// Don't evaluate individuals that have already been evaluated
Expand All @@ -39,19 +49,18 @@ func (indi *Individual) Mutate(mutator Mutator, rng *rand.Rand) {
indi.Evaluated = false
}

// Generate a new individual.
func makeIndividual(nbGenes int, rng *rand.Rand) Individual {
return Individual{
Genome: make([]interface{}, nbGenes),
Fitness: math.Inf(1),
Evaluated: false,
Name: randomString(6, rng),
}
}

// Individuals type is necessary for sorting and selection purposes.
type Individuals []Individual

// Generate a slice of new individuals.
func makeIndividuals(nbIndis, nbGenes int, rng *rand.Rand) Individuals {
var indis = make(Individuals, nbIndis)
for i := range indis {
indis[i] = makeIndividual(nbGenes, rng)
}
return indis
}

// Evaluate each individual
func (indis Individuals) Evaluate(ff FitnessFunction) {
for i := range indis {
Expand All @@ -68,15 +77,6 @@ func (indis Individuals) Mutate(mutator Mutator, mutRate float64, rng *rand.Rand
}
}

// Generate a slice of new individuals.
func makeIndividuals(nbIndis, nbGenes int, rng *rand.Rand) Individuals {
var indis = make(Individuals, nbIndis)
for i := range indis {
indis[i] = makeIndividual(nbGenes, rng)
}
return indis
}

// Sort the individuals of a population in ascending order based on their
// fitness. The convention is that we always want to minimize a function. A
// function f(x) can be function maximized by minimizing -f(x) or 1/f(x).
Expand Down
4 changes: 2 additions & 2 deletions models.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,9 +317,9 @@ func (mod ModMutationOnly) Apply(pop *Population) {
)
// The length of the new slice of individuals varies if the parents are kept or not
if mod.KeepParents {
offsprings = make(Individuals, mod.NbrParents*mod.NbrOffsprings)
} else {
offsprings = make(Individuals, mod.NbrParents*mod.NbrOffsprings+mod.NbrParents)
} else {
offsprings = make(Individuals, mod.NbrParents*mod.NbrOffsprings)
}
// Generate offsprings for each parent by copying the parent and then mutating it
for _, parent := range parents {
Expand Down
116 changes: 116 additions & 0 deletions models_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package gago

import (
"math/rand"
"testing"
"time"
)

func TestGenerateOffsprings(t *testing.T) {
var (
N = []int{0, 1, 3, 10}
indis = makeIndividuals(10, 2, rand.New(rand.NewSource(time.Now().UnixNano())))
sel = SelTournament{3}
cross = CrossPoint{2}
rng = rand.New(rand.NewSource(time.Now().UnixNano()))
)
for _, n := range N {
var offsprings = generateOffsprings(n, indis, sel, cross, rng)
if len(offsprings) != n {
t.Error("GenerateOffsprings didn't produce the expected number of offsprings")
}
}
}

func TestConstantSizeModels(t *testing.T) {
var (
// Testing framework for each model
nbIndis = 10
ff = Float64Function{func(X []float64) float64 {
sum := 0.0
for _, x := range X {
sum += x
}
return sum
}}
init = InitUniformF{
Lower: -1,
Upper: 1,
}
// Model configurations
models = []Model{
ModGenerational{
Selector: SelTournament{3},
Crossover: CrossPoint{2},
Mutator: MutNormalF{0.1, 1},
MutRate: 0.2,
},
ModSteadyState{
Selector: SelTournament{3},
Crossover: CrossPoint{2},
KeepBest: false,
Mutator: MutNormalF{0.1, 1},
MutRate: 0.2,
},
ModSteadyState{
Selector: SelTournament{3},
Crossover: CrossPoint{2},
KeepBest: true,
Mutator: MutNormalF{0.1, 1},
MutRate: 0.2,
},
ModDownToSize{
NbrOffsprings: 5,
SelectorA: SelTournament{3},
Crossover: CrossPoint{2},
SelectorB: SelElitism{},
Mutator: MutNormalF{0.1, 1},
MutRate: 0.2,
},
ModRing{
Crossover: CrossPoint{2},
Selector: SelTournament{3},
Mutator: MutNormalF{0.1, 1},
MutRate: 0.2,
},
ModSimAnn{
Mutator: MutNormalF{0.1, 1},
T: 10,
Tmin: 1,
Alpha: 0.3,
},
ModMutationOnly{
NbrParents: 3,
Selector: SelTournament{2},
KeepParents: false,
NbrOffsprings: 2,
Mutator: MutNormalF{0.1, 1},
},
ModMutationOnly{
NbrParents: 3,
Selector: SelTournament{2},
KeepParents: true,
NbrOffsprings: 2,
Mutator: MutNormalF{0.1, 1},
},
}
)
for _, model := range models {
// Check the model parameters are valid
var err = model.Validate()
if err != nil {
t.Error("The model doesn't contain valid parameters")
}
// Check the number of individuals didn't change
var pop = makePopulation(nbIndis, 4, ff, init)
model.Apply(&pop)
var size = len(pop.Individuals)
// Check the size of the population doesn't change
for i := 0; i < 5; i++ {
model.Apply(&pop)
if len(pop.Individuals) != size {
t.Error("The size of the population was modified")
}
}
}
}

0 comments on commit a886433

Please sign in to comment.