github.com/RH12503/Triangula@v1.2.0/algorithm/simple.go (about) 1 package algorithm 2 3 import ( 4 "github.com/RH12503/Triangula/algorithm/evaluator" 5 "github.com/RH12503/Triangula/fitness" 6 "github.com/RH12503/Triangula/mutation" 7 "github.com/RH12503/Triangula/normgeom" 8 "github.com/panjf2000/ants/v2" 9 "sort" 10 "time" 11 ) 12 13 // An simple is a simplified genetic algorithm. 14 // It runs in parallel, scaling well across multiple cores. 15 // For most cases, a modifiedGenetic algorithm will be more effective. 16 // 17 // Firstly, a new generation is chosen based on fitness. 18 // Secondly, the fitnesses of the new generation are calculated. 19 // Lastly, the fitnesses are sorted in preparation for the next generation. 20 type simple struct { 21 evaluator evaluator.Evaluator // Used to calculate fitnesses. 22 mutator mutation.Method // Used in newGeneration to mutate members of the population. 23 24 population []normgeom.NormPointGroup // The population of the algorithm. 25 newPopulation []normgeom.NormPointGroup // Used in newGeneration. 26 27 fitnesses []FitnessData // fitnesses[i] is the fitness of population[i]. 28 29 mutations [][]mutation.Mutation // Stores the mutations made in newGeneration. 30 31 best normgeom.NormPointGroup // The member of the population with the highest fitness. 32 33 cutoff int // The number of members that survive to the next generation. 34 35 stats Stats // Simple statistics relating to the algorithm. 36 } 37 38 func (s *simple) Step() { 39 t := time.Now() // Measure the time taken for the generation 40 41 // Fill the population with new members 42 s.newGeneration() 43 44 // Calculate and update the fitnesses of the new population 45 46 s.calculateFitnesses() 47 s.updateFitnesses() 48 49 s.stats.Generation++ 50 s.stats.TimeForGen = time.Since(t) 51 } 52 53 // calculateFitnesses calculates the fitnesses of the current generation. 54 func (s *simple) calculateFitnesses() { 55 ch := make(chan FitnessData, len(s.population)) 56 57 for i, p := range s.population { 58 i := i 59 p := p 60 e := s.evaluator.Get(i) 61 ants.Submit( 62 func() { 63 fit := e.Calculate(fitness.PointsData{ 64 Points: p, 65 Mutations: s.mutations[i], 66 }) 67 ch <- FitnessData{ 68 I: i, 69 Fitness: fit, 70 } 71 }, 72 ) 73 s.fitnesses[i].I = i 74 } 75 76 s.evaluator.Prepare() 77 78 done := 0 79 for d := range ch { 80 s.fitnesses[d.I].Fitness = d.Fitness 81 s.evaluator.Update(d.I) 82 83 done++ 84 if done == len(s.population) { 85 close(ch) 86 } 87 } 88 } 89 90 // updateFitnesses prepares the members with calculated fitnesses for the next generation. 91 func (s *simple) updateFitnesses() { 92 sort.Sort(s) 93 94 s.best.Set(s.population[0]) 95 s.stats.BestFitness = s.fitnesses[0].Fitness 96 } 97 98 // newGeneration populates a generation with new members. 99 func (s *simple) newGeneration() { 100 i := 0 101 102 for ; i < s.cutoff; i++ { 103 s.newPopulation[i].Set(s.population[i]) 104 s.mutations[i] = s.mutations[i][:0] 105 } 106 107 for i < len(s.population) { 108 for j := 0; j < s.cutoff && i < len(s.population); j++ { 109 s.mutations[i] = s.mutations[i][:0] // clear all previous mutations 110 s.newPopulation[i].Set(s.population[j]) 111 112 s.evaluator.SetBase(i, j) 113 s.mutator.Mutate(s.newPopulation[i], func(mut mutation.Mutation) { 114 s.mutations[i] = append(s.mutations[i], mut) 115 }) 116 i++ 117 } 118 } 119 120 s.population, s.newPopulation = s.newPopulation, s.population 121 } 122 123 func (s simple) Best() normgeom.NormPointGroup { 124 return s.best 125 } 126 127 func (s simple) Stats() Stats { 128 return s.stats 129 } 130 131 func (s simple) Len() int { 132 return len(s.fitnesses) 133 } 134 135 func (s simple) Less(i, j int) bool { 136 return s.fitnesses[i].Fitness > s.fitnesses[j].Fitness 137 } 138 139 func (s *simple) Swap(i, j int) { 140 s.fitnesses[i], s.fitnesses[j] = s.fitnesses[j], s.fitnesses[i] 141 s.population[i], s.population[j] = s.population[j], s.population[i] 142 s.evaluator.Swap(i, j) 143 } 144 145 // NewSimple returns a new Simple algorithm. 146 func NewSimple(newPointGroup func() normgeom.NormPointGroup, size int, cutoff int, 147 newEvaluators func(n int) evaluator.Evaluator, mutator mutation.Method) *simple { 148 var algo simple 149 150 for i := 0; i < size; i++ { 151 pg := newPointGroup() 152 algo.population = append(algo.population, pg) 153 algo.newPopulation = append(algo.newPopulation, pg.Copy()) 154 } 155 156 algo.best = algo.population[0].Copy() 157 158 algo.evaluator = newEvaluators(size) 159 160 algo.fitnesses = make([]FitnessData, len(algo.population)) 161 162 algo.mutations = make([][]mutation.Mutation, len(algo.population)) 163 164 algo.mutator = mutator 165 166 algo.cutoff = cutoff 167 168 algo.calculateFitnesses() 169 170 algo.updateFitnesses() 171 172 return &algo 173 }