PaGMO  1.1.5
rotated.cpp
1 /*****************************************************************************
2  * Copyright (C) 2004-2015 The PaGMO development team, *
3  * Advanced Concepts Team (ACT), European Space Agency (ESA) *
4  * *
5  * https://github.com/esa/pagmo *
6  * *
7  * act@esa.int *
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  * This program is distributed in the hope that it will be useful, *
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17  * GNU General Public License for more details. *
18  * *
19  * You should have received a copy of the GNU General Public License *
20  * along with this program; if not, write to the *
21  * Free Software Foundation, Inc., *
22  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
23  *****************************************************************************/
24 
25 #include <cmath>
26 #include <algorithm>
27 
28 #include "../exceptions.h"
29 #include "../types.h"
30 #include "../population.h"
31 #include "base.h"
32 #include "rotated.h"
33 
34 namespace pagmo { namespace problem {
35 
45 rotated::rotated(const base &p, const Eigen::MatrixXd &rotation ):
46  base_meta(
47  p,
48  p.get_dimension(),
49  p.get_i_dimension(),
50  p.get_f_dimension(),
51  p.get_c_dimension(),
52  p.get_ic_dimension(),
53  p.get_c_tol()),
54  m_Rotate(rotation), m_normalize_translation(), m_normalize_scale()
55 {
56  m_InvRotate = m_Rotate.transpose();
57 
58  Eigen::MatrixXd check = m_InvRotate * m_Rotate;
59  if(!check.isIdentity(1e-5)){
60  pagmo_throw(value_error,"The input matrix seems not to be orthonormal (to a tolerance of 1e-5)");
61  }
62  if(p.get_i_dimension()>0){
63  pagmo_throw(value_error,"Input problem has an integer dimension. Cannot rotate it.");
64  }
65  configure_new_bounds();
66 }
67 
77  const std::vector<std::vector<double> > &rotation):
78  base_meta(
79  p,
80  p.get_dimension(),
81  p.get_i_dimension(),
82  p.get_f_dimension(),
83  p.get_c_dimension(),
84  p.get_ic_dimension(),
85  p.get_c_tol()),
86  m_Rotate(),m_normalize_translation(), m_normalize_scale()
87 {
88  if(!(rotation.size()==get_dimension())){
89  pagmo_throw(value_error,"The input matrix dimensions seem incorrect");
90  }
91  if(p.get_i_dimension()>0){
92  pagmo_throw(value_error,"Input problem has an integer dimension. Cannot rotate it.");
93  }
94  m_Rotate.resize(rotation.size(),rotation.size());
95  for (base::size_type i = 0; i < rotation.size(); ++i) {
96  if(!(rotation.size()==rotation[i].size())){
97  pagmo_throw(value_error,"The input matrix seems not to be square");
98  }
99  for (base::size_type j = 0; j < rotation[i].size(); ++j) {
100  m_Rotate(i,j) = rotation[i][j];
101  }
102  }
103  m_InvRotate = m_Rotate.transpose();
104 
105  Eigen::MatrixXd check = m_InvRotate * m_Rotate;
106  if(!check.isIdentity(1e-5)){
107  pagmo_throw(value_error,"The input matrix seems not to be orthonormal (to a tolerance of 1e-5)");
108  }
109  configure_new_bounds();
110 }
111 
119  base_meta(
120  p,
121  p.get_dimension(),
122  p.get_i_dimension(),
123  p.get_f_dimension(),
124  p.get_c_dimension(),
125  p.get_ic_dimension(),
126  p.get_c_tol()),
127  m_normalize_translation(), m_normalize_scale()
128 {
129  size_type dim = p.get_dimension();
130  m_Rotate = Eigen::MatrixXd::Random(dim, dim).householderQr().householderQ();
131  m_InvRotate = m_Rotate.transpose();
132 
133  Eigen::MatrixXd check = m_InvRotate * m_Rotate;
134  if(!check.isIdentity(1e-5)){
135  pagmo_throw(value_error,"The input matrix seems not to be orthonormal (to a tolerance of 1e-5)");
136  }
137  if(p.get_i_dimension()>0){
138  pagmo_throw(value_error,"Input problem has an integer dimension. Cannot rotate it.");
139  }
140  configure_new_bounds();
141 }
142 
145 {
146  return base_ptr(new rotated(*this));
147 }
148 
150 // Slight twist here: Rotation causes the new bounds to be
151 // not seperable w.r.t the axes. Here, the approach taken
152 // is to construct a new constraint hyperbox that is a relaxation of
153 // the real non-separable constraints. All the points in the original
154 // search space will be searchable in the new constraint box. Some
155 // additional points (that may be invalid) will be introduced this way,
156 // so in the objfun_impl all these out-of-bounds point will have to be projected
157 // back to valid bounds.
158 
159 void rotated::configure_new_bounds()
160 {
161 
162  for(base::size_type i = 0; i < get_lb().size(); i++){
163  double mean_t = (m_original_problem->get_ub()[i] + m_original_problem->get_lb()[i]) / 2;
164  double spread_t = m_original_problem->get_ub()[i] - m_original_problem->get_lb()[i]; // careful, if zero needs to be acounted for later
165  m_normalize_translation.push_back(mean_t); // Center to origin
166  m_normalize_scale.push_back(spread_t/2); // Scale to [-1, 1] centered at origin
167  }
168  // Expand the box to cover the whole original search space. We may here call directly
169  // the set_bounds(const double &, const double &) as all dimensions are now equal
170  set_bounds(-sqrt(2), sqrt(2));
171 }
172 
173 // Used to normalize the original upper and lower bounds
174 // to [-1, 1], at each dimension
175 decision_vector rotated::normalize_to_center(const decision_vector& x) const
176 {
177  decision_vector normalized_x(x.size(), 0);
178  for(base::size_type i = 0; i < x.size(); i++) {
179  if (m_normalize_scale[i] == 0) { //If the bounds witdth is zero
180  normalized_x[i] = 0;
181  }
182  else {
183  normalized_x[i] = (x[i] - m_normalize_translation[i]) / m_normalize_scale[i];
184  }
185  }
186  return normalized_x;
187 }
188 
189 // For a decision_vector in the normalized [-1, 1] space,
190 // perform the inverse operation of normalization to get it's
191 // location in the original space
192 decision_vector rotated::denormalize_to_original(const decision_vector& x_normed) const
193 {
194  decision_vector denormalized_x(x_normed.size(), 0);
195  for(base::size_type i = 0; i < x_normed.size(); i++){
196  denormalized_x[i] = (x_normed[i] * m_normalize_scale[i]) + m_normalize_translation[i];
197  }
198  return denormalized_x;
199 }
200 
201 // Clip the variables to the valid bounds
202 decision_vector rotated::projection_via_clipping(const decision_vector& x) const
203 {
204  decision_vector x_clipped = x;
205  for(base::size_type i = 0; i < x_clipped.size(); i++){
206  x_clipped[i] = std::max(x_clipped[i], m_original_problem->get_lb()[i]);
207  x_clipped[i] = std::min(x_clipped[i], m_original_problem->get_ub()[i]);
208  }
209  return x_clipped;
210 }
211 
215 {
216  // This may be outside of the original domain, due to the
217  // relaxed variable bounds after rotation -- project it back if so.
218 
219  // 1. De-rotate the vector in the normalized space
220  Eigen::VectorXd x_normed_vec = Eigen::VectorXd::Zero(x_normed.size());
221  Eigen::VectorXd x_derotated_vec;
222  for(base::size_type i = 0; i < x_normed.size(); i++){
223  x_normed_vec(i) = x_normed[i];
224  }
225  x_derotated_vec = m_InvRotate * x_normed_vec;
226 
227  // 2. De-normalize the de-rotated vector to the original bounds
228  decision_vector x_derotated(x_normed.size(), 0);
229  for(base::size_type i = 0; i < x_normed.size(); i++){
230  x_derotated[i] = x_derotated_vec(i);
231  }
232  decision_vector x_wild = denormalize_to_original(x_derotated);
233 
234  // 3. The de-normalized vector may be out of bounds, if so project back in
235  decision_vector x = projection_via_clipping(x_wild);
236 
237  return x;
238 }
239 
243 {
244  m_original_problem->objfun(f, derotate(x));
245 }
246 
250 {
251  m_original_problem->compute_constraints(c, derotate(x));
252 }
253 
255 
258 std::string rotated::human_readable_extra() const
259 {
260  std::ostringstream oss;
261  oss << m_original_problem->human_readable_extra() << std::endl;
262  oss << "\n\tRotation matrix: " << std::endl;
263  if (m_Rotate.cols() > 5) {
264  oss << m_Rotate.block(0,0,5,5) << std::endl;
265  oss << "..." << std::endl;
266  }
267  else {
268  oss << m_Rotate << std::endl;
269  }
270  return oss.str();
271 }
272 
273 std::string rotated::get_name() const
274 {
275  return m_original_problem->get_name() + " [Rotated]";
276 }
277 
283 const Eigen::MatrixXd& rotated::get_rotation_matrix() const {
284  return m_Rotate;
285 }
286 
287 }}
288 
289 BOOST_CLASS_EXPORT_IMPLEMENT(pagmo::problem::rotated)
290 
std::string human_readable_extra() const
Extra human readable info for the problem.
Definition: rotated.cpp:258
Root PaGMO namespace.
boost::shared_ptr< base > base_ptr
Alias for shared pointer to base problem.
Definition: problem/base.h:62
std::vector< double > decision_vector
Decision vector type.
Definition: types.h:40
decision_vector derotate(const decision_vector &) const
Definition: rotated.cpp:214
Base problem class.
Definition: problem/base.h:148
rotated(const base &, const Eigen::MatrixXd &)
Definition: rotated.cpp:45
size_type get_dimension() const
Return global dimension.
const Eigen::MatrixXd & get_rotation_matrix() const
Definition: rotated.cpp:283
std::string get_name() const
Get problem's name.
Definition: rotated.cpp:273
Meta=problems base class.
Definition: base_meta.h:50
Shifted meta-problem.
Definition: rotated.h:46
base_ptr clone() const
Clone method.
Definition: rotated.cpp:144
size_type get_i_dimension() const
Return integer dimension.
std::vector< double > fitness_vector
Fitness vector type.
Definition: types.h:42
std::vector< double > constraint_vector
Constraint vector type.
Definition: types.h:44
void compute_constraints_impl(constraint_vector &, const decision_vector &) const
Definition: rotated.cpp:249
void objfun_impl(fitness_vector &, const decision_vector &) const
Definition: rotated.cpp:242
const decision_vector & get_lb() const
Lower bounds getter.
void set_bounds(const decision_vector &, const decision_vector &)
Bounds setter from pagmo::decision_vector.
decision_vector::size_type size_type
Problem's size type: the same as pagmo::decision_vector's size type.
Definition: problem/base.h:160
base_ptr m_original_problem
Smart pointer to the original problem instance.
Definition: base_meta.h:80