Alexandria  2.25.0
SDC-CH common library for the Euclid project
Module.cpp
Go to the documentation of this file.
1 
19 #include "Pyston/ExceptionRaiser.h"
21 #include "Pyston/Graph/Functors.h"
23 #include "Pyston/Helpers.h"
24 #include "Pyston/NodeConverter.h"
26 #include <boost/python.hpp>
27 #include <boost/python/suite/indexing/vector_indexing_suite.hpp>
28 
29 #if BOOST_VERSION < 105600
30 #include <boost/units/detail/utility.hpp>
31 using boost::units::detail::demangle;
32 #else
33 using boost::core::demangle;
34 #endif
35 
36 namespace py = boost::python;
37 
38 #if BOOST_VERSION < 106300
39 namespace boost {
40 namespace python {
41 namespace converter {
42 
43 template <class T>
45  if (!x)
46  return python::detail::none();
47  else if (shared_ptr_deleter* d = std::get_deleter<shared_ptr_deleter>(x))
48  return incref(get_pointer(d->owner));
49  else
50  return converter::registered<std::shared_ptr<T> const&>::converters.to_python(&x);
51 }
52 
53 } // namespace converter
54 } // namespace python
55 } // namespace boost
56 #endif
57 
58 namespace Pyston {
59 
60 // Sadly these have to be global
61 PyObject* pyRecoverableError = nullptr;
62 PyObject* pyUnrecoverableError = nullptr;
63 
64 template <typename T>
65 struct RegisterNode {
66 
67  using NodeType = py::class_<Node<T>, boost::noncopyable>;
68 
74  template <typename To>
75  static void defCastOperations(NodeType& node) {
76  node.def("__add__", makeBinaryFunction<To(To, To)>("+", std::plus<To>()))
77  .def("__sub__", makeBinaryFunction<To(To, To)>("-", std::minus<To>()))
78  .def("__mul__", makeBinaryFunction<To(To, To)>("*", std::multiplies<To>()))
79  .def("__truediv__", makeBinaryFunction<To(To, To)>("/", std::divides<To>()))
80  .def("__radd__", makeBinaryFunction<To(To, To)>("+", std::plus<To>(), true))
81  .def("__rsub__", makeBinaryFunction<To(To, To)>("-", std::minus<To>(), true))
82  .def("__rmul__", makeBinaryFunction<To(To, To)>("x", std::multiplies<To>(), true))
83  .def("__rtruediv__", makeBinaryFunction<To(To, To)>("/", std::divides<To>(), true));
84  }
85 
89  template <typename Y>
90  static void specialized(NodeType& node, void*);
91 
95  template <typename Y>
96  static void specialized(NodeType& node, typename std::enable_if<std::is_floating_point<Y>::value>::type* = nullptr) {
97  node.def("__pow__", makeFunction<T(T, T)>("^", Pow<T>()))
98  .def("__rpow__", makeBinaryFunction<T(T, T)>("^", Pow<T>(), true))
99  .def("__round__", makeFunction<T(T)>("round", Round<T>()))
100  .def("__abs__", makeFunction<T(T)>("abs", Abs<T>()));
101 
102  // Functions
103  // When using numpy methods, numpy will delegate to these
104  // Taken from here, although there are a bunch not implemented:
105  // https://numpy.org/devdocs/reference/ufuncs.html
106  node.def("exp", makeFunction<T(T)>("exp", Exp<T>()))
107  .def("exp2", makeFunction<T(T)>("exp2", Exp2<T>()))
108  .def("log", makeFunction<T(T)>("log", Log<T>()))
109  .def("log2", makeFunction<T(T)>("log2", Log2<T>()))
110  .def("log10", makeFunction<T(T)>("log10", Log10<T>()))
111  .def("sqrt", makeFunction<T(T)>("sqrt", Sqrt<T>()))
112  .def("sin", makeFunction<T(T)>("sin", Sin<T>()))
113  .def("cos", makeFunction<T(T)>("cos", Cos<T>()))
114  .def("tan", makeFunction<T(T)>("tan", Tan<T>()))
115  .def("arcsin", makeFunction<T(T)>("arcsin", ArcSin<T>()))
116  .def("arccos", makeFunction<T(T)>("arccos", ArcCos<T>()))
117  .def("arctan", makeFunction<T(T)>("arctan", ArcTan<T>()))
118  .def("arctan2", makeBinaryFunction<T(T, T)>("arctan2", ArcTan2<T>()))
119  .def("sinh", makeFunction<T(T)>("sinh", Sinh<T>()))
120  .def("cosh", makeFunction<T(T)>("cosh", Cosh<T>()))
121  .def("tanh", makeFunction<T(T)>("tanh", Tanh<T>()))
122  .def("arcsinh", makeFunction<T(T)>("arcsinh", ArcSinh<T>()))
123  .def("arccosh", makeFunction<T(T)>("arccosh", ArcCosh<T>()))
124  .def("arctanh", makeFunction<T(T)>("arctanh", ArcTanh<T>()))
125  .def("fmod", makeFunction<T(T, T)>("fmod", Fmod<T>()));
126  }
127 
131  template <typename Y>
132  static void specialized(NodeType& node, typename std::enable_if<std::is_same<Y, bool>::value>::type* = nullptr) {
133  // Upcast to double and int
134  defCastOperations<double>(node);
135  defCastOperations<int64_t>(node);
136  }
137 
141  template <typename Y>
142  static void
145  node.def("__abs__", makeFunction<T(T)>("abs", Abs<T>()));
146  // Upcast to double
147  defCastOperations<double>(node);
148  node.def("__pow__", makeBinaryFunction<double(double, double)>("^", Pow<double>()))
149  .def("__rpow__", makeBinaryFunction<double(double, double)>("^", Pow<double>(), true));
150  }
151 
152  static void general(NodeType& node) {
153  // https://docs.python.org/3/reference/datamodel.html#basic-customization
154  node.def("__lt__", makeFunction<bool(T, T)>("<", std::less<T>()))
155  .def("__le__", makeFunction<bool(T, T)>("<=", std::less_equal<T>()))
156  .def("__eq__", makeFunction<bool(T, T)>("==", std::equal_to<T>()))
157  .def("__ne__", makeFunction<bool(T, T)>("!=", std::not_equal_to<T>()))
158  .def("__gt__", makeFunction<bool(T, T)>(">", std::greater<T>()))
159  .def("__ge__", makeFunction<bool(T, T)>(">=", std::greater_equal<T>()));
160 
161  // https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types
162  node.def("__add__", makeFunction<T(T, T)>("+", std::plus<T>()))
163  .def("__sub__", makeFunction<T(T, T)>("-", std::minus<T>()))
164  .def("__mul__", makeFunction<T(T, T)>("*", std::multiplies<T>()))
165  .def("__truediv__", makeFunction<T(T, T)>("/", std::divides<T>()))
166  .def("__div__", makeFunction<T(T, T)>("/", std::divides<T>()))
167  .def("__radd__", makeBinaryFunction<T(T, T)>("+", std::plus<T>(), true))
168  .def("__rsub__", makeBinaryFunction<T(T, T)>("-", std::minus<T>(), true))
169  .def("__rmul__", makeBinaryFunction<T(T, T)>("*", std::multiplies<T>(), true))
170  .def("__rtruediv__", makeBinaryFunction<T(T, T)>("/", std::divides<T>(), true))
171  .def("__rdiv__", makeBinaryFunction<T(T, T)>("/", std::divides<T>(), true))
172  .def("__neg__", makeFunction<T(T)>("-", std::negate<T>()))
173  .def("__pos__", makeFunction<T(T)>("+", (Identity<T>())));
174 
175  // Can not be used in conditionals!
176 #if PY_MAJOR_VERSION >= 3
177 #define AS_BOOL_METHOD "__bool__"
178 #else
179 #define AS_BOOL_METHOD "__nonzero__"
180 #endif
181  node.def(AS_BOOL_METHOD,
182  py::make_function(ExceptionRaiser<T>("Can not use variable placeholders in conditionals", true),
183  py::default_call_policies(),
184  boost::mpl::vector<void, const std::shared_ptr<Node<T>>>()));
185  }
186 
187  static void Do() {
188  auto node_name = std::string("Node<") + demangle(typeid(T).name()) + ">";
189  NodeType node(node_name.c_str(), "AST Node", py::no_init);
190 
191  // Operators and method applicable to all types
192  general(node);
193 
194  // Operators and method applicable only to a given type
195  // i.e. __floordiv__ is not applicable to float
196  specialized<T>(node);
197 
198  // Register convertion between shared pointer and Node
199  py::register_ptr_to_python<std::shared_ptr<Node<T>>>();
200 #if BOOST_VERSION < 106300
201  py::implicitly_convertible<std::shared_ptr<Placeholder<T>>, std::shared_ptr<Node<T>>>();
202 #endif
203  // Custom conversion so primitive values can be converted to a node
204  py::converter::registry::push_back(&NodeConverter<T>::isConvertible, &NodeConverter<T>::construct,
205  py::type_id<std::shared_ptr<Node<T>>>());
206 
207  // Triggers the building of a tree
208  auto placeholder_name = std::string("Placeholder<") + demangle(typeid(T).name()) + ">";
209  py::class_<Placeholder<T>, py::bases<Node<T>>>(placeholder_name.c_str(), "Variable placeholder", py::no_init);
210  py::register_ptr_to_python<std::shared_ptr<Placeholder<T>>>();
211  }
212 };
213 
217 struct VariantToPython : public boost::static_visitor<boost::python::object> {
218  template <typename Content>
219  py::object operator()(Content c) const {
220  return py::object(c);
221  }
222 };
223 
224 py::object attributeSetGetter(const AttributeSet& attr_set, const std::string& name) {
225  auto vi = attr_set.find(name);
226  if (vi == attr_set.end()) {
227  throw UnrecoverableError("AttributeSet has no attribute '" + name + "'");
228  }
229  return boost::apply_visitor(VariantToPython(), vi->second);
230 }
231 
236  py::class_<Placeholder<AttributeSet>, boost::noncopyable> node("AttributeSet", "AST Node", py::no_init);
237 
238  // Register convertion between shared pointer and Node
239  py::register_ptr_to_python<std::shared_ptr<Placeholder<AttributeSet>>>();
240 
241  // Register __getattr__
242  node.def("__getattr__", &Placeholder<AttributeSet>::get);
243 
244  // Register AttributeSet so it can be used directly from Python for
245  // non-compiled expressions
246  py::class_<AttributeSet> attr_set("AttributeSet", "Attribute set", py::no_init);
247  attr_set.def("__getattr__", &attributeSetGetter);
248 }
249 
256 static PyObject* createExceptionClass(const std::string& name) {
257  std::string scope = py::extract<std::string>(py::scope().attr("__name__"));
258  std::string qname = scope + "." + name;
259  PyObject* excType = PyErr_NewException(const_cast<char*>(qname.c_str()), PyExc_RuntimeError, nullptr);
260  if (!excType)
261  py::throw_error_already_set();
262  py::scope().attr(name.c_str()) = py::handle<>(py::borrowed(excType));
263  return excType;
264 }
265 
270  PyErr_SetString(pyRecoverableError, e.what());
271 }
272 
277  PyErr_SetString(pyUnrecoverableError, e.what());
278 }
279 
284 
285  // AttributeSet is a bit special
287 
288  // Vector types
289  py::class_<std::vector<double>>("_DoubleVector").def(py::vector_indexing_suite<std::vector<double>>());
290  py::class_<std::vector<int64_t>>("_IntVector").def(py::vector_indexing_suite<std::vector<int64_t>>());
291 
292  // Exceptions
293  pyRecoverableError = createExceptionClass("RecoverableError");
294  pyUnrecoverableError = createExceptionClass("UnrecoverableError");
295 
296  // Exception translation
297  py::register_exception_translator<RecoverableError>(&translateRecoverable);
298  py::register_exception_translator<UnrecoverableError>(&translateUnrecoverable);
299 }
300 
301 } // end of namespace Pyston
#define AS_BOOL_METHOD
T c_str(T... args)
T end(T... args)
T find(T... args)
@ python
constexpr double e
static PyObject * createExceptionClass(const std::string &name)
Definition: Module.cpp:256
PyObject * pyRecoverableError
Definition: Module.cpp:61
py::object attributeSetGetter(const AttributeSet &attr_set, const std::string &name)
Definition: Module.cpp:224
static boost::python::object makeFunction(const std::string &repr, std::function< Signature > functor)
Definition: Helpers.h:104
void translateRecoverable(const RecoverableError &e)
Definition: Module.cpp:269
void translateUnrecoverable(const UnrecoverableError &e)
Definition: Module.cpp:276
static boost::python::object makeBinaryFunction(const std::string &repr, std::function< Signature > functor, bool reversed=false)
Definition: Helpers.h:124
PyObject * pyUnrecoverableError
Definition: Module.cpp:62
BOOST_PYTHON_MODULE(pyston)
Definition: Module.cpp:280
void RegisterAttributeSet()
Definition: Module.cpp:235
PyObject * shared_ptr_to_python(std::shared_ptr< T > const &x)
Definition: Module.cpp:44
Definition: array.h:33
static void specialized(NodeType &node, typename std::enable_if< std::is_floating_point< Y >::value >::type *=nullptr)
Definition: Module.cpp:96
py::class_< Node< T >, boost::noncopyable > NodeType
Definition: Module.cpp:67
static void Do()
Definition: Module.cpp:187
static void specialized(NodeType &node, void *)
static void general(NodeType &node)
Definition: Module.cpp:152
static void defCastOperations(NodeType &node)
Definition: Module.cpp:75
static void specialized(NodeType &node, typename std::enable_if< std::is_integral< Y >::value &&!std::is_same< Y, bool >::value >::type *=nullptr)
Definition: Module.cpp:143
static void specialized(NodeType &node, typename std::enable_if< std::is_same< Y, bool >::value >::type *=nullptr)
Definition: Module.cpp:132
py::object operator()(Content c) const
Definition: Module.cpp:219