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/numpy.h>
15 #include <pybind11/pybind11.h>
16 
17 #include <pagmo/r_policy.hpp>
18 #include <pagmo/types.hpp>
19 
20 #include "common_utils.hpp"
21 #include "handle_thread_py_exception.hpp"
22 #include "r_policy.hpp"
23 #include "s11n_wrappers.hpp"
24 
25 namespace pagmo
26 {
27 
28 namespace detail
29 {
30 
31 namespace py = pybind11;
32 
r_pol_inner(const py::object & o)33 r_pol_inner<py::object>::r_pol_inner(const py::object &o)
34 {
35     // Forbid the use of a pygmo.r_policy as a UDRP.
36     // The motivation here is consistency with C++. In C++, the use of
37     // a pagmo::r_policy as a UDRP is forbidden and prevented by the fact
38     // that the generic constructor from UDRP is disabled if the input
39     // object is a pagmo::r_policy (the copy/move constructor is
40     // invoked instead). In order to achieve an equivalent behaviour
41     // in pygmo, we throw an error if o is an r_policy, and instruct
42     // the user to employ the standard copy/deepcopy facilities
43     // for creating a copy of the input r_policy.
44     if (pygmo::type(o).equal(py::module::import("pygmo").attr("r_policy"))) {
45         pygmo::py_throw(PyExc_TypeError,
46                         ("a pygmo.r_policy cannot be used as a UDRP for another pygmo.r_policy (if you need to copy a "
47                          "replacement policy please use the standard Python copy()/deepcopy() functions)"));
48     }
49     // Check that o is an instance of a class, and not a type.
50     check_not_type(o, "r_policy");
51     check_mandatory_method(o, "replace", "r_policy");
52     m_value = pygmo::deepcopy(o);
53 }
54 
clone() const55 std::unique_ptr<r_pol_inner_base> r_pol_inner<py::object>::clone() const
56 {
57     // This will make a deep copy using the ctor above.
58     return std::make_unique<r_pol_inner>(m_value);
59 }
60 
61 individuals_group_t
replace(const individuals_group_t & inds,const vector_double::size_type & nx,const vector_double::size_type & nix,const vector_double::size_type & nobj,const vector_double::size_type & nec,const vector_double::size_type & nic,const vector_double & tol,const individuals_group_t & mig) const62 r_pol_inner<py::object>::replace(const individuals_group_t &inds, const vector_double::size_type &nx,
63                                  const vector_double::size_type &nix, const vector_double::size_type &nobj,
64                                  const vector_double::size_type &nec, const vector_double::size_type &nic,
65                                  const vector_double &tol, const individuals_group_t &mig) const
66 {
67     // NOTE: replace() may be called from a separate thread in pagmo::island, need to construct a GTE before
68     // doing anything with the interpreter (including the throws in the checks below).
69     pygmo::gil_thread_ensurer gte;
70 
71     // NOTE: every time we call into the Python interpreter from a separate thread, we need to
72     // handle Python exceptions in a special way.
73     std::string r_pol_name;
74     try {
75         r_pol_name = get_name();
76     } catch (const py::error_already_set &eas) {
77         pygmo::handle_thread_py_exception("Could not fetch the name of a pythonic replacement policy. The error is:\n",
78                                           eas);
79     }
80 
81     try {
82         // Fetch the new individuals in Python form.
83         auto o = m_value.attr("replace")(pygmo::inds_to_tuple(inds), nx, nix, nobj, nec, nic,
84                                          pygmo::vector_to_ndarr<py::array_t<double>>(tol), pygmo::inds_to_tuple(mig));
85 
86         // Convert back to C++ form and return.
87         return pygmo::iterable_to_inds(o);
88     } catch (const py::error_already_set &eas) {
89         pygmo::handle_thread_py_exception("The replace() method of a pythonic replacement policy of type '" + r_pol_name
90                                               + "' raised an error:\n",
91                                           eas);
92     }
93 }
94 
get_name() const95 std::string r_pol_inner<py::object>::get_name() const
96 {
97     return getter_wrapper<std::string>(m_value, "get_name", pygmo::str(pygmo::type(m_value)));
98 }
99 
get_extra_info() const100 std::string r_pol_inner<py::object>::get_extra_info() const
101 {
102     return getter_wrapper<std::string>(m_value, "get_extra_info", std::string{});
103 }
104 
get_type_index() const105 std::type_index r_pol_inner<py::object>::get_type_index() const
106 {
107     return std::type_index(typeid(py::object));
108 }
109 
get_ptr() const110 const void *r_pol_inner<py::object>::get_ptr() const
111 {
112     return &m_value;
113 }
114 
get_ptr()115 void *r_pol_inner<py::object>::get_ptr()
116 {
117     return &m_value;
118 }
119 
120 template <typename Archive>
save(Archive & ar,unsigned) const121 void r_pol_inner<py::object>::save(Archive &ar, unsigned) const
122 {
123     pygmo::inner_class_save<r_pol_inner_base>(ar, *this);
124 }
125 
126 template <typename Archive>
load(Archive & ar,unsigned)127 void r_pol_inner<py::object>::load(Archive &ar, unsigned)
128 {
129     pygmo::inner_class_load<r_pol_inner_base>(ar, *this);
130 }
131 
132 } // namespace detail
133 
134 } // namespace pagmo
135 
136 PAGMO_S11N_R_POLICY_IMPLEMENT(pybind11::object)
137