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 nsga2_test
30 #define BOOST_TEST_DYN_LINK
31 #include <boost/test/unit_test.hpp>
32
33 #include <algorithm>
34 #include <boost/lexical_cast.hpp>
35 #include <boost/test/tools/floating_point_comparison.hpp>
36 #include <iostream>
37 #include <string>
38
39 #include <pagmo/algorithm.hpp>
40 #include <pagmo/algorithms/nsga2.hpp>
41 #include <pagmo/io.hpp>
42 #include <pagmo/problems/dtlz.hpp>
43 #include <pagmo/problems/hock_schittkowsky_71.hpp>
44 #include <pagmo/problems/inventory.hpp>
45 #include <pagmo/problems/rosenbrock.hpp>
46 #include <pagmo/problems/zdt.hpp>
47 #include <pagmo/s11n.hpp>
48 #include <pagmo/types.hpp>
49
50 using namespace pagmo;
51
BOOST_AUTO_TEST_CASE(nsga2_algorithm_construction)52 BOOST_AUTO_TEST_CASE(nsga2_algorithm_construction)
53 {
54 nsga2 user_algo{1u, 0.95, 10., 0.01, 50., 32u};
55 BOOST_CHECK_NO_THROW(nsga2{});
56 BOOST_CHECK(user_algo.get_verbosity() == 0u);
57 BOOST_CHECK(user_algo.get_seed() == 32u);
58 // BOOST_CHECK((user_algo.get_log() == moead::log_type{}));
59
60 // Check the throws
61 // Wrong cr
62 BOOST_CHECK_THROW((nsga2{1u, 1., 10., 0.01, 50., 32u}), std::invalid_argument);
63 BOOST_CHECK_THROW((nsga2{1u, -1., 10., 0.01, 50., 32u}), std::invalid_argument);
64 // Wrong m
65 BOOST_CHECK_THROW((nsga2{1u, .95, 10., 1.1, 50., 32u}), std::invalid_argument);
66 BOOST_CHECK_THROW((nsga2{1u, .95, 10., -1.1, 50., 32u}), std::invalid_argument);
67 // Wrong eta_m
68 BOOST_CHECK_THROW((nsga2{1u, .95, 100.1, 0.01, 50., 32u}), std::invalid_argument);
69 BOOST_CHECK_THROW((nsga2{1u, .95, .98, 0.01, 50., 32u}), std::invalid_argument);
70 // Wrong eta_m
71 BOOST_CHECK_THROW((nsga2{1u, .95, 10., 0.01, 100.1, 32u}), std::invalid_argument);
72 BOOST_CHECK_THROW((nsga2{1u, .95, 10., 0.01, .98, 32u}), std::invalid_argument);
73 }
74
75 struct mo_equal_bounds {
76 /// Fitness
fitnessmo_equal_bounds77 vector_double fitness(const vector_double &) const
78 {
79 return {0., 0.};
80 }
get_nobjmo_equal_bounds81 vector_double::size_type get_nobj() const
82 {
83 return 2u;
84 }
85 /// Problem bounds
get_boundsmo_equal_bounds86 std::pair<vector_double, vector_double> get_bounds() const
87 {
88 return {{0., 0.}, {1., 0.}};
89 }
90 };
91
BOOST_AUTO_TEST_CASE(nsga2_evolve_test)92 BOOST_AUTO_TEST_CASE(nsga2_evolve_test)
93 {
94 // We check that the problem is checked to be suitable
95 // Some bound is equal
96 BOOST_CHECK_THROW(nsga2{10u}.evolve(population{problem{mo_equal_bounds{}}, 0u}), std::invalid_argument);
97 // stochastic
98 BOOST_CHECK_THROW((nsga2{}.evolve(population{inventory{}, 5u, 23u})), std::invalid_argument);
99 // constrained prob
100 BOOST_CHECK_THROW((nsga2{}.evolve(population{hock_schittkowsky_71{}, 5u, 23u})), std::invalid_argument);
101 // single objective prob
102 BOOST_CHECK_THROW((nsga2{}.evolve(population{rosenbrock{}, 5u, 23u})), std::invalid_argument);
103 // wrong population size
104 BOOST_CHECK_THROW((nsga2{}.evolve(population{zdt{}, 3u, 23u})), std::invalid_argument);
105 BOOST_CHECK_THROW((nsga2{}.evolve(population{zdt{}, 50u, 23u})), std::invalid_argument);
106
107 // We check for deterministic behaviour if the seed is controlled
108 // we treat the last three components of the decision vector as integers
109 // to trigger all cases
110 dtlz udp{1u, 10u, 3u};
111
112 population pop1{udp, 52u, 23u};
113 population pop2{udp, 52u, 23u};
114 population pop3{udp, 52u, 23u};
115
116 nsga2 user_algo1{10u, 0.95, 10., 0.01, 50., 32u};
117 user_algo1.set_verbosity(1u);
118 pop1 = user_algo1.evolve(pop1);
119
120 BOOST_CHECK(user_algo1.get_log().size() > 0u);
121
122 nsga2 user_algo2{10u, 0.95, 10., 0.01, 50., 32u};
123 user_algo2.set_verbosity(1u);
124 pop2 = user_algo2.evolve(pop2);
125
126 BOOST_CHECK(user_algo1.get_log() == user_algo2.get_log());
127
128 user_algo2.set_seed(32u);
129 pop3 = user_algo2.evolve(pop3);
130
131 BOOST_CHECK(user_algo1.get_log() == user_algo2.get_log());
132
133 // We evolve for many-objectives and trigger the output with the ellipses
134 udp = dtlz{1u, 12u, 7u};
135 population pop4{udp, 52u, 23u};
136 pop4 = user_algo2.evolve(pop4);
137 }
138
BOOST_AUTO_TEST_CASE(nsga2_setters_getters_test)139 BOOST_AUTO_TEST_CASE(nsga2_setters_getters_test)
140 {
141 nsga2 user_algo{1u, 0.95, 10., 0.01, 50., 32u};
142 user_algo.set_verbosity(200u);
143 BOOST_CHECK(user_algo.get_verbosity() == 200u);
144 user_algo.set_seed(23456u);
145 BOOST_CHECK(user_algo.get_seed() == 23456u);
146 BOOST_CHECK(user_algo.get_name().find("NSGA-II") != std::string::npos);
147 BOOST_CHECK(user_algo.get_extra_info().find("Verbosity") != std::string::npos);
148 // BOOST_CHECK_NO_THROW(user_algo.get_log());
149 }
150
BOOST_AUTO_TEST_CASE(nsga2_zdt5_test)151 BOOST_AUTO_TEST_CASE(nsga2_zdt5_test)
152 {
153 algorithm algo{nsga2(100u, 0.95, 10., 0.01, 50., 32u)};
154 algo.set_verbosity(10u);
155 algo.set_seed(23456u);
156 population pop{zdt(5u, 10u), 20u, 32u};
157 pop = algo.evolve(pop);
158 for (decltype(pop.size()) i = 0u; i < pop.size(); ++i) {
159 auto x = pop.get_x()[i];
160 BOOST_CHECK(std::all_of(x.begin(), x.end(), [](double el) { return (el == std::floor(el)); }));
161 }
162 }
163
BOOST_AUTO_TEST_CASE(nsga2_serialization_test)164 BOOST_AUTO_TEST_CASE(nsga2_serialization_test)
165 {
166 // Make one evolution
167 problem prob{zdt{1u, 30u}};
168 population pop{prob, 40u, 23u};
169 algorithm algo{nsga2{10u, 0.95, 10., 0.01, 50., 32u}};
170 algo.set_verbosity(1u);
171 algo.set_seed(1234u);
172 pop = algo.evolve(pop);
173
174 // Store the string representation of p.
175 std::stringstream ss;
176 auto before_text = boost::lexical_cast<std::string>(algo);
177 auto before_log = algo.extract<nsga2>()->get_log();
178 // Now serialize, deserialize and compare the result.
179 {
180 boost::archive::binary_oarchive oarchive(ss);
181 oarchive << algo;
182 }
183 // Change the content of p before deserializing.
184 algo = algorithm{};
185 {
186 boost::archive::binary_iarchive iarchive(ss);
187 iarchive >> algo;
188 }
189 auto after_text = boost::lexical_cast<std::string>(algo);
190 auto after_log = algo.extract<nsga2>()->get_log();
191 BOOST_CHECK_EQUAL(before_text, after_text);
192 BOOST_CHECK(before_log == after_log);
193 // so we implement a close check
194 BOOST_CHECK(before_log.size() > 0u);
195 for (auto i = 0u; i < before_log.size(); ++i) {
196 BOOST_CHECK_EQUAL(std::get<0>(before_log[i]), std::get<0>(after_log[i]));
197 BOOST_CHECK_EQUAL(std::get<1>(before_log[i]), std::get<1>(after_log[i]));
198 for (auto j = 0u; j < 2u; ++j) {
199 BOOST_CHECK_CLOSE(std::get<2>(before_log[i])[j], std::get<2>(after_log[i])[j], 1e-8);
200 }
201 }
202 }
203
BOOST_AUTO_TEST_CASE(bfe_usage_test)204 BOOST_AUTO_TEST_CASE(bfe_usage_test)
205 {
206 // 1 - Algorithm with bfe disabled
207 problem prob{dtlz(1, 10, 2)};
208 nsga2 uda1{nsga2{10}};
209 uda1.set_verbosity(1u);
210 uda1.set_seed(23u);
211 // 2 - Instantiate
212 algorithm algo1{uda1};
213
214 // 3 - Instantiate populations
215 population pop{prob, 24, 32u};
216 population pop1{prob, 24, 456u};
217 population pop2{prob, 24, 67345u};
218
219 // 4 - Evolve the population
220 pop1 = algo1.evolve(pop);
221
222 // 5 - new algorithm that is bfe enabled
223 nsga2 uda2{nsga2{10}};
224 uda2.set_verbosity(1u);
225 uda2.set_seed(23u);
226 uda2.set_bfe(bfe{}); // This will use the default bfe.
227 // 6 - Instantiate a pagmo algorithm
228 algorithm algo2{uda2};
229
230 // 7 - Evolve the population
231 pop2 = algo2.evolve(pop);
232 BOOST_CHECK(algo1.extract<nsga2>()->get_log() == algo2.extract<nsga2>()->get_log());
233 }
234