Skip to content

Commit a65e1e8

Browse files
ofloveandhatejcarpent
authored andcommitted
source and header for example project
1 parent ca950e1 commit a65e1e8

File tree

2 files changed

+291
-0
lines changed

2 files changed

+291
-0
lines changed
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
2+
3+
#pragma once
4+
5+
#ifndef EXAMPLE_CUSTOM_NUMERIC_TYPE
6+
#define EXAMPLE_CUSTOM_NUMERIC_TYPE
7+
8+
#include <eigenpy/eigenpy.hpp>
9+
#include <eigenpy/user-type.hpp>
10+
#include <eigenpy/ufunc.hpp>
11+
12+
#include <boost/multiprecision/mpc.hpp>
13+
14+
15+
namespace bmp = boost::multiprecision;
16+
using bmp::backends::mpc_complex_backend;
17+
using mpfr_complex = bmp::number<mpc_complex_backend<0>, bmp::et_on >; // T is a variable-precision complex number with expression templates turned on.
18+
19+
20+
21+
void Expose();
22+
23+
24+
25+
26+
27+
28+
// this code derived from
29+
// https://github.com/stack-of-tasks/eigenpy/issues/365
30+
// where I asked about using custom types, and @jcarpent responded with a discussion
31+
// of an application of this in Pinnochio, a library for rigid body dynamics.
32+
namespace eigenpy
33+
{
34+
namespace internal
35+
{
36+
37+
38+
39+
// a template specialization for complex numbers
40+
// derived directly from the example for Pinnochio
41+
template <>
42+
struct getitem<mpfr_complex>
43+
{
44+
static PyObject* run(void* data, void* /* arr */) {
45+
mpfr_complex & mpfr_scalar = *static_cast<mpfr_complex*>(data);
46+
auto & backend = mpfr_scalar.backend();
47+
48+
if(backend.data()[0].re->_mpfr_d == 0) // If the mpfr_scalar is not initialized, we have to init it.
49+
{
50+
mpfr_scalar = mpfr_complex(0);
51+
}
52+
boost::python::object m(boost::ref(mpfr_scalar));
53+
Py_INCREF(m.ptr());
54+
return m.ptr();
55+
}
56+
};
57+
58+
59+
} // namespace internal
60+
61+
62+
63+
64+
65+
66+
// i lifted this from EigenPy and adapted it, basically removing the calls for the comparitors.
67+
template <typename Scalar>
68+
void registerUfunct_without_comparitors(){
69+
const int type_code = Register::getTypeCode<Scalar>();
70+
71+
PyObject *numpy_str;
72+
#if PY_MAJOR_VERSION >= 3
73+
numpy_str = PyUnicode_FromString("numpy");
74+
#else
75+
numpy_str = PyString_FromString("numpy");
76+
#endif
77+
PyObject *numpy;
78+
numpy = PyImport_Import(numpy_str);
79+
Py_DECREF(numpy_str);
80+
81+
import_ufunc();
82+
83+
// Matrix multiply
84+
{
85+
int types[3] = {type_code, type_code, type_code};
86+
87+
std::stringstream ss;
88+
ss << "return result of multiplying two matrices of ";
89+
ss << bp::type_info(typeid(Scalar)).name();
90+
PyUFuncObject *ufunc =
91+
(PyUFuncObject *)PyObject_GetAttrString(numpy, "matmul");
92+
if (!ufunc) {
93+
std::stringstream ss;
94+
ss << "Impossible to define matrix_multiply for given type "
95+
<< bp::type_info(typeid(Scalar)).name() << std::endl;
96+
eigenpy::Exception(ss.str());
97+
}
98+
if (PyUFunc_RegisterLoopForType((PyUFuncObject *)ufunc, type_code,
99+
&internal::gufunc_matrix_multiply<Scalar>,
100+
types, 0) < 0) {
101+
std::stringstream ss;
102+
ss << "Impossible to register matrix_multiply for given type "
103+
<< bp::type_info(typeid(Scalar)).name() << std::endl;
104+
eigenpy::Exception(ss.str());
105+
}
106+
107+
Py_DECREF(ufunc);
108+
}
109+
110+
// Binary operators
111+
EIGENPY_REGISTER_BINARY_UFUNC(add, type_code, Scalar, Scalar, Scalar);
112+
EIGENPY_REGISTER_BINARY_UFUNC(subtract, type_code, Scalar, Scalar, Scalar);
113+
EIGENPY_REGISTER_BINARY_UFUNC(multiply, type_code, Scalar, Scalar, Scalar);
114+
EIGENPY_REGISTER_BINARY_UFUNC(divide, type_code, Scalar, Scalar, Scalar);
115+
116+
// Comparison operators
117+
EIGENPY_REGISTER_BINARY_UFUNC(equal, type_code, Scalar, Scalar, bool);
118+
EIGENPY_REGISTER_BINARY_UFUNC(not_equal, type_code, Scalar, Scalar, bool);
119+
120+
//these are commented out because the comparisons are NOT defined for complex types!!
121+
// EIGENPY_REGISTER_BINARY_UFUNC(greater, type_code, Scalar, Scalar, bool);
122+
// EIGENPY_REGISTER_BINARY_UFUNC(less, type_code, Scalar, Scalar, bool);
123+
// EIGENPY_REGISTER_BINARY_UFUNC(greater_equal, type_code, Scalar, Scalar, bool);
124+
// EIGENPY_REGISTER_BINARY_UFUNC(less_equal, type_code, Scalar, Scalar, bool);
125+
126+
// Unary operators
127+
EIGENPY_REGISTER_UNARY_UFUNC(negative, type_code, Scalar, Scalar);
128+
129+
Py_DECREF(numpy);
130+
}
131+
132+
} // namespace eigenpy
133+
134+
135+
136+
namespace bp = boost::python;
137+
138+
// this derived directly from the code at https://github.com/stack-of-tasks/eigenpy/issues/365, in which this example was requested
139+
template<typename BoostNumber>
140+
struct BoostNumberPythonVisitor
141+
: public boost::python::def_visitor< BoostNumberPythonVisitor<BoostNumber> >
142+
{
143+
144+
public:
145+
146+
template<class PyClass>
147+
void visit(PyClass& cl) const
148+
{
149+
cl
150+
.def(bp::init<>("Default constructor.",bp::arg("self")))
151+
.def(bp::init<BoostNumber>("Copy constructor.",bp::args("self","value")))
152+
.def(bp::init<std::string>("Constructor from a string.",bp::args("self","str_value")))
153+
.def(bp::init<std::string, std::string>("Constructor from a pair of strings.",bp::args("self","real","imag")))
154+
155+
156+
.def(bp::self + bp::self)
157+
.def(bp::self += bp::self)
158+
.def(bp::self - bp::self)
159+
.def(bp::self -= bp::self)
160+
.def(bp::self * bp::self)
161+
.def(bp::self *= bp::self)
162+
.def(bp::self / bp::self)
163+
.def(bp::self /= bp::self)
164+
165+
.def(bp::self == bp::self)
166+
.def(bp::self != bp::self)
167+
.def(bp::self_ns::pow(bp::self_ns::self,long()))
168+
169+
170+
171+
.def("str",&BoostNumber::str,bp::args("self","precision","scientific"))
172+
173+
.def("default_precision",
174+
static_cast<unsigned (*)()>(BoostNumber::default_precision),
175+
"Get the default precision of the class.")
176+
.def("default_precision",
177+
static_cast<void (*)(unsigned)>(BoostNumber::default_precision),bp::arg("digits10"),
178+
"Set the default precision of the class.")
179+
.staticmethod("default_precision")
180+
181+
.def("precision",
182+
static_cast<unsigned (BoostNumber::*)() const>(&BoostNumber::precision),
183+
bp::arg("self"),
184+
"Get the precision of this.")
185+
.def("precision",
186+
static_cast<void (BoostNumber::*)(unsigned)>(&BoostNumber::precision),
187+
bp::args("self","digits10"),
188+
"Set the precision of this.")
189+
190+
191+
.def("__str__",&print,bp::arg("self"))
192+
.def("__repr__",&print,bp::arg("self"))
193+
194+
.def("set_display_precision",&set_display_precision,bp::arg("digit"),
195+
"Set the precision when printing values.")
196+
.staticmethod("set_display_precision")
197+
198+
.def("get_display_precision",&get_display_precision,
199+
"Get the precision when printing values.",
200+
bp::return_value_policy<bp::copy_non_const_reference>())
201+
.staticmethod("get_display_precision")
202+
203+
;
204+
205+
}
206+
207+
static std::string print(const BoostNumber & self)
208+
{
209+
return self.str(get_display_precision(), std::ios_base::dec);
210+
}
211+
212+
static void set_display_precision(const int digit)
213+
{
214+
get_display_precision() = digit;
215+
}
216+
217+
static int & get_display_precision()
218+
{
219+
static int precision = BoostNumber::default_precision();
220+
return precision;
221+
}
222+
};
223+
224+
225+
226+
227+
#endif
228+
229+
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#include "header.hpp"
2+
3+
4+
5+
BOOST_PYTHON_MODULE(eigenpy_example_custom_numeric_type) // this name must match the name of the generated .so file.
6+
{
7+
// see https://stackoverflow.com/questions/6114462/how-to-override-the-automatically-created-docstring-data-for-boostpython
8+
// docstring_options d(true, true, false); // local_
9+
boost::python::docstring_options docopt;
10+
docopt.enable_all();
11+
docopt.disable_cpp_signatures();
12+
13+
boost::python::object package = boost::python::scope();
14+
package.attr("__path__") = "eigenpy_example_custom_numeric_type";
15+
16+
17+
Expose();
18+
}
19+
20+
21+
22+
#define IMPLICITLY_CONVERTIBLE(T1,T2) \
23+
boost::python::implicitly_convertible<T1,T2>();
24+
25+
void Expose(){
26+
27+
eigenpy::enableEigenPy();
28+
29+
boost::python::class_<mpfr_complex>("MpfrComplex",
30+
"",bp::no_init)
31+
.def(BoostNumberPythonVisitor<mpfr_complex>())
32+
;
33+
34+
35+
36+
eigenpy::registerNewType<mpfr_complex>();
37+
eigenpy::registerUfunct_without_comparitors<mpfr_complex>();
38+
39+
40+
eigenpy::registerCast<long,mpfr_complex>(true);
41+
eigenpy::registerCast<int,mpfr_complex>(true);
42+
eigenpy::registerCast<int64_t,mpfr_complex>(true);
43+
44+
45+
46+
IMPLICITLY_CONVERTIBLE(int,mpfr_complex);
47+
IMPLICITLY_CONVERTIBLE(long,mpfr_complex);
48+
IMPLICITLY_CONVERTIBLE(int64_t,mpfr_complex);
49+
50+
using VecX = Eigen::Matrix<mpfr_complex, Eigen::Dynamic, 1>;
51+
using MatXX = Eigen::Matrix<mpfr_complex, Eigen::Dynamic, Eigen::Dynamic>;
52+
53+
54+
55+
56+
eigenpy::enableEigenPySpecific<MatXX>();
57+
eigenpy::enableEigenPySpecific<VecX>();
58+
}
59+
60+
61+
62+
#undef IMPLICITLY_CONVERTIBLE

0 commit comments

Comments
 (0)