Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • jcarpent/eigenpy
  • gsaurel/eigenpy
  • stack-of-tasks/eigenpy
3 results
Show changes
Showing
with 1348 additions and 270 deletions
/*
* Copyright 2018-2020 INRIA
* Copyright 2018-2023 INRIA
*/
#ifndef __eigenpy_numpy_type_hpp__
......@@ -17,17 +17,54 @@ namespace eigenpy {
template <typename Scalar>
bool np_type_is_convertible_into_scalar(const int np_type) {
if (static_cast<NPY_TYPES>(NumpyEquivalentType<Scalar>::type_code) >=
NPY_USERDEF)
const auto scalar_np_code =
static_cast<NPY_TYPES>(NumpyEquivalentType<Scalar>::type_code);
if (scalar_np_code >= NPY_USERDEF)
return np_type == Register::getTypeCode<Scalar>();
if (NumpyEquivalentType<Scalar>::type_code == np_type) return true;
if (scalar_np_code == np_type) return true;
// Manage type promotion
switch (np_type) {
case NPY_BOOL:
return FromTypeToType<bool, Scalar>::value;
case NPY_INT8:
return FromTypeToType<int8_t, Scalar>::value;
case NPY_INT16:
return FromTypeToType<int16_t, Scalar>::value;
case NPY_INT32:
return FromTypeToType<int32_t, Scalar>::value;
case NPY_INT64:
return FromTypeToType<int64_t, Scalar>::value;
case NPY_UINT8:
return FromTypeToType<uint8_t, Scalar>::value;
case NPY_UINT16:
return FromTypeToType<uint16_t, Scalar>::value;
case NPY_UINT32:
return FromTypeToType<uint32_t, Scalar>::value;
case NPY_UINT64:
return FromTypeToType<uint64_t, Scalar>::value;
#if defined _WIN32 || defined __CYGWIN__
// Manage NPY_INT on Windows (NPY_INT32 is NPY_LONG).
// See https://github.com/stack-of-tasks/eigenpy/pull/455
case NPY_INT:
return FromTypeToType<int, Scalar>::value;
case NPY_LONG:
return FromTypeToType<long, Scalar>::value;
return FromTypeToType<int32_t, Scalar>::value;
case NPY_UINT:
return FromTypeToType<uint32_t, Scalar>::value;
#endif // WIN32
#if defined __APPLE__
// Manage NPY_LONGLONG on Mac (NPY_INT64 is NPY_LONG)..
// long long and long are both the same type
// but NPY_LONGLONG and NPY_LONG are different dtype.
// See https://github.com/stack-of-tasks/eigenpy/pull/455
case NPY_LONGLONG:
return FromTypeToType<int64_t, Scalar>::value;
case NPY_ULONGLONG:
return FromTypeToType<uint64_t, Scalar>::value;
#endif // MAC
case NPY_FLOAT:
return FromTypeToType<float, Scalar>::value;
case NPY_CFLOAT:
......@@ -45,54 +82,28 @@ bool np_type_is_convertible_into_scalar(const int np_type) {
}
}
enum NP_TYPE { MATRIX_TYPE, ARRAY_TYPE };
struct EIGENPY_DLLAPI NumpyType {
static NumpyType& getInstance();
operator bp::object() { return getInstance().CurrentNumpyType; }
static bp::object make(PyArrayObject* pyArray, bool copy = false);
static bp::object make(PyObject* pyObj, bool copy = false);
static void setNumpyType(bp::object& obj);
static void sharedMemory(const bool value);
static bool sharedMemory();
static void switchToNumpyArray();
static void switchToNumpyMatrix();
static NP_TYPE& getType();
static bp::object getNumpyType();
static const PyTypeObject* getNumpyMatrixType();
static const PyTypeObject* getNumpyArrayType();
static bool isMatrix();
static bool isArray();
protected:
NumpyType();
bp::object CurrentNumpyType;
bp::object pyModule;
// Numpy types
bp::object NumpyMatrixObject;
PyTypeObject* NumpyMatrixType;
// bp::object NumpyAsMatrixObject; PyTypeObject * NumpyAsMatrixType;
bp::object NumpyArrayObject;
PyTypeObject* NumpyArrayType;
NP_TYPE np_type;
bool shared_memory;
};
} // namespace eigenpy
......
/*
* Copyright 2020-2022 INRIA
* Copyright 2020-2024 INRIA
*/
#ifndef __eigenpy_numpy_hpp__
#define __eigenpy_numpy_hpp__
#include "eigenpy/fwd.hpp"
#include "eigenpy/config.hpp"
#ifndef PY_ARRAY_UNIQUE_SYMBOL
#define PY_ARRAY_UNIQUE_SYMBOL EIGENPY_ARRAY_API
#endif
// For compatibility with Numpy 2.x. See:
// https://numpy.org/devdocs/reference/c-api/array.html#c.NPY_API_SYMBOL_ATTRIBUTE
#define NPY_API_SYMBOL_ATTRIBUTE EIGENPY_DLLAPI
// When building with MSVC, Python headers use some pragma operator to link
// against the Python DLL.
// Unfortunately, it can link against the wrong build type of the library
// leading to some linking issue.
// Boost::Python provides a helper specifically dedicated to selecting the right
// Python library depending on build type, so let's make use of it.
// Numpy headers drags Python with them. As a result, it
// is necessary to include this helper before including Numpy.
// See: https://github.com/stack-of-tasks/eigenpy/pull/514
#include <boost/python/detail/wrap_python.hpp>
#include <numpy/numpyconfig.h>
#ifdef NPY_1_8_API_VERSION
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#endif
// Allow compiling against NumPy 1.x and 2.x. See:
// https://github.com/numpy/numpy/blob/afea8fd66f6bdbde855f5aff0b4e73eb0213c646/doc/source/reference/c-api/array.rst#L1224
#if NPY_ABI_VERSION < 0x02000000
#define PyArray_DescrProto PyArray_Descr
#endif
#include <numpy/ndarrayobject.h>
#include <numpy/ufuncobject.h>
#if NPY_ABI_VERSION < 0x02000000
static inline PyArray_ArrFuncs* PyDataType_GetArrFuncs(PyArray_Descr* descr) {
return descr->f;
}
#endif
/* PEP 674 disallow using macros as l-values
see : https://peps.python.org/pep-0674/
*/
#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE)
static inline void _Py_SET_TYPE(PyObject* o, PyTypeObject* type) {
Py_TYPE(o) = type;
}
#define Py_SET_TYPE(o, type) _Py_SET_TYPE((PyObject*)(o), type)
#endif
#if defined _WIN32 || defined __CYGWIN__
#define EIGENPY_GET_PY_ARRAY_TYPE(array) \
call_PyArray_MinScalarType(array)->type_num
......@@ -26,68 +63,140 @@
#define EIGENPY_GET_PY_ARRAY_TYPE(array) PyArray_MinScalarType(array)->type_num
#endif
#include <complex>
namespace eigenpy {
void EIGENPY_DLLAPI import_numpy();
int EIGENPY_DLLAPI PyArray_TypeNum(PyTypeObject* type);
// By default, the Scalar is considered as a Python object
template <typename Scalar>
template <typename Scalar, typename Enable = void>
struct NumpyEquivalentType {
enum { type_code = NPY_USERDEF };
};
template <>
struct NumpyEquivalentType<float> {
enum { type_code = NPY_FLOAT };
struct NumpyEquivalentType<bool> {
enum { type_code = NPY_BOOL };
};
template <>
struct NumpyEquivalentType<std::complex<float> > {
enum { type_code = NPY_CFLOAT };
struct NumpyEquivalentType<char> {
enum { type_code = NPY_INT8 };
};
template <>
struct NumpyEquivalentType<double> {
enum { type_code = NPY_DOUBLE };
struct NumpyEquivalentType<unsigned char> {
enum { type_code = NPY_UINT8 };
};
template <>
struct NumpyEquivalentType<std::complex<double> > {
enum { type_code = NPY_CDOUBLE };
struct NumpyEquivalentType<int8_t> {
enum { type_code = NPY_INT8 };
};
template <>
struct NumpyEquivalentType<long double> {
enum { type_code = NPY_LONGDOUBLE };
struct NumpyEquivalentType<int16_t> {
enum { type_code = NPY_INT16 };
};
template <>
struct NumpyEquivalentType<std::complex<long double> > {
enum { type_code = NPY_CLONGDOUBLE };
struct NumpyEquivalentType<uint16_t> {
enum { type_code = NPY_UINT16 };
};
template <>
struct NumpyEquivalentType<bool> {
enum { type_code = NPY_BOOL };
struct NumpyEquivalentType<int32_t> {
enum { type_code = NPY_INT32 };
};
template <>
struct NumpyEquivalentType<uint32_t> {
enum { type_code = NPY_UINT32 };
};
// On Windows, long is a 32 bytes type but it's a different type than int
// See https://github.com/stack-of-tasks/eigenpy/pull/455
#if defined _WIN32 || defined __CYGWIN__
template <>
struct NumpyEquivalentType<long> {
enum { type_code = NPY_INT32 };
};
template <>
struct NumpyEquivalentType<unsigned long> {
enum { type_code = NPY_UINT32 };
};
#endif // WIN32
template <>
struct NumpyEquivalentType<int> {
enum { type_code = NPY_INT };
struct NumpyEquivalentType<int64_t> {
enum { type_code = NPY_INT64 };
};
template <>
struct NumpyEquivalentType<unsigned int> {
enum { type_code = NPY_UINT };
struct NumpyEquivalentType<uint64_t> {
enum { type_code = NPY_UINT64 };
};
// On Mac, long is a 64 bytes type but it's a different type than int64_t
// See https://github.com/stack-of-tasks/eigenpy/pull/455
#if defined __APPLE__
template <>
struct NumpyEquivalentType<long> {
enum { type_code = NPY_LONG };
enum { type_code = NPY_INT64 };
};
// #if defined _WIN32 || defined __CYGWIN__
template <>
struct NumpyEquivalentType<long long> {
struct NumpyEquivalentType<unsigned long> {
enum { type_code = NPY_UINT64 };
};
#endif // MAC
// On Linux, long long is a 64 bytes type but it's a different type than int64_t
// See https://github.com/stack-of-tasks/eigenpy/pull/455
#if defined __linux__
#include <type_traits>
template <typename Scalar>
struct NumpyEquivalentType<
Scalar,
typename std::enable_if<!std::is_same<int64_t, long long>::value &&
std::is_same<Scalar, long long>::value>::type> {
enum { type_code = NPY_LONGLONG };
};
// #else
// template <> struct NumpyEquivalentType<long long> { enum { type_code =
// NPY_LONGLONG };};
// #endif
template <typename Scalar>
struct NumpyEquivalentType<
Scalar, typename std::enable_if<
!std::is_same<uint64_t, unsigned long long>::value &&
std::is_same<Scalar, unsigned long long>::value>::type> {
enum { type_code = NPY_ULONGLONG };
};
#endif // Linux
template <>
struct NumpyEquivalentType<unsigned long> {
enum { type_code = NPY_ULONG };
struct NumpyEquivalentType<float> {
enum { type_code = NPY_FLOAT };
};
template <>
struct NumpyEquivalentType<double> {
enum { type_code = NPY_DOUBLE };
};
template <>
struct NumpyEquivalentType<long double> {
enum { type_code = NPY_LONGDOUBLE };
};
template <>
struct NumpyEquivalentType<std::complex<float> > {
enum { type_code = NPY_CFLOAT };
};
template <>
struct NumpyEquivalentType<std::complex<double> > {
enum { type_code = NPY_CDOUBLE };
};
template <>
struct NumpyEquivalentType<std::complex<long double> > {
enum { type_code = NPY_CLONGDOUBLE };
};
template <typename Scalar>
......@@ -122,7 +231,7 @@ EIGENPY_DLLAPI PyArray_Descr* call_PyArray_DescrFromType(int typenum);
EIGENPY_DLLAPI void call_PyArray_InitArrFuncs(PyArray_ArrFuncs* funcs);
EIGENPY_DLLAPI int call_PyArray_RegisterDataType(PyArray_Descr* dtype);
EIGENPY_DLLAPI int call_PyArray_RegisterDataType(PyArray_DescrProto* dtype);
EIGENPY_DLLAPI int call_PyArray_RegisterCanCast(PyArray_Descr* descr,
int totype,
......@@ -170,7 +279,7 @@ inline void call_PyArray_InitArrFuncs(PyArray_ArrFuncs* funcs) {
PyArray_InitArrFuncs(funcs);
}
inline int call_PyArray_RegisterDataType(PyArray_Descr* dtype) {
inline int call_PyArray_RegisterDataType(PyArray_DescrProto* dtype) {
return PyArray_RegisterDataType(dtype);
}
......
///
/// Copyright (c) 2023 CNRS INRIA
///
/// Definitions for exposing boost::optional<T> types.
/// Also works with std::optional.
#ifndef __eigenpy_optional_hpp__
#define __eigenpy_optional_hpp__
#include "eigenpy/fwd.hpp"
#include "eigenpy/eigen-from-python.hpp"
#include "eigenpy/registration.hpp"
#include <boost/optional.hpp>
#ifdef EIGENPY_WITH_CXX17_SUPPORT
#include <optional>
#endif
#ifndef EIGENPY_DEFAULT_OPTIONAL
#define EIGENPY_DEFAULT_OPTIONAL boost::optional
#endif
namespace boost {
namespace python {
namespace converter {
template <typename T>
struct expected_pytype_for_arg<boost::optional<T> >
: expected_pytype_for_arg<T> {};
#ifdef EIGENPY_WITH_CXX17_SUPPORT
template <typename T>
struct expected_pytype_for_arg<std::optional<T> > : expected_pytype_for_arg<T> {
};
#endif
} // namespace converter
} // namespace python
} // namespace boost
namespace eigenpy {
namespace detail {
/// Helper struct to decide which type is the "none" type for a specific
/// optional<T> implementation.
template <template <typename> class OptionalTpl>
struct nullopt_helper {};
template <>
struct nullopt_helper<boost::optional> {
typedef boost::none_t type;
static type value() { return boost::none; }
};
#ifdef EIGENPY_WITH_CXX17_SUPPORT
template <>
struct nullopt_helper<std::optional> {
typedef std::nullopt_t type;
static type value() { return std::nullopt; }
};
#endif
template <typename NoneType>
struct NoneToPython {
static PyObject *convert(const NoneType &) { Py_RETURN_NONE; }
static void registration() {
if (!check_registration<NoneType>()) {
bp::to_python_converter<NoneType, NoneToPython, false>();
}
}
};
template <typename T,
template <typename> class OptionalTpl = EIGENPY_DEFAULT_OPTIONAL>
struct OptionalToPython {
static PyObject *convert(const OptionalTpl<T> &obj) {
if (obj)
return bp::incref(bp::object(*obj).ptr());
else {
return bp::incref(bp::object().ptr()); // None
}
}
static PyTypeObject const *get_pytype() {
return bp::converter::registered_pytype<T>::get_pytype();
}
static void registration() {
if (!check_registration<OptionalTpl<T> >()) {
bp::to_python_converter<OptionalTpl<T>, OptionalToPython, true>();
}
}
};
template <typename T,
template <typename> class OptionalTpl = EIGENPY_DEFAULT_OPTIONAL>
struct OptionalFromPython {
static void *convertible(PyObject *obj_ptr);
static void construct(PyObject *obj_ptr,
bp::converter::rvalue_from_python_stage1_data *memory);
static void registration();
};
template <typename T, template <typename> class OptionalTpl>
void *OptionalFromPython<T, OptionalTpl>::convertible(PyObject *obj_ptr) {
if (obj_ptr == Py_None) {
return obj_ptr;
}
bp::extract<T> bp_obj(obj_ptr);
if (!bp_obj.check())
return 0;
else
return obj_ptr;
}
template <typename T, template <typename> class OptionalTpl>
void OptionalFromPython<T, OptionalTpl>::construct(
PyObject *obj_ptr, bp::converter::rvalue_from_python_stage1_data *memory) {
// create storage
using rvalue_storage_t =
bp::converter::rvalue_from_python_storage<OptionalTpl<T> >;
void *storage =
reinterpret_cast<rvalue_storage_t *>(reinterpret_cast<void *>(memory))
->storage.bytes;
if (obj_ptr == Py_None) {
new (storage) OptionalTpl<T>(nullopt_helper<OptionalTpl>::value());
} else {
const T value = bp::extract<T>(obj_ptr);
new (storage) OptionalTpl<T>(value);
}
memory->convertible = storage;
}
template <typename T, template <typename> class OptionalTpl>
void OptionalFromPython<T, OptionalTpl>::registration() {
bp::converter::registry::push_back(
&convertible, &construct, bp::type_id<OptionalTpl<T> >(),
bp::converter::expected_pytype_for_arg<OptionalTpl<T> >::get_pytype);
}
} // namespace detail
/// Register converters for the type `optional<T>` to Python.
/// By default \tparam optional is `EIGENPY_DEFAULT_OPTIONAL`.
template <typename T,
template <typename> class OptionalTpl = EIGENPY_DEFAULT_OPTIONAL>
struct OptionalConverter {
static void registration() {
detail::OptionalToPython<T, OptionalTpl>::registration();
detail::OptionalFromPython<T, OptionalTpl>::registration();
}
};
} // namespace eigenpy
#endif // __eigenpy_optional_hpp__
......@@ -364,7 +364,8 @@ class QuaternionVisitor
"'q*v' (rotating 'v' by 'q'), "
"'q==q', 'q!=q', 'q[0..3]'.",
bp::no_init)
.def(QuaternionVisitor<Quaternion>());
.def(QuaternionVisitor<Quaternion>())
.def(IdVisitor<Quaternion>());
// Cast to Eigen::QuaternionBase and vice-versa
bp::implicitly_convertible<Quaternion, QuaternionBase>();
......
......@@ -20,6 +20,25 @@ namespace eigenpy {
struct EIGENPY_DLLAPI Register {
static PyArray_Descr *getPyArrayDescr(PyTypeObject *py_type_ptr);
static PyArray_Descr *getPyArrayDescrFromTypeNum(const int type_num);
template <typename Scalar>
static PyArray_Descr *getPyArrayDescrFromScalarType() {
if (!isNumpyNativeType<Scalar>()) {
const std::type_info &info = typeid(Scalar);
if (instance().type_to_py_type_bindings.find(&info) !=
instance().type_to_py_type_bindings.end()) {
PyTypeObject *py_type = instance().type_to_py_type_bindings[&info];
return instance().py_array_descr_bindings[py_type];
} else
return nullptr;
} else {
PyArray_Descr *new_descr =
call_PyArray_DescrFromType(NumpyEquivalentType<Scalar>::type_code);
return new_descr;
}
}
template <typename Scalar>
static bool isRegistered() {
return isRegistered(Register::getPyType<Scalar>());
......@@ -87,7 +106,7 @@ struct EIGENPY_DLLAPI Register {
static Register &instance();
private:
Register(){};
Register() {};
struct Compare_PyTypeObject {
bool operator()(const PyTypeObject *a, const PyTypeObject *b) const {
......
......@@ -7,6 +7,7 @@
#define __eigenpy_registration_hpp__
#include "eigenpy/fwd.hpp"
#include "eigenpy/registration_class.hpp"
namespace eigenpy {
......@@ -44,12 +45,33 @@ inline bool register_symbolic_link_to_registered_type() {
const bp::converter::registration* reg =
bp::converter::registry::query(info);
bp::handle<> class_obj(reg->get_class_object());
bp::incref(class_obj.get());
bp::scope().attr(reg->get_class_object()->tp_name) = bp::object(class_obj);
return true;
}
return false;
}
/// Same as \see register_symbolic_link_to_registered_type() but apply \p
/// visitor on \tparam T if it already exists
template <typename T, typename Visitor>
inline bool register_symbolic_link_to_registered_type(const Visitor& visitor) {
if (eigenpy::check_registration<T>()) {
const bp::type_info info = bp::type_id<T>();
const bp::converter::registration* reg =
bp::converter::registry::query(info);
bp::handle<> class_obj(reg->get_class_object());
bp::incref(class_obj.get());
bp::object object(class_obj);
bp::scope().attr(reg->get_class_object()->tp_name) = object;
registration_class<T> cl(object);
cl.def(visitor);
return true;
}
return false;
}
} // namespace eigenpy
#endif // ifndef __eigenpy_registration_hpp__
/*
* Copyright 2023, INRIA
*/
#ifndef __eigenpy_registration_class_hpp__
#define __eigenpy_registration_class_hpp__
#include <boost/python/class.hpp>
#include "eigenpy/fwd.hpp"
namespace eigenpy {
/*! Copy of the \see boost::python::class_
* This class allow to add methods to an existing class without registering it
* again.
**/
template <class W>
class registration_class {
public:
using self = registration_class;
/// \p object Hold the namespace of the class that will be modified
registration_class(bp::object object) : m_object(object) {}
/// \see boost::python::class_::def(bp::def_visitor<Derived> const& visitor)
template <class Visitor>
self& def(Visitor const& visitor) {
visitor.visit(*this);
return *this;
}
template <class DerivedVisitor>
self& def(bp::def_visitor<DerivedVisitor> const& visitor) {
static_cast<DerivedVisitor const&>(visitor).visit(*this);
return *this;
}
/// \see boost::python::class_::def(char const* name, F f)
template <class F>
self& def(char const* name, F f) {
def_impl(bp::detail::unwrap_wrapper((W*)0), name, f,
bp::detail::def_helper<char const*>(0), &f);
return *this;
}
/// \see boost::python::class_::def(char const* name, A1 a1, A2 const& a2)
template <class A1, class A2>
self& def(char const* name, A1 a1, A2 const& a2) {
def_maybe_overloads(name, a1, a2, &a2);
return *this;
}
/// \see boost::python::class_::def(char const* name, Fn fn, A1 const& a1, A2
/// const& a2)
template <class Fn, class A1, class A2>
self& def(char const* name, Fn fn, A1 const& a1, A2 const& a2) {
def_impl(bp::detail::unwrap_wrapper((W*)0), name, fn,
bp::detail::def_helper<A1, A2>(a1, a2), &fn);
return *this;
}
/// \see boost::python::class_::def(char const* name, Fn fn, A1 const& a1, A2
/// const& a2, A3 const& a3)
template <class Fn, class A1, class A2, class A3>
self& def(char const* name, Fn fn, A1 const& a1, A2 const& a2, A3 const& a3) {
def_impl(bp::detail::unwrap_wrapper((W*)0), name, fn,
bp::detail::def_helper<A1, A2, A3>(a1, a2, a3), &fn);
return *this;
}
private:
/// \see boost::python::class_::def_impl(T*, char const* name, Fn fn, Helper
/// const& helper, ...)
template <class T, class Fn, class Helper>
inline void def_impl(T*, char const* name, Fn fn, Helper const& helper, ...) {
bp::objects::add_to_namespace(
m_object, name,
make_function(fn, helper.policies(), helper.keywords(),
bp::detail::get_signature(fn, (T*)0)),
helper.doc());
def_default(name, fn, helper,
boost::mpl::bool_<Helper::has_default_implementation>());
}
/// \see boost::python::class_::def_default(char const* name, Fn, Helper
/// const& helper, boost::mpl::bool_<true>)
template <class Fn, class Helper>
inline void def_default(char const* name, Fn, Helper const& helper,
boost::mpl::bool_<true>) {
bp::detail::error::virtual_function_default<
W, Fn>::must_be_derived_class_member(helper.default_implementation());
bp::objects::add_to_namespace(
m_object, name,
make_function(helper.default_implementation(), helper.policies(),
helper.keywords()));
}
/// \see boost::python::class_::def_default(char const*, Fn, Helper const&,
/// boost::mpl::bool_<false>)
template <class Fn, class Helper>
inline void def_default(char const*, Fn, Helper const&,
boost::mpl::bool_<false>) {}
/// \see boost::python::class_::def_maybe_overloads(char const* name, SigT
/// sig,OverloadsT const& overloads,bp::detail::overloads_base const*)
template <class OverloadsT, class SigT>
void def_maybe_overloads(char const* name, SigT sig,
OverloadsT const& overloads,
bp::detail::overloads_base const*)
{
bp::detail::define_with_defaults(name, overloads, *this,
bp::detail::get_signature(sig));
}
/// \see boost::python::class_::def_maybe_overloads(char const* name, Fn fn,
/// A1 const& a1, ...)
template <class Fn, class A1>
void def_maybe_overloads(char const* name, Fn fn, A1 const& a1, ...) {
def_impl(bp::detail::unwrap_wrapper((W*)0), name, fn,
bp::detail::def_helper<A1>(a1), &fn);
}
private:
bp::object m_object;
};
} // namespace eigenpy
#endif // ifndef __eigenpy_registration_class_hpp__
//
// Copyright (c) 2014-2020 CNRS INRIA
// Copyright (c) 2014-2024 CNRS INRIA
//
#ifndef __eigenpy_scalar_conversion_hpp__
#define __eigenpy_scalar_conversion_hpp__
#include "eigenpy/config.hpp"
#include <boost/numeric/conversion/conversion_traits.hpp>
#include <complex>
namespace eigenpy {
template <typename SCALAR1, typename SCALAR2>
struct FromTypeToType : public boost::false_type {};
template <typename SCALAR>
struct FromTypeToType<SCALAR, SCALAR> : public boost::true_type {};
template <typename Source, typename Target>
struct FromTypeToType
: public boost::mpl::if_c<std::is_same<Source, Target>::value,
std::true_type,
typename boost::numeric::conversion_traits<
Source, Target>::subranged>::type {};
/// FromTypeToType specialization to manage std::complex
template <typename ScalarSource, typename ScalarTarget>
struct FromTypeToType<std::complex<ScalarSource>, std::complex<ScalarTarget> >
: public boost::mpl::if_c<
std::is_same<ScalarSource, ScalarTarget>::value, std::true_type,
typename boost::numeric::conversion_traits<
ScalarSource, ScalarTarget>::subranged>::type {};
template <>
struct FromTypeToType<int, long> : public boost::true_type {};
template <>
struct FromTypeToType<int, float> : public boost::true_type {};
template <>
struct FromTypeToType<int, std::complex<float> > : public boost::true_type {};
template <>
struct FromTypeToType<int, double> : public boost::true_type {};
template <>
struct FromTypeToType<int, std::complex<double> > : public boost::true_type {};
template <>
struct FromTypeToType<int, long double> : public boost::true_type {};
template <>
struct FromTypeToType<int, std::complex<long double> >
: public boost::true_type {};
template <>
struct FromTypeToType<long, float> : public boost::true_type {};
template <>
struct FromTypeToType<long, std::complex<float> > : public boost::true_type {};
template <>
struct FromTypeToType<long, double> : public boost::true_type {};
template <>
struct FromTypeToType<long, std::complex<double> > : public boost::true_type {};
template <>
struct FromTypeToType<long, long double> : public boost::true_type {};
template <>
struct FromTypeToType<long, std::complex<long double> >
: public boost::true_type {};
template <>
struct FromTypeToType<float, std::complex<float> > : public boost::true_type {};
template <>
struct FromTypeToType<float, double> : public boost::true_type {};
template <>
struct FromTypeToType<float, std::complex<double> > : public boost::true_type {
};
template <>
struct FromTypeToType<float, long double> : public boost::true_type {};
template <>
struct FromTypeToType<float, std::complex<long double> >
: public boost::true_type {};
template <>
struct FromTypeToType<double, std::complex<double> > : public boost::true_type {
};
template <>
struct FromTypeToType<double, long double> : public boost::true_type {};
template <>
struct FromTypeToType<double, std::complex<long double> >
: public boost::true_type {};
} // namespace eigenpy
#endif // __eigenpy_scalar_conversion_hpp__
/*
* Copyright 2024 INRIA
*/
#ifndef __eigenpy_scipy_allocator_hpp__
#define __eigenpy_scipy_allocator_hpp__
#include "eigenpy/fwd.hpp"
#include "eigenpy/eigen-allocator.hpp"
#include "eigenpy/scipy-type.hpp"
#include "eigenpy/register.hpp"
namespace eigenpy {
template <typename EigenType, typename BaseType>
struct scipy_allocator_impl;
template <typename EigenType>
struct scipy_allocator_impl_sparse_matrix;
template <typename MatType>
struct scipy_allocator_impl<
MatType,
Eigen::SparseMatrixBase<typename remove_const_reference<MatType>::type> >
: scipy_allocator_impl_sparse_matrix<MatType> {};
template <typename MatType>
struct scipy_allocator_impl<
const MatType, const Eigen::SparseMatrixBase<
typename remove_const_reference<MatType>::type> >
: scipy_allocator_impl_sparse_matrix<const MatType> {};
// template <typename MatType>
// struct scipy_allocator_impl<MatType &, Eigen::MatrixBase<MatType> > :
// scipy_allocator_impl_sparse_matrix<MatType &>
//{};
template <typename MatType>
struct scipy_allocator_impl<const MatType &,
const Eigen::SparseMatrixBase<MatType> >
: scipy_allocator_impl_sparse_matrix<const MatType &> {};
template <typename EigenType,
typename BaseType = typename get_eigen_base_type<EigenType>::type>
struct ScipyAllocator : scipy_allocator_impl<EigenType, BaseType> {};
template <typename MatType>
struct scipy_allocator_impl_sparse_matrix {
template <typename SimilarMatrixType>
static PyObject *allocate(
const Eigen::SparseCompressedBase<SimilarMatrixType> &mat_,
bool copy = false) {
EIGENPY_UNUSED_VARIABLE(copy);
typedef typename SimilarMatrixType::Scalar Scalar;
typedef typename SimilarMatrixType::StorageIndex StorageIndex;
enum { IsRowMajor = SimilarMatrixType::IsRowMajor };
typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1> DataVector;
typedef const Eigen::Map<const DataVector> MapDataVector;
typedef Eigen::Matrix<StorageIndex, Eigen::Dynamic, 1> StorageIndexVector;
typedef Eigen::Matrix<int32_t, Eigen::Dynamic, 1> ScipyStorageIndexVector;
typedef const Eigen::Map<const StorageIndexVector> MapStorageIndexVector;
SimilarMatrixType &mat = mat_.const_cast_derived();
bp::object scipy_sparse_matrix_type =
ScipyType::get_pytype_object<SimilarMatrixType>();
MapDataVector data(mat.valuePtr(), mat.nonZeros());
MapStorageIndexVector outer_indices(
mat.outerIndexPtr(), (IsRowMajor ? mat.rows() : mat.cols()) + 1);
MapStorageIndexVector inner_indices(mat.innerIndexPtr(), mat.nonZeros());
bp::object scipy_sparse_matrix;
if (mat.rows() == 0 &&
mat.cols() == 0) // handle the specific case of empty matrix
{
// PyArray_Descr* npy_type =
// Register::getPyArrayDescrFromScalarType<Scalar>(); bp::dict args;
// args["dtype"] =
// bp::object(bp::handle<>(bp::borrowed(npy_type->typeobj)));
// args["shape"] = bp::object(bp::handle<>(bp::borrowed(Py_None)));
// scipy_sparse_matrix =
// scipy_sparse_matrix_type(*bp::make_tuple(0,0),**args);
scipy_sparse_matrix = scipy_sparse_matrix_type(
Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>(0, 0));
} else if (mat.nonZeros() == 0) {
scipy_sparse_matrix =
scipy_sparse_matrix_type(bp::make_tuple(mat.rows(), mat.cols()));
} else {
scipy_sparse_matrix = scipy_sparse_matrix_type(bp::make_tuple(
DataVector(data),
ScipyStorageIndexVector(inner_indices.template cast<int32_t>()),
ScipyStorageIndexVector(
outer_indices.template cast<int32_t>()))); //,
// bp::make_tuple(mat.rows(),
// mat.cols())));
}
Py_INCREF(scipy_sparse_matrix.ptr());
return scipy_sparse_matrix.ptr();
}
};
// template <typename MatType>
// struct scipy_allocator_impl_sparse_matrix<MatType &> {
// template <typename SimilarMatrixType>
// static PyArrayObject *allocate(Eigen::PlainObjectBase<SimilarMatrixType>
// &mat,
// npy_intp nd, npy_intp *shape) {
// typedef typename SimilarMatrixType::Scalar Scalar;
// enum {
// NPY_ARRAY_MEMORY_CONTIGUOUS =
// SimilarMatrixType::IsRowMajor ? NPY_ARRAY_CARRAY : NPY_ARRAY_FARRAY
// };
//
// if (NumpyType::sharedMemory()) {
// const int Scalar_type_code = Register::getTypeCode<Scalar>();
// PyArrayObject *pyArray = (PyArrayObject *)call_PyArray_New(
// getPyArrayType(), static_cast<int>(nd), shape, Scalar_type_code,
// mat.data(), NPY_ARRAY_MEMORY_CONTIGUOUS | NPY_ARRAY_ALIGNED);
//
// return pyArray;
// } else {
// return NumpyAllocator<MatType>::allocate(mat, nd, shape);
// }
// }
// };
#if EIGEN_VERSION_AT_LEAST(3, 2, 0)
// template <typename MatType, int Options, typename Stride>
// struct scipy_allocator_impl_sparse_matrix<Eigen::Ref<MatType, Options,
// Stride> > {
// typedef Eigen::Ref<MatType, Options, Stride> RefType;
//
// static PyArrayObject *allocate(RefType &mat, npy_intp nd, npy_intp *shape)
// {
// typedef typename RefType::Scalar Scalar;
// enum {
// NPY_ARRAY_MEMORY_CONTIGUOUS =
// RefType::IsRowMajor ? NPY_ARRAY_CARRAY : NPY_ARRAY_FARRAY
// };
//
// if (NumpyType::sharedMemory()) {
// const int Scalar_type_code = Register::getTypeCode<Scalar>();
// const bool reverse_strides = MatType::IsRowMajor || (mat.rows() == 1);
// Eigen::DenseIndex inner_stride = reverse_strides ? mat.outerStride()
// : mat.innerStride(),
// outer_stride = reverse_strides ? mat.innerStride()
// : mat.outerStride();
//
// const int elsize =
// call_PyArray_DescrFromType(Scalar_type_code)->elsize; npy_intp
// strides[2] = {elsize * inner_stride, elsize * outer_stride};
//
// PyArrayObject *pyArray = (PyArrayObject *)call_PyArray_New(
// getPyArrayType(), static_cast<int>(nd), shape, Scalar_type_code,
// strides, mat.data(), NPY_ARRAY_MEMORY_CONTIGUOUS |
// NPY_ARRAY_ALIGNED);
//
// return pyArray;
// } else {
// return NumpyAllocator<MatType>::allocate(mat, nd, shape);
// }
// }
// };
#endif
// template <typename MatType>
// struct scipy_allocator_impl_sparse_matrix<const MatType &> {
// template <typename SimilarMatrixType>
// static PyArrayObject *allocate(
// const Eigen::PlainObjectBase<SimilarMatrixType> &mat, npy_intp nd,
// npy_intp *shape) {
// typedef typename SimilarMatrixType::Scalar Scalar;
// enum {
// NPY_ARRAY_MEMORY_CONTIGUOUS_RO = SimilarMatrixType::IsRowMajor
// ? NPY_ARRAY_CARRAY_RO
// : NPY_ARRAY_FARRAY_RO
// };
//
// if (NumpyType::sharedMemory()) {
// const int Scalar_type_code = Register::getTypeCode<Scalar>();
// PyArrayObject *pyArray = (PyArrayObject *)call_PyArray_New(
// getPyArrayType(), static_cast<int>(nd), shape, Scalar_type_code,
// const_cast<Scalar *>(mat.data()),
// NPY_ARRAY_MEMORY_CONTIGUOUS_RO | NPY_ARRAY_ALIGNED);
//
// return pyArray;
// } else {
// return NumpyAllocator<MatType>::allocate(mat, nd, shape);
// }
// }
// };
#if EIGEN_VERSION_AT_LEAST(3, 2, 0)
// template <typename MatType, int Options, typename Stride>
// struct scipy_allocator_impl_sparse_matrix<
// const Eigen::Ref<const MatType, Options, Stride> > {
// typedef const Eigen::Ref<const MatType, Options, Stride> RefType;
//
// static PyArrayObject *allocate(RefType &mat, npy_intp nd, npy_intp *shape)
// {
// typedef typename RefType::Scalar Scalar;
// enum {
// NPY_ARRAY_MEMORY_CONTIGUOUS_RO =
// RefType::IsRowMajor ? NPY_ARRAY_CARRAY_RO : NPY_ARRAY_FARRAY_RO
// };
//
// if (NumpyType::sharedMemory()) {
// const int Scalar_type_code = Register::getTypeCode<Scalar>();
//
// const bool reverse_strides = MatType::IsRowMajor || (mat.rows() == 1);
// Eigen::DenseIndex inner_stride = reverse_strides ? mat.outerStride()
// : mat.innerStride(),
// outer_stride = reverse_strides ? mat.innerStride()
// : mat.outerStride();
//
// const int elsize =
// call_PyArray_DescrFromType(Scalar_type_code)->elsize; npy_intp
// strides[2] = {elsize * inner_stride, elsize * outer_stride};
//
// PyArrayObject *pyArray = (PyArrayObject *)call_PyArray_New(
// getPyArrayType(), static_cast<int>(nd), shape, Scalar_type_code,
// strides, const_cast<Scalar *>(mat.data()),
// NPY_ARRAY_MEMORY_CONTIGUOUS_RO | NPY_ARRAY_ALIGNED);
//
// return pyArray;
// } else {
// return NumpyAllocator<MatType>::allocate(mat, nd, shape);
// }
// }
// };
#endif
} // namespace eigenpy
#endif // ifndef __eigenpy_scipy_allocator_hpp__
/*
* Copyright 2024 INRIA
*/
#ifndef __eigenpy_scipy_type_hpp__
#define __eigenpy_scipy_type_hpp__
#include "eigenpy/fwd.hpp"
#include "eigenpy/register.hpp"
#include "eigenpy/scalar-conversion.hpp"
#include "eigenpy/numpy-type.hpp"
namespace eigenpy {
struct EIGENPY_DLLAPI ScipyType {
static ScipyType& getInstance();
static void sharedMemory(const bool value);
static bool sharedMemory();
static bp::object getScipyType();
static const PyTypeObject* getScipyCSRMatrixType();
static const PyTypeObject* getScipyCSCMatrixType();
template <typename SparseMatrix>
static bp::object get_pytype_object(
const Eigen::SparseMatrixBase<SparseMatrix>* ptr = nullptr) {
EIGENPY_UNUSED_VARIABLE(ptr);
return SparseMatrix::IsRowMajor ? getInstance().csr_matrix_obj
: getInstance().csc_matrix_obj;
}
template <typename SparseMatrix>
static PyTypeObject const* get_pytype(
const Eigen::SparseMatrixBase<SparseMatrix>* ptr = nullptr) {
EIGENPY_UNUSED_VARIABLE(ptr);
return SparseMatrix::IsRowMajor ? getInstance().csr_matrix_type
: getInstance().csc_matrix_type;
}
static int get_numpy_type_num(const bp::object& obj) {
const PyTypeObject* type = Py_TYPE(obj.ptr());
EIGENPY_USED_VARIABLE_ONLY_IN_DEBUG_MODE(type);
assert(type == getInstance().csr_matrix_type ||
type == getInstance().csc_matrix_type);
bp::object dtype = obj.attr("dtype");
const PyArray_Descr* npy_type =
reinterpret_cast<PyArray_Descr*>(dtype.ptr());
return npy_type->type_num;
}
protected:
ScipyType();
bp::object sparse_module;
// SciPy types
bp::object csr_matrix_obj, csc_matrix_obj;
PyTypeObject *csr_matrix_type, *csc_matrix_type;
bool shared_memory;
};
} // namespace eigenpy
#endif // ifndef __eigenpy_scipy_type_hpp__
/*
* Copyright 2017, Justin Carpentier, LAAS-CNRS
*
* This file is part of eigenpy.
* eigenpy is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
* eigenpy is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. You should
* have received a copy of the GNU Lesser General Public License along
* with eigenpy. If not, see <http://www.gnu.org/licenses/>.
* Copyright 2017 CNRS
* Copyright 2024 Inria
*/
#ifndef __eigenpy_bfgs_preconditioners_hpp__
......@@ -49,6 +38,7 @@ struct BFGSPreconditionerBaseVisitor
static void expose(const std::string& name) {
bp::class_<Preconditioner>(name, bp::no_init)
.def(IdVisitor<Preconditioner>())
.def(BFGSPreconditionerBaseVisitor<Preconditioner>());
}
};
......@@ -68,6 +58,7 @@ struct LimitedBFGSPreconditionerBaseVisitor
static void expose(const std::string& name) {
bp::class_<Preconditioner>(name.c_str(), bp::no_init)
.def(IdVisitor<Preconditioner>())
.def(LimitedBFGSPreconditionerBaseVisitor<Preconditioner>());
}
};
......
/*
* Copyright 2017, Justin Carpentier, LAAS-CNRS
*
* This file is part of eigenpy.
* eigenpy is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
* eigenpy is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. You should
* have received a copy of the GNU Lesser General Public License along
* with eigenpy. If not, see <http://www.gnu.org/licenses/>.
* Copyright 2017 CNRS
* Copyright 2024 Inria
*/
#ifndef __eigenpy_basic_preconditioners_hpp__
......@@ -81,7 +70,8 @@ struct DiagonalPreconditionerVisitor
"A preconditioner based on the digonal entrie.\n"
"This class allows to approximately solve for A.x = b problems "
"assuming A is a diagonal matrix.",
bp::no_init);
bp::no_init)
.def(IdVisitor<Preconditioner>());
}
};
......@@ -103,7 +93,8 @@ struct LeastSquareDiagonalPreconditionerVisitor
"his class allows to approximately solve for A' A x = A' b problems "
"assuming A' A is a diagonal matrix.",
bp::no_init)
.def(DiagonalPreconditionerVisitor<Scalar>());
.def(DiagonalPreconditionerVisitor<Scalar>())
.def(IdVisitor<Preconditioner>());
}
};
#endif
......@@ -117,7 +108,8 @@ struct IdentityPreconditionerVisitor
static void expose() {
bp::class_<Preconditioner>("IdentityPreconditioner", bp::no_init)
.def(PreconditionerBaseVisitor<Preconditioner>());
.def(PreconditionerBaseVisitor<Preconditioner>())
.def(IdVisitor<Preconditioner>());
}
};
......
/*
* Copyright 2017, Justin Carpentier, LAAS-CNRS
*
* This file is part of eigenpy.
* eigenpy is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
* eigenpy is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. You should
* have received a copy of the GNU Lesser General Public License along
* with eigenpy. If not, see <http://www.gnu.org/licenses/>.
* Copyright 2017 CNRS
*/
#ifndef __eigenpy_conjugate_gradient_hpp__
......@@ -43,7 +31,8 @@ struct ConjugateGradientVisitor
static void expose(const std::string& name = "ConjugateGradient") {
bp::class_<ConjugateGradient, boost::noncopyable>(name.c_str(), bp::no_init)
.def(ConjugateGradientVisitor<ConjugateGradient>());
.def(ConjugateGradientVisitor<ConjugateGradient>())
.def(IdVisitor<ConjugateGradient>());
}
};
......
/*
* Copyright 2017, Justin Carpentier, LAAS-CNRS
*
* This file is part of eigenpy.
* eigenpy is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
* eigenpy is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. You should
* have received a copy of the GNU Lesser General Public License along
* with eigenpy. If not, see <http://www.gnu.org/licenses/>.
* Copyright 2017 CNRS
*/
#ifndef __eigenpy_iterative_solver_base_hpp__
......
/*
* Copyright 2017-2018, Justin Carpentier, LAAS-CNRS
*
* This file is part of eigenpy.
* eigenpy is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
* eigenpy is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. You should
* have received a copy of the GNU Lesser General Public License along
* with eigenpy. If not, see <http://www.gnu.org/licenses/>.
* Copyright 2017-2018 CNRS
*/
#ifndef __eigenpy_least_square_conjugate_gradient_hpp__
......@@ -46,7 +34,8 @@ struct LeastSquaresConjugateGradientVisitor
"LeastSquaresConjugateGradient", bp::no_init)
.def(IterativeSolverVisitor<LeastSquaresConjugateGradient>())
.def(LeastSquaresConjugateGradientVisitor<
LeastSquaresConjugateGradient>());
LeastSquaresConjugateGradient>())
.def(IdVisitor<LeastSquaresConjugateGradient>());
}
};
......
/*
* Copyright 2017, Justin Carpentier, LAAS-CNRS
*
* This file is part of eigenpy.
* eigenpy is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
* eigenpy is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. You should
* have received a copy of the GNU Lesser General Public License along
* with eigenpy. If not, see <http://www.gnu.org/licenses/>.
* Copyright 2017 CNRS
*/
#ifndef __eigenpy_sparse_solver_base_hpp__
......
/*
* Copyright 2017, Justin Carpentier, LAAS-CNRS
*
* This file is part of eigenpy.
* eigenpy is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
* eigenpy is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. You should
* have received a copy of the GNU Lesser General Public License along
* with eigenpy. If not, see <http://www.gnu.org/licenses/>.
* Copyright 2017 CNRS
*/
#ifndef __eigenpy_preconditioners_hpp__
......
//
// Copyright (c) 2024 INRIA
//
#ifndef __eigenpy_sparse_eigen_from_python_hpp__
#define __eigenpy_sparse_eigen_from_python_hpp__
#include "eigenpy/fwd.hpp"
#include "eigenpy/eigen-allocator.hpp"
#include "eigenpy/scipy-type.hpp"
#include "eigenpy/scalar-conversion.hpp"
namespace eigenpy {
template <typename SparseMatrixType>
struct expected_pytype_for_arg<SparseMatrixType,
Eigen::SparseMatrixBase<SparseMatrixType> > {
static PyTypeObject const *get_pytype() {
PyTypeObject const *py_type = ScipyType::get_pytype<SparseMatrixType>();
return py_type;
}
};
} // namespace eigenpy
namespace boost {
namespace python {
namespace converter {
template <typename Scalar, int Options, typename StorageIndex>
struct expected_pytype_for_arg<
Eigen::SparseMatrix<Scalar, Options, StorageIndex> >
: eigenpy::expected_pytype_for_arg<
Eigen::SparseMatrix<Scalar, Options, StorageIndex> > {};
template <typename Scalar, int Options, typename StorageIndex>
struct rvalue_from_python_data<
Eigen::SparseMatrix<Scalar, Options, StorageIndex> const &>
: ::eigenpy::rvalue_from_python_data<
Eigen::SparseMatrix<Scalar, Options, StorageIndex> const &> {
typedef Eigen::SparseMatrix<Scalar, Options, StorageIndex> T;
EIGENPY_RVALUE_FROM_PYTHON_DATA_INIT(T const &)
};
template <typename Derived>
struct rvalue_from_python_data<Eigen::SparseMatrixBase<Derived> const &>
: ::eigenpy::rvalue_from_python_data<Derived const &> {
EIGENPY_RVALUE_FROM_PYTHON_DATA_INIT(Derived const &)
};
} // namespace converter
} // namespace python
} // namespace boost
namespace boost {
namespace python {
namespace detail {
// template <typename TensorType>
// struct referent_storage<Eigen::TensorRef<TensorType> &> {
// typedef Eigen::TensorRef<TensorType> RefType;
// typedef ::eigenpy::details::referent_storage_eigen_ref<RefType>
// StorageType; typedef typename ::eigenpy::aligned_storage<
// referent_size<StorageType &>::value>::type type;
// };
// template <typename TensorType>
// struct referent_storage<const Eigen::TensorRef<const TensorType> &> {
// typedef Eigen::TensorRef<const TensorType> RefType;
// typedef ::eigenpy::details::referent_storage_eigen_ref<RefType>
// StorageType; typedef typename ::eigenpy::aligned_storage<
// referent_size<StorageType &>::value>::type type;
// };
} // namespace detail
} // namespace python
} // namespace boost
namespace eigenpy {
template <typename SparseMatrixType>
struct eigen_from_py_impl<SparseMatrixType,
Eigen::SparseMatrixBase<SparseMatrixType> > {
typedef typename SparseMatrixType::Scalar Scalar;
/// \brief Determine if pyObj can be converted into a MatType object
static void *convertible(PyObject *pyObj);
/// \brief Allocate memory and copy pyObj in the new storage
static void construct(PyObject *pyObj,
bp::converter::rvalue_from_python_stage1_data *memory);
static void registration();
};
template <typename SparseMatrixType>
void *eigen_from_py_impl<
SparseMatrixType,
Eigen::SparseMatrixBase<SparseMatrixType> >::convertible(PyObject *pyObj) {
const PyTypeObject *type = Py_TYPE(pyObj);
const PyTypeObject *sparse_matrix_py_type =
ScipyType::get_pytype<SparseMatrixType>();
typedef typename SparseMatrixType::Scalar Scalar;
if (type != sparse_matrix_py_type) return 0;
bp::object obj(bp::handle<>(bp::borrowed(pyObj)));
const int type_num = ScipyType::get_numpy_type_num(obj);
if (!np_type_is_convertible_into_scalar<Scalar>(type_num)) return 0;
return pyObj;
}
template <typename MatOrRefType>
void eigen_sparse_matrix_from_py_construct(
PyObject *pyObj, bp::converter::rvalue_from_python_stage1_data *memory) {
typedef typename MatOrRefType::Scalar Scalar;
typedef typename MatOrRefType::StorageIndex StorageIndex;
typedef Eigen::Map<MatOrRefType> MapMatOrRefType;
bp::converter::rvalue_from_python_storage<MatOrRefType> *storage =
reinterpret_cast<
bp::converter::rvalue_from_python_storage<MatOrRefType> *>(
reinterpret_cast<void *>(memory));
void *raw_ptr = storage->storage.bytes;
bp::object obj(bp::handle<>(bp::borrowed(pyObj)));
const int type_num_python_sparse_matrix = ScipyType::get_numpy_type_num(obj);
const int type_num_eigen_sparse_matrix = Register::getTypeCode<Scalar>();
if (type_num_eigen_sparse_matrix == type_num_python_sparse_matrix) {
typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1> DataVector;
// typedef const Eigen::Ref<const DataVector> RefDataVector;
DataVector data = bp::extract<DataVector>(obj.attr("data"));
bp::tuple shape = bp::extract<bp::tuple>(obj.attr("shape"));
typedef Eigen::Matrix<StorageIndex, Eigen::Dynamic, 1> StorageIndexVector;
// typedef const Eigen::Ref<const StorageIndexVector>
// RefStorageIndexVector;
StorageIndexVector indices =
bp::extract<StorageIndexVector>(obj.attr("indices"));
StorageIndexVector indptr =
bp::extract<StorageIndexVector>(obj.attr("indptr"));
const Eigen::Index m = bp::extract<Eigen::Index>(shape[0]),
n = bp::extract<Eigen::Index>(shape[1]),
nnz = bp::extract<Eigen::Index>(obj.attr("nnz"));
// Handle the specific case of the null matrix
Scalar *data_ptr = nullptr;
StorageIndex *indices_ptr = nullptr;
if (nnz > 0) {
data_ptr = data.data();
indices_ptr = indices.data();
}
MapMatOrRefType sparse_map(m, n, nnz, indptr.data(), indices_ptr, data_ptr);
new (raw_ptr) MatOrRefType(sparse_map);
}
memory->convertible = storage->storage.bytes;
}
template <typename SparseMatrixType>
void eigen_from_py_impl<SparseMatrixType,
Eigen::SparseMatrixBase<SparseMatrixType> >::
construct(PyObject *pyObj,
bp::converter::rvalue_from_python_stage1_data *memory) {
eigen_sparse_matrix_from_py_construct<SparseMatrixType>(pyObj, memory);
}
template <typename SparseMatrixType>
void eigen_from_py_impl<
SparseMatrixType,
Eigen::SparseMatrixBase<SparseMatrixType> >::registration() {
bp::converter::registry::push_back(
reinterpret_cast<void *(*)(_object *)>(&eigen_from_py_impl::convertible),
&eigen_from_py_impl::construct, bp::type_id<SparseMatrixType>()
#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
,
&eigenpy::expected_pytype_for_arg<SparseMatrixType>::get_pytype
#endif
);
}
template <typename SparseMatrixType>
struct eigen_from_py_converter_impl<
SparseMatrixType, Eigen::SparseMatrixBase<SparseMatrixType> > {
static void registration() {
EigenFromPy<SparseMatrixType>::registration();
// Add conversion to Eigen::SparseMatrixBase<SparseMatrixType>
typedef Eigen::SparseMatrixBase<SparseMatrixType> SparseMatrixBase;
EigenFromPy<SparseMatrixBase>::registration();
// // Add conversion to Eigen::Ref<SparseMatrixType>
// typedef Eigen::Ref<SparseMatrixType> RefType;
// EigenFromPy<SparseMatrixType>::registration();
//
// // Add conversion to Eigen::Ref<const SparseMatrixType>
// typedef const Eigen::Ref<const SparseMatrixType> ConstRefType;
// EigenFromPy<ConstRefType>::registration();
}
};
template <typename SparseMatrixType>
struct EigenFromPy<Eigen::SparseMatrixBase<SparseMatrixType> >
: EigenFromPy<SparseMatrixType> {
typedef EigenFromPy<SparseMatrixType> EigenFromPyDerived;
typedef Eigen::SparseMatrixBase<SparseMatrixType> Base;
static void registration() {
bp::converter::registry::push_back(
reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
&EigenFromPy::construct, bp::type_id<Base>()
#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
,
&eigenpy::expected_pytype_for_arg<SparseMatrixType>::get_pytype
#endif
);
}
};
//
// template <typename TensorType>
// struct EigenFromPy<Eigen::TensorRef<TensorType> > {
// typedef Eigen::TensorRef<TensorType> RefType;
// typedef typename TensorType::Scalar Scalar;
//
// /// \brief Determine if pyObj can be converted into a MatType object
// static void *convertible(PyObject *pyObj) {
// if (!call_PyArray_Check(pyObj)) return 0;
// PyArrayObject *pyArray = reinterpret_cast<PyArrayObject *>(pyObj);
// if (!PyArray_ISWRITEABLE(pyArray)) return 0;
// return EigenFromPy<TensorType>::convertible(pyObj);
// }
//
// static void registration() {
// bp::converter::registry::push_back(
// reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
// &eigen_from_py_construct<RefType>, bp::type_id<RefType>()
// #ifndef BOOST_PYTHON_NO_PY_SIGNATURES
// ,
// &eigenpy::expected_pytype_for_arg<TensorType>::get_pytype
// #endif
// );
// }
//};
// template <typename TensorType>
// struct EigenFromPy<const Eigen::TensorRef<const TensorType> > {
// typedef const Eigen::TensorRef<const TensorType> ConstRefType;
// typedef typename TensorType::Scalar Scalar;
//
// /// \brief Determine if pyObj can be converted into a MatType object
// static void *convertible(PyObject *pyObj) {
// return EigenFromPy<TensorType>::convertible(pyObj);
// }
//
// static void registration() {
// bp::converter::registry::push_back(
// reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
// &eigen_from_py_construct<ConstRefType>, bp::type_id<ConstRefType>()
// #ifndef BOOST_PYTHON_NO_PY_SIGNATURES
// ,
// &eigenpy::expected_pytype_for_arg<TensorType>::get_pytype
// #endif
// );
// }
// };
} // namespace eigenpy
#endif // __eigenpy_sparse_eigen_from_python_hpp__
///
/// Copyright (c) 2023-2024 CNRS INRIA
///
#ifndef __eigenpy_utils_std_array_hpp__
#define __eigenpy_utils_std_array_hpp__
#include <boost/python/suite/indexing/indexing_suite.hpp>
#include "eigenpy/std-vector.hpp"
#include <array>
namespace eigenpy {
template <typename Container, bool NoProxy, class SliceAllocator,
class DerivedPolicies>
class array_indexing_suite;
namespace details {
template <typename Container, bool NoProxy, class SliceAllocator>
class final_array_derived_policies
: public array_indexing_suite<
Container, NoProxy, SliceAllocator,
final_array_derived_policies<Container, NoProxy, SliceAllocator> > {};
} // namespace details
template <typename Container, bool NoProxy = false,
class SliceAllocator = std::allocator<typename Container::value_type>,
class DerivedPolicies = details::final_array_derived_policies<
Container, NoProxy, SliceAllocator> >
class array_indexing_suite
: public bp::vector_indexing_suite<Container, NoProxy, DerivedPolicies> {
public:
typedef typename Container::value_type data_type;
typedef typename Container::value_type key_type;
typedef typename Container::size_type index_type;
typedef typename Container::size_type size_type;
typedef typename Container::difference_type difference_type;
typedef std::vector<data_type, SliceAllocator> slice_vector_type;
static constexpr std::size_t Size = std::tuple_size<Container>{};
template <class Class>
static void extension_def(Class &) {}
// throws exception
static void delete_item(Container &, index_type) {
PyErr_SetString(PyExc_NotImplementedError,
"Cannot delete item from std::array type.");
bp::throw_error_already_set();
}
// throws exception
static void delete_slice(Container &, index_type, index_type) {
PyErr_SetString(PyExc_NotImplementedError,
"Cannot delete slice from std::array type.");
bp::throw_error_already_set();
}
static void set_slice(Container &container, index_type from, index_type to,
data_type const &v) {
if (from >= to) {
PyErr_SetString(PyExc_NotImplementedError,
"Setting this slice would insert into an std::array, "
"which is not supported.");
bp::throw_error_already_set();
} else {
std::fill(container.begin() + from, container.begin() + to, v);
}
}
template <class Iter>
static void set_slice(Container &container, index_type from, index_type to,
Iter first, Iter last) {
if (from >= to) {
PyErr_SetString(PyExc_NotImplementedError,
"Setting this slice would insert into an std::array, "
"which is not supported.");
bp::throw_error_already_set();
} else {
if (long(to - from) == std::distance(first, last)) {
std::copy(first, last, container.begin() + from);
} else {
PyErr_SetString(PyExc_NotImplementedError,
"Size of std::array slice and size of right-hand side "
"iterator are incompatible.");
bp::throw_error_already_set();
}
}
}
static bp::object get_slice(Container &container, index_type from,
index_type to) {
if (from > to) return bp::object(slice_vector_type());
slice_vector_type out;
for (size_t i = from; i < to; i++) {
out.push_back(container[i]);
}
return bp::object(std::move(out));
}
};
/// \brief Expose an std::array (a C++11 fixed-size array) from a given type
/// \tparam array_type std::array type to expose
/// \tparam NoProxy When set to false, the elements will be copied when
/// returned to Python.
/// \tparam SliceAllocator Allocator type to use for slices of std::array type
/// accessed using e.g. __getitem__[0:4] in Python. These slices are returned as
/// std::vector (dynamic size).
template <typename array_type, bool NoProxy = false,
class SliceAllocator =
std::allocator<typename array_type::value_type> >
struct StdArrayPythonVisitor {
typedef typename array_type::value_type value_type;
static ::boost::python::list tolist(array_type &self, const bool deep_copy) {
return details::build_list<array_type, NoProxy>::run(self, deep_copy);
}
static void expose(const std::string &class_name,
const std::string &doc_string = "") {
expose(class_name, doc_string, EmptyPythonVisitor());
}
template <typename DerivedVisitor>
static void expose(const std::string &class_name,
const bp::def_visitor<DerivedVisitor> &visitor) {
expose(class_name, "", visitor);
}
template <typename DerivedVisitor>
static void expose(const std::string &class_name,
const std::string &doc_string,
const bp::def_visitor<DerivedVisitor> &visitor) {
if (!register_symbolic_link_to_registered_type<array_type>()) {
bp::class_<array_type> cl(class_name.c_str(), doc_string.c_str());
cl.def(bp::init<const array_type &>(bp::args("self", "other"),
"Copy constructor"));
cl.def(IdVisitor<array_type>());
array_indexing_suite<array_type, NoProxy, SliceAllocator> indexing_suite;
cl.def(indexing_suite)
.def(visitor)
.def("tolist", tolist,
(bp::arg("self"), bp::arg("deep_copy") = false),
"Returns the std::array as a Python list.");
}
}
};
/// Exposes std::array<MatrixType, Size>
template <typename MatrixType, std::size_t Size>
void exposeStdArrayEigenSpecificType(const char *name) {
std::ostringstream oss;
oss << "StdArr";
oss << Size << "_" << name;
typedef std::array<MatrixType, Size> array_type;
StdArrayPythonVisitor<array_type, false,
Eigen::aligned_allocator<MatrixType> >::
expose(oss.str(),
details::overload_base_get_item_for_std_vector<array_type>());
}
} // namespace eigenpy
#endif // ifndef __eigenpy_utils_std_array_hpp__
/// Copyright (c) 2016-2022 CNRS INRIA
/// This file was taken from Pinocchio (header
/// <pinocchio/bindings/python/utils/std-vector.hpp>)
/// Copyright (c) 2024, INRIA
///
#ifndef __eigenpy_utils_map_hpp__
#define __eigenpy_utils_map_hpp__
#ifndef __eigenpy_std_map_hpp__
#define __eigenpy_std_map_hpp__
#include <boost/python/suite/indexing/map_indexing_suite.hpp>
#include "eigenpy/map.hpp"
#include "eigenpy/deprecated.hpp"
#include <map>
namespace eigenpy {
namespace details {
template <typename Container>
struct overload_base_get_item_for_std_map
: public boost::python::def_visitor<
overload_base_get_item_for_std_map<Container> > {
typedef typename Container::value_type value_type;
typedef typename Container::value_type::second_type data_type;
typedef typename Container::key_type key_type;
typedef typename Container::key_type index_type;
template <class Class>
void visit(Class& cl) const {
cl.def("__getitem__", &base_get_item);
}
private:
static boost::python::object base_get_item(
boost::python::back_reference<Container&> container, PyObject* i_) {
index_type idx = convert_index(container.get(), i_);
typename Container::iterator i = container.get().find(idx);
if (i == container.get().end()) {
PyErr_SetString(PyExc_KeyError, "Invalid key");
bp::throw_error_already_set();
}
typename bp::to_python_indirect<data_type&,
bp::detail::make_reference_holder>
convert;
return bp::object(bp::handle<>(convert(i->second)));
}
static index_type convert_index(Container& /*container*/, PyObject* i_) {
bp::extract<key_type const&> i(i_);
if (i.check()) {
return i();
} else {
bp::extract<key_type> i(i_);
if (i.check()) return i();
}
PyErr_SetString(PyExc_TypeError, "Invalid index type");
bp::throw_error_already_set();
return index_type();
}
};
using overload_base_get_item_for_std_map EIGENPY_DEPRECATED_MESSAGE(
"Use overload_base_get_item_for_map<> instead.") =
overload_base_get_item_for_map<Container>;
namespace details {
using ::eigenpy::overload_base_get_item_for_std_map;
} // namespace details
/**
* @brief Expose an std::map from a type given as template argument.
*
* @param[in] T Type to expose as std::map<T>.
* @param[in] Compare Type for the Compare in std::map<T,Compare,Allocator>.
* @param[in] Allocator Type for the Allocator in
* std::map<T,Compare,Allocator>.
* @param[in] NoProxy When set to false, the elements will be copied when
* returned to Python.
*/
template <class Key, class T, class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >,
bool NoProxy = false>
struct StdMapPythonVisitor
: GenericMapVisitor<std::map<Key, T, Compare, Allocator>, NoProxy> {};
namespace python {
// fix previous mistake
using ::eigenpy::StdMapPythonVisitor;
} // namespace python
} // namespace eigenpy
#endif // ifndef __eigenpy_utils_map_hpp__
#endif // ifndef __eigenpy_std_map_hpp__