Skip to content

Commit

Permalink
Merge pull request #14 from cbarrick/apidev
Browse files Browse the repository at this point in the history
Introduce new polling API
  • Loading branch information
cbarrick committed Feb 11, 2016
2 parents 29bbdad + bbb4dca commit baadc3d
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 93 deletions.
58 changes: 29 additions & 29 deletions example/ackley/ackley_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,27 +133,8 @@ func TestAckley(t *testing.T) {
var pop gen.Population
pop.Evolve(seed, Evolve)

// Tear-down:
// Upon returning, we cleanup our resources and print the solution.
defer func() {
pop.Stop()
selector.Close()
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 and terminate when we
// have a solution or after 200,000 evaluations.
for {
// Continuously print statistics while the optimization runs.
pop.Poll(0, func() bool {
count.Lock()
n := count.n
count.Unlock()
Expand All @@ -166,16 +147,35 @@ func TestAckley(t *testing.T) {
-stats.Min(),
-stats.Mean(),
-stats.Max(),
stats.RSD())
-stats.RSD())

// We've converged once the deviation is within the precision
if stats.SD() < precision {
return
}
return false
})

// Force stop after 200,000 fitness evaluations
if n > 200000 {
return
// Terminate after 200,000 fitness evaluations.
pop.Poll(0, func() bool {
count.Lock()
n := count.n
count.Unlock()
return n > 200000
})

// Terminate if the standard deviation is low.
pop.Poll(0, func() bool {
stats := pop.Stats()
return stats.SD() < precision
})

pop.Wait()
selector.Close()
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)
}
62 changes: 32 additions & 30 deletions example/queens/queens_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,25 +122,8 @@ func TestQueens(t *testing.T) {
pop := graph.Ring(isl)
pop.Evolve(islands, gen.Migrate(migration, delay))

// Teardown:
defer func() {
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)
}()

// Termination:
// We continuously poll the population for statistics to check various
// termination conditions.
for {
// Continuously print statistics while the optimization runs.
pop.Poll(0, func() bool {
count.Lock()
n := count.n
count.Unlock()
Expand All @@ -156,19 +139,38 @@ func TestQueens(t *testing.T) {
-stats.Max(),
-stats.RSD())

// We've found the solution when max is 0
if stats.Max() == 0 {
return
}
return false
})

// We've converged once the deviation is less than 0.01
if stats.SD() < 1e-2 {
return
}
// Terminate when we've found the solution (when max is 0)
pop.Poll(0, func() bool {
stats := pop.Stats()
return stats.Max() == 0
})

// Force stop after 2,000,000 fitness evaluations
if n > 2e6 {
return
// Terminate if We've converged to a deviation is less than 0.01
pop.Poll(0, func() bool {
stats := pop.Stats()
return stats.SD() < 1e-2
})

// Terminate after 2,000,000 fitness evaluations.
pop.Poll(0, func() bool {
count.Lock()
n := count.n
count.Unlock()
return n > 2e6
})

pop.Wait()
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)
}
56 changes: 27 additions & 29 deletions example/tsp/tsp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
const (
dim = len(cities) // the dimension of the problem
size = 256 // the size of the population
stop = 2e6 // terminate after this number of fitness evalutations
)

// Global objects
Expand Down Expand Up @@ -163,26 +162,8 @@ func TestTSP(t *testing.T) {
pop = graph.Hypercube(size)
pop.Evolve(seed, Evolve)

// Tear-down:
// Upon returning, we cleanup our resources and print the solution.
defer func() {
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:
// We continuously poll the population for statistics used in the
// termination conditions.
for {
// Continuously print statistics while the optimization runs.
pop.Poll(0, func() bool {
count.Lock()
n := count.n
count.Unlock()
Expand All @@ -197,17 +178,34 @@ func TestTSP(t *testing.T) {
-stats.Max(),
-stats.RSD())

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

// Force stop after some number fitness evaluations
if n > stop {
t.Fail()
return
// Stop when we get close. Finding the true minimum could take a while.
pop.Poll(0, func() bool {
stats := pop.Stats()
return -stats.Max() < best*1.1
})

// Terminate after 2,000,000 fitness evaluations.
pop.Poll(0, func() bool {
count.Lock()
n := count.n
count.Unlock()
return n > 2e6
})

pop.Wait()
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)
}

// Best is the minimum tour of the cities.
Expand Down
13 changes: 13 additions & 0 deletions interface.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package evo

import "time"

// A ConditionFn describes a termination condition.
type ConditionFn func() bool

// An EvolveFn describes an iteration of the evolution loop. The evolve function
// is called once for each member of the population, possibly in parrallel, and
// is responsible for producing new Genomes given some subset of the population,
Expand Down Expand Up @@ -35,6 +40,14 @@ type Population interface {
// Stop terminates the optimization.
Stop()

// Poll executes a function at some frequency for the duration of the
// current optimization. If the function returns true, the current
// optimization is halted. Use a frequency of 0 for continuous polling.
Poll(freq time.Duration, cond ConditionFn)

// Wait blocks until the evolution terminates.
Wait()

// Stats returns various statistics about the population.
Stats() Stats
}
30 changes: 28 additions & 2 deletions pop/gen/generational.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func (pop *Population) Evolve(members []evo.Genome, body evo.EvolveFn) {
pop.setc = make(chan chan int)
pop.getc = make(chan chan int)
pop.valuec = make(chan evo.Genome)
pop.stopc = make(chan chan struct{})
pop.stopc = make(chan chan struct{}, 1)
go run(*pop, body)
}

Expand All @@ -44,7 +44,32 @@ func (pop *Population) Stop() {
close(pop.setc)
close(pop.getc)
close(pop.valuec)
close(pop.stopc)
}

// Poll executes a function at some frequency for the duration of the
// current optimization. If the function returns true, the current optimization
// is halted.
func (pop *Population) Poll(freq time.Duration, cond evo.ConditionFn) {
done := pop.stopc
go func() {
for {
select {
case <-time.After(freq):
if cond() {
pop.Stop()
return
}
case ch := <-done:
done <- ch
return
}
}
}()
}

// Wait blocks until the evolution terminates.
func (pop *Population) Wait() {
pop.stopc <- <-pop.stopc
}

// Stats returns statistics on the fitness of genomes in the population.
Expand Down Expand Up @@ -195,6 +220,7 @@ func run(pop Population, body evo.EvolveFn) {
}
}
ch <- struct{}{}
pop.stopc <- ch
return
}
}
Expand Down
36 changes: 34 additions & 2 deletions pop/graph/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
// regular population, it is known as the diffusion model.
package graph

import "github.com/cbarrick/evo"
import (
"time"

"github.com/cbarrick/evo"
)

type Graph []node

Expand All @@ -16,6 +20,7 @@ type node struct {
getc chan chan evo.Genome
setc chan chan evo.Genome
closec chan chan struct{}
done chan struct{}
}

// Grid creates a new graph population arranged as a 2D grid.
Expand Down Expand Up @@ -115,7 +120,32 @@ func (n *node) stop() {
<-ch
close(n.getc)
close(n.setc)
close(n.closec)
}

// Poll executes a function at some frequency for the duration of the
// current optimization. If the function returns true, the current optimization
// is halted.
func (g Graph) Poll(freq time.Duration, cond evo.ConditionFn) {
done := g[0].closec
go func() {
for {
select {
case <-time.After(freq):
if cond() {
g.Stop()
return
}
case ch := <-done:
done <- ch
return
}
}
}()
}

// Wait blocks until the evolution terminates.
func (g Graph) Wait() {
g[0].closec <- <-g[0].closec
}

// get returns the genome underlying the node.
Expand Down Expand Up @@ -162,6 +192,8 @@ func (n *node) run(body evo.EvolveFn) {
subpop.Stop()
}
ch <- struct{}{}
n.closec <- ch

return
}
}
Expand Down
2 changes: 1 addition & 1 deletion stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type Stats struct {
max, min float64
mean float64
sumsq float64 // sum of squares of deviation from the mean
count float64
count float64
}

// Put inserts a new value into the data.
Expand Down

0 comments on commit baadc3d

Please sign in to comment.