Skip to content

Commit

Permalink
Merge pull request #13 from cbarrick/apidev
Browse files Browse the repository at this point in the history
Update the core API
  • Loading branch information
cbarrick committed Feb 10, 2016
2 parents d24a1e8 + 8747c4f commit 29bbdad
Show file tree
Hide file tree
Showing 6 changed files with 371 additions and 598 deletions.
42 changes: 19 additions & 23 deletions example/ackley/ackley_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,6 @@ var (
// them to this pool. This pool returns to each member a different one of
// most fit to be their replacement in the next generation.
selector = sel.ElitePool(40, 280)

// A free-list used to recycle memory.
vectors = sync.Pool{
New: func() interface{} {
return make(real.Vector, dim)
},
}
)

// The ackley type specifies our genome. We evolve a real-valued vector that
Expand All @@ -51,12 +44,6 @@ type ackley struct {
once sync.Once // used to compute fitness lazily
}

// Close recycles the memory of this genome to be use for new genomes.
func (ack *ackley) Close() {
vectors.Put(ack.gene)
vectors.Put(ack.steps)
}

// Returns the fitness as a string.
func (ack *ackley) String() string {
return fmt.Sprint(-ack.Fitness())
Expand Down Expand Up @@ -94,13 +81,13 @@ func (ack *ackley) Fitness() float64 {
// The population calls the Evolve method of each genome, in parallel. Then,
// each receiver returns a value to replace it in the next generation. A global
// selector object synchronises replacement among the parallel parents.
func (ack *ackley) Evolve(suitors ...evo.Genome) evo.Genome {
func Evolve(ack evo.Genome, suitors []evo.Genome) evo.Genome {
for i := 0; i < 7; i++ {
// Creation:
// We create the child genome from recycled memory when we can.
var child ackley
child.gene = vectors.Get().(real.Vector)
child.steps = vectors.Get().(real.Vector)
child.gene = make(real.Vector, dim)
child.steps = make(real.Vector, dim)

// Crossover:
// Select two parents at random.
Expand Down Expand Up @@ -136,22 +123,31 @@ func TestAckley(t *testing.T) {
// Setup:
// We initialize a set of 40 random solutions,
// then add them to a generational population.
init := make([]evo.Genome, 40)
for i := range init {
init[i] = &ackley{
seed := make([]evo.Genome, 40)
for i := range seed {
seed[i] = &ackley{
gene: real.Random(dim, 30),
steps: real.Random(dim, 1),
}
}
pop := gen.New(init)
pop.Start()
var pop gen.Population
pop.Evolve(seed, Evolve)

// Tear-down:
// Upon returning, we cleanup our resources and print the solution.
defer func() {
pop.Close()
pop.Stop()
selector.Close()
fmt.Println("\nSolution:", evo.Max(pop))
best := seed[0]
bestFit := seed[0].Fitness()
for i := range seed {
fit := seed[i].Fitness()
if fit > bestFit {
best = seed[i]
bestFit = fit
}
}
fmt.Println("\nSolution:", best)
}()

// Run:
Expand Down
96 changes: 43 additions & 53 deletions example/queens/queens_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package queens

import (
"fmt"
"math"
"math/rand"
"sync"
"testing"
Expand All @@ -17,44 +16,31 @@ import (

// Tuneables
const (
dim = 128 // the dimension of the board
size = dim * 2 // the size of the population
dim = 128 // the dimension of the problem
size = dim * 4 // the size of the population
isl = 4 // the number of islands in which to divide the population

// the delay between island communications
delay = 500 * time.Millisecond
migration = size / isl / 8 // the size of migrations
delay = 1 * time.Second // the delay between migrations
)

// Global objects
var (
// Count of the number of fitness evaluations.
// counts the number of fitness evaluations
count struct {
sync.Mutex
n int
}

// A free-list used to recycle memory.
pool = sync.Pool{
New: func() interface{} {
return rand.Perm(dim)
},
}
)

// The queens type is our genome type. We evolve a permuation of [0,n)
// The queens type is our genome. We evolve a permuation of [0,n)
// representing the position of queens on an n x n board
type queens struct {
gene []int // permutation representation of an n-queens solution
fitness float64 // the negative of the number of conflicts in the solution
once sync.Once // used to compute fitness lazily
}

// Close recycles the memory of this genome to be use for new genomes.
func (q *queens) Close() {
pool.Put(q.gene)
q.gene = nil
}

// String returns the gene contents and number of conflicts.
func (q *queens) String() string {
return fmt.Sprintf("%v@%v", q.gene, -q.Fitness())
Expand Down Expand Up @@ -86,23 +72,22 @@ func (q *queens) Fitness() float64 {
return q.fitness
}

// Evolve implements the inner loop of the evolutionary algorithm.
// The population calls the Evolve method of each genome, in parallel. Then,
// each receiver returns a value to replace it in the next generation.
func (q *queens) Evolve(matingPool ...evo.Genome) evo.Genome {
// Evolution implements the body of the evolution loop.
func Evolution(q evo.Genome, suitors []evo.Genome) evo.Genome {
// Crossover:
// We're implementing a diffusion model. For each member of the population,
// we receive a small mating pool containing only our neighbors. We choose
// a mate using a random binary tournament and create a child with
// partially mapped crossover.
mate := sel.BinaryTournament(matingPool...).(*queens)
child := &queens{gene: pool.Get().([]int)}
perm.PMX(child.gene, q.gene, mate.gene)
mom := q.(*queens)
dad := sel.BinaryTournament(suitors...).(*queens)
child := &queens{gene: make([]int, len(mom.gene))}
perm.PMX(child.gene, mom.gene, dad.gene)

// Mutation:
// Perform n random swaps where n is taken from an exponential distribution.
mutationCount := math.Ceil(rand.ExpFloat64() - 0.5)
for i := float64(0); i < mutationCount; i++ {
// mutationCount := math.Ceil(rand.ExpFloat64() - 0.5)
for i := float64(0); i < 5; i++ {
j := rand.Intn(len(child.gene))
k := rand.Intn(len(child.gene))
child.gene[j], child.gene[k] = child.gene[k], child.gene[j]
Expand All @@ -120,48 +105,56 @@ func TestQueens(t *testing.T) {
fmt.Printf("Find a solution to %d-queens\n", dim)

// Setup:
// We create a random initial population and divide it into islands. Each
// island is evolved independently. The islands are grouped together into
// a wrapping population. The wrapper coordiates migrations between the
// islands according to the delay period.
init := make([]evo.Genome, size)
for i := range init {
init[i] = &queens{gene: pool.Get().([]int)}
// We create an initial set of random candidates and divide them into "islands".
// Each island is evolved independently in a generational population.
// The islands are then linked together into a graph population with
seed := make([]evo.Genome, size)
for i := range seed {
seed[i] = &queens{gene: perm.New(dim)}
}
islands := make([]evo.Genome, isl)
islSize := size / isl
for i := range islands {
islands[i] = gen.New(init[i*islSize : (i+1)*islSize])
islands[i].(evo.Population).Start()
var island gen.Population
island.Evolve(seed[i*islSize:(i+1)*islSize], Evolution)
islands[i] = &island
}
pop := graph.Ring(islands)
pop.SetDelay(delay)
pop.Start()
pop := graph.Ring(isl)
pop.Evolve(islands, gen.Migrate(migration, delay))

// Tear-down:
// Upon returning, we cleanup our resources and print the solution.
// Teardown:
defer func() {
pop.Close()
fmt.Println("\nSolution:", evo.Max(pop))
pop.Stop()
best := seed[0]
bestFit := seed[0].Fitness()
for i := range seed {
fit := seed[i].Fitness()
if fit > bestFit {
best = seed[i]
bestFit = fit
}
}
fmt.Println("\nSolution:", best)
}()

// Run:
// We continuously poll the population for statistics used in the
// Termination:
// We continuously poll the population for statistics to check various
// termination conditions.
for {
count.Lock()
n := count.n
count.Unlock()
stats := pop.Stats()

// "\x1b[2K" is the escape code to clear the line
// The fitness of minimization problems is negative
// "\x1b[2K" is the xterm escape code to clear the line
// Because this is a minimization problem, the fitness is negative.
// Thus we update the statistics accordingly.
fmt.Printf("\x1b[2K\rCount: %7d | Max: %3.0f | Mean: %3.0f | Min: %3.0f | RSD: %9.2e",
n,
-stats.Min(),
-stats.Mean(),
-stats.Max(),
stats.RSD())
-stats.RSD())

// We've found the solution when max is 0
if stats.Max() == 0 {
Expand All @@ -177,8 +170,5 @@ func TestQueens(t *testing.T) {
if n > 2e6 {
return
}

// var blocker chan struct{}
// <-blocker
}
}
31 changes: 20 additions & 11 deletions example/tsp/tsp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func (t *tsp) TwoOpt() {
// Evolve implements the inner loop of the evolutionary algorithm.
// The population calls the Evolve method of each genome, in parallel. Then,
// each receiver returns a value to replace it in the next generation.
func (t *tsp) Evolve(matingPool ...evo.Genome) evo.Genome {
func Evolve(current evo.Genome, matingPool []evo.Genome) evo.Genome {
// Selection:
// Select each parent using a simple random binary tournament
mom := sel.BinaryTournament(matingPool...).(*tsp)
Expand All @@ -144,8 +144,8 @@ func (t *tsp) Evolve(matingPool ...evo.Genome) evo.Genome {

// Replacement:
// Only replace if the child is better or equal
if t.Fitness() > child.Fitness() {
return t
if current.Fitness() > child.Fitness() {
return current
}
return child
}
Expand All @@ -156,18 +156,27 @@ func TestTSP(t *testing.T) {
// Setup:
// We create a random initial population
// and evolve it using a generational model.
init := make([]evo.Genome, size)
for i := range init {
init[i] = &tsp{gene: pool.Get().([]int)}
seed := make([]evo.Genome, size)
for i := range seed {
seed[i] = &tsp{gene: pool.Get().([]int)}
}
pop = graph.Hypercube(init)
pop.Start()
pop = graph.Hypercube(size)
pop.Evolve(seed, Evolve)

// Tear-down:
// Upon returning, we cleanup our resources and print the solution.
defer func() {
pop.Close()
fmt.Println("\nTour:", evo.Max(pop))
pop.Stop()
best := seed[0]
bestFit := seed[0].Fitness()
for i := range seed {
fit := seed[i].Fitness()
if fit > bestFit {
best = seed[i]
bestFit = fit
}
}
fmt.Println("\nTour:", best)
}()

// Run:
Expand All @@ -186,7 +195,7 @@ func TestTSP(t *testing.T) {
-stats.Min(),
-stats.Mean(),
-stats.Max(),
stats.RSD())
-stats.RSD())

// Stop when we get close. Finding the true minimum could take a while.
if -stats.Max() < best*1.1 {
Expand Down
Loading

0 comments on commit 29bbdad

Please sign in to comment.