Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2019 Jakub Rydzewski (jr@fizyka.umk.pl). All rights reserved.
3 :
4 : See http://www.maze-code.github.io for more information.
5 :
6 : This file is part of maze.
7 :
8 : maze is free software: you can redistribute it and/or modify it under the
9 : terms of the GNU Lesser General Public License as published by the Free
10 : Software Foundation, either version 3 of the License, or (at your option)
11 : any later version.
12 :
13 : maze is distributed in the hope that it will be useful, but WITHOUT ANY
14 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 : FOR A PARTICULAR PURPOSE.
16 :
17 : See the GNU Lesser General Public License for more details.
18 :
19 : You should have received a copy of the GNU Lesser General Public License
20 : along with maze. If not, see <https://www.gnu.org/licenses/>.
21 : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
22 : #ifndef __PLUMED_maze_Memetic_h
23 : #define __PLUMED_maze_Memetic_h
24 :
25 : /**
26 : * @file Memetic.h
27 : *
28 : * @author J. Rydzewski (jr@fizyka.umk.pl)
29 : */
30 :
31 : #include "core/ActionRegister.h"
32 :
33 : #include "Core.h"
34 : #include "Member.h"
35 : #include "Optimizer.h"
36 :
37 : namespace PLMD {
38 : namespace maze {
39 :
40 : /**
41 : * @class Memetic Memetic.h "maze/Memetic.h"
42 : *
43 : * @brief Memetic algorithms for the optimization of the loss function.
44 : */
45 : class Memetic: public Optimizer {
46 : public:
47 : /**
48 : * PLMD constructor.
49 : *
50 : * @param[in] ao PLMD::ActionOptions&.
51 : */
52 : explicit Memetic(const ActionOptions& ao);
53 :
54 : /**
55 : * Registers PLMD keywords.
56 : *
57 : * @param[in] keys Keywords.
58 : */
59 : static void registerKeywords(Keywords& keys);
60 :
61 : /**
62 : * Each class deriving from Optimizer needs to override this function.
63 : */
64 : void optimize() override;
65 :
66 : private:
67 : /**
68 : * Create a set of translations relative to the ligand, each translation
69 : * encodes a probable biasing direction.
70 : */
71 : void initialize_members();
72 :
73 : /**
74 : * Score each translation by the loss function.
75 : */
76 : void score_members();
77 :
78 : /**
79 : * Calculate the mean score.
80 : */
81 : double score_mean();
82 :
83 : /**
84 : * Sort the population using heaps, required for finding a minimum of the loss
85 : * function.
86 : */
87 : void sort_members();
88 :
89 : /**
90 : * Encode a ligand conformation.
91 : */
92 : Vector create_coding();
93 :
94 : /**
95 : * Check if the vector length is out of bounds.
96 : *
97 : * @param[in] v Vector length.
98 : */
99 : bool out_of_bounds(double v);
100 :
101 : /**
102 : * Score a single member.
103 : *
104 : * @param[in] v Member's translation.
105 : */
106 : double score_member(const Vector& v);
107 :
108 : /**
109 : * Print a status.
110 : */
111 : void print_status() const;
112 :
113 : /**
114 : * Select a new population using the roulette selection.
115 : */
116 : void selection_roulette();
117 :
118 : /**
119 : * Perform mating in the population.
120 : *
121 : * @param[in,out] members Population.
122 : */
123 : void mating(std::vector<Member>& members);
124 :
125 : /**
126 : * Mate two members.
127 : *
128 : * @param[in,out] m1 1st member.
129 : * @param[in,out] m2 2nd member.
130 : */
131 : void crossover(Member& m1, Member& m2);
132 :
133 : /**
134 : * Mutate a member.
135 : *
136 : * @param[in,out] m Member.
137 : */
138 : void mutation(Member& m);
139 :
140 : /**
141 : * Mutate the population.
142 : *
143 : * @param[in,out] members Population.
144 : */
145 : void mutation(std::vector<Member>& members);
146 :
147 : protected:
148 : /**
149 : * Local searches to improve global solutions.
150 : */
151 :
152 : /**
153 : * Stochastic hill climbing.
154 : *
155 : * Neighbors with better or equal cost should be accepted, allowing the
156 : * technique to navigate across plateaus in the response surface.
157 : *
158 : * @param[in,out] m Member.
159 : * @param[in] params None.
160 : */
161 : void stochastic_hill_climbing(
162 : Member& m,
163 : /* none */ const std::vector<double>& params = {}
164 : );
165 :
166 : /**
167 : * Random-restart hill climbing.
168 : *
169 : * The algorithm can be restarted and repeated a number of times after it
170 : * converges to provide an improved result.
171 : *
172 : * @param[in,out] m Member.
173 : * @param[in] params Number of restarts.
174 : */
175 : void random_restart_hill_climbing(
176 : Member& m,
177 : /* n_restarts */ const std::vector<double>& params = {10}
178 : );
179 :
180 : /**
181 : * Solis-Wets random walk.
182 : *
183 : * Adaptive random search algorithm was designed to address the limitations of
184 : * the fixed step size. The strategy for adaptive random search is to
185 : * continually approximate the optimal step size required to reach the global
186 : * optimum in the search space. This is achieved by trialling and adopting
187 : * smaller or larger step sizes only if they result in an improvement in the
188 : * search performance.
189 : *
190 : * Very large step sizes are trialled with a much lower frequency. This
191 : * strategy of preferring large moves is intended to allow the technique to
192 : * escape local optima. Smaller step sizes are adopted if no improvement is
193 : * made for an extended period.
194 : *
195 : * @param[in,out] m Member.
196 : * @param[in] params
197 : */
198 : void adaptive_random_search(
199 : Member& m,
200 : /* */ const std::vector<double>& params = {1.0, 1.e-5, 2.0, 2.0, 3.0, 3.0}
201 : );
202 :
203 : /**
204 : * Luus–Jaakola heuristics.
205 : *
206 : * @param[in,out] m Member.
207 : * @param[in] params Bounds.
208 : */
209 : void luus_jaakola(
210 : Member& m,
211 : /* bounds */ const std::vector<double>& params
212 : );
213 :
214 : /**
215 : * Local annealing.
216 : *
217 : * @param[in,out] m Member.
218 : * @param[in] params T, alpha.
219 : */
220 : void annealing(
221 : Member& m,
222 : /* T, alpha */const std::vector<double>& params = {300.0, 0.95}
223 : );
224 :
225 : /**
226 : * Apply local search to members.
227 : *
228 : * @param[in,out] members Population.
229 : */
230 : void local_search(std::vector<Member>& members);
231 :
232 : protected:
233 : /**
234 : * Return an optimal biasing direction.
235 : */
236 : Vector solve();
237 :
238 : public:
239 : /**
240 : * Setters and getters.
241 : */
242 :
243 : unsigned int get_capacity() const;
244 : void set_capacity(unsigned int);
245 :
246 : unsigned int get_coding_len() const;
247 : void set_coding_len(unsigned int);
248 :
249 : unsigned int get_n_local_iterations() const;
250 : void set_n_local_iterations(unsigned int);
251 :
252 : unsigned int get_n_global_iterations() const;
253 : void set_n_global_iterations(unsigned int);
254 :
255 : double get_mutation_rate() const;
256 : void set_mutation_rate(double);
257 :
258 : double get_mating_rate() const;
259 : void set_mating_rate(double);
260 :
261 : double get_cauchy_mean() const;
262 : void set_cauchy_mean(double);
263 :
264 : double get_cauchy_spread() const;
265 : void set_cauchy_spread(double);
266 :
267 : bool is_local_search_on() const;
268 : void local_search_on();
269 : void local_search_off();
270 :
271 : double get_local_search_rate() const;
272 : void set_local_search_rate(double);
273 :
274 : std::string get_local_search_type() const;
275 : void set_local_search_type(const std::string&);
276 :
277 : protected:
278 : //! Population
279 : std::vector<Member> members_;
280 :
281 : //! Bound
282 : double bound_;
283 :
284 : //! Scores
285 : double score_worst_;
286 : double score_best_;
287 : Member member_best_;
288 :
289 : //! If a local search is performed
290 : bool adaptation_;
291 :
292 : protected:
293 : //! Size of population
294 : unsigned int capacity_;
295 : //! Length of coding
296 : unsigned int coding_len_;
297 :
298 : //! Number of local search iterations
299 : unsigned int n_local_iterations_;
300 : //! Number of global search iterations, doomsday
301 : unsigned int n_global_iterations_;
302 :
303 : //! Probability of mutation
304 : double mutation_rate_;
305 : //! Probability of mating
306 : double mating_rate_;
307 :
308 : //! Mean and spread of cauchy sampler
309 : double cauchy_mean_alpha_;
310 : double cauchy_mean_beta_;
311 :
312 : //! If local search is employed in sampling
313 : bool local_search_on_;
314 : //! Rate of local mutation
315 : double local_search_rate_;
316 : //! Type of local search, stochastic_hill_climbing or adaptive_random_search
317 : std::string local_search_type_;
318 : };
319 :
320 6 : void Memetic::initialize_members() {
321 6 : members_.clear();
322 6 : members_.resize(capacity_);
323 :
324 42 : for (size_t i = 0; i < capacity_; ++i) {
325 36 : Member m{};
326 :
327 36 : m.score=0;
328 36 : m.translation=create_coding();
329 :
330 36 : members_.at(i) = m;
331 : }
332 6 : }
333 :
334 30 : void Memetic::score_members() {
335 210 : for (size_t i = 0; i < members_.size(); ++i) {
336 180 : double s = score_member(members_[i].translation);
337 180 : members_[i].score=s;
338 : }
339 30 : }
340 :
341 30 : void Memetic::sort_members() {
342 30 : std::make_heap(
343 : members_.begin(),
344 : members_.end(),
345 : compare
346 : );
347 :
348 30 : std::sort_heap(
349 : members_.begin(),
350 : members_.end(),
351 : compare
352 : );
353 :
354 30 : member_best_ = members_[capacity_ - 1];
355 30 : score_best_ = members_[capacity_ - 1].score;
356 30 : score_worst_ = members_[0].score;
357 30 : }
358 :
359 0 : double Memetic::score_mean() {
360 0 : auto acc = [](double s, const Member& m) { return s + m.score; };
361 :
362 : return std::accumulate(
363 : members_.begin(),
364 : members_.end(),
365 : 0.0,
366 0 : acc) / capacity_;
367 : }
368 :
369 30 : void Memetic::selection_roulette() {
370 30 : std::vector<Member> sel(members_);
371 30 : std::vector<double> rel_scores(capacity_, 0.0);
372 :
373 210 : for (std::size_t i = 0; i < capacity_; ++i) {
374 180 : double r = 1.0 / (members_[i].score + 0.01);
375 180 : rel_scores.at(i) = r;
376 : }
377 :
378 30 : std::vector<double> cum_sum(capacity_, 0.0);
379 30 : std::partial_sum(
380 : rel_scores.begin(),
381 : rel_scores.end(),
382 : cum_sum.begin(),
383 : std::plus<double>()
384 : );
385 :
386 30 : double sum = cum_sum.back();
387 : members_.clear();
388 30 : members_.resize(capacity_);
389 : int chosen = -1;
390 :
391 210 : for (size_t j = 0; j < capacity_; ++j) {
392 : double probability=rnd::next_double(sum);
393 600 : for (size_t i = 0; i < cum_sum.size(); ++i) {
394 600 : if (cum_sum[i] > probability) {
395 180 : chosen = i;
396 :
397 180 : members_.at(j).score = sel.at(chosen).score;
398 180 : members_.at(j).translation = sel.at(chosen).translation;
399 :
400 180 : break;
401 : }
402 : }
403 : }
404 30 : }
405 :
406 1 : void Memetic::crossover(Member& s1, Member& s2) {
407 1 : size_t i = rnd::next_int(1, coding_len_ - 1);
408 :
409 1 : Member z1(s1);
410 1 : Member z2(s2);
411 :
412 3 : for (size_t j = i; j < coding_len_; ++j) {
413 2 : z1.translation[j] = s2.translation[j];
414 2 : z2.translation[j] = s1.translation[j];
415 : }
416 :
417 1 : if (!out_of_bounds(z1.translation.modulo()) && !out_of_bounds(z2.translation.modulo())) {
418 1 : s1 = z1;
419 1 : s2 = z2;
420 : }
421 1 : }
422 :
423 116 : void Memetic::mutation(Member& m) {
424 116 : int which = rnd::next_int(coding_len_);
425 116 : double v = rnd::next_cauchy(cauchy_mean_alpha_, cauchy_mean_beta_);
426 116 : m.translation[which] += v;
427 116 : if (out_of_bounds(m.translation.modulo())) {
428 49 : m.translation[which] -= v;
429 : }
430 116 : }
431 :
432 30 : void Memetic::mutation(std::vector<Member>& m) {
433 210 : for (std::vector<Member>::iterator it = m.begin(), end = m.end(); it != end; ++it) {
434 180 : double r = rnd::next_double();
435 180 : if (r < mutation_rate_) {
436 6 : mutation(*it);
437 : }
438 : }
439 30 : }
440 :
441 11 : void Memetic::stochastic_hill_climbing(
442 : Member& m,
443 : const std::vector<double>& params)
444 : {
445 121 : for (std::size_t i = 0; i < n_local_iterations_; ++i) {
446 110 : Member n;
447 110 : n.translation = m.translation;
448 110 : mutation(n);
449 110 : double score_n = score_member(n.translation);
450 :
451 110 : if (m.score > score_n) {
452 60 : m.translation = n.translation;
453 : }
454 : }
455 11 : }
456 :
457 0 : void Memetic::random_restart_hill_climbing(
458 : Member& m,
459 : const std::vector<double>& params)
460 : {
461 0 : unsigned int n_restarts = static_cast<int>(params[0]);
462 0 : std::vector<Member> s(n_restarts);
463 : unsigned int ndx = 0;
464 0 : double min = m.score;
465 :
466 0 : for (std::size_t r = 0; r < n_restarts; ++r) {
467 0 : Member n = m;
468 0 : stochastic_hill_climbing(n);
469 0 : s[r] = n;
470 :
471 0 : if (min > n.score) {
472 : min = n.score;
473 0 : ndx = r;
474 : }
475 : }
476 :
477 0 : m = s[ndx];
478 0 : }
479 :
480 0 : void Memetic::annealing(
481 : Member& m,
482 : const std::vector<double>& params)
483 : {
484 0 : double T = params[0];
485 0 : double alpha = params[1];
486 :
487 0 : for (std::size_t i = 0; i < n_local_iterations_; ++i) {
488 0 : Member n = m;
489 0 : mutation(n);
490 0 : double score_n = score_member(n.translation);
491 :
492 : double probability = std::min(
493 0 : 1.0,
494 0 : std::exp(-(score_n - m.score) / T)
495 0 : );
496 :
497 0 : double r = rnd::next_double();
498 :
499 0 : if (r < probability) {
500 0 : m = n;
501 : }
502 :
503 0 : T *= alpha;
504 : }
505 0 : }
506 :
507 0 : void Memetic::luus_jaakola(
508 : Member& m,
509 : const std::vector<double>& params)
510 : {
511 : /* TODO */
512 0 : }
513 :
514 0 : void Memetic::adaptive_random_search(
515 : Member& m,
516 : const std::vector<double>& params)
517 : {
518 0 : double rho_start = params[0];
519 0 : double rho_lower_bound = params[1];
520 0 : double ex = params[2];
521 0 : double ct = params[3];
522 0 : int s_ex = static_cast<int>(params[4]);
523 0 : int f_ct = static_cast<int>(params[5]);
524 :
525 : unsigned int k = 0;
526 0 : Vector xk = m.translation;
527 : int ns = 0;
528 : int nf = 0;
529 0 : Vector bk;
530 0 : bk.zero();
531 : double rho_k = rho_start;
532 :
533 0 : while (rho_k > rho_lower_bound && k < n_local_iterations_) {
534 0 : if (ns >= s_ex) {
535 0 : rho_k *= ex;
536 : }
537 0 : else if (nf >= f_ct) {
538 0 : rho_k *= ct;
539 : }
540 :
541 0 : std::vector<double> chiv = rnd::next_double(-rho_k, rho_k, 3);
542 0 : Vector chi = tls::vector2Vector(chiv);
543 0 : Vector tmp;
544 :
545 0 : for (unsigned int i = 0; i < 3; ++i) {
546 0 : tmp[i] = 2.0 * (xk[i] - chi[i]);
547 : }
548 :
549 0 : double score_chi = score_member(chi);
550 0 : double score_xk = score_member(xk);
551 0 : double score_tmp = score_member(tmp);
552 :
553 0 : if (score_chi < score_xk) {
554 0 : ns++;
555 : nf = 0;
556 :
557 0 : for (unsigned int i = 0; i < 3; i++) {
558 0 : bk[i] = 0.2 * bk[i] + 0.4 * (chi[i] - xk[i]);
559 0 : xk[i] = chi[i];
560 : }
561 : }
562 0 : else if (score_tmp < score_xk && score_xk <= score_chi) {
563 0 : ns++;
564 : nf = 0;
565 :
566 0 : for (unsigned int i = 0; i < 3; i++) {
567 0 : bk[i] = bk[i] - 0.4 * (chi[i] - xk[i]);
568 0 : xk[i] = 2.0 * xk[i] - chi[i];
569 : }
570 : }
571 : else {
572 : ns = 0;
573 0 : nf++;
574 :
575 0 : for (unsigned int i = 0; i < 3; i++) {
576 0 : bk[i] = 0.5 * bk[i];
577 : }
578 : }
579 :
580 0 : k++;
581 : }
582 :
583 0 : m.translation = xk;
584 0 : }
585 :
586 30 : void Memetic::local_search(std::vector<Member>& m) {
587 30 : adaptation_ = true;
588 :
589 30 : if (local_search_on_) {
590 105 : for (std::size_t i = 0; i < capacity_; ++i) {
591 90 : double probability = rnd::next_double();
592 :
593 90 : if (probability < local_search_rate_) {
594 11 : if (local_search_type_ == "stochastic_hill_climbing")
595 22 : stochastic_hill_climbing(m[i]);
596 0 : else if (local_search_type_ == "adaptive_random_search")
597 0 : adaptive_random_search(m[i]);
598 0 : else if (local_search_type_ == "random_restart_hill_climbing")
599 0 : random_restart_hill_climbing(m[i]);
600 : }
601 : }
602 : }
603 :
604 30 : adaptation_ = false;
605 30 : }
606 :
607 30 : void Memetic::mating(std::vector<Member>& m) {
608 : std::vector<Member> offspring;
609 :
610 120 : while (m.size() != 0) {
611 90 : int j = rnd::next_int(m.size());
612 90 : int i = rnd::next_int(m.size());
613 :
614 133 : while (i == j) {
615 43 : j=rnd::next_int(m.size());
616 : }
617 :
618 90 : Member m1 = m[i];
619 90 : Member m2 = m[j];
620 :
621 90 : if (i > j) {
622 : m.erase(m.begin() + i);
623 : m.erase(m.begin() + j);
624 : }
625 43 : else if (j > i) {
626 : m.erase(m.begin() + j);
627 : m.erase(m.begin() + i);
628 : }
629 :
630 90 : double probability = rnd::next_double();
631 90 : if (probability < mating_rate_) {
632 1 : crossover(m1, m2);
633 : }
634 :
635 90 : offspring.push_back(m1);
636 90 : offspring.push_back(m2);
637 : }
638 :
639 30 : m = offspring;
640 : offspring.clear();
641 30 : }
642 :
643 36 : Vector Memetic::create_coding() {
644 36 : double s = sampling_radius();
645 : double r = rnd::next_double(s);
646 :
647 36 : return rnd::next_plmd_vector(r);
648 : }
649 :
650 118 : bool Memetic::out_of_bounds(double v) {
651 118 : double s = sampling_radius();
652 :
653 118 : return v > s;
654 : }
655 :
656 290 : double Memetic::score_member(const Vector& coding) {
657 : double action = 0;
658 290 : Vector distance;
659 290 : const unsigned nl_size = neighbor_list_->size();
660 290 : Vector dev = coding;
661 :
662 290 : #pragma omp parallel num_threads(get_n_threads_openmp())
663 : {
664 : #pragma omp for reduction(+:action)
665 : for (unsigned int i = 0; i < nl_size; i++) {
666 : unsigned i0 = neighbor_list_->getClosePair(i).first;
667 : unsigned i1 = neighbor_list_->getClosePair(i).second;
668 :
669 : if (getAbsoluteIndex(i0) == getAbsoluteIndex(i1)) {
670 : continue;
671 : }
672 :
673 : if (pbc_) {
674 : distance = pbcDistance(getPosition(i0) + dev, getPosition(i1));
675 : }
676 : else {
677 : distance = delta(getPosition(i0) + dev, getPosition(i1));
678 : }
679 :
680 : action += pairing(distance.modulo());
681 : }
682 : }
683 :
684 290 : return action;
685 : }
686 :
687 0 : void Memetic::print_status() const {
688 0 : log.printf("Lowest score: %f \n", score_best_);
689 0 : }
690 :
691 6 : Vector Memetic::solve() {
692 6 : initialize_members();
693 :
694 : size_t epoch = 0;
695 36 : while (epoch < n_global_iterations_) {
696 30 : score_members();
697 :
698 30 : selection_roulette();
699 30 : mating(members_);
700 30 : mutation(members_);
701 30 : local_search(members_);
702 :
703 30 : sort_members();
704 :
705 30 : epoch++;
706 : }
707 :
708 6 : return member_best_.translation / member_best_.translation.modulo();
709 : }
710 :
711 : inline unsigned int Memetic::get_capacity() const {
712 : return capacity_;
713 : }
714 :
715 : inline void Memetic::set_capacity(unsigned int capacity) {
716 : capacity_ = capacity;
717 : }
718 :
719 : inline unsigned int Memetic::get_coding_len() const {
720 : return coding_len_;
721 : }
722 :
723 : inline void Memetic::set_coding_len(unsigned int coding_len) {
724 : coding_len_ = coding_len;
725 : }
726 :
727 : inline unsigned int Memetic::get_n_global_iterations() const {
728 : return n_global_iterations_;
729 : }
730 :
731 : inline void Memetic::set_n_global_iterations(unsigned int n_global_iterations) {
732 2 : n_global_iterations_ = n_global_iterations;
733 : }
734 :
735 : inline unsigned int Memetic::get_n_local_iterations() const {
736 : return n_local_iterations_;
737 : }
738 :
739 : inline void Memetic::set_n_local_iterations(unsigned int n_local_iterations) {
740 : n_local_iterations_ = n_local_iterations;
741 : }
742 :
743 : inline double Memetic::get_mating_rate() const {
744 : return mating_rate_;
745 : }
746 :
747 : inline void Memetic::set_mating_rate(double mating_rate) {
748 : mating_rate_ = mating_rate;
749 : }
750 :
751 : inline double Memetic::get_mutation_rate() const {
752 : return mutation_rate_;
753 : }
754 :
755 : inline void Memetic::set_mutation_rate(double mutation_rate) {
756 : mutation_rate_ = mutation_rate;
757 : }
758 :
759 : inline double Memetic::get_cauchy_mean() const {
760 : return cauchy_mean_alpha_;
761 : }
762 :
763 : inline void Memetic::set_cauchy_mean(double cauchy_mean_alpha) {
764 : cauchy_mean_alpha_ = cauchy_mean_alpha;
765 : }
766 :
767 : inline double Memetic::get_cauchy_spread() const {
768 : return cauchy_mean_beta_;
769 : }
770 :
771 : inline void Memetic::set_cauchy_spread(double cauchy_mean_beta) {
772 : cauchy_mean_beta_ = cauchy_mean_beta;
773 : }
774 :
775 : inline bool Memetic::is_local_search_on() const {
776 : return local_search_on_;
777 : }
778 :
779 : inline void Memetic::local_search_on() {
780 : local_search_on_ = true;
781 : }
782 :
783 : inline void Memetic::local_search_off() {
784 : local_search_on_ = false;
785 : }
786 :
787 : inline double Memetic::get_local_search_rate() const {
788 : return local_search_rate_;
789 : }
790 :
791 : inline void Memetic::set_local_search_rate(double local_search_rate) {
792 : local_search_rate_ = local_search_rate;
793 : }
794 :
795 : inline std::string Memetic::get_local_search_type() const {
796 : return local_search_type_;
797 : }
798 :
799 : inline void Memetic::set_local_search_type(const std::string& local_search_type) {
800 : local_search_type_ = local_search_type;
801 : }
802 :
803 : } // namespace maze
804 : } // namespace PLMD
805 :
806 : #endif // __PLUMED_maze_Memetic_h
|