func (stats *Stats) SaveSnapshot(pop *base.Population, quiet bool, cntKeys, staKeys, intCntKeys []string) (snapName, snapPopName string) { timeDelay := time.Since(stats.lastTime) stats.lastTime = time.Now() // Sort population, to easy reading when printing and drawing sort.Sort(pop) // Build paths prefix := fmt.Sprintf("%v/snapshot/%v-", stats.basedir, stats.basename) snapName = fmt.Sprintf(prefix+"snapshot-%v.png", stats.snapCount) snapPopName = fmt.Sprintf(prefix+"pop_snapshot-%v.png", stats.snapCount) logPrefix := fmt.Sprintf("%v/log/%v-", stats.basedir, stats.basename) bestTree := fmt.Sprintf(logPrefix+"tree-%v.json", stats.snapCount) writeIndividual(pop.BestIndividual(), bestTree) const wideField = 40 if !quiet { if stats.snapCount == 0 { fmt.Print("Generation | Tree depth (mean, stdev) | Tree size (mean, stdev) | Fitness (min, mean, max, stdev) | XO Improv (abs, rel) | MUT Improv (abs, rel) | Time delay |") for _, k := range staKeys { fmt.Printf(" %21s |", k) } for _, k := range cntKeys { fmt.Printf(" %21s |", k) } for _, k := range intCntKeys { fmt.Printf(" %*s |", wideField, k) } fmt.Println() } fmt.Printf("%10v | %11.4f %11.4f | %11.4f %11.4f | %10.2f %10.2f %10.2f %10.2f | %10v %10.3f | %10v %10.3f | %13v |", stats.obsCount-1, stats.depth.PartialMean(), math.Sqrt(stats.depth.PartialVar()), stats.size.PartialMean(), math.Sqrt(stats.size.PartialVar()), stats.min.Get(), stats.fitness.PartialMean(), stats.max.Get(), math.Sqrt(stats.fitness.PartialVar()), stats.xoImpr.AbsoluteFrequency(), stats.xoImpr.RelativeFrequency(), stats.mutImpr.AbsoluteFrequency(), stats.mutImpr.RelativeFrequency(), fmt.Sprintf("%v", timeDelay), ) for _, k := range staKeys { if sst, ok := pop.Set.Statistics[k]; ok { fmt.Printf(" %6d %6g %6g %10.6g %10.6g |", sst.Variance.Count(), sst.Min.Get(), sst.Max.Get(), sst.PartialMean(), sst.PartialVarBessel()) sst.Clear() } else { fmt.Printf(" %6v %6v %6v %10v %10v |", "-", "-", "-", "-", "-") } } for _, k := range cntKeys { if cst, ok := pop.Set.Counters[k]; ok { fmt.Printf(" %10d %10.6g |", cst.AbsoluteFrequency(), cst.RelativeFrequency()) cst.Clear() } else { fmt.Printf(" %10v %10v |", "-", "-") } } for _, k := range intCntKeys { if nst, ok := pop.Set.IntCounters[k]; ok { keys := nst.Counted() if len(keys) == 0 { fmt.Printf(" %*s |", wideField, "0:0") } else { vals := make([]string, len(keys)) for i, n := range keys { vals[i] = fmt.Sprintf("%d:%d", n, nst.AbsoluteFrequency(n)) } fmt.Printf(" %*s |", wideField, strings.Join(vals, ",")) nst.Clear() } } else { fmt.Printf(" %*s |", wideField, "0:0") } } fmt.Println() } // Increment snapshot count stats.snapCount++ return }
func Evolve(calcMaxDepth func(*imgut.Image) int, fun, ter []gp.Primitive, drawfun func(*base.Individual, *imgut.Image)) { startTime := time.Now() // Setup options fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError) numGen := fs.Int("g", 100, "Number of generations") popSize := fs.Int("p", 1000, "Size of population") saveInterval := fs.Int("n", 25, "Generations interval between two snapshot saves") tournSize := fs.Int("T", 3, "Tournament size") pCross := fs.Float64("C", 0.8, "Crossover probability") pMut := fs.Float64("M", 0.1, "Bit mutation probability") quiet := fs.Bool("q", false, "Quiet mode") fElite := fs.Bool("el", false, "Enable elite individual") fInitFull := fs.Bool("full", true, "Enable full initialization") fInitGrow := fs.Bool("grow", true, "Enable grow initialization") fInitRamped := fs.Bool("ramp", true, "Enable ramped initialization") fMutSin := fs.Bool("ms", false, "Enable Single Mutation") fMutNod := fs.Bool("mn", false, "Enable Node Mutation") fMutSub := fs.Bool("mt", false, "Enable Subtree Mutation") fMutAre := fs.Bool("ma", false, "Enable Area Mutation") fMutLsubt := fs.Bool("mlt", false, "Enable Level-Subtree Mutation") fMutLoc := fs.Bool("ml", false, "Enable Local Mutation") fSelect := fs.String("sel", "torun", "Pick selection method (tourn, rmad, irmad)") fMultiMut := fs.Bool("mM", false, "Enable multiple mutations") fFitness := fs.String("fit", "rmse", "Pick fitness function (rmse, mse, rmsed, ssim)") //advStats := fs.Bool("stats", false, "Enable advanced statistics") //nps := fs.Bool("nps", false, "Disable population snapshot (no-pop-snap)") targetPath := fs.String("t", "", "Target image (PNG) path") var basedir, basename string cpuProfile := fs.String("cpuprofile", "", "Write CPU profile to file") fs.Usage = func() { fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) fs.PrintDefaults() fmt.Fprintf(os.Stderr, " basedir (string) to be used for saving logs and files\n") fmt.Fprintf(os.Stderr, " basename (string) to be used for saving logs and files\n") } fs.Parse(os.Args[1:]) // Check if the argument args := fs.Args() if len(args) != 2 { fs.Usage() fmt.Fprintf(os.Stderr, "\nBasename/basedir parameter not specified\n") return } else { basedir = args[0] basename = args[1] } sta := stats.Create(basedir, basename) // Build settings var settings base.Settings // Primitives to use settings.Functionals = fun settings.Terminals = ter // Draw function to use settings.Draw = drawfun settings.Ramped = *fInitRamped if settings.Ramped { fmt.Println("Using ramped initialization") } // Pick initialization method based on flags var genFuncBit func(minH, maxH int, funcs, terms []gp.Primitive) *node.Node if *fInitFull && !*fInitGrow { fmt.Println("Using init strategy: full") genFuncBit = node.MakeTreeFull // Initialize tree using full } else if !*fInitFull && *fInitGrow { fmt.Println("Using init strategy: balanced grow") genFuncBit = node.MakeTreeGrowBalanced // Initialize using grow } else { fmt.Println("Using init strategy: half-and-half") genFuncBit = node.MakeTreeHalfAndHalf // Initialize using both (half and half) } settings.GenFunc = func(maxDep int) *node.Node { t := genFuncBit(0, maxDep, fun, ter) // /* TODO */ sistemare la minH s := settings if _, ok := s.IntCounters[tree_init_depth]; !ok { s.IntCounters[tree_init_depth] = new(counter.IntCounter) } s.IntCounters[tree_init_depth].Count(node.Depth(t)) return t } // Build statistic map settings.Statistics = make(map[string]*sequence.SequenceStats) settings.Counters = make(map[string]*counter.BoolCounter) settings.IntCounters = make(map[string]*counter.IntCounter) // Names of extra statistics statsKeys := []string{} countersKeys := []string{} intCountersKeys := []string{} intCountersKeys = append(intCountersKeys, tree_init_depth) if *fMutSin { countersKeys = append(countersKeys, mut_single_event, mut_single_improv, mut_single_node_leaves) intCountersKeys = append(intCountersKeys, mut_single_node_depth, mut_single_node_repld) } if *fMutNod { countersKeys = append(countersKeys, mut_multi_event, mut_multi_improv, mut_multi_node_leaves) intCountersKeys = append(intCountersKeys, mut_multi_node_depth, mut_multi_node_repld) } if *fMutSub { countersKeys = append(countersKeys, mut_tree_event, mut_tree_improv, mut_tree_node_leaves) intCountersKeys = append(intCountersKeys, mut_tree_node_depth, mut_tree_node_repld) } if *fMutAre { countersKeys = append(countersKeys, mut_area_event, mut_area_improv, mut_area_node_leaves) intCountersKeys = append(intCountersKeys, mut_area_node_depth, mut_area_node_repld) } if *fMutLsubt { countersKeys = append(countersKeys, mut_lsubt_event, mut_lsubt_improv, mut_lsubt_improv) intCountersKeys = append(intCountersKeys, mut_lsubt_node_depth, mut_lsubt_node_repld) } if *fMutLoc { countersKeys = append(countersKeys, mut_local_event, mut_local_improv) // intCountersKeys = append(intCountersKeys, "mut_local_") TODO } if *fMultiMut { intCountersKeys = append(intCountersKeys, mut_count_multi) } if *cpuProfile != "" { f, err := os.Create(*cpuProfile) if err != nil { fmt.Println("ERROR", err) panic("Cannot create cpuprofile") } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } // Load the target var err error settings.ImgTarget, err = imgut.Load(*targetPath) if err != nil { fmt.Fprintln(os.Stderr, "ERROR: Cannot load image", *targetPath) panic("Cannot load image") } if !*quiet { fmt.Println("Image format RGB?", settings.ImgTarget.ColorSpace == imgut.MODE_RGB, settings.ImgTarget.ColorSpace) } // Compute the right value of maxDepth settings.MaxDepth = calcMaxDepth(settings.ImgTarget) if !*quiet { fmt.Println("For area of", settings.ImgTarget.W*settings.ImgTarget.H, "pixels, max depth is", settings.MaxDepth) } // Create temporary surface, of same size and mode //settings.ImgTemp = imgut.Create(settings.ImgTarget.W, settings.ImgTarget.H, settings.ImgTarget.ColorSpace) // Create temporary surface for the entire population pImgCols := int(math.Ceil(math.Sqrt(float64(*popSize)))) pImgRows := int(math.Ceil(float64(*popSize) / float64(pImgCols))) imgTempPop := imgut.Create(pImgCols*settings.ImgTarget.W, pImgRows*settings.ImgTarget.H, settings.ImgTarget.ColorSpace) // Define the operators settings.CrossOver = makeCrossover(&settings) settings.Mutate = makeMultiMutation(&settings, *fMultiMut, *fMutSin, *fMutNod, *fMutSub, *fMutAre, *fMutLsubt, *fMutLoc) // Fitness if *fFitness == "mse" { settings.FitFunc = base.MakeFitMSE(settings.ImgTarget) } else if *fFitness == "rmsed" { statsKeys = append(statsKeys, "fit-delta-rmse") settings.FitFunc = base.MakeFitEdge(settings.ImgTarget, settings.Statistics) } else if *fFitness == "ssim" { settings.FitFunc = base.MakeFitSSIM(settings.ImgTarget) } else { statsKeys = append(statsKeys, "fit-delta-rmse") settings.FitFunc = base.MakeFitRMSE(settings.ImgTarget) } // Selection ts := *tournSize if *fSelect == "rmad" { settings.Select = base.MakeSelectRMAD(ts, ts*ts, settings.BetterThan) } else if *fSelect == "irmad" { settings.Select = base.MakeSelectIRMAD(ts, ts*ts, settings.BetterThan) } else { settings.Select = base.MakeSelectTourn(ts, settings.BetterThan) } // Seed rng if !*quiet { fmt.Println("Number of CPUs", runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU()) fmt.Println("CPUs limits", runtime.GOMAXPROCS(0)) } // Build population pop := new(base.Population) pop.Set = &settings //pop.TournSize = *tournSize pop.Initialize(*popSize) // Save initial population FIXME it's for debugging /* for i := range pop.Pop { pop.Pop[i].Draw(imgTemp) imgTemp.WritePNG(fmt.Sprintf("pop_ind_%v.png", i)) } */ // Number of parallel generators to setup pipelineSize := 1 // Was 4 FIXME but there are statistics used in parallel and not ready for concurrent access // Containers for pipelined operators chXo := make([]<-chan ga.PipelineIndividual, pipelineSize) chMut := make([]<-chan ga.PipelineIndividual, pipelineSize) // Save best individual, for elitism var elite ga.Individual = nil // Save time before starting //genTime := time.Now() // Loop until max number of generation is reached for g := 0; g < *numGen; g++ { // Compute fitness for every individual with no fitness fitnessEval := pop.Evaluate() fitnessEval = fitnessEval // //if !*quiet { // fmt.Println("Generation ", g, "fit evals", fitnessEval, time.Since(genTime)) // genTime = time.Now() //} // Compute various statistics sta.Observe(pop) // Statistics and samples if g%*saveInterval == 0 { snapName, snapPopName := sta.SaveSnapshot(pop, *quiet, countersKeys, statsKeys, intCountersKeys) // Save best individual if false { pop.BestIndividual().Fitness() pop.BestIndividual().(*base.Individual).ImgTemp.WritePNG(snapName) } // Save best individual code // pop.BestIndividual().(*base.Individual).Draw(settings.ImgTemp) // settings.ImgTemp.WritePNG(snapName) // Save pop images pop.Draw(imgTempPop, pImgCols, pImgRows) imgTempPop.WritePNG(snapPopName) } // Setup parallel pipeline selectionSize := len(pop.Pop) // int(float64(len(pop.Pop))*0.3)) if you want to randomly generate new individuals chSel := ga.GenSelect(pop, selectionSize, float32(g)/float32(*numGen), elite) for i := 0; i < pipelineSize; i++ { chXo[i] = ga.GenCrossover(chSel, *pCross) chMut[i] = ga.GenMutate(chXo[i], *pMut) } var sel []ga.PipelineIndividual = ga.Collector(ga.FanIn(chMut...), selectionSize) // Replace old population and compute statistics for i := range sel { pop.Pop[i] = sel[i].Ind.(*base.Individual) sta.ObserveCrossoverFitness(sel[i].CrossoverFitness, sel[i].InitialFitness) sta.ObserveMutationFitness(sel[i].MutationFitness, sel[i].CrossoverFitness) } // When elitism is activated, get best individual if *fElite { elite = pop.BestIndividual() } // Build new individuals //base.RampedFill(pop, len(sel), len(pop.Pop)) } fitnessEval := pop.Evaluate() fitnessEval = fitnessEval // Population statistics sta.Observe(pop) //if !*quiet { // fmt.Println("Generation", *numGen, "fit evals", fitnessEval) // fmt.Println("Best individual", pop.BestIndividual()) //} snapName, snapPopName := sta.SaveSnapshot(pop, *quiet, countersKeys, statsKeys, intCountersKeys) // Save best individual if false { pop.BestIndividual().Fitness() pop.BestIndividual().(*base.Individual).ImgTemp.WritePNG(snapName) } // pop.BestIndividual().(*base.Individual).Draw(settings.ImgTemp) // settings.ImgTemp.WritePNG(snapName) // Save pop images pop.Draw(imgTempPop, pImgCols, pImgRows) imgTempPop.WritePNG(snapPopName) if !*quiet { fmt.Println("Best individual:") fmt.Println(pop.BestIndividual()) } elapsedTime := time.Since(startTime) fmt.Println("Execution took %s", elapsedTime) /* bestName := fmt.Sprintf("%v/best/%v.png", basedir, basename) if !*quiet { fmt.Println("Saving best individual in", bestName) fmt.Println(pop.BestIndividual()) } pop.BestIndividual().(*base.Individual).Draw(settings.ImgTemp) settings.ImgTemp.WritePNG(bestName) */ }