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 <memory>
10 #include <string>
11 #include <typeindex>
12 #include <typeinfo>
13
14 #include <pybind11/pybind11.h>
15
16 #include <pagmo/algorithm.hpp>
17 #include <pagmo/island.hpp>
18 #include <pagmo/population.hpp>
19
20 #include "common_utils.hpp"
21 #include "handle_thread_py_exception.hpp"
22 #include "island.hpp"
23 #include "s11n_wrappers.hpp"
24
25 namespace pagmo
26 {
27
28 namespace detail
29 {
30
31 namespace py = pybind11;
32
isl_inner(const py::object & o)33 isl_inner<py::object>::isl_inner(const py::object &o)
34 {
35 // NOTE: unlike pygmo.problem/algorithm, we don't have to enforce
36 // that o is not a pygmo.island, because pygmo.island does not satisfy
37 // the UDI interface and thus check_mandatory_method() below will fail
38 // if o is a pygmo.island.
39 //
40 // Check that o is an instance of a class, and not a type.
41 check_not_type(o, "island");
42 check_mandatory_method(o, "run_evolve", "island");
43 m_value = pygmo::deepcopy(o);
44 }
45
clone() const46 std::unique_ptr<isl_inner_base> isl_inner<py::object>::clone() const
47 {
48 // This will make a deep copy using the ctor above.
49 return std::make_unique<isl_inner>(m_value);
50 }
51
run_evolve(island & isl) const52 void isl_inner<py::object>::run_evolve(island &isl) const
53 {
54 // NOTE: run_evolve() is called from a separate thread in pagmo::island, need to construct a GTE before
55 // doing anything with the interpreter (including the throws in the checks below).
56 pygmo::gil_thread_ensurer gte;
57
58 // NOTE: every time we call into the Python interpreter from a separate thread, we need to
59 // handle Python exceptions in a special way.
60 std::string isl_name;
61 try {
62 isl_name = get_name();
63 } catch (const py::error_already_set &eas) {
64 pygmo::handle_thread_py_exception("Could not fetch the name of a pythonic island. The error is:\n", eas);
65 }
66
67 try {
68 auto ret = m_value.attr("run_evolve")(isl.get_algorithm(), isl.get_population());
69
70 py::tuple ret_tup;
71 try {
72 ret_tup = py::cast<py::tuple>(ret);
73 } catch (const py::cast_error &) {
74 pygmo::py_throw(PyExc_TypeError, ("the 'run_evolve()' method of a user-defined island "
75 "must return a tuple, but it returned an object of type '"
76 + pygmo::str(pygmo::type(ret)) + "' instead")
77 .c_str());
78 }
79 if (py::len(ret_tup) != 2) {
80 pygmo::py_throw(PyExc_ValueError,
81 ("the tuple returned by the 'run_evolve()' method of a user-defined island "
82 "must have 2 elements, but instead it has "
83 + std::to_string(py::len(ret_tup)) + " element(s)")
84 .c_str());
85 }
86
87 algorithm ret_algo;
88 try {
89 ret_algo = py::cast<algorithm>(ret_tup[0]);
90 } catch (const py::cast_error &) {
91 pygmo::py_throw(PyExc_TypeError,
92 ("the first value returned by the 'run_evolve()' method of a user-defined island "
93 "must be an algorithm, but an object of type '"
94 + pygmo::str(pygmo::type(ret_tup[0])) + "' was returned instead")
95 .c_str());
96 }
97
98 population ret_pop;
99 try {
100 ret_pop = py::cast<population>(ret_tup[1]);
101 } catch (const py::cast_error &) {
102 pygmo::py_throw(PyExc_TypeError,
103 ("the second value returned by the 'run_evolve()' method of a user-defined island "
104 "must be a population, but an object of type '"
105 + pygmo::str(pygmo::type(ret_tup[1])) + "' was returned instead")
106 .c_str());
107 }
108
109 isl.set_algorithm(ret_algo);
110 isl.set_population(ret_pop);
111 } catch (const py::error_already_set &eas) {
112 pygmo::handle_thread_py_exception(
113 "The asynchronous evolution of a pythonic island of type '" + isl_name + "' raised an error:\n", eas);
114 }
115 }
116
get_name() const117 std::string isl_inner<py::object>::get_name() const
118 {
119 return getter_wrapper<std::string>(m_value, "get_name", pygmo::str(pygmo::type(m_value)));
120 }
121
get_extra_info() const122 std::string isl_inner<py::object>::get_extra_info() const
123 {
124 return getter_wrapper<std::string>(m_value, "get_extra_info", std::string{});
125 }
126
get_type_index() const127 std::type_index isl_inner<py::object>::get_type_index() const
128 {
129 return std::type_index(typeid(py::object));
130 }
131
get_ptr() const132 const void *isl_inner<py::object>::get_ptr() const
133 {
134 return &m_value;
135 }
136
get_ptr()137 void *isl_inner<py::object>::get_ptr()
138 {
139 return &m_value;
140 }
141
142 template <typename Archive>
save(Archive & ar,unsigned) const143 void isl_inner<py::object>::save(Archive &ar, unsigned) const
144 {
145 pygmo::inner_class_save<isl_inner_base>(ar, *this);
146 }
147
148 template <typename Archive>
load(Archive & ar,unsigned)149 void isl_inner<py::object>::load(Archive &ar, unsigned)
150 {
151 pygmo::inner_class_load<isl_inner_base>(ar, *this);
152 }
153
154 } // namespace detail
155
156 } // namespace pagmo
157
158 PAGMO_S11N_ISLAND_IMPLEMENT(pybind11::object)
159