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  * <http://shark-ml.org/>
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 
40 namespace 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  {
145  refs = sampleLatticeUniformly(
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  }
163 private:
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