// Solve returns a Magic representing the solution to a magic square schema. The // schema is a partially filled magic square, interpreted as the concatination // of the rows of the square. Values greater than or equal to 0 appearing in the // schema must appear in the solution at the same position, and values less than // 0 in the schema represent unknown values. // // The value is returned on a channel allocated by the caller. // // The population is reseeded occasionally if the solution is not found. The // return value rsc is the "reseed count" giving the number of times reseeding // occurs. func Solve(schema Square, ret chan Square) { var ( // the number of values in the square order = len(schema.Sq) // the size of the population popz = 10 * order // the interface into the GA loop pop evo.Population // the initial population seed = make([]evo.Genome, popz) ) // Print the population when receiving a SIGQUIT. // (press ctrl-/ at the terminal) go func() { sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGQUIT) <-sigs stats := pop.Stats() fmt.Println(stats.SD(), stats.RSD()) for i := pop.Iter(); i.Value() != nil; i.Next() { fmt.Println(i.Value().(*Genome).Express(), i.Value().(*Genome).Fitness()) } os.Exit(2) }() // start the GA in the background for i := range seed { seed[i] = &Genome{RandSiam(schema)} } pop = gen.New(seed) pop.Start() for { best := evo.Max(pop) stats := pop.Stats() // halt when we find a solution if best.Fitness() == 0 { ret <- best.(*Genome).Square pop.Close() return } // reseed if stats.RSD() < 0.3 { pop.Close() for i := range seed { seed[i] = &Genome{RandSiam(schema)} } seed[0] = best pop = gen.New(seed) pop.Start() } } }
func TestAckley(t *testing.T) { fmt.Printf("Minimize the Ackley function with n=%d\n", dim) // 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{ gene: real.Random(dim, 30), steps: real.Random(dim, 1), } } pop := gen.New(init) pop.Start() // Tear-down: // Upon returning, we cleanup our resources and print the solution. defer func() { pop.Close() selector.Close() fmt.Println("\nSolution:", evo.Max(pop)) }() // Run: // We continuously poll the population for statistics and terminate when we // have a solution or after 200,000 evaluations. 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 fmt.Printf("\x1b[2K\rCount: %7d | Max: %8.3g | Mean: %8.3g | Min: %8.3g | RSD: %9.2e", n, -stats.Min(), -stats.Mean(), -stats.Max(), stats.RSD()) // We've converged once the deviation is within the precision if stats.SD() < precision { return } // Force stop after 200,000 fitness evaluations if n > 200000 { return } } }
func TestTSP(t *testing.T) { fmt.Println("Minimize tour of US capitals - optimal is", best) // 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)} } pop = graph.Hypercube(init) pop.Start() // Tear-down: // Upon returning, we cleanup our resources and print the solution. defer func() { pop.Close() fmt.Println("\nTour:", evo.Max(pop)) }() // Run: // We continuously poll the population for statistics used in the // 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 fmt.Printf("\x1b[2K\rCount: %7d | Max: %6.0f | Mean: %6.0f | Min: %6.0f | RSD: %7.2e", n, -stats.Min(), -stats.Mean(), -stats.Max(), stats.RSD()) // Stop when we get close. Finding the true minimum could take a while. if -stats.Max() < best*1.1 { return } // Force stop after some number fitness evaluations if n > stop { t.Fail() return } } }
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)} } 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() } pop := graph.Ring(islands) pop.SetDelay(delay) pop.Start() // Tear-down: // Upon returning, we cleanup our resources and print the solution. defer func() { pop.Close() fmt.Println("\nSolution:", evo.Max(pop)) }() // Run: // We continuously poll the population for statistics used in the // 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 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()) // We've found the solution when max is 0 if stats.Max() == 0 { return } // We've converged once the deviation is less than 0.01 if stats.SD() < 1e-2 { return } // Force stop after 2,000,000 fitness evaluations if n > 2e6 { return } // var blocker chan struct{} // <-blocker } }