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 878 additions and 698 deletions
///
/// Copyright 2023 CNRS, INRIA
///
#include "eigenpy/optional.hpp"
namespace eigenpy {
void exposeNoneType() {
detail::NoneToPython<boost::none_t>::registration();
#ifdef EIGENPY_WITH_CXX17_SUPPORT
detail::NoneToPython<std::nullopt_t>::registration();
#endif
}
} // namespace eigenpy
/* /*
* Copyright 2014-2019, CNRS * Copyright 2014-2019, CNRS
* Copyright 2018-2019, INRIA * Copyright 2018-2023, INRIA
*/ */
#include "eigenpy/memory.hpp" #include <Eigen/Geometry>
#include "eigenpy/geometry.hpp" #include "eigenpy/geometry.hpp"
#include "eigenpy/quaternion.hpp" #include "eigenpy/quaternion.hpp"
EIGENPY_DEFINE_STRUCT_ALLOCATOR_SPECIALIZATION(Eigen::Quaterniond) namespace eigenpy {
void exposeQuaternion() { expose<Eigen::Quaterniond>(); }
namespace eigenpy } // namespace eigenpy
{
void exposeQuaternion()
{
expose<Eigen::Quaterniond>();
}
} // namespace eigenpy
/*
* Copyright 2020-2021 INRIA
*/
#include "eigenpy/register.hpp"
namespace eigenpy {
PyArray_Descr* Register::getPyArrayDescr(PyTypeObject* py_type_ptr) {
MapDescr::iterator it = instance().py_array_descr_bindings.find(py_type_ptr);
if (it != instance().py_array_descr_bindings.end())
return it->second;
else
return NULL;
}
PyArray_Descr* Register::getPyArrayDescrFromTypeNum(const int type_num) {
if (type_num >= NPY_USERDEF) {
for (const auto& elt : instance().py_array_code_bindings) {
if (elt.second == type_num)
return instance().py_array_descr_bindings[elt.first];
}
return nullptr;
} else
return PyArray_DescrFromType(type_num);
}
bool Register::isRegistered(PyTypeObject* py_type_ptr) {
if (getPyArrayDescr(py_type_ptr) != NULL)
return true;
else
return false;
}
int Register::getTypeCode(PyTypeObject* py_type_ptr) {
MapCode::iterator it = instance().py_array_code_bindings.find(py_type_ptr);
if (it != instance().py_array_code_bindings.end())
return it->second;
else
return PyArray_TypeNum(py_type_ptr);
}
int Register::registerNewType(
PyTypeObject* py_type_ptr, const std::type_info* type_info_ptr,
const int type_size, const int alignement, PyArray_GetItemFunc* getitem,
PyArray_SetItemFunc* setitem, PyArray_NonzeroFunc* nonzero,
PyArray_CopySwapFunc* copyswap, PyArray_CopySwapNFunc* copyswapn,
PyArray_DotFunc* dotfunc, PyArray_FillFunc* fill,
PyArray_FillWithScalarFunc* fillwithscalar) {
bp::tuple tp_bases_extended(
bp::make_tuple(bp::handle<>(bp::borrowed(&PyGenericArrType_Type))));
tp_bases_extended +=
bp::tuple(bp::handle<>(bp::borrowed(py_type_ptr->tp_bases)));
Py_INCREF(tp_bases_extended.ptr());
py_type_ptr->tp_bases = tp_bases_extended.ptr();
py_type_ptr->tp_flags &= ~Py_TPFLAGS_READY; // to force the rebuild
// py_type_ptr->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE;
if (PyType_Ready(py_type_ptr) <
0) // Force rebuilding of the __bases__ and mro
{
throw std::invalid_argument("PyType_Ready fails to initialize input type.");
}
PyArray_DescrProto* descr_ptr = new PyArray_DescrProto();
PyArray_DescrProto& descr = *descr_ptr;
descr.typeobj = py_type_ptr;
descr.kind = 'V';
descr.byteorder = '=';
descr.type = 'r';
descr.elsize = type_size;
descr.flags =
NPY_NEEDS_PYAPI | NPY_USE_GETITEM | NPY_USE_SETITEM | NPY_NEEDS_INIT;
descr.type_num = 0;
descr.names = 0;
descr.fields = 0;
descr.alignment =
alignement; // call_PyArray_DescrFromType(NPY_OBJECT)->alignment;
PyArray_ArrFuncs* funcs_ptr = new PyArray_ArrFuncs;
PyArray_ArrFuncs& funcs = *funcs_ptr;
descr.f = funcs_ptr;
call_PyArray_InitArrFuncs(funcs_ptr);
funcs.getitem = getitem;
funcs.setitem = setitem;
funcs.nonzero = nonzero;
funcs.copyswap = copyswap;
funcs.copyswapn = copyswapn;
funcs.dotfunc = dotfunc;
funcs.fill = fill;
funcs.fillwithscalar = fillwithscalar;
// f->cast = cast;
Py_SET_TYPE(descr_ptr, &PyArrayDescr_Type);
const int code = call_PyArray_RegisterDataType(descr_ptr);
assert(code >= 0 && "The return code should be positive");
PyArray_Descr* new_descr = call_PyArray_DescrFromType(code);
if (PyDict_SetItemString(py_type_ptr->tp_dict, "dtype",
(PyObject*)new_descr) < 0) {
throw std::invalid_argument("PyDict_SetItemString fails.");
}
instance().type_to_py_type_bindings.insert(
std::make_pair(type_info_ptr, py_type_ptr));
instance().py_array_descr_bindings[py_type_ptr] = new_descr;
instance().py_array_code_bindings[py_type_ptr] = code;
// PyArray_RegisterCanCast(descr,NPY_OBJECT,NPY_NOSCALAR);
return code;
}
Register& Register::instance() {
static Register self;
return self;
}
} // namespace eigenpy
/*
* Copyright 2024 INRIA
*/
#include "eigenpy/scipy-type.hpp"
#include <patchlevel.h> // For PY_MAJOR_VERSION
namespace eigenpy {
ScipyType& ScipyType::getInstance() {
static ScipyType instance;
return instance;
}
void ScipyType::sharedMemory(const bool value) {
getInstance().shared_memory = value;
}
bool ScipyType::sharedMemory() { return getInstance().shared_memory; }
const PyTypeObject* ScipyType::getScipyCSRMatrixType() {
return getInstance().csr_matrix_type;
}
const PyTypeObject* ScipyType::getScipyCSCMatrixType() {
return getInstance().csc_matrix_type;
}
ScipyType::ScipyType() {
try {
sparse_module = bp::import("scipy.sparse");
} catch (...) {
throw std::runtime_error(
"SciPy is not installed. "
"You can install it using the command \'pip install scipy\'.");
}
#if PY_MAJOR_VERSION >= 3
// TODO I don't know why this Py_INCREF is necessary.
// Without it, the destructor of ScipyType SEGV sometimes.
Py_INCREF(sparse_module.ptr());
#endif
csr_matrix_obj = sparse_module.attr("csr_matrix");
csr_matrix_type = reinterpret_cast<PyTypeObject*>(csr_matrix_obj.ptr());
csc_matrix_obj = sparse_module.attr("csc_matrix");
csc_matrix_type = reinterpret_cast<PyTypeObject*>(csc_matrix_obj.ptr());
shared_memory = true;
}
} // namespace eigenpy
/* /*
* Copyright 2017-2018, Justin Carpentier, LAAS-CNRS * Copyright 2017 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/>.
*/ */
#include <Eigen/Core> #include <Eigen/Core>
#if EIGEN_VERSION_AT_LEAST(3,2,0) #if EIGEN_VERSION_AT_LEAST(3, 2, 0)
#include "eigenpy/solvers/preconditioners.hpp"
#include "eigenpy/solvers/BasicPreconditioners.hpp" #include "eigenpy/solvers/BasicPreconditioners.hpp"
//#include "eigenpy/solvers/BFGSPreconditioners.hpp" #include "eigenpy/solvers/preconditioners.hpp"
// #include "eigenpy/solvers/BFGSPreconditioners.hpp"
namespace eigenpy namespace eigenpy {
{
void exposePreconditioners() void exposePreconditioners() {
{ using namespace Eigen;
using namespace Eigen;
DiagonalPreconditionerVisitor<double>::expose();
DiagonalPreconditionerVisitor<double>::expose(); #if EIGEN_VERSION_AT_LEAST(3, 3, 5)
#if EIGEN_VERSION_AT_LEAST(3,3,5) LeastSquareDiagonalPreconditionerVisitor<double>::expose();
LeastSquareDiagonalPreconditionerVisitor<double>::expose();
#endif #endif
IdentityPreconditionerVisitor::expose(); IdentityPreconditionerVisitor::expose();
// LimitedBFGSPreconditionerBaseVisitor< LimitedBFGSPreconditioner<double,Eigen::Dynamic,Eigen::Upper|Eigen::Lower> >::expose("LimitedBFGSPreconditioner"); // LimitedBFGSPreconditionerBaseVisitor<
// LimitedBFGSPreconditioner<double,Eigen::Dynamic,Eigen::Upper|Eigen::Lower>
} // >::expose("LimitedBFGSPreconditioner");
}
} // namespace eigenpy } // namespace eigenpy
#endif #endif
/* /*
* Copyright 2017-2018, Justin Carpentier, LAAS-CNRS * Copyright 2017-2020 CNRS INRIA
*
* 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/>.
*/ */
#include <Eigen/Core> #include <Eigen/Core>
#if EIGEN_VERSION_AT_LEAST(3,2,0) #if EIGEN_VERSION_AT_LEAST(3, 2, 0)
#include "eigenpy/solvers/solvers.hpp"
#include "eigenpy/solvers/ConjugateGradient.hpp" #include "eigenpy/solvers/ConjugateGradient.hpp"
#include "eigenpy/solvers/solvers.hpp"
#if EIGEN_VERSION_AT_LEAST(3,3,5) #if EIGEN_VERSION_AT_LEAST(3, 3, 5)
#include "eigenpy/solvers/LeastSquaresConjugateGradient.hpp" #include "eigenpy/solvers/LeastSquaresConjugateGradient.hpp"
#endif #endif
namespace eigenpy namespace eigenpy {
{ void exposeSolvers() {
void exposeSolvers() using namespace Eigen;
{ ConjugateGradientVisitor<
using namespace Eigen; ConjugateGradient<MatrixXd, Lower | Upper> >::expose();
ConjugateGradientVisitor< ConjugateGradient<MatrixXd,Lower|Upper> >::expose(); #if EIGEN_VERSION_AT_LEAST(3, 3, 5)
#if EIGEN_VERSION_AT_LEAST(3,3,5) LeastSquaresConjugateGradientVisitor<LeastSquaresConjugateGradient<
LeastSquaresConjugateGradientVisitor< LeastSquaresConjugateGradient<MatrixXd, LeastSquareDiagonalPreconditioner<MatrixXd::Scalar> > >::expose(); MatrixXd,
LeastSquareDiagonalPreconditioner<MatrixXd::Scalar> > >::expose();
#endif #endif
// Conjugate gradient with limited BFGS preconditioner
ConjugateGradientVisitor< ConjugateGradient<MatrixXd,Lower|Upper,IdentityPreconditioner > >::expose("IdentityConjugateGradient");
// ConjugateGradientVisitor< ConjugateGradient<MatrixXd,Lower|Upper,LimitedBFGSPreconditioner<double,Dynamic,Lower|Upper> > >::expose("LimitedBFGSConjugateGradient");
boost::python::enum_<Eigen::ComputationInfo>("ComputationInfo")
.value("Success",Eigen::Success)
.value("NumericalIssue",Eigen::NumericalIssue)
.value("NoConvergence",Eigen::NoConvergence)
.value("InvalidInput",Eigen::InvalidInput)
;
}
} // namespace eigenpy
#endif // Conjugate gradient with limited BFGS preconditioner
ConjugateGradientVisitor<
ConjugateGradient<MatrixXd, Lower | Upper, IdentityPreconditioner> >::
expose("IdentityConjugateGradient");
// ConjugateGradientVisitor<
// ConjugateGradient<MatrixXd,Lower|Upper,LimitedBFGSPreconditioner<double,Dynamic,Lower|Upper>
// > >::expose("LimitedBFGSConjugateGradient");
}
} // namespace eigenpy
#endif
/*
* Copyright 2022, CNRS
* Copyright 2022, INRIA
*/
#include "eigenpy/std-vector.hpp"
namespace eigenpy {
void exposeStdVector() {
exposeStdVectorEigenSpecificType<Eigen::MatrixXd>("MatrixXd");
exposeStdVectorEigenSpecificType<Eigen::VectorXd>("VectorXd");
exposeStdVectorEigenSpecificType<Eigen::MatrixXi>("MatrixXi");
exposeStdVectorEigenSpecificType<Eigen::VectorXi>("VectorXi");
}
} // namespace eigenpy
///
/// Copyright 2024 INRIA
///
#include <typeinfo>
#include <typeindex>
#include <boost/python.hpp>
#include <boost/type_index.hpp>
#include "eigenpy/registration.hpp"
namespace bp = boost::python;
namespace eigenpy {
void exposeStdTypeIndex() {
typedef std::type_index Self;
if (register_symbolic_link_to_registered_type<Self>()) return;
bp::class_<Self>(
"std_type_index",
"The class type_index holds implementation-specific information about a "
"type, including the name of the type and means to compare two types for "
"equality or collating order.",
bp::no_init)
.def(bp::self == bp::self)
.def(bp::self >= bp::self)
.def(bp::self > bp::self)
.def(bp::self < bp::self)
.def(bp::self <= bp::self)
.def("hash_code", &Self::hash_code, bp::arg("self"),
"Returns an unspecified value (here denoted by hash code) such that "
"for all std::type_info objects referring to the same type, their "
"hash code is the same.")
.def("name", &Self::name, bp::arg("self"),
"Returns an implementation defined null-terminated character string "
"containing the name of the type. No guarantees are given; in "
"particular, the returned string can be identical for several types "
"and change between invocations of the same program.")
.def(
"pretty_name",
+[](const Self &value) -> std::string {
return boost::core::demangle(value.name());
},
bp::arg("self"), "Human readible name.");
}
void exposeBoostTypeIndex() {
typedef boost::typeindex::type_index Self;
if (register_symbolic_link_to_registered_type<Self>()) return;
bp::class_<Self>(
"boost_type_index",
"The class type_index holds implementation-specific information about a "
"type, including the name of the type and means to compare two types for "
"equality or collating order.",
bp::no_init)
.def(bp::self == bp::self)
.def(bp::self >= bp::self)
.def(bp::self > bp::self)
.def(bp::self < bp::self)
.def(bp::self <= bp::self)
.def("hash_code", &Self::hash_code, bp::arg("self"),
"Returns an unspecified value (here denoted by hash code) such that "
"for all std::type_info objects referring to the same type, their "
"hash code is the same.")
.def("name", &Self::name, bp::arg("self"),
"Returns an implementation defined null-terminated character string "
"containing the name of the type. No guarantees are given; in "
"particular, the returned string can be identical for several types "
"and change between invocations of the same program.")
.def("pretty_name", &Self::pretty_name, bp::arg("self"),
"Human readible name.");
}
void exposeTypeInfo() {
exposeStdTypeIndex();
exposeBoostTypeIndex();
}
} // namespace eigenpy
//
// Copyright (c) 2019-2023 INRIA
//
#include "eigenpy/version.hpp"
#include "eigenpy/config.hpp"
#include <sstream>
#include <Eigen/Core>
namespace eigenpy {
std::string printVersion(const std::string& delimiter) {
std::ostringstream oss;
oss << EIGENPY_MAJOR_VERSION << delimiter << EIGENPY_MINOR_VERSION
<< delimiter << EIGENPY_PATCH_VERSION;
return oss.str();
}
std::string printEigenVersion(const std::string& delimiter) {
std::ostringstream oss;
oss << EIGEN_MAJOR_VERSION << delimiter << EIGEN_MINOR_VERSION << delimiter
<< EIGEN_MINOR_VERSION;
return oss.str();
}
bool checkVersionAtLeast(unsigned int major_version, unsigned int minor_version,
unsigned int patch_version) {
return EIGENPY_VERSION_AT_LEAST(major_version, minor_version, patch_version);
}
} // namespace eigenpy
#!/bin/bash
set -e
# Setup environment variables.
export CMAKE_ADDITIONAL_OPTIONS=" ${CMAKE_ADDITIONAL_OPTIONS} -DBUILD_BENCHMARK=\"ON\" -DBUILD_UNIT_TESTS=\"ON\" -DCMAKE_CXX_FLAGS=-DBOOST_SYSTEM_NO_DEPRECATED"
if [[ ";${DO_INSTALL_DOC_EXCEPT_ON_BRANCH};" == *";${CI_BRANCH};"* ]]; then
export CMAKE_ADDITIONAL_OPTIONS=" ${CMAKE_ADDITIONAL_OPTIONS} -DINSTALL_DOCUMENTATION=\"OFF\""
else
export CMAKE_ADDITIONAL_OPTIONS=" ${CMAKE_ADDITIONAL_OPTIONS} -DINSTALL_DOCUMENTATION=\"ON\""
fi
# Setup environment variables.
. ./.travis/run ../.travis/build
# #
# Copyright (c) 2014-2019 CNRS # Copyright (c) 2014-2019 CNRS Copyright (c) 2018-2024 INRIA
# Copyright (c) 2018-2019 INRIA
# #
MACRO(ADD_LIB_UNIT_TEST test PKGS) function(ADD_LIB_UNIT_TEST test)
CREATE_CTEST_BUILD_TESTS_TARGET() create_ctest_build_tests_target()
set(test_target ${PROJECT_NAME}-${test})
IF(BUILD_UNIT_TESTS) if(BUILD_TESTING)
ADD_LIBRARY(${test} SHARED ${test}) add_library(${test_target} SHARED "${test}.cpp")
ELSE(BUILD_UNIT_TESTS) else()
ADD_LIBRARY(${test} SHARED EXCLUDE_FROM_ALL ${test}) add_library(${test_target} SHARED EXCLUDE_FROM_ALL "${test}.cpp")
ENDIF(BUILD_UNIT_TESTS) endif()
set_standard_output_directory(${test_target})
FOREACH(PKG ${PKGS}) target_link_libraries(${test_target} PUBLIC ${PROJECT_NAME})
PKG_CONFIG_USE_DEPENDENCY(${test} ${PKG}) set_target_properties(
ENDFOREACH(PKG) ${test_target}
PROPERTIES PREFIX ""
LIBRARY_OUTPUT_NAME ${test}
RUNTIME_OUTPUT_NAME ${test})
TARGET_LINK_LIBRARIES(${test} ${PROJECT_NAME}) set_target_properties(${test_target} PROPERTIES SUFFIX ${PYTHON_EXT_SUFFIX})
TARGET_LINK_BOOST_PYTHON(${test})
SET_TARGET_PROPERTIES(${test} PROPERTIES PREFIX "")
IF(APPLE) add_test(
# We need to change the extension for python bindings NAME ${test_target}
SET_TARGET_PROPERTIES(${test} PROPERTIES SUFFIX ".so") COMMAND ${PYTHON_EXECUTABLE} -c "import ${test}"
ENDIF(APPLE) WORKING_DIRECTORY $<TARGET_FILE_DIR:${test_target}>)
ADD_TEST(NAME ${test} COMMAND ${PYTHON_EXECUTABLE} -c "import ${test}") add_dependencies(build_tests ${test_target})
if(NOT BUILD_TESTING)
set_tests_properties(${test_target} PROPERTIES DEPENDS ctest_build_tests)
endif(NOT BUILD_TESTING)
endfunction()
ADD_DEPENDENCIES(build_tests ${test}) add_dependencies(build_tests ${PYWRAP})
IF(NOT BUILD_UNIT_TESTS) add_lib_unit_test(matrix)
SET_TESTS_PROPERTIES(${test} PROPERTIES DEPENDS ctest_build_tests) add_lib_unit_test(type_info)
ENDIF(NOT BUILD_UNIT_TESTS) add_lib_unit_test(multiple_registration)
ENDMACRO(ADD_LIB_UNIT_TEST) if(BUILD_TESTING_SCIPY)
find_scipy()
add_lib_unit_test(sparse_matrix)
endif()
add_lib_unit_test(tensor)
add_lib_unit_test(geometry)
add_lib_unit_test(complex)
add_lib_unit_test(deprecation_policy)
add_lib_unit_test(return_by_ref)
add_lib_unit_test(include)
if(NOT ${EIGEN3_VERSION} VERSION_LESS "3.2.0")
add_lib_unit_test(eigen_ref)
endif()
ADD_LIB_UNIT_TEST(matrix "eigen3") if(NOT NUMPY_WITH_BROKEN_UFUNC_SUPPORT)
ADD_LIB_UNIT_TEST(geometry "eigen3") add_lib_unit_test(user_type)
IF(NOT ${EIGEN3_VERSION} VERSION_LESS "3.2.0") endif()
ADD_LIB_UNIT_TEST(ref "eigen3") add_lib_unit_test(std_vector)
ENDIF() add_lib_unit_test(std_array)
add_lib_unit_test(std_pair)
add_lib_unit_test(std_map)
add_lib_unit_test(user_struct)
ADD_PYTHON_UNIT_TEST("py-matrix" "unittest/python/test_matrix.py" "unittest") if(CMAKE_CXX_STANDARD GREATER 14 AND CMAKE_CXX_STANDARD LESS 98)
ADD_PYTHON_UNIT_TEST("py-geometry" "unittest/python/test_geometry.py" "unittest") add_lib_unit_test(std_unique_ptr)
endif()
ADD_PYTHON_UNIT_TEST("py-switch" "unittest/python/test_switch.py" "python/eigenpy") function(add_python_lib_unit_test name source)
SET_TESTS_PROPERTIES("py-switch" PROPERTIES DEPENDS ${PYWRAP}) set(test_target ${PROJECT_NAME}-${name})
add_python_unit_test(${test_target} ${source} "lib" "bin")
endfunction()
ADD_PYTHON_UNIT_TEST("py-dimensions" "unittest/python/test_dimensions.py" "python/eigenpy") function(add_python_eigenpy_lib_unit_test name source)
SET_TESTS_PROPERTIES("py-dimensions" PROPERTIES DEPENDS ${PYWRAP}) set(test_target ${PROJECT_NAME}-${name})
add_python_unit_test(${test_target} ${source} "lib" "bin" "python")
set_tests_properties(${test_target} PROPERTIES DEPENDS ${PYWRAP})
endfunction()
function(add_python_eigenpy_unit_test name source)
set(test_target ${PROJECT_NAME}-${name})
add_python_unit_test(${test_target} ${source} "python")
set_tests_properties(${test_target} PROPERTIES DEPENDS ${PYWRAP})
endfunction()
function(config_test test tagname opttype)
set(MODNAME ${test}_${tagname})
set(TEST_TYPE ${opttype})
configure_file(${test}.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/${MODNAME}.cpp)
set(py_file test_${test}_${tagname}.py)
configure_file(python/test_${test}.py.in
${CMAKE_CURRENT_BINARY_DIR}/python/${py_file})
add_lib_unit_test(${MODNAME})
set(PYTHON_TEST_NAME "${PROJECT_NAME}-py-${test}-${tagname}")
add_test(NAME ${PYTHON_TEST_NAME}
COMMAND ${PYTHON_EXECUTABLE}
"${CMAKE_CURRENT_BINARY_DIR}/python/${py_file}")
compute_pythonpath(ENV_VARIABLES "lib" "bin")
set_tests_properties(${PYTHON_TEST_NAME} PROPERTIES ENVIRONMENT
"${ENV_VARIABLES}")
endfunction()
config_test(variant boost "boost::variant")
if(CMAKE_CXX_STANDARD GREATER 14 AND CMAKE_CXX_STANDARD LESS 98)
config_test(variant std "std::variant")
endif()
config_test(bind_optional boost "boost::optional")
if(CMAKE_CXX_STANDARD GREATER 14 AND CMAKE_CXX_STANDARD LESS 98)
config_test(bind_optional std "std::optional")
endif()
add_lib_unit_test(bind_virtual_factory)
add_python_lib_unit_test("py-matrix" "unittest/python/test_matrix.py")
add_python_lib_unit_test("py-type-info" "unittest/python/test_type_info.py")
add_python_lib_unit_test("py-multiple-registration"
"unittest/python/test_multiple_registration.py")
add_python_lib_unit_test("py-tensor" "unittest/python/test_tensor.py")
add_python_lib_unit_test("py-geometry" "unittest/python/test_geometry.py")
add_python_lib_unit_test("py-complex" "unittest/python/test_complex.py")
add_python_lib_unit_test("py-deprecation-policy"
"unittest/python/test_deprecation_policy.py")
add_python_lib_unit_test("py-return-by-ref"
"unittest/python/test_return_by_ref.py")
add_python_lib_unit_test("py-eigen-ref" "unittest/python/test_eigen_ref.py")
if(NOT NUMPY_WITH_BROKEN_UFUNC_SUPPORT)
add_python_lib_unit_test("py-user-type" "unittest/python/test_user_type.py")
endif()
add_python_eigenpy_lib_unit_test("py-dimensions"
"unittest/python/test_dimensions.py")
add_python_eigenpy_lib_unit_test("py-version" "unittest/python/test_version.py")
add_python_eigenpy_lib_unit_test("py-eigen-solver"
"unittest/python/test_eigen_solver.py")
add_python_eigenpy_lib_unit_test(
"py-self-adjoint-eigen-solver"
"unittest/python/test_self_adjoint_eigen_solver.py")
add_python_eigenpy_lib_unit_test("py-LLT" "unittest/python/test_LLT.py")
add_python_eigenpy_lib_unit_test("py-LDLT" "unittest/python/test_LDLT.py")
add_python_eigenpy_lib_unit_test("py-id" "unittest/python/test_id.py")
add_python_eigenpy_lib_unit_test("py-QR" "unittest/python/test_QR.py")
if(NOT WIN32)
add_python_eigenpy_lib_unit_test("py-MINRES" "unittest/python/test_MINRES.py")
endif(NOT WIN32)
add_python_eigenpy_lib_unit_test("py-std-vector"
"unittest/python/test_std_vector.py")
add_python_lib_unit_test("py-std-array" "unittest/python/test_std_array.py")
add_python_lib_unit_test("py-std-map" "unittest/python/test_std_map.py")
add_python_lib_unit_test("py-std-pair" "unittest/python/test_std_pair.py")
add_python_lib_unit_test("py-user-struct" "unittest/python/test_user_struct.py")
if(CMAKE_CXX_STANDARD GREATER 14 AND CMAKE_CXX_STANDARD LESS 98)
add_python_lib_unit_test("py-std-unique-ptr"
"unittest/python/test_std_unique_ptr.py")
endif()
add_python_lib_unit_test("py-bind-virtual"
"unittest/python/test_bind_virtual.py")
if(BUILD_TESTING_SCIPY)
add_python_lib_unit_test("py-sparse-matrix"
"unittest/python/test_sparse_matrix.py")
add_python_eigenpy_unit_test(
"py-SimplicialLLT"
"unittest/python/decompositions/sparse/test_SimplicialLLT.py")
add_python_eigenpy_unit_test(
"py-SimplicialLDLT"
"unittest/python/decompositions/sparse/test_SimplicialLDLT.py")
if(BUILD_WITH_CHOLMOD_SUPPORT)
add_python_eigenpy_unit_test(
"py-CholmodSimplicialLLT"
"unittest/python/decompositions/sparse/cholmod/test_CholmodSimplicialLLT.py"
)
add_python_eigenpy_unit_test(
"py-CholmodSimplicialLDLT"
"unittest/python/decompositions/sparse/cholmod/test_CholmodSimplicialLDLT.py"
)
add_python_eigenpy_unit_test(
"py-CholmodSupernodalLLT"
"unittest/python/decompositions/sparse/cholmod/test_CholmodSupernodalLLT.py"
)
endif(BUILD_WITH_CHOLMOD_SUPPORT)
if(BUILD_WITH_ACCELERATE_SUPPORT)
add_python_eigenpy_unit_test(
"py-Accelerate"
"unittest/python/decompositions/sparse/test_Accelerate.py")
endif(BUILD_WITH_ACCELERATE_SUPPORT)
endif()
/* Simple test using the boost::numpy interface: return an array and a matrix. */
#include <boost/python.hpp>
#include "boost/numpy.hpp"
namespace bp = boost::python;
namespace bn = boost::numpy;
/* Return an dim-1 array with 5 elements. */
bn::ndarray array() {
std::vector<double> v(5);
v[0] = 56;
Py_intptr_t shape[1] = { v.size() };
bn::ndarray result = bn::zeros(1, shape, bn::dtype::get_builtin<double>());
std::copy(v.begin(), v.end(), reinterpret_cast<double*>(result.get_data()));
return result;
}
/* Return a dim-1 matrix with five elements. */
boost::python::object matrix() {
std::vector<double> v(5);
v[0] = 56;
Py_intptr_t shape[1] = { v.size() };
bn::matrix t(bn::zeros(1, shape, bn::dtype::get_builtin<double>()));
std::copy(v.begin(), v.end(), reinterpret_cast<double*>(t.get_data()));
return t;
}
BOOST_PYTHON_MODULE(libbnpy) {
bn::initialize();
bp::def("array", array);
bp::def("matrix", matrix);
}
#include <Eigen/Core>
#include <boost/python.hpp>
#include <boost/numpy.hpp>
namespace boopy
{
namespace bpn = boost::numpy;
namespace bp = boost::python;
struct Eigenvec_to_python_matrix
{
static PyObject* convert(Eigen::VectorXd const& v)
{
Py_intptr_t shape[1] = { v.size() };
bpn::matrix result(bpn::zeros(1, shape, bpn::dtype::get_builtin<double>()));
std::copy(v.data(), v.data()+v.size(), reinterpret_cast<double*>(result.get_data()));
return bp::incref(result.ptr());
}
};
struct Eigenvec_from_python_array
{
Eigenvec_from_python_array()
{
bp::converter::registry
::push_back(&convertible,
&construct,
bp::type_id<Eigen::VectorXd>());
}
// Determine if obj_ptr can be converted in a Eigenvec
static void* convertible(PyObject* obj_ptr)
{
try {
bp::object obj(bp::handle<>(bp::borrowed(obj_ptr)));
std::auto_ptr<bpn::ndarray>
array(new bpn::ndarray(bpn::from_object(obj,
bpn::dtype::get_builtin<double>(),
bpn::ndarray::V_CONTIGUOUS)));
if( (array->get_nd()==1)
|| ( (array->get_nd()==2) && (array->get_shape()[1]==1) ))
return array.release();
else
return 0;
} catch (bp::error_already_set & err) {
bp::handle_exception();
return 0;
}
}
// Convert obj_ptr into a Eigenvec
static void construct(PyObject* ,
bp::converter::rvalue_from_python_stage1_data* memory)
{
// Recover the pointer created in <convertible>
std::auto_ptr<bpn::ndarray>
array(reinterpret_cast<bpn::ndarray*>(memory->convertible));
const int nrow = array->get_shape()[0];
std::cout << "nrow = " << nrow << std::endl;
// Get the memory where to create the vector
void* storage
= ((bp::converter::rvalue_from_python_storage<Eigen::VectorXd>*)memory)
->storage.bytes;
// Create the vector
Eigen::VectorXd & res = * new (storage) Eigen::VectorXd(nrow);
// Copy the data
double * data = (double*)array->get_data();
for(int i=0;i<nrow;++i)
res[i] = data[i];
// Stash the memory chunk pointer for later use by boost.python
memory->convertible = storage;
}
};
}
Eigen::VectorXd test()
{
Eigen::VectorXd v = Eigen::VectorXd::Random(5);
std::cout << v.transpose() << std::endl;
return v;
}
void test2( Eigen::VectorXd v )
{
std::cout << "test2: dim = " << v.size() << " ||| v[0] = " << v[0] << std::endl;
}
BOOST_PYTHON_MODULE(libeigen)
{
namespace bpn = boost::numpy;
namespace bp = boost::python;
bpn::initialize();
bp::to_python_converter<Eigen::VectorXd,
boopy::Eigenvec_to_python_matrix>();
boopy::Eigenvec_from_python_array();
bp::def("test", test);
bp::def("test2", test2);
}
#include <Eigen/Core>
#include <boost/python.hpp>
#include <numpy/arrayobject.h>
namespace boopy
{
namespace bp = boost::python;
template <typename SCALAR> struct NumpyEquivalentType {};
template <> struct NumpyEquivalentType<double> { enum { type_code = NPY_DOUBLE };};
template <> struct NumpyEquivalentType<int> { enum { type_code = NPY_INT };};
template <> struct NumpyEquivalentType<float> { enum { type_code = NPY_FLOAT };};
struct EigenMatrix_to_python_matrix
{
static PyObject* convert(Eigen::MatrixXd const& mat)
{
typedef Eigen::MatrixXd::Scalar T;
npy_intp shape[2] = { mat.rows(), mat.cols() };
PyArrayObject* pyArray = (PyArrayObject*)
PyArray_SimpleNew(2, shape,
NumpyEquivalentType<T>::type_code);
T* pyData = (T*)PyArray_DATA(pyArray);
for(int i=0;i<mat.rows();++i)
for(int j=0;j<mat.cols();++j)
pyData[i*mat.cols()+j] = mat(i,j);
return ((PyObject*)pyArray);
}
};
struct EigenMatrix_from_python_array
{
EigenMatrix_from_python_array()
{
bp::converter::registry
::push_back(&convertible,
&construct,
bp::type_id<Eigen::MatrixXd>());
}
// Determine if obj_ptr can be converted in a Eigenvec
static void* convertible(PyObject* obj_ptr)
{
typedef Eigen::MatrixXd::Scalar T;
if (!PyArray_Check(obj_ptr)) {
return 0;
}
if (PyArray_NDIM(obj_ptr) > 2) {
return 0;
}
if (PyArray_ObjectType(obj_ptr, 0) != NumpyEquivalentType<T>::type_code) {
return 0;
}
int flags = PyArray_FLAGS(obj_ptr);
if (!(flags & NPY_C_CONTIGUOUS)) {
return 0;
}
if (!(flags & NPY_ALIGNED)) {
return 0;
}
return obj_ptr;
}
// Convert obj_ptr into a Eigenvec
static void construct(PyObject* pyObj,
bp::converter::rvalue_from_python_stage1_data* memory)
{
typedef Eigen::MatrixXd::Scalar T;
using namespace Eigen;
PyArrayObject * pyArray = reinterpret_cast<PyArrayObject*>(pyObj);
int ndims = PyArray_NDIM(pyArray);
assert(ndims == 2);
int dtype_size = (PyArray_DESCR(pyArray))->elsize;
int s1 = PyArray_STRIDE(pyArray, 0);
assert(s1 % dtype_size == 0);
int R = MatrixXd::RowsAtCompileTime;
int C = MatrixXd::ColsAtCompileTime;
if (R == Eigen::Dynamic) R = PyArray_DIMS(pyArray)[0];
else assert(PyArray_DIMS(pyArray)[0]==R);
if (C == Eigen::Dynamic) C = PyArray_DIMS(pyArray)[1];
else assert(PyArray_DIMS(pyArray)[1]==C);
T* pyData = reinterpret_cast<T*>(PyArray_DATA(pyArray));
void* storage = ((bp::converter::rvalue_from_python_storage<MatrixXd>*)
(memory))->storage.bytes;
MatrixXd & mat = * new (storage) MatrixXd(R,C);
for(int i=0;i<R;++i)
for(int j=0;j<C;++j)
mat(i,j) = pyData[i*C+j];
memory->convertible = storage;
}
};
}
Eigen::MatrixXd test()
{
Eigen::MatrixXd mat = Eigen::MatrixXd::Random(5,5);
std::cout << "EigenMAt = " << mat << std::endl;
return mat;
}
void test2( Eigen::MatrixXd mat )
{
std::cout << "test2: dim = " << mat.rows() << " ||| m[0,0] = " << mat(0,0) << std::endl;
}
BOOST_PYTHON_MODULE(libeigenc)
{
import_array();
namespace bp = boost::python;
bp::to_python_converter<Eigen::MatrixXd,
boopy::EigenMatrix_to_python_matrix>();
boopy::EigenMatrix_from_python_array();
bp::def("test", test);
bp::def("test2", test2);
}
#include <Eigen/Core>
#include <boost/python.hpp>
#include <numpy/arrayobject.h>
namespace boopy
{
namespace bp = boost::python;
template <typename SCALAR> struct NumpyEquivalentType {};
template <> struct NumpyEquivalentType<double> { enum { type_code = NPY_DOUBLE };};
template <> struct NumpyEquivalentType<int> { enum { type_code = NPY_INT };};
template <> struct NumpyEquivalentType<float> { enum { type_code = NPY_FLOAT };};
/* --- TO PYTHON -------------------------------------------------------------- */
template< typename MatType >
struct EigenMatrix_to_python_matrix
{
static PyObject* convert(MatType const& mat)
{
typedef typename MatType::Scalar T;
const int R = mat.rows(), C = mat.cols();
npy_intp shape[2] = { R,C };
PyArrayObject* pyArray = (PyArrayObject*)
PyArray_SimpleNew(2, shape,
NumpyEquivalentType<T>::type_code);
T* pyData = (T*)PyArray_DATA(pyArray);
Eigen::Map< Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> > pyMatrix(pyData,R,C);
pyMatrix = mat;
return (PyObject*)pyArray;
}
};
/* --- FROM PYTHON ------------------------------------------------------------ */
template<typename MatType>
struct EigenMatrix_from_python_array
{
EigenMatrix_from_python_array()
{
bp::converter::registry
::push_back(&convertible,
&construct,
bp::type_id<MatType>());
}
// Determine if obj_ptr can be converted in a Eigenvec
static void* convertible(PyObject* obj_ptr)
{
typedef typename MatType::Scalar T;
if (!PyArray_Check(obj_ptr)) return 0;
std::cout << "Until here ok. ndim = " << PyArray_NDIM(obj_ptr) << " isvec " << MatType::IsVectorAtCompileTime << std::endl;
if (PyArray_NDIM(obj_ptr) != 2)
if ( (PyArray_NDIM(obj_ptr) !=1) || (! MatType::IsVectorAtCompileTime) )
return 0;
std::cout << "Until here ok." << std::endl;
if (PyArray_ObjectType(obj_ptr, 0) != NumpyEquivalentType<T>::type_code)
return 0;
if (!(PyArray_FLAGS(obj_ptr) & NPY_ALIGNED))
{
std::cerr << "NPY non-aligned matrices are not implemented." << std::endl;
return 0;
}
return obj_ptr;
}
// Convert obj_ptr into a Eigenvec
static void construct(PyObject* pyObj,
bp::converter::rvalue_from_python_stage1_data* memory)
{
typedef typename MatType::Scalar T;
using namespace Eigen;
std::cout << "Until here ok. Constructing..." << std::endl;
PyArrayObject * pyArray = reinterpret_cast<PyArrayObject*>(pyObj);
if ( PyArray_NDIM(pyArray) == 2 )
{
int R = MatType::RowsAtCompileTime;
int C = MatType::ColsAtCompileTime;
if (R == Eigen::Dynamic) R = PyArray_DIMS(pyArray)[0];
else assert(PyArray_DIMS(pyArray)[0]==R);
if (C == Eigen::Dynamic) C = PyArray_DIMS(pyArray)[1];
else assert(PyArray_DIMS(pyArray)[1]==C);
T* pyData = reinterpret_cast<T*>(PyArray_DATA(pyArray));
int itemsize = PyArray_ITEMSIZE(pyArray);
int stride1 = PyArray_STRIDE(pyArray, 0) / itemsize;
int stride2 = PyArray_STRIDE(pyArray, 1) / itemsize;
std::cout << "STRIDE = " << stride1 << " x " << stride2 << std::endl;
Eigen::Map<MatType,0,Eigen::Stride<Eigen::Dynamic,Eigen::Dynamic> >
pyMap( pyData, R,C, Eigen::Stride<Eigen::Dynamic,Eigen::Dynamic>(stride2,stride1) );
std::cout << "Map = " << pyMap << std::endl;
void* storage = ((bp::converter::rvalue_from_python_storage<MatType>*)
(memory))->storage.bytes;
MatType & mat = * new (storage) MatType(R,C);
mat = pyMap;
memory->convertible = storage;
}
else
{
int R = MatType::MaxSizeAtCompileTime, C=1;
if(R==Eigen::Dynamic) R = PyArray_DIMS(pyArray)[0];
else assert(PyArray_DIMS(pyArray)[0]==R);
T* pyData = reinterpret_cast<T*>(PyArray_DATA(pyArray));
int itemsize = PyArray_ITEMSIZE(pyArray);
int stride = PyArray_STRIDE(pyArray, 0) / itemsize;
Eigen::Stride<Eigen::Dynamic,Eigen::Dynamic> s(stride,0);
Eigen::Map<MatType,0,Eigen::InnerStride<Eigen::Dynamic> >
pyMap( pyData, R, 1, Eigen::InnerStride<Eigen::Dynamic>(stride) );
std::cout << "Map = " << pyMap << std::endl;
void* storage = ((bp::converter::rvalue_from_python_storage<MatType>*)
(memory))->storage.bytes;
MatType & mat = * new (storage) MatType(R,C);
mat = pyMap;
memory->convertible = storage;
}
}
};
}
Eigen::MatrixXd test()
{
Eigen::MatrixXd mat = Eigen::MatrixXd::Random(3,6);
std::cout << "EigenMAt = " << mat << std::endl;
return mat;
}
Eigen::VectorXd testVec()
{
Eigen::VectorXd mat = Eigen::VectorXd::Random(6);
std::cout << "EigenVec = " << mat << std::endl;
return mat;
}
void test2( Eigen::MatrixXd mat )
{
std::cout << "Test2 mat = " << mat << std::endl;
}
void test2Vec( Eigen::VectorXd v )
{
std::cout << "Test2 vec = " << v << std::endl;
}
BOOST_PYTHON_MODULE(libeigentemplate)
{
import_array();
namespace bp = boost::python;
bp::to_python_converter<Eigen::MatrixXd,
boopy::EigenMatrix_to_python_matrix<Eigen::MatrixXd> >();
boopy::EigenMatrix_from_python_array<Eigen::MatrixXd>();
bp::to_python_converter<Eigen::VectorXd,
boopy::EigenMatrix_to_python_matrix<Eigen::VectorXd> >();
boopy::EigenMatrix_from_python_array<Eigen::VectorXd>();
bp::def("test", test);
bp::def("testVec", testVec);
bp::def("test2", test2);
bp::def("test2Vec", test2Vec);
}
/* Tutorial with boost::python. Using the converter to access a home-made
* string class and bind it to the python strings. */
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/python/to_python_converter.hpp>
namespace homemadestring
{
/* This is the home-made string class. */
class custom_string
{
public:
custom_string() {}
custom_string(std::string const& value) : value_(value) {}
std::string const& value() const { return value_; }
private:
std::string value_;
};
/* Two simple functions with this class */
custom_string hello() { return custom_string("Hello world."); }
std::size_t size(custom_string const& s) { return s.value().size(); }
/* From c to python converter */
struct custom_string_to_python_str
{
static PyObject* convert(custom_string const& s)
{
return boost::python::incref(boost::python::object(s.value()).ptr());
}
};
struct custom_string_from_python_str
{
custom_string_from_python_str()
{
boost::python::converter::registry
::push_back(&convertible,
&construct,
boost::python::type_id<custom_string>());
}
static void* convertible(PyObject* obj_ptr)
{
if (!PyString_Check(obj_ptr)) return 0;
return obj_ptr;
}
static void
construct(PyObject* obj_ptr,
boost::python::converter::rvalue_from_python_stage1_data* data)
{
const char* value = PyString_AsString(obj_ptr);
if (value == 0) boost::python::throw_error_already_set();
void* storage =
((boost::python::converter::rvalue_from_python_storage<custom_string>*)data)
->storage.bytes;
new (storage) custom_string(value);
data->convertible = storage;
}
};
void init_module()
{
using namespace boost::python;
boost::python::to_python_converter<custom_string,
custom_string_to_python_str>();
custom_string_from_python_str();
def("hello", hello);
def("size", size);
}
} // namespace homemagestring
BOOST_PYTHON_MODULE(libmystring)
{
homemadestring::init_module();
}
/* Simple test with boost::python.
* Declare and bind three function, returning char*, string, and Eigen::Vector. The last
* function raises and error at runtime due to inadequate binding.
*/
#include <boost/python.hpp>
#include <string>
#include <Eigen/Core>
char const* testchar()
{
return "Yay char!";
}
std::string teststr()
{
return "Yay str!";
}
Eigen::VectorXd testeigenvec()
{
Eigen::VectorXd v(555);
return v;
}
BOOST_PYTHON_MODULE(libsimple)
{
using namespace boost::python;
def("char", testchar);
def("str", teststr);
def("eigenvec",testeigenvec);
}
///
/// Copyright (c) 2023 CNRS INRIA
///
#include "eigenpy/eigenpy.hpp"
#include "eigenpy/optional.hpp"
#ifdef EIGENPY_WITH_CXX17_SUPPORT
#include <optional>
#endif
#cmakedefine TEST_TYPE @TEST_TYPE@
#define OPTIONAL TEST_TYPE
typedef eigenpy::detail::nullopt_helper<OPTIONAL> none_helper;
static auto OPT_NONE = none_helper::value();
typedef OPTIONAL<double> opt_dbl;
struct mystruct {
OPTIONAL<int> a;
opt_dbl b;
OPTIONAL<std::string> msg{"i am struct"};
mystruct() : a(OPT_NONE), b(OPT_NONE) {}
mystruct(int a, const opt_dbl &b = OPT_NONE) : a(a), b(b) {}
};
OPTIONAL<int> none_if_zero(int i) {
if (i == 0)
return OPT_NONE;
else
return i;
}
OPTIONAL<mystruct> create_if_true(bool flag, opt_dbl b = OPT_NONE) {
if (flag) {
return mystruct(0, b);
} else {
return OPT_NONE;
}
}
OPTIONAL<Eigen::MatrixXd> random_mat_if_true(bool flag) {
if (flag)
return Eigen::MatrixXd(Eigen::MatrixXd::Random(4, 4));
else
return OPT_NONE;
}
BOOST_PYTHON_MODULE(@MODNAME@) {
using namespace eigenpy;
OptionalConverter<int, OPTIONAL>::registration();
OptionalConverter<double, OPTIONAL>::registration();
OptionalConverter<std::string, OPTIONAL>::registration();
OptionalConverter<mystruct, OPTIONAL>::registration();
OptionalConverter<Eigen::MatrixXd, OPTIONAL>::registration();
enableEigenPy();
bp::class_<mystruct>("mystruct", bp::no_init)
.def(bp::init<>(bp::args("self")))
.def(bp::init<int, bp::optional<const opt_dbl &> >(
bp::args("self", "a", "b")))
.add_property(
"a",
bp::make_getter(&mystruct::a,
bp::return_value_policy<bp::return_by_value>()),
bp::make_setter(&mystruct::a))
.add_property(
"b",
bp::make_getter(&mystruct::b,
bp::return_value_policy<bp::return_by_value>()),
bp::make_setter(&mystruct::b))
.add_property(
"msg",
bp::make_getter(&mystruct::msg,
bp::return_value_policy<bp::return_by_value>()),
bp::make_setter(&mystruct::msg));
bp::def("none_if_zero", none_if_zero, bp::args("i"));
bp::def("create_if_true", create_if_true,
(bp::arg("flag"), bp::arg("b") = OPT_NONE));
bp::def("random_mat_if_true", random_mat_if_true, bp::args("flag"));
}
/// Copyright 2023 LAAS-CNRS, INRIA
#include <eigenpy/eigenpy.hpp>
using std::shared_ptr;
namespace bp = boost::python;
// fwd declaration
struct MyVirtualData;
/// A virtual class with two pure virtual functions taking different signatures,
/// and a polymorphic factory function.
struct MyVirtualClass {
MyVirtualClass() {}
virtual ~MyVirtualClass() {}
// polymorphic fn taking arg by shared_ptr
virtual void doSomethingPtr(shared_ptr<MyVirtualData> const &data) const = 0;
// polymorphic fn taking arg by reference
virtual void doSomethingRef(MyVirtualData &data) const = 0;
virtual shared_ptr<MyVirtualData> createData() const {
return std::make_shared<MyVirtualData>(*this);
}
};
struct MyVirtualData {
MyVirtualData(MyVirtualClass const &) {}
virtual ~MyVirtualData() {} // virtual dtor to mark class as polymorphic
};
shared_ptr<MyVirtualData> callDoSomethingPtr(const MyVirtualClass &obj) {
auto d = obj.createData();
printf("Created MyVirtualData with address %p\n", (void *)d.get());
obj.doSomethingPtr(d);
return d;
}
shared_ptr<MyVirtualData> callDoSomethingRef(const MyVirtualClass &obj) {
auto d = obj.createData();
printf("Created MyVirtualData with address %p\n", (void *)d.get());
obj.doSomethingRef(*d);
return d;
}
void throw_virtual_not_implemented_error() {
throw std::runtime_error("Called C++ virtual function.");
}
/// Wrapper classes
struct VirtualClassWrapper : MyVirtualClass, bp::wrapper<MyVirtualClass> {
void doSomethingPtr(shared_ptr<MyVirtualData> const &data) const override {
if (bp::override fo = this->get_override("doSomethingPtr")) {
/// shared_ptr HAS to be passed by value.
/// Boost.Python's argument converter has the wrong behaviour for
/// reference_wrapper<shared_ptr<T>>, so boost::ref(data) does not work.
fo(data);
return;
}
throw_virtual_not_implemented_error();
}
/// The data object is passed by mutable reference to this function,
/// and wrapped in a @c boost::reference_wrapper when passed to the override.
/// Otherwise, Boost.Python's argument converter will convert to Python by
/// value and create a copy.
void doSomethingRef(MyVirtualData &data) const override {
if (bp::override fo = this->get_override("doSomethingRef")) {
fo(boost::ref(data));
return;
}
throw_virtual_not_implemented_error();
}
shared_ptr<MyVirtualData> createData() const override {
if (bp::override fo = this->get_override("createData")) {
bp::object result = fo().as<bp::object>();
return bp::extract<shared_ptr<MyVirtualData> >(result);
}
return default_createData();
}
shared_ptr<MyVirtualData> default_createData() const {
return MyVirtualClass::createData();
}
};
/// This "trampoline class" does nothing but is ABSOLUTELY required to ensure
/// downcasting works properly with non-smart ptr signatures. Otherwise,
/// there is no handle to the original Python object ( @c PyObject *).
/// Every single polymorphic type exposed to Python should be exposed through
/// such a trampoline. Users can also create their own wrapper classes by taking
/// inspiration from boost::python::wrapper<T>.
struct DataWrapper : MyVirtualData, bp::wrapper<MyVirtualData> {
/// we have to use-declare non-defaulted constructors
/// (see https://en.cppreference.com/w/cpp/language/default_constructor)
/// or define them manually.
using MyVirtualData::MyVirtualData;
};
/// Take and return a const reference
const MyVirtualData &iden_ref(const MyVirtualData &d) {
// try cast to holder
return d;
}
/// Take a shared_ptr (by const reference or value, doesn't matter), return by
/// const reference
const MyVirtualData &iden_shared(const shared_ptr<MyVirtualData> &d) {
// get boost.python's custom deleter
// boost.python hides the handle to the original object in there
// dter being nonzero indicates shared_ptr was wrapped by Boost.Python
auto *dter = std::get_deleter<bp::converter::shared_ptr_deleter>(d);
if (dter != 0) printf("> input shared_ptr has a deleter\n");
return *d;
}
/// Take and return a shared_ptr
shared_ptr<MyVirtualData> copy_shared(const shared_ptr<MyVirtualData> &d) {
auto *dter = std::get_deleter<bp::converter::shared_ptr_deleter>(d);
if (dter != 0) printf("> input shared_ptr has a deleter\n");
return d;
}
BOOST_PYTHON_MODULE(bind_virtual_factory) {
assert(std::is_polymorphic<MyVirtualClass>::value &&
"MyVirtualClass should be polymorphic!");
assert(std::is_polymorphic<MyVirtualData>::value &&
"MyVirtualData should be polymorphic!");
bp::class_<VirtualClassWrapper, boost::noncopyable>(
"MyVirtualClass", bp::init<>(bp::args("self")))
.def("doSomething", bp::pure_virtual(&MyVirtualClass::doSomethingPtr),
bp::args("self", "data"))
.def("doSomethingRef", bp::pure_virtual(&MyVirtualClass::doSomethingRef),
bp::args("self", "data"))
.def("createData", &MyVirtualClass::createData,
&VirtualClassWrapper::default_createData, bp::args("self"));
bp::register_ptr_to_python<shared_ptr<MyVirtualData> >();
/// Trampoline used as 1st argument
/// otherwise if passed as "HeldType", we need to define
/// the constructor and call initializer manually.
bp::class_<DataWrapper, boost::noncopyable>("MyVirtualData", bp::no_init)
.def(bp::init<MyVirtualClass const &>(bp::args("self", "model")));
bp::def("callDoSomethingPtr", callDoSomethingPtr, bp::args("obj"));
bp::def("callDoSomethingRef", callDoSomethingRef, bp::args("obj"));
bp::def("iden_ref", iden_ref, bp::return_internal_reference<>());
bp::def("iden_shared", iden_shared, bp::return_internal_reference<>());
bp::def("copy_shared", copy_shared);
}
/*
* Copyright 2020 INRIA
*/
#include "eigenpy/eigenpy.hpp"
namespace Eigen {
#define EIGEN_MAKE_TYPEDEFS(Type, TypeSuffix, Size, SizeSuffix) \
/** \ingroup matrixtypedefs */ \
typedef Matrix<Type, Size, Size> Matrix##SizeSuffix##TypeSuffix; \
/** \ingroup matrixtypedefs */ \
typedef Matrix<Type, Size, 1> Vector##SizeSuffix##TypeSuffix; \
/** \ingroup matrixtypedefs */ \
typedef Matrix<Type, 1, Size> RowVector##SizeSuffix##TypeSuffix;
#define EIGEN_MAKE_FIXED_TYPEDEFS(Type, TypeSuffix, Size) \
/** \ingroup matrixtypedefs */ \
typedef Matrix<Type, Size, Dynamic> Matrix##Size##X##TypeSuffix; \
/** \ingroup matrixtypedefs */ \
typedef Matrix<Type, Dynamic, Size> Matrix##X##Size##TypeSuffix;
#define EIGEN_MAKE_TYPEDEFS_ALL_SIZES(Type, TypeSuffix) \
EIGEN_MAKE_TYPEDEFS(Type, TypeSuffix, 2, 2) \
EIGEN_MAKE_TYPEDEFS(Type, TypeSuffix, 3, 3) \
EIGEN_MAKE_TYPEDEFS(Type, TypeSuffix, 4, 4) \
EIGEN_MAKE_TYPEDEFS(Type, TypeSuffix, Dynamic, X) \
EIGEN_MAKE_FIXED_TYPEDEFS(Type, TypeSuffix, 2) \
EIGEN_MAKE_FIXED_TYPEDEFS(Type, TypeSuffix, 3) \
EIGEN_MAKE_FIXED_TYPEDEFS(Type, TypeSuffix, 4)
EIGEN_MAKE_TYPEDEFS_ALL_SIZES(long double, ld)
EIGEN_MAKE_TYPEDEFS_ALL_SIZES(std::complex<long double>, cld)
#undef EIGEN_MAKE_TYPEDEFS_ALL_SIZES
#undef EIGEN_MAKE_TYPEDEFS
#undef EIGEN_MAKE_FIXED_TYPEDEFS
} // namespace Eigen
template <typename ComplexMatrix>
typename Eigen::Matrix<typename ComplexMatrix::RealScalar,
ComplexMatrix::RowsAtCompileTime,
ComplexMatrix::ColsAtCompileTime, ComplexMatrix::Options>
real(const Eigen::MatrixBase<ComplexMatrix> &complex_mat) {
return complex_mat.real();
}
template <typename ComplexMatrix>
typename Eigen::Matrix<typename ComplexMatrix::RealScalar,
ComplexMatrix::RowsAtCompileTime,
ComplexMatrix::ColsAtCompileTime, ComplexMatrix::Options>
imag(const Eigen::MatrixBase<ComplexMatrix> &complex_mat) {
return complex_mat.imag();
}
template <typename Scalar, int Rows, int Cols, int Options>
Eigen::Matrix<std::complex<Scalar>, Rows, Cols, Options> ascomplex(
const Eigen::Matrix<Scalar, Rows, Cols, Options> &mat) {
typedef Eigen::Matrix<std::complex<Scalar>, Rows, Cols, Options> ReturnType;
return ReturnType(mat.template cast<std::complex<Scalar> >());
}
BOOST_PYTHON_MODULE(complex) {
using namespace Eigen;
namespace bp = boost::python;
eigenpy::enableEigenPy();
bp::def("ascomplex", ascomplex<float, Eigen::Dynamic, Eigen::Dynamic, 0>);
bp::def("ascomplex", ascomplex<double, Eigen::Dynamic, Eigen::Dynamic, 0>);
bp::def("ascomplex",
ascomplex<long double, Eigen::Dynamic, Eigen::Dynamic, 0>);
bp::def("real",
(MatrixXf(*)(const Eigen::MatrixBase<MatrixXcf> &))&real<MatrixXcf>);
bp::def("real",
(MatrixXd(*)(const Eigen::MatrixBase<MatrixXcd> &))&real<MatrixXcd>);
bp::def(
"real",
(MatrixXld(*)(const Eigen::MatrixBase<MatrixXcld> &))&real<MatrixXcld>);
bp::def("imag",
(MatrixXf(*)(const Eigen::MatrixBase<MatrixXcf> &))&imag<MatrixXcf>);
bp::def("imag",
(MatrixXd(*)(const Eigen::MatrixBase<MatrixXcd> &))&imag<MatrixXcd>);
bp::def(
"imag",
(MatrixXld(*)(const Eigen::MatrixBase<MatrixXcld> &))&imag<MatrixXcld>);
}