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