PaGMO  1.1.5
sa_corana.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 "sa_corana.h"
26 #include <boost/random/uniform_int.hpp>
27 #include <boost/random/uniform_real.hpp>
28 
29 
30 namespace pagmo { namespace algorithm {
31 
33 
45 sa_corana::sa_corana(int niter, const double &Ts, const double &Tf, int niterT, int niterR, const double &range):
46  base(),m_niter(niter),m_Ts(Ts),m_Tf(Tf),m_step_adj(niterT),m_bin_size(niterR),m_range(range)
47 {
48  if (niter < 0) {
49  pagmo_throw(value_error,"number of iterations must be nonnegative");
50  }
51  if (Ts <= 0 || Tf <= 0 || Ts <= Tf) {
52  pagmo_throw(value_error,"temperatures must be positive and Ts must be greater than Tf");
53  }
54  if (niterT < 0) {
55  pagmo_throw(value_error,"number of iteration before adjusting the temperature must be positive");
56  }
57  if (niterR < 0) {
58  pagmo_throw(value_error,"number of iteration before adjusting the neighbourhood must be positive");
59  }
60  if (range < 0 || range >1) {
61  pagmo_throw(value_error,"Initial range must be between 0 and 1");
62  }
63 }
66 {
67  return base_ptr(new sa_corana(*this));
68 }
69 
71 
79 void sa_corana::evolve(population &pop) const {
80 
81  // Let's store some useful variables.
82  const problem::base &prob = pop.problem();
83  const problem::base::size_type D = prob.get_dimension(), prob_i_dimension = prob.get_i_dimension(), prob_c_dimension = prob.get_c_dimension(), prob_f_dimension = prob.get_f_dimension();
84  const decision_vector &lb = prob.get_lb(), &ub = prob.get_ub();
85  const population::size_type NP = pop.size();
86  const problem::base::size_type Dc = D - prob_i_dimension;
87 
88  //We perform some checks to determine wether the problem/population are suitable for sa_corana
89  if ( Dc == 0 ) {
90  pagmo_throw(value_error,"There is no continuous part in the problem decision vector for sa_corana to optimise");
91  }
92 
93  if ( prob_c_dimension != 0 ) {
94  pagmo_throw(value_error,"The problem is not box constrained and sa_corana is not suitable to solve it");
95  }
96 
97  if ( prob_f_dimension != 1 ) {
98  pagmo_throw(value_error,"The problem is not single objective and sa_corana is not suitable to solve it");
99  }
100 
101  //Determines the number of temperature adjustment for the annealing procedure
102  const size_t n_T = m_niter / (m_step_adj * m_bin_size * Dc);
103 
104  // Get out if there is nothing to do.
105  if (NP == 0 || m_niter == 0) {
106  return;
107  }
108  if (n_T == 0) {
109  pagmo_throw(value_error,"n_T is zero, increase niter");
110  }
111 
112  //Starting point is the best individual
113  const int bestidx = pop.get_best_idx();
114  const decision_vector &x0 = pop.get_individual(bestidx).cur_x;
115  const fitness_vector &fit0 = pop.get_individual(bestidx).cur_f;
116  //Determines the coefficient to dcrease the temperature
117  const double Tcoeff = std::pow(m_Tf/m_Ts,1.0/(double)(n_T));
118  //Stores the current and new points
119  decision_vector xNEW = x0, xOLD = xNEW;
120  fitness_vector fNEW = fit0, fOLD = fNEW;
121  //Stores the adaptive steps of each component (integer part included but not used)
122  decision_vector step(D,m_range);
123 
124  //Stores the number of accepted points per component (integer part included but not used)
125  std::vector<int> acp(D,0) ;
126  double ratio = 0, currentT = m_Ts, probab = 0;
127 
128  //Main SA loops
129  for (size_t jter = 0; jter < n_T; ++jter) {
130  for (int mter = 0; mter < m_step_adj; ++mter) {
131  for (int kter = 0; kter < m_bin_size; ++kter) {
132  size_t nter = boost::uniform_int<int>(0,Dc-1)(m_urng);
133  for (size_t numb = 0; numb < Dc ; ++numb) {
134  nter = (nter + 1) % Dc;
135  //We modify the current point actsol by mutating its nter component within
136  //a step that we will later adapt
137  xNEW[nter] = xOLD[nter] + boost::uniform_real<double>(-1,1)(m_drng) * step[nter] * (ub[nter]-lb[nter]);
138 
139  // If new solution produced is infeasible ignore it
140  if ((xNEW[nter] > ub[nter]) || (xNEW[nter] < lb[nter])) {
141  xNEW[nter]=xOLD[nter];
142  continue;
143  }
144  //And we valuate the objective function for the new point
145  prob.objfun(fNEW,xNEW);
146 
147  // We decide wether to accept or discard the point
148  if (prob.compare_fitness(fNEW,fOLD) ) {
149  //accept
150  xOLD[nter] = xNEW[nter];
151  fOLD = fNEW;
152  acp[nter]++; //Increase the number of accepted values
153  } else {
154  //test it with Boltzmann to decide the acceptance
155  probab = exp ( - fabs(fOLD[0] - fNEW[0] ) / currentT );
156 
157  // we compare prob with a random probability.
158  if (probab > m_drng()) {
159  xOLD[nter] = xNEW[nter];
160  fOLD = fNEW;
161  acp[nter]++; //Increase the number of accepted values
162  } else {
163  xNEW[nter] = xOLD[nter];
164  }
165  } // end if
166  } // end for(nter = 0; ...
167  } // end for(kter = 0; ...
168  // adjust the step (adaptively)
169  for (size_t iter = 0; iter < Dc; ++iter) {
170  ratio = (double)acp[iter]/(double)m_bin_size;
171  acp[iter] = 0; //reset the counter
172  if (ratio > .6) {
173  //too many acceptances, increase the step by a factor 3 maximum
174  step[iter] = step [iter] * (1 + 2 *(ratio - .6)/.4);
175  } else {
176  if (ratio < .4) {
177  //too few acceptance, decrease the step by a factor 3 maximum
178  step [iter]= step [iter] / (1 + 2 * ((.4 - ratio)/.4));
179  };
180  };
181  //And if it becomes too large, reset it to its initial value
182  if ( step[iter] > m_range ) {
183  step [iter] = m_range;
184  };
185  }
186  }
187  // Cooling schedule
188  currentT *= Tcoeff;
189  }
190  if ( prob.compare_fitness(fOLD,fit0) ){
191  pop.set_x(bestidx,xOLD); //new evaluation is possible here......
192  std::transform(xOLD.begin(), xOLD.end(), pop.get_individual(bestidx).cur_x.begin(), xOLD.begin(),std::minus<double>());
193  pop.set_v(bestidx,xOLD);
194  }
195 }
196 
197 
199 std::string sa_corana::get_name() const
200 {
201  return "Simulated Annealing (Corana's)";
202 }
203 
205 
209 {
210  std::ostringstream s;
211  s << "iter:" << m_niter << ' ';
212  s << "Ts:" << m_Ts << ' ';
213  s << "Tf:" << m_Tf << ' ';
214  s << "steps:" << m_step_adj << ' ';
215  s << "bin_size:" << m_bin_size << ' ';
216  s << "range:" << m_range << ' ';
217  return s.str();
218 }
219 
220 }} //namespaces
221 
222 BOOST_CLASS_EXPORT_IMPLEMENT(pagmo::algorithm::sa_corana)
boost::shared_ptr< base > base_ptr
Alias for shared pointer to base algorithm.
Root PaGMO namespace.
std::vector< double > decision_vector
Decision vector type.
Definition: types.h:40
fitness_vector cur_f
Current fitness vector.
Definition: population.h:89
const individual_type & get_individual(const size_type &) const
Get constant reference to individual at position n.
Definition: population.cpp:277
Base algorithm class.
sa_corana(int niter=1, const double &Ts=10, const double &Tf=.1, int m_step_adj=1, int m_bin_size=20, const double &range=1)
Constructor.
Definition: sa_corana.cpp:45
Base problem class.
Definition: problem/base.h:148
std::string human_readable_extra() const
Extra human readable algorithm info.
Definition: sa_corana.cpp:208
Population class.
Definition: population.h:70
size_type get_dimension() const
Return global dimension.
bool compare_fitness(const fitness_vector &, const fitness_vector &) const
Compare fitness vectors.
base_ptr clone() const
Clone method.
Definition: sa_corana.cpp:65
fitness_vector objfun(const decision_vector &) const
Return fitness of pagmo::decision_vector.
Simulated Annealing, Corana's version with adaptive neighbourhood.
Definition: sa_corana.h:57
size_type get_i_dimension() const
Return integer dimension.
std::vector< double > fitness_vector
Fitness vector type.
Definition: types.h:42
void evolve(population &) const
Evolve implementation.
Definition: sa_corana.cpp:79
c_size_type get_c_dimension() const
Return global constraints dimension.
const decision_vector & get_ub() const
Upper bounds getter.
container_type::size_type size_type
Population size type.
Definition: population.h:192
decision_vector cur_x
Current decision vector.
Definition: population.h:83
rng_uint32 m_urng
Random number generator for unsigned integer values.
f_size_type get_f_dimension() const
Return fitness dimension.
std::string get_name() const
Algorithm name.
Definition: sa_corana.cpp:199
const decision_vector & get_lb() const
Lower bounds getter.
rng_double m_drng
Random number generator for double-precision floating point values.
decision_vector::size_type size_type
Problem's size type: the same as pagmo::decision_vector's size type.
Definition: problem/base.h:160