|
1 | 1 | package ga |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "math/rand" |
| 6 | + "testing" |
| 7 | + |
| 8 | + "github.com/golang/protobuf/ptypes/empty" |
| 9 | + |
| 10 | + "github.com/salvacorts/TFG-Parasitic-Metaheuristics/mlp-ea-decentralized/common/mlp" |
| 11 | + "github.com/salvacorts/eaopt" |
| 12 | + "github.com/sirupsen/logrus" |
| 13 | +) |
| 14 | + |
| 15 | +func TestBorrowIndividual(t *testing.T) { |
| 16 | + mlp.Config.FactoryCfg.MinHiddenNeurons = 2 |
| 17 | + mlp.Config.FactoryCfg.MaxHiddenNeurons = 4 |
| 18 | + pool := &PoolModel{Delegate: mlp.DelegateImpl{}} |
| 19 | + |
| 20 | + // We need a buffered chan to not get stuck |
| 21 | + input := make(chan eaopt.Individual, 100) |
| 22 | + output := make(chan eaopt.Individual, 100) |
| 23 | + |
| 24 | + server := Server{ |
| 25 | + Log: logrus.New(), |
| 26 | + Input: input, |
| 27 | + Output: output, |
| 28 | + Pool: pool, |
| 29 | + } |
| 30 | + |
| 31 | + server.Input <- eaopt.Individual{ |
| 32 | + ID: "Test", |
| 33 | + Fitness: 0, |
| 34 | + Evaluated: false, |
| 35 | + Genome: mlp.NewRandMLP(rand.New(rand.NewSource(7))), |
| 36 | + } |
| 37 | + |
| 38 | + indiv, err := server.BorrowIndividual(context.Background(), &empty.Empty{}) |
| 39 | + if err != nil { |
| 40 | + t.Errorf("Error on server.BorrowIndividual(). %s", err.Error()) |
| 41 | + } else if indiv.IndividualID != "Test" { |
| 42 | + t.Errorf("ID of individual do not match") |
| 43 | + } |
| 44 | +} |
| 45 | + |
| 46 | +func TestReturnIndividual(t *testing.T) { |
| 47 | + mlp.Config.FactoryCfg.MinHiddenNeurons = 2 |
| 48 | + mlp.Config.FactoryCfg.MaxHiddenNeurons = 4 |
| 49 | + pool := &PoolModel{Delegate: mlp.DelegateImpl{}} |
| 50 | + |
| 51 | + input := make(chan eaopt.Individual, 100) |
| 52 | + output := make(chan eaopt.Individual, 100) |
| 53 | + |
| 54 | + server := Server{ |
| 55 | + Log: logrus.New(), |
| 56 | + Input: input, |
| 57 | + Output: output, |
| 58 | + Pool: pool, |
| 59 | + } |
| 60 | + |
| 61 | + indiv := &Individual{ |
| 62 | + IndividualID: "Test", |
| 63 | + Evaluated: true, |
| 64 | + Fitness: 0, |
| 65 | + Genome: pool.Delegate.SerializeGenome( |
| 66 | + mlp.NewRandMLP(rand.New(rand.NewSource(7)))), |
| 67 | + } |
| 68 | + |
| 69 | + _, err := server.ReturnIndividual(context.Background(), indiv) |
| 70 | + if err != nil { |
| 71 | + t.Errorf("Error on server.ReturnIndividual(). %s", err.Error()) |
| 72 | + } else { |
| 73 | + indiv2 := <-server.Output |
| 74 | + |
| 75 | + if indiv2.ID != indiv.IndividualID { |
| 76 | + t.Errorf("Returned individual ID do not match the original") |
| 77 | + } |
| 78 | + } |
| 79 | +} |
| 80 | + |
| 81 | +func TestMigrateIndividuals(t *testing.T) { |
| 82 | + size := 10 |
| 83 | + rnd := rand.New(rand.NewSource(7)) |
| 84 | + mlp.Config.FactoryCfg.MinHiddenNeurons = 2 |
| 85 | + mlp.Config.FactoryCfg.MaxHiddenNeurons = 4 |
| 86 | + pool := MakePool(size, 9999, 9998, []string{}, rnd, mlp.NewRandMLP) |
| 87 | + pool.Delegate = mlp.DelegateImpl{} |
| 88 | + pool.SortFunc = mlp.SortByFitnessAndNeurons |
| 89 | + pool.NMigrate = 4 |
| 90 | + |
| 91 | + input := make(chan eaopt.Individual, 100) |
| 92 | + output := make(chan eaopt.Individual, 100) |
| 93 | + |
| 94 | + server := Server{ |
| 95 | + Log: logrus.New(), |
| 96 | + Input: input, |
| 97 | + Output: output, |
| 98 | + Pool: pool, |
| 99 | + } |
| 100 | + |
| 101 | + // All individuals in population to fitness [0, 10, 20, ..., 100] |
| 102 | + for i, in := range pool.GetPopulationSnapshot() { |
| 103 | + in.Fitness = float64(i * 100) |
| 104 | + in.Evaluated = true |
| 105 | + pool.population.Add(in) |
| 106 | + } |
| 107 | + |
| 108 | + batch := &IndividualsBatch{ |
| 109 | + Individuals: []Individual{ |
| 110 | + Individual{ |
| 111 | + IndividualID: "Test1", |
| 112 | + Fitness: 5, |
| 113 | + Genome: pool.Delegate.SerializeGenome(mlp.NewRandMLP(rnd)), |
| 114 | + }, |
| 115 | + Individual{ |
| 116 | + IndividualID: "Test2", |
| 117 | + Fitness: 15, |
| 118 | + Genome: pool.Delegate.SerializeGenome(mlp.NewRandMLP(rnd)), |
| 119 | + }, |
| 120 | + Individual{ |
| 121 | + IndividualID: "Test3", |
| 122 | + Fitness: 1005, |
| 123 | + Genome: pool.Delegate.SerializeGenome(mlp.NewRandMLP(rnd)), |
| 124 | + }, |
| 125 | + Individual{ |
| 126 | + IndividualID: "Test4", |
| 127 | + Fitness: 2005, |
| 128 | + Genome: pool.Delegate.SerializeGenome(mlp.NewRandMLP(rnd)), |
| 129 | + }, |
| 130 | + }, |
| 131 | + } |
| 132 | + |
| 133 | + _, err := server.MigrateIndividuals(context.Background(), batch) |
| 134 | + if err != nil { |
| 135 | + t.Errorf("Error on server.TestMigrateIndividuals(). %s", err.Error()) |
| 136 | + } else { |
| 137 | + |
| 138 | + if pool.population.Length() != size { |
| 139 | + t.Errorf("Migrate increased population size") |
| 140 | + } |
| 141 | + |
| 142 | + snap := pool.GetPopulationSnapshot() |
| 143 | + scores := make([]float64, 0, len(snap)) |
| 144 | + found := make(map[string]bool) |
| 145 | + for _, is := range snap { |
| 146 | + scores = append(scores, is.Fitness) |
| 147 | + for _, ib := range batch.Individuals { |
| 148 | + if is.ID == ib.IndividualID { |
| 149 | + found[ib.IndividualID] = true |
| 150 | + } |
| 151 | + } |
| 152 | + } |
| 153 | + |
| 154 | + t.Logf("Snap: %v", scores) |
| 155 | + |
| 156 | + // We expect Test1 and Test2 to be on the population |
| 157 | + // and Test3 and T4 to not being in there |
| 158 | + if found["Test3"] || found["Test4"] { |
| 159 | + t.Errorf("Migration replaced better individuals by worse ones") |
| 160 | + } |
| 161 | + |
| 162 | + if !found["Test1"] || !found["Test2"] { |
| 163 | + t.Errorf("Migration did not replace better individuals by worst ones") |
| 164 | + } |
| 165 | + |
| 166 | + if pool.BestSolution.ID != "Test1" || pool.BestSolution.Fitness != 5 { |
| 167 | + t.Errorf("Migration did not replace the best individual") |
| 168 | + } |
| 169 | + } |
| 170 | +} |
0 commit comments