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 compass_search_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 <iostream>
36 #include <string>
37 
38 #include <pagmo/algorithm.hpp>
39 #include <pagmo/algorithms/compass_search.hpp>
40 #include <pagmo/io.hpp>
41 #include <pagmo/population.hpp>
42 #include <pagmo/problems/hock_schittkowsky_71.hpp>
43 #include <pagmo/problems/inventory.hpp>
44 #include <pagmo/problems/rosenbrock.hpp>
45 #include <pagmo/problems/zdt.hpp>
46 #include <pagmo/s11n.hpp>
47 #include <pagmo/types.hpp>
48 
49 using namespace pagmo;
50 
BOOST_AUTO_TEST_CASE(compass_search_algorithm_construction)51 BOOST_AUTO_TEST_CASE(compass_search_algorithm_construction)
52 {
53     compass_search user_algo{100u, 0.1, 0.001, 0.7};
54     BOOST_CHECK(user_algo.get_verbosity() == 0u);
55     BOOST_CHECK((user_algo.get_log() == compass_search::log_type{}));
56 
57     BOOST_CHECK_THROW((compass_search{1234u, 1.1}), std::invalid_argument);
58     BOOST_CHECK_THROW((compass_search{1234u, -0.3}), std::invalid_argument);
59     BOOST_CHECK_THROW((compass_search{1234u, 0.7, 1.1}), std::invalid_argument);
60     BOOST_CHECK_THROW((compass_search{1234u, 0.7, 0.8}), std::invalid_argument);
61     BOOST_CHECK_THROW((compass_search{1234u, 0.7, 0.1, 1.3}), std::invalid_argument);
62     BOOST_CHECK_THROW((compass_search{1234u, 0.7, 0.1, -0.3}), std::invalid_argument);
63 }
64 
BOOST_AUTO_TEST_CASE(compass_search_evolve_test)65 BOOST_AUTO_TEST_CASE(compass_search_evolve_test)
66 {
67     double stop_range = 1e-5;
68 
69     // Here we only test that evolution is deterministic (stop criteria will be range)
70     problem prob1{hock_schittkowsky_71{}};
71     prob1.set_c_tol({1e-3, 1e-3});
72     population pop1{prob1, 5u, 23u};
73 
74     problem prob2{hock_schittkowsky_71{}};
75     prob2.set_c_tol({1e-3, 1e-3});
76     population pop2{prob2, 5u, 23u};
77 
78     compass_search user_algo1{10000u, 0.5, stop_range, 0.5};
79     user_algo1.set_verbosity(1u);
80     pop1 = user_algo1.evolve(pop1);
81 
82     compass_search user_algo2{10000u, 0.5, stop_range, 0.5};
83     user_algo2.set_verbosity(1u);
84     pop2 = user_algo2.evolve(pop2);
85 
86     BOOST_CHECK(user_algo1.get_log().size() > 0u);
87     BOOST_CHECK(user_algo1.get_log() == user_algo2.get_log());
88     BOOST_CHECK(static_cast<double>(std::get<2>(user_algo1.get_log()[user_algo1.get_log().size() - 1])) <= stop_range);
89 
90     // We test the max_fevals stopping criteria
91     auto max_fevals = 10u;
92     compass_search user_algo3{max_fevals, 0.5, stop_range, 0.5};
93     user_algo3.set_verbosity(1u);
94     population pop3{prob2, 5u, 23u};
95     pop3 = user_algo3.evolve(pop3);
96     BOOST_CHECK(std::get<0>(user_algo3.get_log()[user_algo3.get_log().size() - 1]) > max_fevals);
97 
98     // We then check that the evolve throws if called on unsuitable problems
99     BOOST_CHECK_THROW(compass_search{10u}.evolve(population{problem{rosenbrock{}}, 0u}), std::invalid_argument);
100     BOOST_CHECK_THROW(compass_search{10u}.evolve(population{problem{zdt{}}, 15u}), std::invalid_argument);
101     BOOST_CHECK_THROW(compass_search{10u}.evolve(population{problem{inventory{}}, 15u}), std::invalid_argument);
102     // And a clean exit for 0 generations
103     population pop{rosenbrock{25u}, 10u};
104     BOOST_CHECK(compass_search{0u}.evolve(pop).get_x()[0] == pop.get_x()[0]);
105 }
106 
BOOST_AUTO_TEST_CASE(compass_search_setters_getters_test)107 BOOST_AUTO_TEST_CASE(compass_search_setters_getters_test)
108 {
109     compass_search user_algo{10000u, 0.5, 0.1, 0.5};
110     user_algo.set_verbosity(23u);
111     BOOST_CHECK(user_algo.get_verbosity() == 23u);
112     BOOST_CHECK(user_algo.get_max_fevals() == 10000u);
113     BOOST_CHECK(user_algo.get_start_range() == 0.5);
114     BOOST_CHECK(user_algo.get_stop_range() == 0.1);
115     BOOST_CHECK(user_algo.get_reduction_coeff() == 0.5);
116     BOOST_CHECK(user_algo.get_name().find("Compass") != std::string::npos);
117     BOOST_CHECK(user_algo.get_extra_info().find("Reduction coefficient") != std::string::npos);
118     BOOST_CHECK_NO_THROW(user_algo.get_log());
119 }
120 
BOOST_AUTO_TEST_CASE(compass_search_serialization_test)121 BOOST_AUTO_TEST_CASE(compass_search_serialization_test)
122 {
123     // We test the serialization of a pagmo algorithm when constructed with compass_search
124     // Make one evolution
125     problem prob{rosenbrock{25u}};
126     population pop{prob, 10u, 23u};
127     algorithm algo{compass_search{10000u, 0.5, 0.1, 0.5}};
128     algo.set_verbosity(1u); // allows the log to be filled
129     pop = algo.evolve(pop);
130 
131     // Store the string representation of p.
132     std::stringstream ss;
133     auto before_text = boost::lexical_cast<std::string>(algo);
134     auto before_log = algo.extract<compass_search>()->get_log();
135     // Now serialize, deserialize and compare the result.
136     {
137         boost::archive::binary_oarchive oarchive(ss);
138         oarchive << algo;
139     }
140     // Change the content of p before deserializing.
141     algo = algorithm{};
142     {
143         boost::archive::binary_iarchive iarchive(ss);
144         iarchive >> algo;
145     }
146     auto after_text = boost::lexical_cast<std::string>(algo);
147     auto after_log = algo.extract<compass_search>()->get_log();
148     BOOST_CHECK_EQUAL(before_text, after_text);
149     BOOST_CHECK(before_log == after_log);
150     // so we implement a close check
151     BOOST_CHECK(before_log.size() > 0u);
152     for (auto i = 0u; i < before_log.size(); ++i) {
153         BOOST_CHECK_EQUAL(std::get<0>(before_log[i]), std::get<0>(after_log[i]));
154         BOOST_CHECK_CLOSE(std::get<1>(before_log[i]), std::get<1>(after_log[i]), 1e-8);
155         BOOST_CHECK_EQUAL(std::get<2>(before_log[i]), std::get<2>(after_log[i]));
156         BOOST_CHECK_CLOSE(std::get<3>(before_log[i]), std::get<3>(after_log[i]), 1e-8);
157         BOOST_CHECK_CLOSE(std::get<4>(before_log[i]), std::get<4>(after_log[i]), 1e-8);
158     }
159 }
160