1 /* Copyright 2017-2021 PaGMO development team
2
3 This file is part of the PaGMO library.
4
5 The PaGMO library is free software; you can redistribute it and/or modify
6 it under the terms of either:
7
8 * the GNU Lesser General Public License as published by the Free
9 Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11
12 or
13
14 * the GNU General Public License as published by the Free Software
15 Foundation; either version 3 of the License, or (at your option) any
16 later version.
17
18 or both in parallel, as here.
19
20 The PaGMO library is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 for more details.
24
25 You should have received copies of the GNU General Public License and the
26 GNU Lesser General Public License along with the PaGMO library. If not,
27 see https://www.gnu.org/licenses/. */
28
29 #define BOOST_TEST_MODULE mbh_test
30 #define BOOST_TEST_DYN_LINK
31 #include <boost/test/unit_test.hpp>
32
33 #include <boost/lexical_cast.hpp>
34 #include <boost/test/tools/floating_point_comparison.hpp>
35 #include <cmath>
36 #include <iostream>
37 #include <string>
38
39 #include <pagmo/algorithm.hpp>
40 #include <pagmo/algorithms/compass_search.hpp>
41 #include <pagmo/algorithms/mbh.hpp>
42 #include <pagmo/io.hpp>
43 #include <pagmo/population.hpp>
44 #include <pagmo/problems/hock_schittkowsky_71.hpp>
45 #include <pagmo/problems/inventory.hpp>
46 #include <pagmo/problems/rosenbrock.hpp>
47 #include <pagmo/problems/zdt.hpp>
48 #include <pagmo/s11n.hpp>
49 #include <pagmo/threading.hpp>
50 #include <pagmo/types.hpp>
51
52 using namespace pagmo;
53
BOOST_AUTO_TEST_CASE(mbh_algorithm_construction)54 BOOST_AUTO_TEST_CASE(mbh_algorithm_construction)
55 {
56 compass_search inner_algo{100u, 0.1, 0.001, 0.7};
57 {
58 mbh user_algo{inner_algo, 5, 1e-3};
59 BOOST_CHECK((user_algo.get_perturb() == vector_double{1e-3}));
60 BOOST_CHECK(user_algo.get_verbosity() == 0u);
61 BOOST_CHECK((user_algo.get_log() == mbh::log_type{}));
62 }
63 {
64 mbh user_algo{inner_algo, 5, {1e-3, 1e-2, 1e-3, 1e-2}};
65 BOOST_CHECK((user_algo.get_perturb() == vector_double{1e-3, 1e-2, 1e-3, 1e-2}));
66 BOOST_CHECK(user_algo.get_verbosity() == 0u);
67 BOOST_CHECK((user_algo.get_log() == mbh::log_type{}));
68 }
69 BOOST_CHECK_THROW((mbh{inner_algo, 5u, -2.1}), std::invalid_argument);
70 BOOST_CHECK_THROW((mbh{inner_algo, 5u, 3.2}), std::invalid_argument);
71 BOOST_CHECK_THROW((mbh{inner_algo, 5u, std::nan("")}), std::invalid_argument);
72 BOOST_CHECK_THROW((mbh{inner_algo, 5u, {0.2, 0.1, 0.}}), std::invalid_argument);
73 BOOST_CHECK_THROW((mbh{inner_algo, 5u, {0.2, 0.1, -0.12}}), std::invalid_argument);
74 BOOST_CHECK_THROW((mbh{inner_algo, 5u, {0.2, 1.1, 0.12}}), std::invalid_argument);
75 BOOST_CHECK_THROW((mbh{inner_algo, 5u, {0.2, std::nan(""), 0.12}}), std::invalid_argument);
76 BOOST_CHECK_NO_THROW(mbh{});
77 }
78
BOOST_AUTO_TEST_CASE(mbh_evolve_test)79 BOOST_AUTO_TEST_CASE(mbh_evolve_test)
80 {
81 // Here we only test that evolution is deterministic if the
82 // seed is controlled
83 {
84 problem prob{hock_schittkowsky_71{}};
85 prob.set_c_tol({1e-3, 1e-3});
86 population pop1{prob, 5u, 23u};
87 population pop2{prob, 5u, 23u};
88 population pop3{prob, 5u, 23u};
89
90 mbh user_algo1{compass_search{100u, 0.1, 0.001, 0.7}, 5u, 0.1, 23u};
91 user_algo1.set_verbosity(1u);
92 pop1 = user_algo1.evolve(pop1);
93
94 BOOST_CHECK(user_algo1.get_log().size() > 0u);
95
96 mbh user_algo2{compass_search{100u, 0.1, 0.001, 0.7}, 5u, 0.1, 23u};
97 user_algo2.set_verbosity(1u);
98 pop2 = user_algo2.evolve(pop2);
99
100 BOOST_CHECK(user_algo1.get_log() == user_algo2.get_log());
101
102 user_algo2.set_seed(23u);
103 pop3 = user_algo2.evolve(pop3);
104
105 BOOST_CHECK(user_algo1.get_log() == user_algo2.get_log());
106 }
107 // We then check that the evolve throws if called on unsuitable problems
108 {
109 mbh user_algo{compass_search{100u, 0.1, 0.001, 0.7}, 5u, 0.1, 23u};
110 BOOST_CHECK_THROW(user_algo.evolve(population{problem{zdt{}}, 15u}), std::invalid_argument);
111 }
112 {
113 mbh user_algo{compass_search{100u, 0.1, 0.001, 0.7}, 5u, 0.1, 23u};
114 BOOST_CHECK_THROW(user_algo.evolve(population{problem{inventory{}}, 15u}), std::invalid_argument);
115 }
116 // And that it throws if called with a wrong dimension of the perturbation vector
117 {
118 mbh user_algo{compass_search{100u, 0.1, 0.001, 0.7}, 5u, {1e-3, 1e-2}, 23u};
119 BOOST_CHECK_THROW(user_algo.evolve(population{problem{hock_schittkowsky_71{}}, 15u}), std::invalid_argument);
120 }
121 // And a clean exit for 0 generations
122 problem prob{hock_schittkowsky_71{}};
123 population pop{prob, 10u};
124 BOOST_CHECK((mbh{compass_search{100u, 0.1, 0.001, 0.7}, 0u, {1e-3, 1e-2}, 23u}.evolve(pop).get_x()[0])
125 == (pop.get_x()[0]));
126 }
127
BOOST_AUTO_TEST_CASE(mbh_setters_getters_test)128 BOOST_AUTO_TEST_CASE(mbh_setters_getters_test)
129 {
130 mbh user_algo{compass_search{100u, 0.1, 0.001, 0.7}, 5u, {1e-3, 1e-2}, 23u};
131 user_algo.set_verbosity(23u);
132 BOOST_CHECK(user_algo.get_verbosity() == 23u);
133 user_algo.set_seed(23u);
134 BOOST_CHECK(user_algo.get_seed() == 23u);
135 user_algo.set_perturb({0.1, 0.2, 0.3, 0.4});
136 BOOST_CHECK((user_algo.get_perturb() == vector_double{0.1, 0.2, 0.3, 0.4}));
137 BOOST_CHECK_THROW(user_algo.set_perturb({0.1, std::nan(""), 0.3, 0.4}), std::invalid_argument);
138 BOOST_CHECK_THROW(user_algo.set_perturb({0.1, -0.2, 0.3, 0.4}), std::invalid_argument);
139 BOOST_CHECK_THROW(user_algo.set_perturb({0.1, 2.3, 0.3, 0.4}), std::invalid_argument);
140 BOOST_CHECK(user_algo.get_name().find("Monotonic Basin Hopping") != std::string::npos);
141 BOOST_CHECK(user_algo.get_extra_info().find("Inner algorithm extra info") != std::string::npos);
142 BOOST_CHECK_NO_THROW(user_algo.get_log());
143 }
144
BOOST_AUTO_TEST_CASE(mbh_serialization_test)145 BOOST_AUTO_TEST_CASE(mbh_serialization_test)
146 {
147 // Make one evolution
148 problem prob{hock_schittkowsky_71{}};
149 population pop{prob, 10u, 23u};
150 algorithm algo{mbh{compass_search{100u, 0.1, 0.001, 0.7}, 5u, 1e-3, 23u}};
151 algo.set_verbosity(1u);
152 pop = algo.evolve(pop);
153
154 // Store the string representation of p.
155 std::stringstream ss;
156 auto before_text = boost::lexical_cast<std::string>(algo);
157 auto before_log = algo.extract<mbh>()->get_log();
158 // Now serialize, deserialize and compare the result.
159 {
160 boost::archive::binary_oarchive oarchive(ss);
161 oarchive << algo;
162 }
163 // Change the content of p before deserializing.
164 algo = algorithm{};
165 {
166 boost::archive::binary_iarchive iarchive(ss);
167 iarchive >> algo;
168 }
169 auto after_text = boost::lexical_cast<std::string>(algo);
170 auto after_log = algo.extract<mbh>()->get_log();
171 BOOST_CHECK_EQUAL(before_text, after_text);
172 BOOST_CHECK(before_log == after_log);
173 // so we implement a close check
174 BOOST_CHECK(before_log.size() > 0u);
175 for (auto i = 0u; i < before_log.size(); ++i) {
176 BOOST_CHECK_EQUAL(std::get<0>(before_log[i]), std::get<0>(after_log[i]));
177 BOOST_CHECK_CLOSE(std::get<1>(before_log[i]), std::get<1>(after_log[i]), 1e-8);
178 BOOST_CHECK_EQUAL(std::get<2>(before_log[i]), std::get<2>(after_log[i]));
179 BOOST_CHECK_CLOSE(std::get<3>(before_log[i]), std::get<3>(after_log[i]), 1e-8);
180 BOOST_CHECK_EQUAL(std::get<4>(before_log[i]), std::get<4>(after_log[i]));
181 }
182 }
183
184 struct ts1 {
evolvets1185 population evolve(population pop) const
186 {
187 return pop;
188 }
189 };
190
191 struct ts2 {
evolvets2192 population evolve(population pop) const
193 {
194 return pop;
195 }
get_thread_safetyts2196 thread_safety get_thread_safety() const
197 {
198 return thread_safety::none;
199 }
200 };
201
202 struct ts3 {
evolvets3203 population evolve(population pop) const
204 {
205 return pop;
206 }
get_thread_safetyts3207 thread_safety get_thread_safety()
208 {
209 return thread_safety::none;
210 }
211 };
212
BOOST_AUTO_TEST_CASE(mbh_threading_test)213 BOOST_AUTO_TEST_CASE(mbh_threading_test)
214 {
215 BOOST_CHECK((algorithm{mbh{ts1{}, 5u, 1e-2, 23u}}.get_thread_safety() == thread_safety::basic));
216 BOOST_CHECK((algorithm{mbh{ts2{}, 5u, 1e-2, 23u}}.get_thread_safety() == thread_safety::none));
217 BOOST_CHECK((algorithm{mbh{ts3{}, 5u, 1e-2, 23u}}.get_thread_safety() == thread_safety::basic));
218 }
219
220 struct ia1 {
evolveia1221 population evolve(const population &pop) const
222 {
223 return pop;
224 }
225 double m_data = 0.;
226 };
227
BOOST_AUTO_TEST_CASE(mbh_inner_algo_get_test)228 BOOST_AUTO_TEST_CASE(mbh_inner_algo_get_test)
229 {
230 // We check that the correct overload is called according to (*this) being const or not
231 {
232 const mbh uda(ia1{}, 5u, 1e-2, 23u);
233 BOOST_CHECK(std::is_const<decltype(uda)>::value);
234 BOOST_CHECK(std::is_const<std::remove_reference<decltype(uda.get_inner_algorithm())>::type>::value);
235 }
236 {
237 mbh uda(ia1{}, 5u, 1e-2, 23u);
238 BOOST_CHECK(!std::is_const<decltype(uda)>::value);
239 BOOST_CHECK(!std::is_const<std::remove_reference<decltype(uda.get_inner_algorithm())>::type>::value);
240 }
241 }
242