MultiObjectiveBenchmark.h
Go to the documentation of this file.
1 //===========================================================================
2 /*!
3  *
4  *
5  * \author Oswin Krause
6  * \date 2016
7  *
8  *
9  * \par Copyright 1995-2017 Shark Development Team
10  *
11  * <BR><HR>
12  * This file is part of Shark.
13  * <http://shark-ml.org/>
14  *
15  * Shark is free software: you can redistribute it and/or modify
16  * it under the terms of the GNU Lesser General Public License as published
17  * by the Free Software Foundation, either version 3 of the License, or
18  * (at your option) any later version.
19  *
20  * Shark is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23  * GNU Lesser General Public License for more details.
24  *
25  * You should have received a copy of the GNU Lesser General Public License
26  * along with Shark. If not, see <http://www.gnu.org/licenses/>.
27  *
28  */
29 //===========================================================================
30 #ifndef SHARK_OBJECTIVEFUNCTIONS_BENCHMARK_MULTIOBJECTIVEBENCHMARK_H
31 #define SHARK_OBJECTIVEFUNCTIONS_BENCHMARK_MULTIOBJECTIVEBENCHMARK_H
32 
36 #include <shark/Core/Random.h>
37 
38 #include <shark/LinAlg/rotations.h>
39 #include <tuple>
40 
41 namespace shark {
42 
43 namespace detail{
44  //taken from the web. implements an std::integer_sequence type representing a sequence 0,...,N-1, std::integer_sequence is not here until C++14
45  template<int...> struct integer_sequence { using type = integer_sequence; };
46  template<typename T1, typename T2> struct integer_sequence_concat;
47  template<int... I1, int... I2> struct integer_sequence_concat<integer_sequence<I1...>, integer_sequence<I2...>>: integer_sequence<I1..., (sizeof...(I1) + I2)...> {};
48 
49  //generate_integer_sequence generates an integer sequence of integers 0....N. requires log(N) template instantiations
50  template<int N> struct generate_integer_sequence;
51  template<int N> struct generate_integer_sequence: integer_sequence_concat<typename generate_integer_sequence<N/2>::type, typename generate_integer_sequence<N-N/2>::type>::type {};
52  template <> struct generate_integer_sequence<0>: integer_sequence<>{};
53  template <> struct generate_integer_sequence<1>: integer_sequence<0>{};
54 };
55 
56 /// \brief Creates a multi-objective Benchmark from a set of given single objective functions
57 ///
58 /// A variadic template is used to generate a set of benchmarks.
59 /// eg MultiObjectiveBenchmark<Sphere,Ellispoid,Rosenbrock> sets up a three-objective Benchmark.
60 ///
61 /// A random rotation and translation is applied to each benchmark function, thus
62 /// MultiObjectiveBenchmark<Sphere,Sphere> forms a non-degenerate front.
63 /// the ith objective can be queried via the get<i> member function.
64 ///
65 /// The generated translations are approximately sampled from the unit ball and starting points are also drawn
66 /// by the same distribution around a random optimum (assuming the optimum is at (0,0) of the untranslated function
67 ///
68 /// Note that all objectives must have scalable dimensionality
69 template<class ... Objectives>
71 public:
72  MultiObjectiveBenchmark(std::size_t numVariables = 5){
73  m_features |= CAN_PROPOSE_STARTING_POINT;
74  m_features |= HAS_FIRST_DERIVATIVE;
75  setupRotations(typename detail::generate_integer_sequence<sizeof...(Objectives)>::type());
76  setNumberOfVariables(numVariables);//prevent that different objectives have different default number of variables.
77  for(auto& f: m_rotations){
78  //if one function does not have a first derivative, no function has
79  if(!f.hasFirstDerivative())
80  m_features.reset(HAS_FIRST_DERIVATIVE);
81  }
82  };
83 
84  ///\brief Name of the Benchmark
85  ///
86  /// The name has the form Objective1/Objective2/Objective3/.../ObjectiveN
87  /// where ObjectiveK is the name of the k-th objective.
88  std::string name()const{
89  return generateName(typename detail::generate_integer_sequence<sizeof...(Objectives)>::type());
90 
91  }
92 
94  return true;
95  }
96 
97  void setNumberOfVariables( std::size_t numberOfVariables ){
98  for(auto& f: m_rotations){
99  SHARK_RUNTIME_CHECK(f.hasScalableDimensionality(),"Function is not scalable");
100  f.setNumberOfVariables(numberOfVariables);
101  }
102  }
103 
104  std::size_t numberOfObjectives()const{
105  return sizeof...(Objectives);
106  }
107 
108  std::size_t numberOfVariables()const{
109  return get<0>().numberOfVariables();
110  }
111 
112  template<int N>
113  typename std::tuple_element<N, std::tuple<Objectives...> >::type& get(){
114  return std::get<N>(m_objectives);
115  }
116  template<int N>
117  typename std::tuple_element<N, std::tuple<Objectives...> >::type const& get()const{
118  return std::get<N>(m_objectives);
119  }
120 
121  ///\ Initializes the functions as well as picks random rotations and translations
122  void init() {
123  m_translations.clear();
124 
125  for(auto& f: m_rotations)
126  {
127  RealVector translation(numberOfVariables());
128  for(double& v: translation){
129  v=random::gauss(*mep_rng, 0,1)/std::sqrt(double(numberOfVariables()));
130  }
131  m_translations.push_back(translation);
132  f.setRng(mep_rng);
133  f.init();
134  }
135  }
136 
138  RealVector x(numberOfVariables());
139 
140  std::size_t index = random::discrete(0,m_rotations.size()-1);
141  for (std::size_t i = 0; i < x.size(); i++) {
142  x(i) = m_translations[index](i)+random::gauss(*mep_rng, 0,1)/std::sqrt(double(numberOfVariables()));
143  }
144  return x;
145  }
146 
147  /// Returns the vector (f_1(x),...,f_N(x)) of the N objectives in the benchmark for the current point.
148  ResultType eval( SearchPointType const& x ) const {
149  m_evaluationCounter++;
150 
151  ResultType value( numberOfObjectives() );
152  for(std::size_t i = 0; i != value.size(); ++i){
153  value(i) = m_rotations[i].eval( x - m_translations[i]);
154  }
155  return value;
156  }
157  /// Calculates function value as well as the the Jacobian( d/dxf_1(x),...,d/dx f_N(x)) of the N objectives in the benchmark for the current point.
159  derivative.resize(numberOfObjectives(), numberOfVariables());
160  RealVector singleDerivative;
161  RealVector value(numberOfObjectives());
162  for(std::size_t i = 0; i != value.size(); ++i){
163  value(i) = m_rotations[i].evalDerivative( x - m_translations[i],singleDerivative);
164  noalias(row(derivative,i)) = singleDerivative;
165  }
166 
167  return value;
168  }
169 private:
170  //generate a rotated objective function for each function in the m_objectives tuple
171  template<int ... I>
172  void setupRotations(detail::integer_sequence<I...>){
173  m_rotations.insert(m_rotations.begin(), {RotatedObjectiveFunction(&std::get<I>(m_objectives))... });
174  }
175 
176  template<int ... I>
177  std::string generateName(detail::integer_sequence<I...>)const{
178  std::string name;
179  for(auto const& fname:{std::get<I>(m_objectives).name()... }){
180  name+=fname+'/';
181  }
182  name.pop_back();
183  return name;
184  }
185 
186  std::tuple<Objectives...> m_objectives;
187  std::vector<RotatedObjectiveFunction> m_rotations;
188  std::vector<RealVector> m_translations;
189 };
190 
191 
192 }
193 #endif