예제 #1
0
파일: ga.go 프로젝트: cbarrick/magic-square
// 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()
		}
	}
}
예제 #2
0
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
		}
	}
}
예제 #3
0
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
		}
	}
}
예제 #4
0
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
	}
}