NSGA3Indicator.h
Go to the documentation of this file.
1/*!
2 *
3 *
4 * \brief Algorithm selecting front based on their crowding distance
5 *
6 * \author O.Krause
7 * \date 2017
8 *
9 *
10 * \par Copyright 1995-2017 Shark Development Team
11 *
12 * <BR><HR>
13 * This file is part of Shark.
14 * <https://shark-ml.github.io/Shark/>
15 *
16 * Shark is free software: you can redistribute it and/or modify
17 * it under the terms of the GNU Lesser General Public License as published
18 * by the Free Software Foundation, either version 3 of the License, or
19 * (at your option) any later version.
20 *
21 * Shark is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU Lesser General Public License for more details.
25 *
26 * You should have received a copy of the GNU Lesser General Public License
27 * along with Shark. If not, see <http://www.gnu.org/licenses/>.
28 *
29 */
30#ifndef SHARK_ALGORITHMS_DIRECTSEARCH_INDICATORS_NSGA3_INDICATOR_H
31#define SHARK_ALGORITHMS_DIRECTSEARCH_INDICATORS_NSGA3_INDICATOR_H
32
33#include <shark/LinAlg/Base.h>
36#include <limits>
37#include <vector>
38#include <utility>
39
40namespace shark {
41
42
44 template<typename ParetofrontType, typename ParetoArchive>
45 std::vector<std::size_t> leastContributors(ParetofrontType const& front, ParetoArchive const& archive, std::size_t K)const{
46 //copy both fronts together into temporary.
47 //remember: archived(preselected) points are at the front, points to select are at the back
48 std::vector<RealVector> points;
49 for(auto const& point: archive)
50 points.push_back(point);
51 for(auto const& point: front)
52 points.push_back(point);
53
54 //step 1: compute ideal point
55 RealVector ideal = points.front();
56 for(auto& point: points){
57 noalias(ideal) = min(ideal,point);
58 }
59 //move current optimum in all objectives to 0
60 for(auto& point: points)
61 noalias(point) = point - ideal;
62 RealVector normalizer = computeNormalizer(points);
63
64 //step 2.3: create normalized fitness values
65 for(auto& point: points)
66 noalias(point) = point/ normalizer;
67
68 typedef KeyValuePair<double,std::pair<std::size_t, std::size_t> > Pair;//stores (dist(p_j,z_i),j,i)
69 // step 3: generate associative pairings between all points and the reference points
70 std::vector<Pair> pairing(points.size(),makeKeyValuePair(std::numeric_limits<double>::max(),std::pair<std::size_t, std::size_t>()));
71 for(std::size_t j = 0; j != points.size(); ++j){
72 // find the reference this point is closest to
73 for(std::size_t i = 0; i != m_Z.size(); ++i){
74 //by pythagoras law we have a right triangle between our point x,
75 //the projection onto the line z_i = <z_i,x>c_i and 0.
76 //therefore we have |x-<z_i,x>z_i|^2 = |x|^2 - <z_i,x>^2
77 // using |z_i| = 1
78 double dist = norm_sqr(points[j]) - sqr(inner_prod(m_Z[i],points[j]));
79 pairing[j] = min(pairing[j],makeKeyValuePair(dist,std::make_pair(j,i)));
80 }
81 }
82
83 //check how points are assigned in the archive
84 std::vector<std::size_t> rho(m_Z.size(),0);//rho_i counts the number of points assigned to Z_i
85 std::size_t k = 0;
86 for(; k != archive.size(); ++k){
87 rho[pairing[k].value.second] ++;
88 }
89 // step 4: select the points to keep
90 while(k < points.size() - K){
91 //find reference point that got least points assigned;
92 std::size_t index = std::min_element(rho.begin(),rho.end()) - rho.begin();
93 //Search for an unassigned associated point that is close to it
94 bool found = false;
95 KeyValuePair<double,std::size_t > closest(std::numeric_limits<double>::max(),0);
96 for(std::size_t i = k; i != points.size(); ++i){
97 if(pairing[i].value.second == index){//is this point associated?
98 found = true;
99 closest = std::min(closest,makeKeyValuePair(pairing[i].key,i));
100 }
101 }
102 //if no point was found, we disregard the reference point from now on
103 if(!found){
104 rho[index] = points.size() +1;
105 }else{
106 //we remove it from the list of unassigned points
107 swap(pairing[k],pairing[closest.value]);
108 //we found a point and assign it
109 rho[index]++;
110 ++k;
111 }
112 }
113 //return the indices of the remaining unselected points
114 std::vector<std::size_t> unselected;
115 for(; k != points.size(); ++k){
116 SIZE_CHECK(pairing[k].value.first >= archive.size());
117 unselected.push_back(pairing[k].value.first - archive.size());
118 }
119 return unselected;
120
121 }
122
123 template<typename Archive>
124 void serialize( Archive & ar, const unsigned int ) {
125 ar & m_Z;
126 }
127
128 void setReferencePoints(std::vector<RealVector> const& Z){
129 m_Z = Z;
130
131 for(auto& z: m_Z){
132 z /= norm_2(z);
133 }
134 }
135
136 template<class random>
137 void init(std::size_t numOfObjectives,
138 std::size_t mu,
139 random& rng,
140 std::vector<Preference> const & preferences = std::vector<Preference>()){
141 std::size_t numLatticeTicks = computeOptimalLatticeTicks(numOfObjectives, mu);
142 RealMatrix refs;
143 if(preferences.empty())
144 {
146 rng,
147 unitVectorsOnLattice(numOfObjectives, numLatticeTicks),
148 mu);
149 }
150 else
151 {
153 numOfObjectives, numLatticeTicks, preferences);
154 }
155 // m_Z either has size mu or it, if preference points are given, its
156 // size equals the number of preference points times the number of
157 // points in the lattice structure.
158 m_Z.resize(refs.size1());
159 for(std::size_t i = 0; i < refs.size1(); ++i){
160 m_Z[i] = row(refs, i);
161 }
162 }
163private:
164 std::vector<RealVector> m_Z;
165
166 // approximates the points of the front by a plane spanned by the most extreme points.
167 // then a normalizer is computed such that for the normalized points the plane has normal
168 // (1,1,...,1). If this fails, the normalizer is chosen such that all values lie between 0 and 1.
169 RealVector computeNormalizer(std::vector<RealVector> const& points)const{
170 //step 1 find points spanning the plane
171 double epsilon = 0.00001;
172 std::size_t dimensions = points.front().size();
173 RealMatrix cornerPoints(dimensions, dimensions,0.0);
174 for(std::size_t dim = 0; dim != dimensions; ++dim){
175 KeyValuePair<double,std::size_t> best(std::numeric_limits<double>::max(),0);
176 for(std::size_t i = 0; i != points.size(); ++i){
177 auto const& point = points[i];
178 double dist = epsilon * sum(point) + (1-epsilon) * point[dim];
179 best = std::min(best,makeKeyValuePair(dist,i));
180 }
181 noalias(row(cornerPoints,dim)) = points[best.value];
182 }
183 // compute plane equation
184 // set up system of equations for linear regression
185 // the plane equation is c_i^T w + b = 0 for all
186 // corner points c_i. as w can be scaled freely, any value of b != 0
187 // will give rise to a solution as long as the c_i are not linearly dependent.
188 RealMatrix A = trans((cornerPoints|1)) % (cornerPoints|1);
189 RealVector b = trans((cornerPoints|1)) % blas::repeat(-1.0,dimensions);//value of the right hand side of % does not matter as long as it is < 0
190 blas::symm_pos_semi_definite_solver<RealMatrix> solver(A);
191 if(solver.rank() == dimensions){//check if system is solvable
192 solver.solve(b, blas::left());
193 RealVector w = subrange(b,0,dimensions);// get the plane normal.
194 if(min(w) >= 0){//check if linear factors make sense
195 return blas::repeat(1.0,dimensions)/w;
196 }
197 }
198
199 // if some of the error conditions are true,
200 // we use the worst function values as
201 // normalizer
202 RealVector nadir = points.front();
203 for(auto& point: points){
204 noalias(nadir) = max(nadir,point);
205 }
206 return nadir;
207
208
209 }
210};
211
212}
213
214#endif