CSA
csa.hpp
1 /*
2  * C++ library for Coupled Simulated Annealing.
3  *
4  *
5  * Copyright (c) 2009 Samuel Xavier-de-Souza, Johan A.K. Suykens,
6  * Joos Vandewalle, De ́sire ́ Bolle ́
7  * Copyright (c) 2018 Evan Pete Walsh
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a copy
10  * of this software and associated documentation files (the "Software"), to deal
11  * in the Software without restriction, including without limitation the rights
12  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13  * copies of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included in all
17  * copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25  * SOFTWARE.
26  */
27 
28 #ifndef CSA_H
29 #define CSA_H
30 
31 #ifndef CSA_ITER_MONO
32 #define CSA_ITER_MONO 0
33 #endif
34 
35 #include <cmath>
36 #include <omp.h>
37 #include <vector>
38 
39 
40 namespace CSA {
41 
42 template<typename Scalar_x, typename Scalar_fx>
43 class State
44 {
45 public:
49  std::vector<Scalar_x> x;
53  std::vector<Scalar_x> best_x;
57  Scalar_fx cost;
61  Scalar_fx best_cost;
62 
70  State(int n, const Scalar_x* x0, Scalar_fx fx0)
71  {
72  this->x = std::vector<Scalar_x>(x0, x0 + n);
73  this->best_x = std::vector<Scalar_x>(x0, x0 + n);
74  this->cost = fx0;
75  this->best_cost = fx0;
76  }
77 
85  void step(std::vector<Scalar_x> &y, Scalar_fx y_cost)
86  {
87  this->cost = y_cost;
88  this->x.swap(y);
89  }
90 }; // class State
91 
92 
93 template<typename Scalar_x, typename Scalar_fx>
95 {
96 public:
100  const int m;
104  const int n;
108  std::vector<State<Scalar_x, Scalar_fx>> states;
109 
110  State<Scalar_x, Scalar_fx>& operator[](int i) {
111  return this->states[i];
112  }
113 
114  const State<Scalar_x, Scalar_fx>& operator[](int i) const {
115  return this->states[i];
116  }
117 
127  SharedStates(int m, int n, const Scalar_x* x0, Scalar_fx fx0)
128  : m(m), n(n), states(m, State<Scalar_x, Scalar_fx>(n, x0, fx0))
129  {
130  }
131 }; // class SharedStates
132 
133 
134 template<typename Scalar_x, typename Scalar_fx>
135 class Solver
136 {
137 public:
141  int m = 4;
145  int max_iterations = 1000000;
149  float tgen_initial = 0.01;
153  float tgen_schedule = 0.99999;
157  float tacc_initial = 0.9;
162  float tacc_schedule = 0.01;
166  float desired_variance = 0.99;
167 
171  Solver() { };
172 
202  inline int minimize(
203  int n,
204  Scalar_x* x,
205  Scalar_fx (*fx)(void*, Scalar_x*),
206  void (*step)(void*, Scalar_x* y, const Scalar_x*, float tgen),
207  void (*progress)(void*,
208  Scalar_fx cost,
209  float tgen,
210  float tacc,
211  int opt_id,
212  int iter),
213  void* instance)
214  {
215  Scalar_fx fx0 = fx(instance, x);
216 
217  // Initialize shared values.
218  SharedStates<Scalar_x, Scalar_fx> shared_states(this->m, n, x, fx0);
219  float tacc = this->tacc_initial;
220  float tgen = this->tgen_initial;
221  float tmp, sum_a, prob_var, gamma = m;
222 
223  omp_lock_t lock;
224  omp_init_lock(&lock);
225 
226  #pragma omp parallel shared(n, shared_states, tacc, tgen, gamma) num_threads(this->m) default(none)
227  {
228  int k, opt_id = omp_get_thread_num();
229 
230  Scalar_fx max_cost = shared_states[0].cost;
231  Scalar_fx cost;
232  std::vector<Scalar_x> y(n, Scalar_x(0));
233  float unif, prob;
234 
235 #if CSA_ITER_MONO == 0
236  #pragma omp for
237 #else
238  #pragma omp for schedule(monotonic:static)
239 #endif
240  for (int iter = 0; iter < this->max_iterations; ++iter) {
241  // Generate a new solution.
242  step(instance, y.data(), shared_states[opt_id].x.data(), tgen);
243  cost = fx(instance, y.data());
244 
245  // Decide if we should take a step and update best solution so far.
246  if (cost < shared_states[opt_id].cost) {
247  omp_set_lock(&lock);
248  // Update best solution for this thread.
249  if (cost < shared_states[opt_id].best_cost) {
250  shared_states[opt_id].best_cost = cost;
251  shared_states[opt_id].best_x = y;
252  if (progress != nullptr)
253  progress(instance, cost, tgen, tacc, opt_id, iter);
254  }
255 
256  // Take the step.
257  shared_states[opt_id].step(y, cost);
258  omp_unset_lock(&lock);
259  } else {
260  // We accept the "worse" solution with probability `prob`:
261  unif = drand48();
262  prob = std::exp((shared_states[opt_id].cost - max_cost) / tacc) / gamma;
263  if (prob > unif) {
264  omp_set_lock(&lock);
265  shared_states[opt_id].step(y, cost);
266  omp_unset_lock(&lock);
267  }
268  }
269 
270  // Update temperatures.
271  if (omp_test_lock(&lock)) {
272 
273  // Get maximum cost.
274  max_cost = shared_states[0].cost;
275  for (k = 0; k < this->m; ++k)
276  if (shared_states[k].cost > max_cost)
277  max_cost = shared_states[k].cost;
278 
279  // Update `gamma` and `prob_var`;
280  gamma = sum_a = 0.;
281  for (k = 0; k < this->m; ++k) {
282  tmp = (shared_states[k].cost - max_cost) / tacc;
283  gamma += std::exp(tmp);
284  sum_a += std::exp(2.0 * tmp);
285  }
286  prob_var = (this->m * (sum_a / (gamma * gamma)) - 1.) /
287  (this->m * this->m);
288 
289  // Update the acceptance temperature.
290  if (prob_var > this->desired_variance)
291  tacc += this->tacc_schedule * tacc;
292  else
293  tacc -= this->tacc_schedule * tacc;
294 
295  // Update the generation temperature.
296  tgen = this->tgen_schedule * tgen;
297 
298  // Unset the lock.
299  omp_unset_lock(&lock);
300  }
301  } // End of "#pragma omp for"
302  } // End of "#pragma omp parallel"
303 
304  // Get best result.
305  int best_ind = 0;
306  Scalar_fx best_cost = shared_states[0].best_cost;
307  for (int k = 0; k < this->m; ++k) {
308  if (shared_states[k].best_cost < best_cost) {
309  best_cost = shared_states[k].best_cost;
310  best_ind = k;
311  }
312  }
313  State<Scalar_x,Scalar_fx> best_state = shared_states[best_ind];
314  for (int i = 0; i < n; ++i)
315  x[i] = best_state.best_x[i];
316 
317  // Clean up.
318  omp_destroy_lock(&lock);
319 
320  return 0;
321  }
322 }; // class Solver
323 
324 } // namespace CSA
325 
326 #endif
float tgen_initial
Definition: csa.hpp:149
const int m
Definition: csa.hpp:100
std::vector< Scalar_x > x
Definition: csa.hpp:49
State(int n, const Scalar_x *x0, Scalar_fx fx0)
Definition: csa.hpp:70
std::vector< Scalar_x > best_x
Definition: csa.hpp:53
Scalar_fx best_cost
Definition: csa.hpp:61
float tgen_schedule
Definition: csa.hpp:153
Scalar_fx cost
Definition: csa.hpp:57
Definition: csa.hpp:135
Definition: csa.hpp:40
int minimize(int n, Scalar_x *x, Scalar_fx(*fx)(void *, Scalar_x *), void(*step)(void *, Scalar_x *y, const Scalar_x *, float tgen), void(*progress)(void *, Scalar_fx cost, float tgen, float tacc, int opt_id, int iter), void *instance)
Definition: csa.hpp:202
Solver()
Definition: csa.hpp:171
float desired_variance
Definition: csa.hpp:166
int m
Definition: csa.hpp:141
float tacc_schedule
Definition: csa.hpp:162
int max_iterations
Definition: csa.hpp:145
float tacc_initial
Definition: csa.hpp:157
void step(std::vector< Scalar_x > &y, Scalar_fx y_cost)
Definition: csa.hpp:85
Definition: csa.hpp:94
std::vector< State< Scalar_x, Scalar_fx > > states
Definition: csa.hpp:108
SharedStates(int m, int n, const Scalar_x *x0, Scalar_fx fx0)
Definition: csa.hpp:127
const int n
Definition: csa.hpp:104
Definition: csa.hpp:43