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/population.hpp>
17 #include <pagmo/threading.hpp>
18 
19 #include "algorithm.hpp"
20 #include "common_utils.hpp"
21 #include "s11n_wrappers.hpp"
22 
23 namespace pagmo
24 {
25 
26 namespace detail
27 {
28 
29 namespace py = pybind11;
30 
algo_inner(const py::object & o)31 algo_inner<py::object>::algo_inner(const py::object &o)
32 {
33     // Forbid the use of a pygmo.algorithm as a UDA.
34     // The motivation here is consistency with C++. In C++, the use of
35     // a pagmo::algorithm as a UDA is forbidden and prevented by the fact
36     // that the generic constructor from UDA is disabled if the input
37     // object is a pagmo::algorithm (the copy/move constructor is
38     // invoked instead). In order to achieve an equivalent behaviour
39     // in pygmo, we throw an error if o is a algorithm, and instruct
40     // the user to employ the standard copy/deepcopy facilities
41     // for creating a copy of the input algorithm.
42     if (pygmo::type(o).equal(py::module::import("pygmo").attr("algorithm"))) {
43         pygmo::py_throw(
44             PyExc_TypeError,
45             ("a pygmo.algorithm cannot be used as a UDA for another pygmo.algorithm (if you need to copy an "
46              "algorithm please use the standard Python copy()/deepcopy() functions)"));
47     }
48     // Check that o is an instance of a class, and not a type.
49     check_not_type(o, "algorithm");
50     check_mandatory_method(o, "evolve", "algorithm");
51     m_value = pygmo::deepcopy(o);
52 }
53 
clone() const54 std::unique_ptr<algo_inner_base> algo_inner<py::object>::clone() const
55 {
56     // This will make a deep copy using the ctor above.
57     return std::make_unique<algo_inner>(m_value);
58 }
59 
evolve(const population & pop) const60 population algo_inner<py::object>::evolve(const population &pop) const
61 {
62     return py::cast<population>(m_value.attr("evolve")(pop));
63 }
64 
set_seed(unsigned n)65 void algo_inner<py::object>::set_seed(unsigned n)
66 {
67     auto ss = pygmo::callable_attribute(m_value, "set_seed");
68     if (ss.is_none()) {
69         pygmo::py_throw(PyExc_NotImplementedError,
70                         ("set_seed() has been invoked but it is not implemented "
71                          "in the user-defined Python algorithm '"
72                          + pygmo::str(m_value) + "' of type '" + pygmo::str(pygmo::type(m_value))
73                          + "': the method is either not present or not callable")
74                             .c_str());
75     }
76     ss(n);
77 }
78 
has_set_seed() const79 bool algo_inner<py::object>::has_set_seed() const
80 {
81     auto ss = pygmo::callable_attribute(m_value, "set_seed");
82     if (ss.is_none()) {
83         return false;
84     }
85     auto hss = pygmo::callable_attribute(m_value, "has_set_seed");
86     if (hss.is_none()) {
87         return true;
88     }
89     return py::cast<bool>(hss());
90 }
91 
get_thread_safety() const92 thread_safety algo_inner<py::object>::get_thread_safety() const
93 {
94     return thread_safety::none;
95 }
96 
get_name() const97 std::string algo_inner<py::object>::get_name() const
98 {
99     return getter_wrapper<std::string>(m_value, "get_name", pygmo::str(pygmo::type(m_value)));
100 }
101 
get_extra_info() const102 std::string algo_inner<py::object>::get_extra_info() const
103 {
104     return getter_wrapper<std::string>(m_value, "get_extra_info", std::string{});
105 }
106 
set_verbosity(unsigned n)107 void algo_inner<py::object>::set_verbosity(unsigned n)
108 {
109     auto sv = pygmo::callable_attribute(m_value, "set_verbosity");
110     if (sv.is_none()) {
111         pygmo::py_throw(PyExc_NotImplementedError,
112                         ("set_verbosity() has been invoked but it is not implemented "
113                          "in the user-defined Python algorithm '"
114                          + pygmo::str(m_value) + "' of type '" + pygmo::str(pygmo::type(m_value))
115                          + "': the method is either not present or not callable")
116                             .c_str());
117     }
118     sv(n);
119 }
120 
has_set_verbosity() const121 bool algo_inner<py::object>::has_set_verbosity() const
122 {
123     auto sv = pygmo::callable_attribute(m_value, "set_verbosity");
124     if (sv.is_none()) {
125         return false;
126     }
127     auto hsv = pygmo::callable_attribute(m_value, "has_set_verbosity");
128     if (hsv.is_none()) {
129         return true;
130     }
131     return py::cast<bool>(hsv());
132 }
133 
get_type_index() const134 std::type_index algo_inner<py::object>::get_type_index() const
135 {
136     return std::type_index(typeid(py::object));
137 }
138 
get_ptr() const139 const void *algo_inner<py::object>::get_ptr() const
140 {
141     return &m_value;
142 }
143 
get_ptr()144 void *algo_inner<py::object>::get_ptr()
145 {
146     return &m_value;
147 }
148 
149 template <typename Archive>
save(Archive & ar,unsigned) const150 void algo_inner<py::object>::save(Archive &ar, unsigned) const
151 {
152     pygmo::inner_class_save<algo_inner_base>(ar, *this);
153 }
154 
155 template <typename Archive>
load(Archive & ar,unsigned)156 void algo_inner<py::object>::load(Archive &ar, unsigned)
157 {
158     pygmo::inner_class_load<algo_inner_base>(ar, *this);
159 }
160 
161 } // namespace detail
162 
163 } // namespace pagmo
164 
165 PAGMO_S11N_ALGORITHM_IMPLEMENT(pybind11::object)
166