1 // Copyright 2020, 2021 PaGMO development team
2 //
3 // This file is part of the pygmo library.
4 //
5 // This Source Code Form is subject to the terms of the Mozilla
6 // Public License v. 2.0. If a copy of the MPL was not distributed
7 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #include <cstddef>
10 #include <exception>
11 #include <limits>
12 #include <memory>
13 #include <string>
14 #include <tuple>
15 #include <vector>
16 
17 #include <pybind11/numpy.h>
18 #include <pybind11/pybind11.h>
19 
20 #include <pagmo/algorithm.hpp>
21 #include <pagmo/archipelago.hpp>
22 #include <pagmo/batch_evaluators/default_bfe.hpp>
23 #include <pagmo/batch_evaluators/member_bfe.hpp>
24 #include <pagmo/batch_evaluators/thread_bfe.hpp>
25 #include <pagmo/bfe.hpp>
26 #include <pagmo/config.hpp>
27 #include <pagmo/detail/gte_getter.hpp>
28 #include <pagmo/exceptions.hpp>
29 #include <pagmo/island.hpp>
30 #include <pagmo/islands/thread_island.hpp>
31 #include <pagmo/population.hpp>
32 #include <pagmo/r_policy.hpp>
33 #include <pagmo/rng.hpp>
34 #include <pagmo/s_policy.hpp>
35 #include <pagmo/threading.hpp>
36 #include <pagmo/topology.hpp>
37 #include <pagmo/types.hpp>
38 #include <pagmo/utils/constrained.hpp>
39 #include <pagmo/utils/generic.hpp>
40 #include <pagmo/utils/genetic_operators.hpp>
41 #include <pagmo/utils/gradients_and_hessians.hpp>
42 #include <pagmo/utils/hv_algos/hv_bf_approx.hpp>
43 #include <pagmo/utils/hv_algos/hv_bf_fpras.hpp>
44 #include <pagmo/utils/hv_algos/hv_hv2d.hpp>
45 #include <pagmo/utils/hv_algos/hv_hv3d.hpp>
46 #include <pagmo/utils/hv_algos/hv_hvwfg.hpp>
47 #include <pagmo/utils/hypervolume.hpp>
48 #include <pagmo/utils/multi_objective.hpp>
49 
50 #include "algorithm.hpp"
51 #include "bfe.hpp"
52 #include "common_utils.hpp"
53 #include "docstrings.hpp"
54 #include "expose_algorithms.hpp"
55 #include "expose_bfes.hpp"
56 #include "expose_islands.hpp"
57 #include "expose_problems.hpp"
58 #include "expose_r_policies.hpp"
59 #include "expose_s_policies.hpp"
60 #include "expose_topologies.hpp"
61 #include "island.hpp"
62 #include "problem.hpp"
63 #include "r_policy.hpp"
64 #include "s11n_wrappers.hpp"
65 #include "s_policy.hpp"
66 #include "topology.hpp"
67 
68 namespace py = pybind11;
69 namespace pg = pagmo;
70 
71 namespace pygmo
72 {
73 
74 namespace detail
75 {
76 
77 namespace
78 {
79 
80 // NOTE: we need to provide a custom raii waiter in the island. The reason is the following.
81 // When we call wait() from Python, the calling thread will be holding the GIL and then we will be waiting
82 // for evolutions in the island to finish. During this time, no
83 // Python code will be executed because the GIL is locked. This means that if we have a Python thread doing background
84 // work (e.g., managing the task queue in pythonic islands), it will have to wait before doing any progress. By
85 // unlocking the GIL before calling thread_island::wait(), we give the chance to other Python threads to continue
86 // doing some work.
87 // NOTE: here we have 2 RAII classes interacting with the GIL. The GIL releaser is the *second* one,
88 // and it is the one that is responsible for unlocking the Python interpreter while wait() is running.
89 // The *first* one, the GIL thread ensurer, does something else: it makes sure that we can call the Python
90 // interpreter from the current C++ thread. In a normal situation, in which islands are just instantiated
91 // from the main thread, the gte object is superfluous. However, if we are interacting with islands from a
92 // separate C++ thread, then we need to make sure that every time we call into the Python interpreter (e.g., by
93 // using the GIL releaser below) we inform Python we are about to call from a separate thread. This is what
94 // the GTE object does. This use case is, for instance, what happens with the PADE algorithm when, algo, prob,
95 // etc. are all C++ objects (when at least one object is pythonic, we will not end up using the thread island).
96 // NOTE: by ordering the class members in this way we ensure that gte is constructed before gr, which is essential
97 // (otherwise we might be calling into the interpreter with a releaser before informing Python we are calling
98 // from a separate thread).
99 struct py_wait_locks {
100     gil_thread_ensurer gte;
101     gil_releaser gr;
102 };
103 
104 } // namespace
105 
106 } // namespace detail
107 
108 } // namespace pygmo
109 
PYBIND11_MODULE(core,m)110 PYBIND11_MODULE(core, m)
111 {
112     using namespace pybind11::literals;
113 
114 #if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 9
115     // This function needs to be called before doing anything with threads.
116     // https://docs.python.org/3/c-api/init.html
117     // NOTE: this is deprecated and does nothing since Python 3.9.
118     PyEval_InitThreads();
119 #endif
120 
121     // Disable automatic function signatures in the docs.
122     // NOTE: the 'options' object needs to stay alive
123     // throughout the whole definition of the module.
124     py::options options;
125     options.disable_function_signatures();
126 
127     // Export the pagmo version.
128     m.attr("_pagmo_version_major") = PAGMO_VERSION_MAJOR;
129     m.attr("_pagmo_version_minor") = PAGMO_VERSION_MINOR;
130     m.attr("_pagmo_version_patch") = PAGMO_VERSION_PATCH;
131 
132     // Expose some internal functions for testing.
133     m.def("_callable", &pygmo::callable);
134     m.def("_callable_attribute", &pygmo::callable_attribute);
135     m.def("_str", &pygmo::str);
136     m.def("_type", &pygmo::type);
137     m.def("_builtins", &pygmo::builtins);
138     m.def("_deepcopy", &pygmo::deepcopy);
139     m.def("_max_unsigned", []() {
140         // Small helper function to get the max value of unsigned.
141         return std::numeric_limits<unsigned>::max();
142     });
143 
144     // The random_device_next() helper.
145     m.def("_random_device_next", []() { return pg::random_device::next(); });
146 
147     // Global random number generator
148     m.def(
149         "set_global_rng_seed", [](unsigned seed) { pg::random_device::set_seed(seed); },
150         pygmo::set_global_rng_seed_docstring().c_str(), py::arg("seed"));
151 
152     // Override the default implementation of the island factory.
153     pg::detail::island_factory
154         = [](const pg::algorithm &algo, const pg::population &pop, std::unique_ptr<pg::detail::isl_inner_base> &ptr) {
155               if (algo.get_thread_safety() >= pg::thread_safety::basic
156                   && pop.get_problem().get_thread_safety() >= pg::thread_safety::basic) {
157                   // Both algo and prob have at least the basic thread safety guarantee. Use the thread island.
158                   ptr = std::make_unique<pg::detail::isl_inner<pg::thread_island>>();
159               } else {
160                   // NOTE: here we are re-implementing a piece of code that normally
161                   // is pure C++. We are calling into the Python interpreter, so, in order to handle
162                   // the case in which we are invoking this code from a separate C++ thread, we construct a GIL ensurer
163                   // in order to guard against concurrent access to the interpreter. The idea here is that this piece
164                   // of code normally would provide a basic thread safety guarantee, and in order to continue providing
165                   // it we use the ensurer.
166                   pygmo::gil_thread_ensurer gte;
167                   auto py_island = py::module::import("pygmo").attr("mp_island");
168                   ptr = std::make_unique<pg::detail::isl_inner<py::object>>(py_island());
169               }
170           };
171 
172     // Override the default implementation of default_bfe.
173     pg::detail::default_bfe_impl = [](const pg::problem &p, const pg::vector_double &dvs) -> pg::vector_double {
174         // The member function batch_fitness() of p, if present, has priority.
175         if (p.has_batch_fitness()) {
176             return pg::member_bfe{}(p, dvs);
177         }
178 
179         // Otherwise, we run the generic thread-based bfe, if the problem
180         // is thread-safe enough.
181         if (p.get_thread_safety() >= pg::thread_safety::basic) {
182             return pg::thread_bfe{}(p, dvs);
183         }
184 
185         // NOTE: in this last bit of the implementation we need to call
186         // into the Python interpreter. In order to ensure that default_bfe
187         // still works also from a C++ thread of which Python knows nothing about,
188         // we will be using a thread ensurer, so that the thread safety
189         // guarantee provided by default_bfe is still respected.
190         // NOTE: the original default_bfe code is thread safe in the sense that the
191         // code directly implemented within that class is thread safe. Invoking the call
192         // operator of default_bfe might still end up being thread unsafe if p
193         // itself is thread unsafe (the same happens, e.g., in a thread-safe algorithm
194         // which uses a thread-unsafe problem in its evolve()).
195         pygmo::gil_thread_ensurer gte;
196         // Otherwise, we go for the multiprocessing bfe.
197         return pygmo::ndarr_to_vector<pg::vector_double>(
198             py::cast<py::array_t<double>>(py::module::import("pygmo").attr("mp_bfe")().attr("__call__")(
199                 p, pygmo::vector_to_ndarr<py::array_t<double>>(dvs))));
200     };
201 
202     // Override the default RAII waiter. We need to use shared_ptr because we don't want to move/copy/destroy
203     // the locks when invoking this from island::wait(), we need to instaniate exactly 1 py_wait_lock and have it
204     // destroyed at the end of island::wait().
205     pg::detail::wait_raii_getter = []() { return std::make_shared<pygmo::detail::py_wait_locks>(); };
206 
207     // NOTE: set the gte getter.
208     pg::detail::gte_getter = []() { return std::make_shared<pygmo::gil_thread_ensurer>(); };
209 
210     // Register pagmo's custom exceptions.
211     py::register_exception_translator([](std::exception_ptr p) {
212         try {
213             if (p) {
214                 std::rethrow_exception(p);
215             }
216         } catch (const pg::not_implemented_error &nie) {
217             PyErr_SetString(PyExc_NotImplementedError, nie.what());
218         }
219     });
220 
221     // The thread_safety enum.
222     py::enum_<pg::thread_safety>(m, "thread_safety", pygmo::thread_safety_docstring().c_str())
223         .value("none", pg::thread_safety::none, pygmo::thread_safety_none_docstring().c_str())
224         .value("basic", pg::thread_safety::basic, pygmo::thread_safety_basic_docstring().c_str())
225         .value("constant", pg::thread_safety::constant, pygmo::thread_safety_constant_docstring().c_str());
226 
227     // The evolve_status enum.
228     py::enum_<pg::evolve_status>(m, "evolve_status", pygmo::evolve_status_docstring().c_str())
229         .value("idle", pg::evolve_status::idle, pygmo::evolve_status_idle_docstring().c_str())
230         .value("busy", pg::evolve_status::busy, pygmo::evolve_status_busy_docstring().c_str())
231         .value("idle_error", pg::evolve_status::idle_error, pygmo::evolve_status_idle_error_docstring().c_str())
232         .value("busy_error", pg::evolve_status::busy_error, pygmo::evolve_status_busy_error_docstring().c_str());
233 
234     // Migration type enum.
235     py::enum_<pg::migration_type>(m, "migration_type", pygmo::migration_type_docstring().c_str())
236         .value("p2p", pg::migration_type::p2p, pygmo::migration_type_p2p_docstring().c_str())
237         .value("broadcast", pg::migration_type::broadcast, pygmo::migration_type_broadcast_docstring().c_str());
238 
239     // Migrant handling policy enum.
240     py::enum_<pg::migrant_handling>(m, "migrant_handling", pygmo::migrant_handling_docstring().c_str())
241         .value("preserve", pg::migrant_handling::preserve, pygmo::migrant_handling_preserve_docstring().c_str())
242         .value("evict", pg::migrant_handling::evict, pygmo::migrant_handling_evict_docstring().c_str());
243 
244     // Generic utilities
245     m.def(
246         "random_decision_vector",
247         [](const pg::problem &p) -> py::array_t<double> {
248             using reng_t = pg::detail::random_engine_type;
249             reng_t tmp_rng(static_cast<reng_t::result_type>(pg::random_device::next()));
250             auto retval = pg::random_decision_vector(p, tmp_rng);
251             return pygmo::vector_to_ndarr<py::array_t<double>>(retval);
252         },
253         pygmo::random_decision_vector_docstring().c_str(), py::arg("prob"));
254     m.def(
255         "batch_random_decision_vector",
256         [](const pg::problem &p, pg::vector_double::size_type n) -> py::array_t<double> {
257             using reng_t = pg::detail::random_engine_type;
258             reng_t tmp_rng(static_cast<reng_t::result_type>(pg::random_device::next()));
259             auto retval = pg::batch_random_decision_vector(p, n, tmp_rng);
260             return pygmo::vector_to_ndarr<py::array_t<double>>(retval);
261         },
262         pygmo::batch_random_decision_vector_docstring().c_str(), py::arg("prob"), py::arg("n"));
263     // Genetic operators
264     m.def(
265         "sbx_crossover",
266         [](const py::array_t<double> &parent1, const py::array_t<double> &parent2, const py::iterable &bounds,
267            pg::vector_double::size_type nix, const double p_cr, const double eta_c, unsigned seed) {
268             auto pg_bounds = pygmo::iterable_to_bounds(bounds);
269             using reng_t = pg::detail::random_engine_type;
270             reng_t tmp_rng(static_cast<reng_t::result_type>(seed));
271             auto retval = pagmo::sbx_crossover(pygmo::ndarr_to_vector<pg::vector_double>(parent1),
272                                                pygmo::ndarr_to_vector<pg::vector_double>(parent2), pg_bounds, nix, p_cr,
273                                                eta_c, tmp_rng);
274             return py::make_tuple(pygmo::vector_to_ndarr<py::array_t<double>>(retval.first),
275                                   pygmo::vector_to_ndarr<py::array_t<double>>(retval.second));
276         },
277         pygmo::sbx_crossover_docstring().c_str(), py::arg("parent1"), py::arg("parent2"), py::arg("bounds"),
278         py::arg("nix"), py::arg("p_cr"), py::arg("eta_c"), py::arg("seed"));
279 
280     m.def(
281         "polynomial_mutation",
282         [](const py::array_t<double> &dv, const py::iterable &bounds, pg::vector_double::size_type nix,
283            const double p_m, const double eta_m, unsigned seed) {
284             auto pg_bounds = pygmo::iterable_to_bounds(bounds);
285             using reng_t = pg::detail::random_engine_type;
286             reng_t tmp_rng(static_cast<reng_t::result_type>(seed));
287             auto dv_c = pygmo::ndarr_to_vector<pg::vector_double>(dv);
288             pagmo::polynomial_mutation(dv_c, pg_bounds, nix, p_m, eta_m, tmp_rng);
289             return pygmo::vector_to_ndarr<py::array_t<double>>(dv_c);
290         },
291         pygmo::polynomial_mutation_docstring().c_str(), py::arg("dv"), py::arg("bounds"), py::arg("nix"),
292         py::arg("p_m"), py::arg("eta_m"), py::arg("seed"));
293     // Hypervolume class
294     py::class_<pg::hypervolume> hv_class(m, "hypervolume", "Hypervolume Class");
295     hv_class
296         .def(py::init([](const py::array_t<double> &points) {
297                  return std::make_unique<pg::hypervolume>(
298                      pygmo::ndarr_to_vvector<std::vector<pg::vector_double>>(points), true);
299              }),
300              py::arg("points"), pygmo::hv_init2_docstring().c_str())
301         .def(py::init([](const pg::population &pop) { return std::make_unique<pg::hypervolume>(pop, true); }),
302              py::arg("pop"), pygmo::hv_init1_docstring().c_str())
303         .def(
304             "compute",
305             [](const pg::hypervolume &hv, const py::array_t<double> &r_point) {
306                 return hv.compute(pygmo::ndarr_to_vector<pg::vector_double>(r_point));
307             },
308             py::arg("ref_point"))
309         .def(
310             "compute",
311             [](const pg::hypervolume &hv, const py::array_t<double> &r_point, pg::hv_algorithm &hv_algo) {
312                 return hv.compute(pygmo::ndarr_to_vector<pg::vector_double>(r_point), hv_algo);
313             },
314             pygmo::hv_compute_docstring().c_str(), py::arg("ref_point"), py::arg("hv_algo"))
315         .def(
316             "exclusive",
317             [](const pg::hypervolume &hv, unsigned p_idx, const py::array_t<double> &r_point) {
318                 return hv.exclusive(p_idx, pygmo::ndarr_to_vector<pg::vector_double>(r_point));
319             },
320             py::arg("idx"), py::arg("ref_point"))
321         .def(
322             "exclusive",
323             [](const pg::hypervolume &hv, unsigned p_idx, const py::array_t<double> &r_point,
324                pg::hv_algorithm &hv_algo) {
325                 return hv.exclusive(p_idx, pygmo::ndarr_to_vector<pg::vector_double>(r_point), hv_algo);
326             },
327             pygmo::hv_exclusive_docstring().c_str(), py::arg("idx"), py::arg("ref_point"), py::arg("hv_algo"))
328         .def(
329             "least_contributor",
330             [](const pg::hypervolume &hv, const py::array_t<double> &r_point) {
331                 return hv.least_contributor(pygmo::ndarr_to_vector<pg::vector_double>(r_point));
332             },
333             py::arg("ref_point"))
334         .def(
335             "least_contributor",
336             [](const pg::hypervolume &hv, const py::array_t<double> &r_point, pg::hv_algorithm &hv_algo) {
337                 return hv.least_contributor(pygmo::ndarr_to_vector<pg::vector_double>(r_point), hv_algo);
338             },
339             pygmo::hv_least_contributor_docstring().c_str(), py::arg("ref_point"), py::arg("hv_algo"))
340         .def(
341             "greatest_contributor",
342             [](const pg::hypervolume &hv, const py::array_t<double> &r_point) {
343                 return hv.greatest_contributor(pygmo::ndarr_to_vector<pg::vector_double>(r_point));
344             },
345             py::arg("ref_point"))
346         .def(
347             "greatest_contributor",
348             [](const pg::hypervolume &hv, const py::array_t<double> &r_point, pg::hv_algorithm &hv_algo) {
349                 return hv.greatest_contributor(pygmo::ndarr_to_vector<pg::vector_double>(r_point), hv_algo);
350             },
351             pygmo::hv_greatest_contributor_docstring().c_str(), py::arg("ref_point"), py::arg("hv_algo"))
352         .def(
353             "contributions",
354             [](const pg::hypervolume &hv, const py::array_t<double> &r_point) {
355                 return pygmo::vector_to_ndarr<py::array_t<double>>(
356                     hv.contributions(pygmo::ndarr_to_vector<pg::vector_double>(r_point)));
357             },
358             py::arg("ref_point"))
359         .def(
360             "contributions",
361             [](const pg::hypervolume &hv, const py::array_t<double> &r_point, pg::hv_algorithm &hv_algo) {
362                 return pygmo::vector_to_ndarr<py::array_t<double>>(
363                     hv.contributions(pygmo::ndarr_to_vector<pg::vector_double>(r_point), hv_algo));
364             },
365             pygmo::hv_contributions_docstring().c_str(), py::arg("ref_point"), py::arg("hv_algo"))
366         .def("get_points",
367              [](const pg::hypervolume &hv) { return pygmo::vvector_to_ndarr<py::array_t<double>>(hv.get_points()); })
368         .def(
369             "refpoint",
370             [](const pg::hypervolume &hv, double offset) {
371                 return pygmo::vector_to_ndarr<py::array_t<double>>(hv.refpoint(offset));
372             },
373             pygmo::hv_refpoint_docstring().c_str(), py::arg("offset") = 0)
374         .def_property("copy_points", &pg::hypervolume::get_copy_points, &pg::hypervolume::set_copy_points);
375 
376     // Hypervolume algorithms
377     py::class_<pg::hv_algorithm> hv_algorithm_class(m, "_hv_algorithm");
378     hv_algorithm_class.def("get_name", &pg::hv_algorithm::get_name);
379 
380     py::class_<pg::hvwfg, pg::hv_algorithm> hvwfg_class(m, "hvwfg", pygmo::hvwfg_docstring().c_str());
381     hvwfg_class.def(py::init<unsigned>(), py::arg("stop_dimension") = 2);
382 
383     py::class_<pg::bf_approx, pg::hv_algorithm> bf_approx_class(m, "bf_approx", pygmo::bf_approx_docstring().c_str());
384     bf_approx_class
385         .def(py::init<bool, unsigned, double, double, double, double, double, double>(), py::arg("use_exact") = true,
386              py::arg("trivial_subcase_size") = 1u, py::arg("eps") = 1e-2, py::arg("delta") = 1e-6,
387              py::arg("delta_multiplier") = 0.775, py::arg("alpha") = 0.2, py::arg("initial_delta_coeff") = 0.1,
388              py::arg("gamma") = 0.25)
389         .def(py::init<bool, unsigned, double, double, double, double, double, double, unsigned>(),
390              py::arg("use_exact") = true, py::arg("trivial_subcase_size") = 1u, py::arg("eps") = 1e-2,
391              py::arg("delta") = 1e-6, py::arg("delta_multiplier") = 0.775, py::arg("alpha") = 0.2,
392              py::arg("initial_delta_coeff") = 0.1, py::arg("gamma") = 0.25, py::arg("seed"));
393 
394     py::class_<pg::bf_fpras, pg::hv_algorithm> bf_fpras_class(m, "bf_fpras", pygmo::bf_fpras_docstring().c_str());
395     bf_fpras_class.def(py::init<double, double>(), py::arg("eps") = 1e-2, py::arg("delta") = 1e-2)
396         .def(py::init<double, double, unsigned>(), py::arg("eps") = 1e-2, py::arg("delta") = 1e-2, py::arg("seed"));
397 
398     py::class_<pg::hv2d, pg::hv_algorithm> hv2d_class(m, "hv2d", pygmo::hv2d_docstring().c_str());
399     hv2d_class.def(py::init<>());
400 
401     py::class_<pg::hv3d, pg::hv_algorithm> hv3d_class(m, "hv3d", pygmo::hv3d_docstring().c_str());
402     hv3d_class.def(py::init<>());
403 
404     // Multi-objective utilities
405     m.def(
406         "fast_non_dominated_sorting",
407         [](const py::array_t<double> &x) -> py::tuple {
408             auto fnds = pg::fast_non_dominated_sorting(pygmo::ndarr_to_vvector<std::vector<pg::vector_double>>(x));
409             // the non-dominated fronts
410             py::list ndf_py;
411             for (const auto &front : std::get<0>(fnds)) {
412                 ndf_py.append(pygmo::vector_to_ndarr<py::array_t<pg::pop_size_t>>(front));
413             }
414             // the domination list
415             py::list dl_py;
416             for (const auto &item : std::get<1>(fnds)) {
417                 dl_py.append(pygmo::vector_to_ndarr<py::array_t<pg::pop_size_t>>(item));
418             }
419             return py::make_tuple(ndf_py, dl_py, pygmo::vector_to_ndarr<py::array_t<pg::pop_size_t>>(std::get<2>(fnds)),
420                                   pygmo::vector_to_ndarr<py::array_t<pg::pop_size_t>>(std::get<3>(fnds)));
421         },
422         pygmo::fast_non_dominated_sorting_docstring().c_str(), py::arg("points"));
423 
424     m.def(
425         "pareto_dominance",
426         [](const py::array_t<double> &obj1, const py::array_t<double> &obj2) {
427             return pg::pareto_dominance(pygmo::ndarr_to_vector<pg::vector_double>(obj1),
428                                         pygmo::ndarr_to_vector<pg::vector_double>(obj2));
429         },
430         pygmo::pareto_dominance_docstring().c_str(), py::arg("obj1"), py::arg("obj2"));
431 
432     m.def(
433         "non_dominated_front_2d",
434         [](const py::array_t<double> &points) {
435             return pygmo::vector_to_ndarr<py::array_t<pg::pop_size_t>>(
436                 pg::non_dominated_front_2d(pygmo::ndarr_to_vvector<std::vector<pg::vector_double>>(points)));
437         },
438         pygmo::non_dominated_front_2d_docstring().c_str(), py::arg("points"));
439 
440     m.def(
441         "crowding_distance",
442         [](const py::array_t<double> &points) {
443             return pygmo::vector_to_ndarr<py::array_t<double>>(
444                 pg::crowding_distance(pygmo::ndarr_to_vvector<std::vector<pg::vector_double>>(points)));
445         },
446         pygmo::crowding_distance_docstring().c_str(), py::arg("points"));
447 
448     m.def(
449         "sort_population_mo",
450         [](const py::array_t<double> &input_f) {
451             return pygmo::vector_to_ndarr<py::array_t<pg::pop_size_t>>(
452                 pg::sort_population_mo(pygmo::ndarr_to_vvector<std::vector<pg::vector_double>>(input_f)));
453         },
454         pygmo::sort_population_mo_docstring().c_str(), py::arg("points"));
455 
456     m.def(
457         "select_best_N_mo",
458         [](const py::array_t<double> &input_f, unsigned N) {
459             return pygmo::vector_to_ndarr<py::array_t<pg::pop_size_t>>(
460                 pg::select_best_N_mo(pygmo::ndarr_to_vvector<std::vector<pg::vector_double>>(input_f), N));
461         },
462         pygmo::select_best_N_mo_docstring().c_str(), py::arg("points"), py::arg("N"));
463 
464     m.def(
465         "decomposition_weights",
466         [](pg::vector_double::size_type n_f, pg::vector_double::size_type n_w, const std::string &method,
467            unsigned seed) {
468             using reng_t = pg::detail::random_engine_type;
469             reng_t tmp_rng(static_cast<reng_t::result_type>(seed));
470             return pygmo::vvector_to_ndarr<py::array_t<double>>(pg::decomposition_weights(n_f, n_w, method, tmp_rng));
471         },
472         pygmo::decomposition_weights_docstring().c_str(), py::arg("n_f"), py::arg("n_w"), py::arg("method"),
473         py::arg("seed"));
474 
475     m.def(
476         "decompose_objectives",
477         [](const py::array_t<double> &objs, const py::array_t<double> &weights, const py::array_t<double> &ref_point,
478            const std::string &method) {
479             return pygmo::vector_to_ndarr<py::array_t<double>>(pg::decompose_objectives(
480                 pygmo::ndarr_to_vector<pg::vector_double>(objs), pygmo::ndarr_to_vector<pg::vector_double>(weights),
481                 pygmo::ndarr_to_vector<pg::vector_double>(ref_point), method));
482         },
483         pygmo::decompose_objectives_docstring().c_str(), py::arg("objs"), py::arg("weights"), py::arg("ref_point"),
484         py::arg("method"));
485 
486     m.def(
487         "nadir",
488         [](const py::array_t<double> &p) {
489             return pygmo::vector_to_ndarr<py::array_t<double>>(
490                 pg::nadir(pygmo::ndarr_to_vvector<std::vector<pg::vector_double>>(p)));
491         },
492         pygmo::nadir_docstring().c_str(), py::arg("points"));
493 
494     m.def(
495         "ideal",
496         [](const py::array_t<double> &p) {
497             return pygmo::vector_to_ndarr<py::array_t<double>>(
498                 pg::ideal(pygmo::ndarr_to_vvector<std::vector<pg::vector_double>>(p)));
499         },
500         pygmo::ideal_docstring().c_str(), py::arg("points"));
501 
502     // Gradient and Hessians utilities
503     m.def(
504         "estimate_sparsity",
505         [](const py::object &func, const py::array_t<double> &x,
506            double dx) -> py::array_t<pg::vector_double::size_type> {
507             auto f = [&func](const pg::vector_double &x_) {
508                 return pygmo::ndarr_to_vector<pg::vector_double>(
509                     py::cast<py::array_t<double>>(func(pygmo::vector_to_ndarr<py::array_t<double>>(x_))));
510             };
511             return pygmo::sp_to_ndarr(pg::estimate_sparsity(f, pygmo::ndarr_to_vector<pg::vector_double>(x), dx));
512         },
513         pygmo::estimate_sparsity_docstring().c_str(), py::arg("callable"), py::arg("x"), py::arg("dx") = 1e-8);
514 
515     m.def(
516         "estimate_gradient",
517         [](const py::object &func, const py::array_t<double> &x, double dx) -> py::array_t<double> {
518             auto f = [&func](const pg::vector_double &x_) {
519                 return pygmo::ndarr_to_vector<pg::vector_double>(
520                     py::cast<py::array_t<double>>(func(pygmo::vector_to_ndarr<py::array_t<double>>(x_))));
521             };
522             return pygmo::vector_to_ndarr<py::array_t<double>>(
523                 pg::estimate_gradient(f, pygmo::ndarr_to_vector<pg::vector_double>(x), dx));
524         },
525         pygmo::estimate_gradient_docstring().c_str(), py::arg("callable"), py::arg("x"), py::arg("dx") = 1e-8);
526 
527     m.def(
528         "estimate_gradient_h",
529         [](const py::object &func, const py::array_t<double> &x, double dx) -> py::array_t<double> {
530             auto f = [&func](const pg::vector_double &x_) {
531                 return pygmo::ndarr_to_vector<pg::vector_double>(
532                     py::cast<py::array_t<double>>(func(pygmo::vector_to_ndarr<py::array_t<double>>(x_))));
533             };
534             return pygmo::vector_to_ndarr<py::array_t<double>>(
535                 pg::estimate_gradient_h(f, pygmo::ndarr_to_vector<pg::vector_double>(x), dx));
536         },
537         pygmo::estimate_gradient_h_docstring().c_str(), py::arg("callable"), py::arg("x"), py::arg("dx") = 1e-2);
538 
539     // Constrained optimization utilities
540     m.def(
541         "compare_fc",
542         [](const py::array_t<double> &f1, const py::array_t<double> &f2, pg::vector_double::size_type nec,
543            const py::array_t<double> &tol) {
544             return pg::compare_fc(pygmo::ndarr_to_vector<pg::vector_double>(f1),
545                                   pygmo::ndarr_to_vector<pg::vector_double>(f2), nec,
546                                   pygmo::ndarr_to_vector<pg::vector_double>(tol));
547         },
548         pygmo::compare_fc_docstring().c_str(), py::arg("f1"), py::arg("f2"), py::arg("nec"), py::arg("tol"));
549 
550     m.def(
551         "sort_population_con",
552         [](const py::array_t<double> &input_f, pg::vector_double::size_type nec, const py::array_t<double> &tol) {
553             return pygmo::vector_to_ndarr<py::array_t<pg::pop_size_t>>(
554                 pg::sort_population_con(pygmo::ndarr_to_vvector<std::vector<pg::vector_double>>(input_f), nec,
555                                         pygmo::ndarr_to_vector<pg::vector_double>(tol)));
556         },
557         pygmo::sort_population_con_docstring().c_str(), py::arg("input_f"), py::arg("nec"), py::arg("tol"));
558 
559     // Add the submodules.
560     auto problems_module = m.def_submodule("problems");
561     auto algorithms_module = m.def_submodule("algorithms");
562     auto islands_module = m.def_submodule("islands");
563     auto batch_evaluators_module = m.def_submodule("batch_evaluators");
564     auto topologies_module = m.def_submodule("topologies");
565     auto r_policies_module = m.def_submodule("r_policies");
566     auto s_policies_module = m.def_submodule("s_policies");
567 
568     // Population class.
569     py::class_<pg::population> pop_class(m, "population", pygmo::population_docstring().c_str());
570     pop_class
571         // Def ctor.
572         .def(py::init<>())
573         // Ctors from problem.
574         // NOTE: we expose only the ctors from pagmo::problem, not from C++ or Python UDPs. An __init__ wrapper
575         // on the Python side will take care of cting a pagmo::problem from the input UDP, and then invoke this ctor.
576         // This way we avoid having to expose a different ctor for every exposed C++ prob. Same idea with
577         // the bfe argument.
578         .def(py::init<const pg::problem &, pg::population::size_type, unsigned>())
579         .def(py::init<const pg::problem &, const pg::bfe &, pg::population::size_type, unsigned>())
580         // repr().
581         .def("__repr__", &pygmo::ostream_repr<pg::population>)
582         // Copy and deepcopy.
583         .def("__copy__", &pygmo::generic_copy_wrapper<pg::population>)
584         .def("__deepcopy__", &pygmo::generic_deepcopy_wrapper<pg::population>, "memo"_a)
585         // Pickle support.
586         .def(py::pickle(&pygmo::pickle_getstate_wrapper<pg::population>,
587                         &pygmo::pickle_setstate_wrapper<pg::population>))
588         .def(
589             "push_back",
590             [](pg::population &pop, const py::array_t<double> &x, const py::object &f) {
591                 if (f.is_none()) {
592                     pop.push_back(pygmo::ndarr_to_vector<pg::vector_double>(x));
593                 } else {
594                     pop.push_back(pygmo::ndarr_to_vector<pg::vector_double>(x),
595                                   pygmo::ndarr_to_vector<pg::vector_double>(py::cast<py::array_t<double>>(f)));
596                 }
597             },
598             pygmo::population_push_back_docstring().c_str(), py::arg("x"), py::arg("f") = py::none())
599         .def(
600             "random_decision_vector",
601             [](const pg::population &pop) {
602                 return pygmo::vector_to_ndarr<py::array_t<double>>(pop.random_decision_vector());
603             },
604             pygmo::population_random_decision_vector_docstring().c_str())
605         .def(
606             "best_idx",
607             [](const pg::population &pop, const py::array_t<double> &tol) {
608                 return pop.best_idx(pygmo::ndarr_to_vector<pg::vector_double>(tol));
609             },
610             py::arg("tol"))
611         .def(
612             "best_idx", [](const pg::population &pop, double tol) { return pop.best_idx(tol); }, py::arg("tol"))
613         .def(
614             "best_idx", [](const pg::population &pop) { return pop.best_idx(); },
615             pygmo::population_best_idx_docstring().c_str())
616         .def(
617             "worst_idx",
618             [](const pg::population &pop, const py::array_t<double> &tol) {
619                 return pop.worst_idx(pygmo::ndarr_to_vector<pg::vector_double>(tol));
620             },
621             py::arg("tol"))
622         .def(
623             "worst_idx", [](const pg::population &pop, double tol) { return pop.worst_idx(tol); }, py::arg("tol"))
624         .def(
625             "worst_idx", [](const pg::population &pop) { return pop.worst_idx(); },
626             pygmo::population_worst_idx_docstring().c_str())
627         .def("__len__", &pg::population::size)
628         .def(
629             "set_xf",
630             [](pg::population &pop, pg::population::size_type i, const py::array_t<double> &x,
631                const py::array_t<double> &f) {
632                 pop.set_xf(i, pygmo::ndarr_to_vector<pg::vector_double>(x),
633                            pygmo::ndarr_to_vector<pg::vector_double>(f));
634             },
635             pygmo::population_set_xf_docstring().c_str())
636         .def(
637             "set_x",
638             [](pg::population &pop, pg::population::size_type i, const py::array_t<double> &x) {
639                 pop.set_x(i, pygmo::ndarr_to_vector<pg::vector_double>(x));
640             },
641             pygmo::population_set_x_docstring().c_str())
642         .def(
643             "get_f",
644             [](const pg::population &pop) { return pygmo::vvector_to_ndarr<py::array_t<double>>(pop.get_f()); },
645             pygmo::population_get_f_docstring().c_str())
646         .def(
647             "get_x",
648             [](const pg::population &pop) { return pygmo::vvector_to_ndarr<py::array_t<double>>(pop.get_x()); },
649             pygmo::population_get_x_docstring().c_str())
650         .def(
651             "get_ID",
652             [](const pg::population &pop) {
653                 return pygmo::vector_to_ndarr<py::array_t<unsigned long long>>(pop.get_ID());
654             },
655             pygmo::population_get_ID_docstring().c_str())
656         .def("get_seed", &pg::population::get_seed, pygmo::population_get_seed_docstring().c_str())
657         .def_property_readonly(
658             "champion_x",
659             [](const pg::population &pop) { return pygmo::vector_to_ndarr<py::array_t<double>>(pop.champion_x()); },
660             pygmo::population_champion_x_docstring().c_str())
661         .def_property_readonly(
662             "champion_f",
663             [](const pg::population &pop) { return pygmo::vector_to_ndarr<py::array_t<double>>(pop.champion_f()); },
664             pygmo::population_champion_f_docstring().c_str())
665         .def_property_readonly(
666             "problem", [](pg::population &pop) -> pg::problem & { return pop.get_problem(); },
667             py::return_value_policy::reference_internal, pygmo::population_problem_docstring().c_str());
668 
669     // Archi.
670     py::class_<pg::archipelago> archi_class(m, "archipelago", pygmo::archipelago_docstring().c_str());
671     archi_class
672         // Def ctor.
673         .def(py::init<>())
674         .def(py::init<const pg::topology &>())
675         // repr().
676         .def("__repr__", &pygmo::ostream_repr<pg::archipelago>)
677         // Copy and deepcopy.
678         .def("__copy__", &pygmo::generic_copy_wrapper<pg::archipelago>)
679         .def("__deepcopy__", &pygmo::generic_deepcopy_wrapper<pg::archipelago>, "memo"_a)
680         // Pickle support.
681         .def(py::pickle(&pygmo::pickle_getstate_wrapper<pg::archipelago>,
682                         &pygmo::pickle_setstate_wrapper<pg::archipelago>))
683         // Size.
684         .def("__len__", &pg::archipelago::size)
685         .def(
686             "evolve", [](pg::archipelago &archi, unsigned n) { archi.evolve(n); },
687             pygmo::archipelago_evolve_docstring().c_str(), py::arg("n") = 1u)
688         .def("wait", &pg::archipelago::wait, pygmo::archipelago_wait_docstring().c_str())
689         .def("wait_check", &pg::archipelago::wait_check, pygmo::archipelago_wait_check_docstring().c_str())
690         .def(
691             "__getitem__",
692             [](pg::archipelago &archi, pg::archipelago::size_type n) -> pg::island & { return archi[n]; },
693             pygmo::archipelago_getitem_docstring().c_str(), py::return_value_policy::reference_internal)
694         // NOTE: docs for push_back() are in the Python reimplementation.
695         .def("_push_back", [](pg::archipelago &archi, const pg::island &isl) { archi.push_back(isl); })
696         // Champions.
697         .def(
698             "get_champions_f",
699             [](const pg::archipelago &archi) -> py::list {
700                 py::list retval;
701                 auto fs = archi.get_champions_f();
702                 for (const auto &f : fs) {
703                     retval.append(pygmo::vector_to_ndarr<py::array_t<double>>(f));
704                 }
705                 return retval;
706             },
707             pygmo::archipelago_get_champions_f_docstring().c_str())
708         .def(
709             "get_champions_x",
710             [](const pg::archipelago &archi) -> py::list {
711                 py::list retval;
712                 auto xs = archi.get_champions_x();
713                 for (const auto &x : xs) {
714                     retval.append(pygmo::vector_to_ndarr<py::array_t<double>>(x));
715                 }
716                 return retval;
717             },
718             pygmo::archipelago_get_champions_x_docstring().c_str())
719         .def(
720             "get_migrants_db",
721             [](const pg::archipelago &archi) -> py::list {
722                 py::list retval;
723                 const auto tmp = archi.get_migrants_db();
724                 for (const auto &ig : tmp) {
725                     retval.append(pygmo::inds_to_tuple(ig));
726                 }
727                 return retval;
728             },
729             pygmo::archipelago_get_migrants_db_docstring().c_str())
730         .def(
731             "set_migrants_db",
732             [](pg::archipelago &archi, const py::list &mig) {
733                 pg::archipelago::migrants_db_t mig_db;
734 
735                 for (auto o : mig) {
736                     mig_db.push_back(pygmo::iterable_to_inds(py::cast<py::iterable>(o)));
737                 }
738 
739                 archi.set_migrants_db(mig_db);
740             },
741             pygmo::archipelago_set_migrants_db_docstring().c_str())
742         .def(
743             "get_migration_log",
744             [](const pg::archipelago &archi) -> py::list {
745                 py::list retval;
746                 const auto tmp = archi.get_migration_log();
747                 for (const auto &le : tmp) {
748                     retval.append(py::make_tuple(std::get<0>(le), std::get<1>(le),
749                                                  pygmo::vector_to_ndarr<py::array_t<double>>(std::get<2>(le)),
750                                                  pygmo::vector_to_ndarr<py::array_t<double>>(std::get<3>(le)),
751                                                  std::get<4>(le), std::get<5>(le)));
752                 }
753                 return retval;
754             },
755             pygmo::archipelago_get_migration_log_docstring().c_str())
756         .def("get_topology", &pg::archipelago::get_topology, pygmo::archipelago_get_topology_docstring().c_str())
757         .def("_set_topology", &pg::archipelago::set_topology)
758         .def("set_migration_type", &pg::archipelago::set_migration_type,
759              pygmo::archipelago_set_migration_type_docstring().c_str(), py::arg("mt"))
760         .def("set_migrant_handling", &pg::archipelago::set_migrant_handling,
761              pygmo::archipelago_set_migrant_handling_docstring().c_str(), py::arg("mh"))
762         .def("get_migration_type", &pg::archipelago::get_migration_type,
763              pygmo::archipelago_get_migration_type_docstring().c_str())
764         .def("get_migrant_handling", &pg::archipelago::get_migrant_handling,
765              pygmo::archipelago_get_migrant_handling_docstring().c_str())
766         .def_property_readonly("status", &pg::archipelago::status, pygmo::archipelago_status_docstring().c_str());
767 
768     // Problem class.
769     py::class_<pg::problem> problem_class(m, "problem", pygmo::problem_docstring().c_str());
770     problem_class
771         // Def ctor.
772         .def(py::init<>())
773         // repr().
774         .def("__repr__", &pygmo::ostream_repr<pg::problem>)
775         // Copy and deepcopy.
776         .def("__copy__", &pygmo::generic_copy_wrapper<pg::problem>)
777         .def("__deepcopy__", &pygmo::generic_deepcopy_wrapper<pg::problem>, "memo"_a)
778         // Pickle support.
779         .def(py::pickle(&pygmo::pickle_getstate_wrapper<pg::problem>, &pygmo::pickle_setstate_wrapper<pg::problem>))
780         // UDP extraction.
781         .def("_py_extract", &pygmo::generic_py_extract<pg::problem>)
782         // Problem methods.
783         .def(
784             "fitness",
785             [](const pg::problem &p, const py::array_t<double> &dv) {
786                 return pygmo::vector_to_ndarr<py::array_t<double>>(
787                     p.fitness(pygmo::ndarr_to_vector<pg::vector_double>(dv)));
788             },
789             pygmo::problem_fitness_docstring().c_str(), py::arg("dv"))
790         .def(
791             "get_bounds",
792             [](const pg::problem &p) {
793                 return py::make_tuple(pygmo::vector_to_ndarr<py::array_t<double>>(p.get_lb()),
794                                       pygmo::vector_to_ndarr<py::array_t<double>>(p.get_ub()));
795             },
796             pygmo::problem_get_bounds_docstring().c_str())
797         .def(
798             "get_lb", [](const pg::problem &p) { return pygmo::vector_to_ndarr<py::array_t<double>>(p.get_lb()); },
799             pygmo::problem_get_lb_docstring().c_str())
800         .def(
801             "get_ub", [](const pg::problem &p) { return pygmo::vector_to_ndarr<py::array_t<double>>(p.get_ub()); },
802             pygmo::problem_get_ub_docstring().c_str())
803         .def(
804             "batch_fitness",
805             [](const pg::problem &p, const py::array_t<double> &dvs) {
806                 return pygmo::vector_to_ndarr<py::array_t<double>>(
807                     p.batch_fitness(pygmo::ndarr_to_vector<pg::vector_double>(dvs)));
808             },
809             pygmo::problem_batch_fitness_docstring().c_str(), py::arg("dvs"))
810         .def("has_batch_fitness", &pg::problem::has_batch_fitness, pygmo::problem_has_batch_fitness_docstring().c_str())
811         .def(
812             "gradient",
813             [](const pg::problem &p, const py::array_t<double> &dv) {
814                 return pygmo::vector_to_ndarr<py::array_t<double>>(
815                     p.gradient(pygmo::ndarr_to_vector<pg::vector_double>(dv)));
816             },
817             pygmo::problem_gradient_docstring().c_str(), py::arg("dv"))
818         .def("has_gradient", &pg::problem::has_gradient, pygmo::problem_has_gradient_docstring().c_str())
819         .def(
820             "gradient_sparsity", [](const pg::problem &p) { return pygmo::sp_to_ndarr(p.gradient_sparsity()); },
821             pygmo::problem_gradient_sparsity_docstring().c_str())
822         .def("has_gradient_sparsity", &pg::problem::has_gradient_sparsity,
823              pygmo::problem_has_gradient_sparsity_docstring().c_str())
824         .def(
825             "hessians",
826             [](const pg::problem &p, const py::array_t<double> &dv) -> py::list {
827                 py::list retval;
828                 for (const auto &v : p.hessians(pygmo::ndarr_to_vector<pg::vector_double>(dv))) {
829                     retval.append(pygmo::vector_to_ndarr<py::array_t<double>>(v));
830                 }
831                 return retval;
832             },
833             pygmo::problem_hessians_docstring().c_str(), py::arg("dv"))
834         .def("has_hessians", &pg::problem::has_hessians, pygmo::problem_has_hessians_docstring().c_str())
835         .def(
836             "hessians_sparsity",
837             [](const pg::problem &p) -> py::list {
838                 py::list retval;
839                 for (const auto &sp : p.hessians_sparsity()) {
840                     retval.append(pygmo::sp_to_ndarr(sp));
841                 }
842                 return retval;
843             },
844             pygmo::problem_hessians_sparsity_docstring().c_str())
845         .def("has_hessians_sparsity", &pg::problem::has_hessians_sparsity,
846              pygmo::problem_has_hessians_sparsity_docstring().c_str())
847         .def("get_nobj", &pg::problem::get_nobj, pygmo::problem_get_nobj_docstring().c_str())
848         .def("get_nx", &pg::problem::get_nx, pygmo::problem_get_nx_docstring().c_str())
849         .def("get_nix", &pg::problem::get_nix, pygmo::problem_get_nix_docstring().c_str())
850         .def("get_ncx", &pg::problem::get_ncx, pygmo::problem_get_ncx_docstring().c_str())
851         .def("get_nf", &pg::problem::get_nf, pygmo::problem_get_nf_docstring().c_str())
852         .def("get_nec", &pg::problem::get_nec, pygmo::problem_get_nec_docstring().c_str())
853         .def("get_nic", &pg::problem::get_nic, pygmo::problem_get_nic_docstring().c_str())
854         .def("get_nc", &pg::problem::get_nc, pygmo::problem_get_nc_docstring().c_str())
855         .def("get_fevals", &pg::problem::get_fevals, pygmo::problem_get_fevals_docstring().c_str())
856         .def("increment_fevals", &pg::problem::increment_fevals, pygmo::problem_increment_fevals_docstring().c_str(),
857              py::arg("n"))
858         .def("get_gevals", &pg::problem::get_gevals, pygmo::problem_get_gevals_docstring().c_str())
859         .def("get_hevals", &pg::problem::get_hevals, pygmo::problem_get_hevals_docstring().c_str())
860         .def("set_seed", &pg::problem::set_seed, pygmo::problem_set_seed_docstring().c_str(), py::arg("seed"))
861         .def("has_set_seed", &pg::problem::has_set_seed, pygmo::problem_has_set_seed_docstring().c_str())
862         .def("is_stochastic", &pg::problem::is_stochastic,
863              "is_stochastic()\n\nAlias for :func:`~pygmo.problem.has_set_seed()`.\n")
864         .def(
865             "feasibility_x",
866             [](const pg::problem &p, const py::array_t<double> &x) {
867                 return p.feasibility_x(pygmo::ndarr_to_vector<pg::vector_double>(x));
868             },
869             pygmo::problem_feasibility_x_docstring().c_str(), py::arg("x"))
870         .def(
871             "feasibility_f",
872             [](const pg::problem &p, const py::array_t<double> &f) {
873                 return p.feasibility_f(pygmo::ndarr_to_vector<pg::vector_double>(f));
874             },
875             pygmo::problem_feasibility_f_docstring().c_str(), py::arg("f"))
876         .def("get_name", &pg::problem::get_name, pygmo::problem_get_name_docstring().c_str())
877         .def("get_extra_info", &pg::problem::get_extra_info, pygmo::problem_get_extra_info_docstring().c_str())
878         .def("get_thread_safety", &pg::problem::get_thread_safety, pygmo::problem_get_thread_safety_docstring().c_str())
879         .def_property(
880             "c_tol",
881             [](const pg::problem &prob) { return pygmo::vector_to_ndarr<py::array_t<double>>(prob.get_c_tol()); },
882             [](pg::problem &prob, const py::object &c_tol) {
883                 try {
884                     prob.set_c_tol(py::cast<double>(c_tol));
885                 } catch (const py::cast_error &) {
886                     prob.set_c_tol(pygmo::ndarr_to_vector<pg::vector_double>(py::cast<py::array_t<double>>(c_tol)));
887                 }
888             },
889             pygmo::problem_c_tol_docstring().c_str());
890 
891     // Expose the C++ problems.
892     pygmo::expose_problems_0(m, problem_class, problems_module);
893     pygmo::expose_problems_1(m, problem_class, problems_module);
894 
895     // Finalize.
896     problem_class.def(py::init<const py::object &>(), py::arg("udp"));
897 
898     // Algorithm class.
899     py::class_<pg::algorithm> algorithm_class(m, "algorithm", pygmo::algorithm_docstring().c_str());
900     algorithm_class
901         // Def ctor.
902         .def(py::init<>())
903         // repr().
904         .def("__repr__", &pygmo::ostream_repr<pg::algorithm>)
905         // Copy and deepcopy.
906         .def("__copy__", &pygmo::generic_copy_wrapper<pg::algorithm>)
907         .def("__deepcopy__", &pygmo::generic_deepcopy_wrapper<pg::algorithm>, "memo"_a)
908         // Pickle support.
909         .def(py::pickle(&pygmo::pickle_getstate_wrapper<pg::algorithm>, &pygmo::pickle_setstate_wrapper<pg::algorithm>))
910         // UDA extraction.
911         .def("_py_extract", &pygmo::generic_py_extract<pg::algorithm>)
912         // Algorithm methods.
913         .def("evolve", &pg::algorithm::evolve, pygmo::algorithm_evolve_docstring().c_str(), py::arg("pop"))
914         .def("set_seed", &pg::algorithm::set_seed, pygmo::algorithm_set_seed_docstring().c_str(), py::arg("seed"))
915         .def("has_set_seed", &pg::algorithm::has_set_seed, pygmo::algorithm_has_set_seed_docstring().c_str())
916         .def("set_verbosity", &pg::algorithm::set_verbosity, pygmo::algorithm_set_verbosity_docstring().c_str(),
917              py::arg("level"))
918         .def("has_set_verbosity", &pg::algorithm::has_set_verbosity,
919              pygmo::algorithm_has_set_verbosity_docstring().c_str())
920         .def("is_stochastic", &pg::algorithm::is_stochastic,
921              "is_stochastic()\n\nAlias for :func:`~pygmo.algorithm.has_set_seed()`.\n")
922         .def("get_name", &pg::algorithm::get_name, pygmo::algorithm_get_name_docstring().c_str())
923         .def("get_extra_info", &pg::algorithm::get_extra_info, pygmo::algorithm_get_extra_info_docstring().c_str())
924         .def("get_thread_safety", &pg::algorithm::get_thread_safety,
925              pygmo::algorithm_get_thread_safety_docstring().c_str());
926 
927     // Expose the C++ algos.
928     pygmo::expose_algorithms_0(m, algorithm_class, algorithms_module);
929     pygmo::expose_algorithms_1(m, algorithm_class, algorithms_module);
930 
931     // Finalize.
932     algorithm_class.def(py::init<const py::object &>(), py::arg("uda"));
933 
934     // bfe class.
935     py::class_<pg::bfe> bfe_class(m, "bfe", pygmo::bfe_docstring().c_str());
936     bfe_class
937         // Def ctor.
938         .def(py::init<>())
939         // repr().
940         .def("__repr__", &pygmo::ostream_repr<pg::bfe>)
941         // Copy and deepcopy.
942         .def("__copy__", &pygmo::generic_copy_wrapper<pg::bfe>)
943         .def("__deepcopy__", &pygmo::generic_deepcopy_wrapper<pg::bfe>, "memo"_a)
944         // Pickle support.
945         .def(py::pickle(&pygmo::pickle_getstate_wrapper<pg::bfe>, &pygmo::pickle_setstate_wrapper<pg::bfe>))
946         // UDBFE extraction.
947         .def("_py_extract", &pygmo::generic_py_extract<pg::bfe>)
948         // Bfe methods.
949         .def("_call_impl",
950              [](const pg::bfe &b, const pg::problem &prob, const py::array_t<double> &dvs) {
951                  return pygmo::vector_to_ndarr<py::array_t<double>>(
952                      b(prob, pygmo::ndarr_to_vector<pg::vector_double>(dvs)));
953              })
954         .def("get_name", &pg::bfe::get_name, pygmo::bfe_get_name_docstring().c_str())
955         .def("get_extra_info", &pg::bfe::get_extra_info, pygmo::bfe_get_extra_info_docstring().c_str())
956         .def("get_thread_safety", &pg::bfe::get_thread_safety, pygmo::bfe_get_thread_safety_docstring().c_str());
957 
958     // Expose the C++ bfes.
959     pygmo::expose_bfes(m, bfe_class, batch_evaluators_module);
960 
961     // Finalize.
962     bfe_class.def(py::init<const py::object &>(), py::arg("udbfe"));
963 
964     // Island class.
965     py::class_<pg::island> island_class(m, "island", pygmo::island_docstring().c_str());
966     island_class
967         // Def ctor.
968         .def(py::init<>())
969         // Ctor from algo, pop, and policies.
970         .def(py::init<const pg::algorithm &, const pg::population &, const pg::r_policy &, const pg::s_policy &>())
971         // repr().
972         .def("__repr__", &pygmo::ostream_repr<pg::island>)
973         // Copy and deepcopy.
974         .def("__copy__", &pygmo::generic_copy_wrapper<pg::island>)
975         .def("__deepcopy__", &pygmo::generic_deepcopy_wrapper<pg::island>, "memo"_a)
976         // Pickle support.
977         .def(py::pickle(&pygmo::pickle_getstate_wrapper<pg::island>, &pygmo::pickle_setstate_wrapper<pg::island>))
978         // UDI extraction.
979         .def("_py_extract", &pygmo::generic_py_extract<pg::island>)
980         .def(
981             "evolve", [](pg::island &isl, unsigned n) { isl.evolve(n); }, pygmo::island_evolve_docstring().c_str(),
982             py::arg("n") = 1u)
983         .def("wait", &pg::island::wait, pygmo::island_wait_docstring().c_str())
984         .def("wait_check", &pg::island::wait_check, pygmo::island_wait_check_docstring().c_str())
985         .def("get_population", &pg::island::get_population, pygmo::island_get_population_docstring().c_str())
986         .def("get_algorithm", &pg::island::get_algorithm, pygmo::island_get_algorithm_docstring().c_str())
987         .def("set_population", &pg::island::set_population, pygmo::island_set_population_docstring().c_str(),
988              py::arg("pop"))
989         .def("set_algorithm", &pg::island::set_algorithm, pygmo::island_set_algorithm_docstring().c_str(),
990              py::arg("algo"))
991         .def("get_name", &pg::island::get_name, pygmo::island_get_name_docstring().c_str())
992         .def("get_extra_info", &pg::island::get_extra_info, pygmo::island_get_extra_info_docstring().c_str())
993         .def("get_r_policy", &pg::island::get_r_policy, pygmo::island_get_r_policy_docstring().c_str())
994         .def("get_s_policy", &pg::island::get_s_policy, pygmo::island_get_s_policy_docstring().c_str())
995         .def_property_readonly("status", &pg::island::status, pygmo::island_status_docstring().c_str());
996 
997     // Expose the C++ islands.
998     pygmo::expose_islands(m, island_class, islands_module);
999 
1000     // Finalize.
1001     island_class.def(py::init<const py::object &, const pg::algorithm &, const pg::population &, const pg::r_policy &,
1002                               const pg::s_policy &>());
1003 
1004     // Replacement policy class.
1005     py::class_<pg::r_policy> r_policy_class(m, "r_policy", pygmo::r_policy_docstring().c_str());
1006     r_policy_class
1007         // Def ctor.
1008         .def(py::init<>())
1009         // repr().
1010         .def("__repr__", &pygmo::ostream_repr<pg::r_policy>)
1011         // Copy and deepcopy.
1012         .def("__copy__", &pygmo::generic_copy_wrapper<pg::r_policy>)
1013         .def("__deepcopy__", &pygmo::generic_deepcopy_wrapper<pg::r_policy>, "memo"_a)
1014         // Pickle support.
1015         .def(py::pickle(&pygmo::pickle_getstate_wrapper<pg::r_policy>, &pygmo::pickle_setstate_wrapper<pg::r_policy>))
1016         // UDRP extraction.
1017         .def("_py_extract", &pygmo::generic_py_extract<pg::r_policy>)
1018         // r_policy methods.
1019         .def(
1020             "replace",
1021             [](const pg::r_policy &r, const py::iterable &inds, const pg::vector_double::size_type &nx,
1022                const pg::vector_double::size_type &nix, const pg::vector_double::size_type &nobj,
1023                const pg::vector_double::size_type &nec, const pg::vector_double::size_type &nic,
1024                const py::array_t<double> &tol, const py::iterable &mig) {
1025                 return pygmo::inds_to_tuple(r.replace(pygmo::iterable_to_inds(inds), nx, nix, nobj, nec, nic,
1026                                                       pygmo::ndarr_to_vector<pg::vector_double>(tol),
1027                                                       pygmo::iterable_to_inds(mig)));
1028             },
1029             pygmo::r_policy_replace_docstring().c_str(), py::arg("inds"), py::arg("nx"), py::arg("nix"),
1030             py::arg("nobj"), py::arg("nec"), py::arg("nic"), py::arg("tol"), py::arg("mig"))
1031         .def("get_name", &pg::r_policy::get_name, pygmo::r_policy_get_name_docstring().c_str())
1032         .def("get_extra_info", &pg::r_policy::get_extra_info, pygmo::r_policy_get_extra_info_docstring().c_str());
1033 
1034     // Expose the C++ replacement policies.
1035     pygmo::expose_r_policies(m, r_policy_class, r_policies_module);
1036 
1037     // Finalize.
1038     r_policy_class.def(py::init<const py::object &>(), py::arg("udrp"));
1039 
1040     // Selection policy class.
1041     py::class_<pg::s_policy> s_policy_class(m, "s_policy", pygmo::s_policy_docstring().c_str());
1042     s_policy_class
1043         // Def ctor.
1044         .def(py::init<>())
1045         // repr().
1046         .def("__repr__", &pygmo::ostream_repr<pg::s_policy>)
1047         // Copy and deepcopy.
1048         .def("__copy__", &pygmo::generic_copy_wrapper<pg::s_policy>)
1049         .def("__deepcopy__", &pygmo::generic_deepcopy_wrapper<pg::s_policy>, "memo"_a)
1050         // Pickle support.
1051         .def(py::pickle(&pygmo::pickle_getstate_wrapper<pg::s_policy>, &pygmo::pickle_setstate_wrapper<pg::s_policy>))
1052         // UDSP extraction.
1053         .def("_py_extract", &pygmo::generic_py_extract<pg::s_policy>)
1054         // s_policy methods.
1055         .def(
1056             "select",
1057             [](const pg::s_policy &s, const py::iterable &inds, const pg::vector_double::size_type &nx,
1058                const pg::vector_double::size_type &nix, const pg::vector_double::size_type &nobj,
1059                const pg::vector_double::size_type &nec, const pg::vector_double::size_type &nic,
1060                const py::array_t<double> &tol) {
1061                 return pygmo::inds_to_tuple(s.select(pygmo::iterable_to_inds(inds), nx, nix, nobj, nec, nic,
1062                                                      pygmo::ndarr_to_vector<pg::vector_double>(tol)));
1063             },
1064             pygmo::s_policy_select_docstring().c_str(), py::arg("inds"), py::arg("nx"), py::arg("nix"), py::arg("nobj"),
1065             py::arg("nec"), py::arg("nic"), py::arg("tol"))
1066         .def("get_name", &pg::s_policy::get_name, pygmo::s_policy_get_name_docstring().c_str())
1067         .def("get_extra_info", &pg::s_policy::get_extra_info, pygmo::s_policy_get_extra_info_docstring().c_str());
1068 
1069     // Expose the C++ selection policies.
1070     pygmo::expose_s_policies(m, s_policy_class, s_policies_module);
1071 
1072     // Finalize.
1073     s_policy_class.def(py::init<const py::object &>(), py::arg("udsp"));
1074 
1075     // Topology class.
1076     py::class_<pg::topology> topology_class(m, "topology", pygmo::topology_docstring().c_str());
1077     topology_class
1078         // Def ctor.
1079         .def(py::init<>())
1080         // repr().
1081         .def("__repr__", &pygmo::ostream_repr<pg::topology>)
1082         // Copy and deepcopy.
1083         .def("__copy__", &pygmo::generic_copy_wrapper<pg::topology>)
1084         .def("__deepcopy__", &pygmo::generic_deepcopy_wrapper<pg::topology>, "memo"_a)
1085         // Pickle support.
1086         .def(py::pickle(&pygmo::pickle_getstate_wrapper<pg::topology>, &pygmo::pickle_setstate_wrapper<pg::topology>))
1087         // UDT extraction.
1088         .def("_py_extract", &pygmo::generic_py_extract<pg::topology>)
1089         // Topology methods.
1090         .def(
1091             "get_connections",
1092             [](const pg::topology &t, std::size_t n) -> py::tuple {
1093                 auto ret = t.get_connections(n);
1094                 return py::make_tuple(pygmo::vector_to_ndarr<py::array_t<std::size_t>>(ret.first),
1095                                       pygmo::vector_to_ndarr<py::array_t<double>>(ret.second));
1096             },
1097             pygmo::topology_get_connections_docstring().c_str(), py::arg("n"))
1098         .def(
1099             "push_back", [](pg::topology &t, unsigned n) { t.push_back(n); },
1100             pygmo::topology_push_back_docstring().c_str(), py::arg("n") = std::size_t(1))
1101         .def(
1102             "to_networkx", [](const pg::topology &t) { return pygmo::bgl_graph_t_to_networkx(t.to_bgl()); },
1103             pygmo::topology_to_networkx_docstring().c_str())
1104         .def("get_name", &pg::topology::get_name, pygmo::topology_get_name_docstring().c_str())
1105         .def("get_extra_info", &pg::topology::get_extra_info, pygmo::topology_get_extra_info_docstring().c_str());
1106 
1107     // Expose the C++ topologies.
1108     pygmo::expose_topologies(m, topology_class, topologies_module);
1109 
1110     // Finalize.
1111     topology_class.def(py::init<const py::object &>(), py::arg("udt"));
1112 }
1113