Brush C++ API
A flexible interpretable machine learning framework
Loading...
Searching...
No Matches
nsga2.cpp
Go to the documentation of this file.
1#include "nsga2.h"
2
3namespace Brush {
4namespace Sel {
5
6using namespace Brush;
7using namespace Pop;
8using namespace Sel;
9
10template<ProgramType T>
12{
13 this->name = "nsga2";
14 this->survival = surv;
15}
16
17template<ProgramType T>
18size_t NSGA2<T>::tournament(Population<T>& pop, size_t i, size_t j) const
19{
20 // gets two individuals and compares them. i and j bhould be within island range
21 const Individual<T>& ind1 = pop[i];
22 const Individual<T>& ind2 = pop[j];
23
24 int flag = ind1.fitness.dominates(ind2.fitness);
25
26 if (flag == 1) // ind1 dominates ind2
27 return i;
28 else if (flag == -1) // ind2 dominates ind1
29 return j;
30 else if (ind1.fitness.crowding_dist > ind2.fitness.crowding_dist)
31 return i;
32 else if (ind2.fitness.crowding_dist > ind1.fitness.crowding_dist)
33 return j;
34 else
35 return i;
36}
37
38template<ProgramType T>
39vector<size_t> NSGA2<T>::select(Population<T>& pop, int island,
40 const Parameters& params)
41{
42 // tournament selection. TODO: move this to tournament selection file, and throw not implemented error in nsga.
44
45 // if this is first generation, just return indices to pop
46 if (params.current_gen==0)
47 return island_pool;
48
49 // i am not sure if I need this update of rank and crowding distance (bc first generation is ignored by if above, and the other generations will always have individuals that went through survival, which already calculates this information. TODO: in the final algorithm, I need to make sure this is correct)
50 auto front = fast_nds(pop, island_pool);
51 for (size_t i = 0; i< front.size(); i++)
52 {
53 crowding_distance(pop, front, i);
54 }
55
56 vector<size_t> selected(0);
57 for (int i = 0; i < island_pool.size(); ++i) // selecting based on island_pool size
58 {
59 size_t winner = tournament(pop,
60 *r.select_randomly(island_pool.begin(), island_pool.end()),
61 *r.select_randomly(island_pool.begin(), island_pool.end()));
62
63 selected.push_back(winner);
64 }
65 return selected;
66}
67
68template<ProgramType T>
69vector<size_t> NSGA2<T>::survive(Population<T>& pop, int island,
70 const Parameters& params)
71{
72 size_t idx_start = std::floor(island*params.pop_size/params.num_islands);
73 size_t idx_end = std::floor((island+1)*params.pop_size/params.num_islands);
74
75 auto original_size = idx_end - idx_start; // original island size (survive must be called with an island with offfspring)
76
78
79 // fast non-dominated sort
80 auto front = fast_nds(pop, island_pool);
81
82 // Push back selected individuals until full
83 vector<size_t> selected;
84 selected.resize(0);
85
86 int i = 0;
87 while (
88 i < front.size()
89 && ( selected.size() + front.at(i).size() < original_size )
90 )
91 {
92 std::vector<int>& Fi = front.at(i); // indices in front i
93
94 crowding_distance(pop, front, i); // calculate crowding in Fi
95
96 for (int j = 0; j < Fi.size(); ++j) // Pt+1 = Pt+1 U Fi
97 selected.push_back(Fi.at(j));
98
99 ++i;
100 }
101
102 // fmt::print("crowding distance\n");
103 crowding_distance(pop, front, i); // calculate crowding in final front to include
104 std::sort(front.at(i).begin(),front.at(i).end(),sort_n(pop));
105
106 // fmt::print("adding last front)\n");
107 const int extra = original_size - selected.size();
108 for (int j = 0; j < extra; ++j) // Pt+1 = Pt+1 U Fi[1:N-|Pt+1|]
109 selected.push_back(front.at(i).at(j));
110
111 // fmt::print("returning\n");
112 return selected;
113}
114
115template<ProgramType T>
116vector<vector<int>> NSGA2<T>::fast_nds(Population<T>& pop, vector<size_t>& island_pool)
117{
118 // this will update pareto dominance attributes in fitness class
119 // based on the population
120
121 //< the Pareto fronts
122 vector<vector<int>> front;
123
124 front.resize(1);
125 front.at(0).clear();
126
127 for (int i = 0; i < island_pool.size(); ++i) {
128
129 std::vector<unsigned int> dom;
130 int dcount = 0;
131
132 auto p = pop.individuals.at(island_pool[i]);
133
134 for (int j = 0; j < island_pool.size(); ++j) {
135
136 const Individual<T>& q = pop[island_pool[j]];
137
138 int compare = p->fitness.dominates(q.fitness);
139 if (compare == 1) { // p dominates q
140 //p.dominated.push_back(j);
141 dom.push_back(island_pool[j]);
142 } else if (compare == -1) { // q dominates p
143 //p.dcounter += 1;
144 dcount += 1;
145 }
146 }
147 p->fitness.dcounter = dcount;
148 p->fitness.dominated.clear();
149 p->fitness.dominated = dom; // dom will have values already referring to island indexes
150
151 if (p->fitness.dcounter == 0) {
152 // fmt::print("pushing {}...\n", island_pool[i]);
153 p->fitness.set_rank(1);
154 // front will have values already referring to island indexes
155 front.at(0).push_back(island_pool[i]);
156 }
157
158 }
159
160 // fmt::print("First front size {}...\n", front.at(0).size());
161
162 // using OpenMP can have different orders in the front.at(0)
163 // so let's sort it so that the algorithm is deterministic
164 // given a seed
165 std::sort(front.at(0).begin(), front.at(0).end());
166
167 int fi = 1;
168 while (front.at(fi-1).size() > 0) {
169 std::vector<int>& fronti = front.at(fi-1);
170 std::vector<int> Q;
171 for (int i = 0; i < fronti.size(); ++i) {
172
173 const Individual<T>& p = pop[fronti.at(i)];
174
175 // iterating over dominated individuals
176 for (int j = 0; j < p.fitness.dominated.size() ; ++j) {
177 // fmt::print("decreased counter of ind {} for {} to {} \n", j, p.fitness.dominated.at(j), pop.individuals.at(p.fitness.dominated.at(j))->fitness.dcounter);
178
179 auto q = pop.individuals.at(p.fitness.dominated.at(j));
180
181 // fmt::print("decreased counter \n");
182 q->fitness.dcounter -= 1;
183
184 if (q->fitness.dcounter == 0) {
185 // fmt::print("updated counter for ind {} \n", j);
186
187 q->fitness.set_rank(fi+1);
188 Q.push_back(p.fitness.dominated.at(j));
189 }
190 }
191 }
192
193 front.push_back(Q);
194
195 fi += 1;
196 }
197 return front;
198}
199
200template<ProgramType T>
201void NSGA2<T>::crowding_distance(Population<T>& pop, vector<vector<int>>& front, int fronti)
202{
203
204 // fmt::print("inside crowding distance for front {}...\n", fronti);
205
206 std::vector<int> F = front.at(fronti);
207 if (F.size() == 0 ){
208 // fmt::print("empty front\n");
209 return;
210 }
211
212 const int fsize = F.size();
213 // fmt::print("front size is {}...\n", fsize);
214
215 for (int i = 0; i < fsize; ++i)
216 pop.individuals.at(F.at(i))->fitness.crowding_dist = 0;
217
218 // fmt::print("reseted crowding distance for individuals in this front\n");
219
220 const int limit = pop.individuals.at(0)->fitness.get_wvalues().size();
221 // fmt::print("limit is {}\n", limit);
222
223 for (int m = 0; m < limit; ++m) {
224 // fmt::print("m {}\n", m);
225
226 std::sort(F.begin(), F.end(), comparator_obj(pop,m));
227
228 // in the paper dist=INF for the first and last, in the code
229 // this is only done to the first one or to the two first when size=2
230 pop.individuals.at(F.at(0))->fitness.crowding_dist = std::numeric_limits<float>::max();
231 if (fsize > 1)
232 pop.individuals.at(F.at(fsize-1))->fitness.crowding_dist = std::numeric_limits<float>::max();
233
234 for (int i = 1; i < fsize-1; ++i)
235 {
236 if (pop.individuals.at(F.at(i))->fitness.crowding_dist != std::numeric_limits<float>::max())
237 { // crowd over obj
238 // TODO: this could be improved
239 pop.individuals.at(F.at(i))->fitness.crowding_dist +=
240 (pop.individuals.at(F.at(i+1))->fitness.get_wvalues().at(m) - pop.individuals.at(F.at(i-1))->fitness.get_wvalues().at(m))
241 / (pop.individuals.at(F.at(fsize-1))->fitness.get_wvalues().at(m) - pop.individuals.at(F.at(0))->fitness.get_wvalues().at(m));
242 }
243 }
244 }
245}
246
247} // selection
248} // Brush
void bind_engine(py::module &m, string name)
vector< size_t > get_island_indexes(int island)
Definition population.h:38
vector< std::shared_ptr< Individual< T > > > individuals
Definition population.h:18
void crowding_distance(Population< T > &, vector< vector< int > > &, int)
Definition nsga2.cpp:201
size_t tournament(Population< T > &pop, size_t i, size_t j) const
Definition nsga2.cpp:18
vector< size_t > survive(Population< T > &pop, int island, const Parameters &p)
survival according to the survival scheme of NSGA-II
Definition nsga2.cpp:69
NSGA2(bool surv=false)
Definition nsga2.cpp:11
vector< vector< int > > fast_nds(Population< T > &, vector< size_t > &)
Definition nsga2.cpp:116
vector< size_t > select(Population< T > &pop, int island, const Parameters &p)
selection according to the survival scheme of NSGA-II
Definition nsga2.cpp:39
Iter select_randomly(Iter start, Iter end)
Definition rnd.h:61
static Rnd & r
Definition rnd.h:174
< nsga2 selection operator for getting the front
Definition data.cpp:12
unsigned int current_gen
Definition params.h:27
sort based on objective m
Definition nsga2.h:65
sort based on rank, breaking ties with crowding distance
Definition nsga2.h:44