1 /* Copyright 2009-2016 Francesco Biscani (bluescarni@gmail.com)
2
3 This file is part of the Piranha library.
4
5 The Piranha 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 Piranha 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 Piranha library. If not,
27 see https://www.gnu.org/licenses/. */
28
29 #include "python_includes.hpp"
30
31 #include <boost/numeric/conversion/cast.hpp>
32 #include <boost/python/class.hpp>
33 #include <boost/python/def.hpp>
34 #include <boost/python/docstring_options.hpp>
35 #include <boost/python/enum.hpp>
36 #include <boost/python/errors.hpp>
37 #include <boost/python/extract.hpp>
38 #include <boost/python/handle.hpp>
39 #include <boost/python/init.hpp>
40 #include <boost/python/module.hpp>
41 #include <boost/python/object.hpp>
42 #include <boost/python/scope.hpp>
43 #include <boost/python/stl_iterator.hpp>
44 #include <cstdint>
45 #include <mutex>
46 #include <stdexcept>
47 #include <string>
48 #include <type_traits>
49
50 #include "../src/binomial.hpp"
51 #include "../src/config.hpp"
52 #include "../src/divisor.hpp"
53 #include "../src/divisor_series.hpp"
54 #include "../src/exceptions.hpp"
55 #include "../src/init.hpp"
56 #include "../src/invert.hpp"
57 #include "../src/kronecker_monomial.hpp"
58 #include "../src/math.hpp"
59 #include "../src/monomial.hpp"
60 #include "../src/mp_integer.hpp"
61 #include "../src/mp_rational.hpp"
62 #include "../src/poisson_series.hpp"
63 #include "../src/polynomial.hpp"
64 #include "../src/real.hpp"
65 #include "../src/s11n.hpp"
66 #include "../src/safe_cast.hpp"
67 #include "../src/thread_pool.hpp"
68 #include "../src/type_traits.hpp"
69 #include "exceptions.hpp"
70 #include "expose_divisor_series.hpp"
71 #include "expose_poisson_series.hpp"
72 #include "expose_polynomials.hpp"
73 #include "expose_utils.hpp"
74 #include "python_converters.hpp"
75 #include "type_system.hpp"
76 #include "utils.hpp"
77
78 namespace bp = boost::python;
79
80 static std::mutex global_mutex;
81 static bool inited = false;
82
83 namespace pyranha
84 {
85
86 PYRANHA_DECLARE_T_NAME(piranha::monomial)
87 PYRANHA_DECLARE_T_NAME(piranha::divisor)
88 }
89
90 // A couple of utils to test exception translation.
91 template <typename Exc, piranha::enable_if_t<std::is_constructible<Exc, std::string>::value, int> = 0>
test_exception()92 static inline void test_exception()
93 {
94 piranha_throw(Exc, "hello world");
95 }
96
97 template <typename Exc, piranha::enable_if_t<!std::is_constructible<Exc, std::string>::value, int> = 0>
test_exception()98 static inline void test_exception()
99 {
100 piranha_throw(Exc, );
101 }
102
103 // Small helper to retrieve the argument error exception from python.
generate_argument_error(int)104 static inline void generate_argument_error(int)
105 {
106 }
107
BOOST_PYTHON_MODULE(_core)108 BOOST_PYTHON_MODULE(_core)
109 {
110 // NOTE: this is a single big lock to avoid registering types/conversions multiple times and prevent contention
111 // if the module is loaded from multiple threads.
112 std::lock_guard<std::mutex> lock(global_mutex);
113 if (inited) {
114 return;
115 }
116 // Set the inited flag.
117 // NOTE: we do it here because if something goes wrong in the rest of the init, we do not have a way
118 // to roll back the initialisation, and if the user tries again the init probably a lot of things would
119 // go haywire. Like this, we will not re-run any init code in a successive attempt at loading the module.
120 inited = true;
121 // Piranha init.
122 piranha::init();
123 // Docstring options setup.
124 bp::docstring_options doc_options(false, false, false);
125 // Type generator class.
126 bp::class_<pyranha::type_generator> tg_class("_type_generator", bp::no_init);
127 tg_class.def("__call__", &pyranha::type_generator::operator());
128 tg_class.def("__repr__", &pyranha::type_generator::repr);
129 // Type generator template class.
130 bp::class_<pyranha::type_generator_template> tgt_class("_type_generator_template", bp::no_init);
131 tgt_class.def("__getitem__", &pyranha::type_generator_template::getitem_o);
132 tgt_class.def("__getitem__", &pyranha::type_generator_template::getitem_t);
133 tgt_class.def("__repr__", &pyranha::type_generator_template::repr);
134 // Create the types submodule.
135 std::string types_module_name = bp::extract<std::string>(bp::scope().attr("__name__") + ".types");
136 // NOTE: the nested namespace is created if not there, otherwise it will be returned.
137 ::PyObject *types_module_ptr = ::PyImport_AddModule(types_module_name.c_str());
138 if (!types_module_ptr) {
139 ::PyErr_SetString(PyExc_RuntimeError, "error while creating the 'types' submodule");
140 bp::throw_error_already_set();
141 }
142 // NOTE: I think at one point in the past there was a typo in the Python3 C API documentation
143 // which hinted at a difference in behaviour for PyImport_AddModule between 2 and 3 (new reference
144 // vs borrowed reference). Current documentation states that the reference is always
145 // borrowed, both in Python 2 and 3.
146 auto types_module = bp::object(bp::handle<>(bp::borrowed(types_module_ptr)));
147 bp::scope().attr("types") = types_module;
148 // Expose concrete instances of type generators.
149 pyranha::instantiate_type_generator<std::int_least16_t>("int16", types_module);
150 pyranha::instantiate_type_generator<double>("double", types_module);
151 pyranha::instantiate_type_generator<piranha::integer>("integer", types_module);
152 pyranha::instantiate_type_generator<piranha::rational>("rational", types_module);
153 pyranha::instantiate_type_generator<piranha::real>("real", types_module);
154 pyranha::instantiate_type_generator<piranha::k_monomial>("k_monomial", types_module);
155 // Register template instances of monomial, and instantiate the type generator template.
156 pyranha::instantiate_type_generator_template<piranha::monomial>("monomial", types_module);
157 pyranha::register_template_instance<piranha::monomial, piranha::rational>();
158 pyranha::register_template_instance<piranha::monomial, std::int_least16_t>();
159 // Same for divisor.
160 pyranha::instantiate_type_generator_template<piranha::divisor>("divisor", types_module);
161 pyranha::register_template_instance<piranha::divisor, std::int_least16_t>();
162 // Arithmetic converters.
163 pyranha::integer_converter i_c;
164 pyranha::rational_converter ra_c;
165 pyranha::real_converter re_c;
166 // Exceptions translation.
167 pyranha::generic_translate<&PyExc_ZeroDivisionError, piranha::zero_division_error>();
168 pyranha::generic_translate<&PyExc_NotImplementedError, piranha::not_implemented_error>();
169 pyranha::generic_translate<&PyExc_OverflowError, std::overflow_error>();
170 pyranha::generic_translate<&PyExc_OverflowError, boost::numeric::positive_overflow>();
171 pyranha::generic_translate<&PyExc_OverflowError, boost::numeric::negative_overflow>();
172 pyranha::generic_translate<&PyExc_OverflowError, boost::numeric::bad_numeric_cast>();
173 pyranha::generic_translate<&PyExc_ArithmeticError, piranha::math::inexact_division>();
174 pyranha::generic_translate<&PyExc_ValueError, piranha::safe_cast_failure>();
175 #if defined(PIRANHA_WITH_MSGPACK)
176 pyranha::generic_translate<&PyExc_TypeError, msgpack::type_error>();
177 #endif
178 // Exposed types list.
179 bp::def("_get_exposed_types_list", pyranha::get_exposed_types_list);
180 // The s11n enums.
181 bp::enum_<piranha::data_format>("data_format")
182 .value("boost_binary", piranha::data_format::boost_binary)
183 .value("boost_portable", piranha::data_format::boost_portable)
184 .value("msgpack_binary", piranha::data_format::msgpack_binary)
185 .value("msgpack_portable", piranha::data_format::msgpack_portable);
186 bp::enum_<piranha::compression>("compression")
187 .value("none", piranha::compression::none)
188 .value("zlib", piranha::compression::zlib)
189 .value("gzip", piranha::compression::gzip)
190 .value("bzip2", piranha::compression::bzip2);
191 // Expose polynomials.
192 pyranha::instantiate_type_generator_template<piranha::polynomial>("polynomial", types_module);
193 pyranha::expose_polynomials_0();
194 pyranha::expose_polynomials_1();
195 pyranha::expose_polynomials_2();
196 pyranha::expose_polynomials_3();
197 pyranha::expose_polynomials_4();
198 pyranha::expose_polynomials_5();
199 pyranha::expose_polynomials_6();
200 pyranha::expose_polynomials_7();
201 pyranha::expose_polynomials_8();
202 pyranha::expose_polynomials_9();
203 pyranha::expose_polynomials_10();
204 // Expose Poisson series.
205 pyranha::instantiate_type_generator_template<piranha::poisson_series>("poisson_series", types_module);
206 pyranha::expose_poisson_series_0();
207 pyranha::expose_poisson_series_1();
208 pyranha::expose_poisson_series_2();
209 pyranha::expose_poisson_series_3();
210 pyranha::expose_poisson_series_4();
211 pyranha::expose_poisson_series_5();
212 pyranha::expose_poisson_series_6();
213 pyranha::expose_poisson_series_7();
214 pyranha::expose_poisson_series_8();
215 pyranha::expose_poisson_series_9();
216 pyranha::expose_poisson_series_10();
217 pyranha::expose_poisson_series_11();
218 // Expose divisor series.
219 pyranha::instantiate_type_generator_template<piranha::divisor_series>("divisor_series", types_module);
220 pyranha::expose_divisor_series_0();
221 pyranha::expose_divisor_series_1();
222 pyranha::expose_divisor_series_2();
223 pyranha::expose_divisor_series_3();
224 pyranha::expose_divisor_series_4();
225 pyranha::expose_divisor_series_5();
226 // Expose the settings class.
227 bp::class_<piranha::settings> settings_class("_settings", bp::init<>());
228 settings_class.def("_get_max_term_output", piranha::settings::get_max_term_output)
229 .staticmethod("_get_max_term_output");
230 settings_class.def("_set_max_term_output", piranha::settings::set_max_term_output)
231 .staticmethod("_set_max_term_output");
232 settings_class.def("_reset_max_term_output", piranha::settings::reset_max_term_output)
233 .staticmethod("_reset_max_term_output");
234 settings_class.def("_set_n_threads", piranha::settings::set_n_threads).staticmethod("_set_n_threads");
235 settings_class.def("_get_n_threads", piranha::settings::get_n_threads).staticmethod("_get_n_threads");
236 settings_class.def("_reset_n_threads", piranha::settings::reset_n_threads).staticmethod("_reset_n_threads");
237 settings_class.def("_set_min_work_per_thread", piranha::settings::set_min_work_per_thread)
238 .staticmethod("_set_min_work_per_thread");
239 settings_class.def("_get_min_work_per_thread", piranha::settings::get_min_work_per_thread)
240 .staticmethod("_get_min_work_per_thread");
241 settings_class.def("_reset_min_work_per_thread", piranha::settings::reset_min_work_per_thread)
242 .staticmethod("_reset_min_work_per_thread");
243 settings_class.def("_set_thread_binding", piranha::settings::set_thread_binding)
244 .staticmethod("_set_thread_binding");
245 settings_class.def("_get_thread_binding", piranha::settings::get_thread_binding)
246 .staticmethod("_get_thread_binding");
247 // Factorial.
248 bp::def("_factorial", &piranha::math::factorial<0>);
249 // Binomial coefficient.
250 #define PYRANHA_EXPOSE_BINOMIAL(top, bot) bp::def("_binomial", &piranha::math::binomial<top, bot>)
251 PYRANHA_EXPOSE_BINOMIAL(double, double);
252 PYRANHA_EXPOSE_BINOMIAL(double, piranha::integer);
253 PYRANHA_EXPOSE_BINOMIAL(double, piranha::rational);
254 PYRANHA_EXPOSE_BINOMIAL(double, piranha::real);
255 PYRANHA_EXPOSE_BINOMIAL(piranha::integer, double);
256 PYRANHA_EXPOSE_BINOMIAL(piranha::integer, piranha::integer);
257 PYRANHA_EXPOSE_BINOMIAL(piranha::integer, piranha::rational);
258 PYRANHA_EXPOSE_BINOMIAL(piranha::integer, piranha::real);
259 PYRANHA_EXPOSE_BINOMIAL(piranha::rational, double);
260 PYRANHA_EXPOSE_BINOMIAL(piranha::rational, piranha::integer);
261 PYRANHA_EXPOSE_BINOMIAL(piranha::rational, piranha::rational);
262 PYRANHA_EXPOSE_BINOMIAL(piranha::rational, piranha::real);
263 PYRANHA_EXPOSE_BINOMIAL(piranha::real, double);
264 PYRANHA_EXPOSE_BINOMIAL(piranha::real, piranha::integer);
265 PYRANHA_EXPOSE_BINOMIAL(piranha::real, piranha::rational);
266 PYRANHA_EXPOSE_BINOMIAL(piranha::real, piranha::real);
267 #undef PYRANHA_EXPOSE_BINOMIAL
268 // Sine and cosine.
269 #define PYRANHA_EXPOSE_SIN_COS(arg) \
270 bp::def("_sin", &piranha::math::sin<arg>); \
271 bp::def("_cos", &piranha::math::cos<arg>)
272 PYRANHA_EXPOSE_SIN_COS(double);
273 PYRANHA_EXPOSE_SIN_COS(piranha::integer);
274 PYRANHA_EXPOSE_SIN_COS(piranha::rational);
275 PYRANHA_EXPOSE_SIN_COS(piranha::real);
276 #undef PYRANHA_EXPOSE_SIN_COS
277 #define PYRANHA_EXPOSE_INVERT(arg) bp::def("_invert", &piranha::math::invert<arg>)
278 PYRANHA_EXPOSE_INVERT(double);
279 PYRANHA_EXPOSE_INVERT(piranha::integer);
280 PYRANHA_EXPOSE_INVERT(piranha::rational);
281 PYRANHA_EXPOSE_INVERT(piranha::real);
282 #undef PYRANHA_EXPOSE_INVERT
283 // GCD.
284 bp::def("_gcd", &piranha::math::gcd<piranha::integer, piranha::integer>);
285 // Tests for exception translation.
286 bp::def("_test_safe_cast_failure", &test_exception<piranha::safe_cast_failure>);
287 bp::def("_test_zero_division_error", &test_exception<piranha::zero_division_error>);
288 bp::def("_test_not_implemented_error", &test_exception<piranha::not_implemented_error>);
289 bp::def("_test_overflow_error", &test_exception<std::overflow_error>);
290 bp::def("_test_bn_poverflow_error", &test_exception<boost::numeric::positive_overflow>);
291 bp::def("_test_bn_noverflow_error", &test_exception<boost::numeric::negative_overflow>);
292 bp::def("_test_bn_bnc", &test_exception<boost::numeric::bad_numeric_cast>);
293 bp::def("_test_inexact_division", &test_exception<piranha::math::inexact_division>);
294 // Helper to generate an argument error.
295 bp::def("_generate_argument_error", &generate_argument_error);
296
297 // Define a cleanup functor to be run when the module is unloaded.
298 struct cleanup_functor {
299 void operator()() const
300 {
301 // First we clean up the custom derivatives.
302 auto e_types = pyranha::get_exposed_types_list();
303 bp::stl_input_iterator<bp::object> end_it;
304 for (bp::stl_input_iterator<bp::object> it(e_types); it != end_it; ++it) {
305 if (pyranha::hasattr(*it, "unregister_all_custom_derivatives")) {
306 it->attr("unregister_all_custom_derivatives")();
307 }
308 }
309 pyranha::builtin().attr("print")("Custom derivatives cleanup completed.");
310 // Next we clean up the pow caches.
311 for (bp::stl_input_iterator<bp::object> it(e_types); it != end_it; ++it) {
312 if (pyranha::hasattr(*it, "clear_pow_cache")) {
313 it->attr("clear_pow_cache")();
314 }
315 }
316 pyranha::builtin().attr("print")("Pow caches cleanup completed.");
317 // Clean up the pyranha type system.
318 pyranha::et_map.clear();
319 pyranha::builtin().attr("print")("Pyranha's type system cleanup completed.");
320 // Finally, shut down the thread pool.
321 // NOTE: this is necessary in Windows/MinGW currently, otherwise the python
322 // interpreter hangs on exit (possibly due to either some implementation-defined
323 // static order destruction fiasco, or maybe a threading bug in MinGW).
324 std::cout << "Shutting down the thread pool.\n";
325 piranha::thread_pool_shutdown<void>();
326 }
327 };
328 // Expose it.
329 bp::class_<cleanup_functor> cl_c("_cleanup_functor", bp::init<>());
330 cl_c.def("__call__", &cleanup_functor::operator());
331 // Register it.
332 bp::object atexit_mod = bp::import("atexit");
333 atexit_mod.attr("register")(cleanup_functor{});
334 }
335