Skip to content

Commit

Permalink
Merge pull request #426 from jcarpent/topic/sparse
Browse files Browse the repository at this point in the history
Add support for Eigen::SparseMatrix
  • Loading branch information
jcarpent authored Jan 26, 2024
2 parents 4beb4fb + f0a536a commit 2b550cb
Show file tree
Hide file tree
Showing 28 changed files with 904 additions and 93 deletions.
1 change: 1 addition & 0 deletions .github/workflows/conda/environment_macos_linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ dependencies:
- ccache
- cxx-compiler
- ninja
- scipy
1 change: 1 addition & 0 deletions .github/workflows/conda/environment_windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ dependencies:
- boost
- ccache
- ninja
- scipy
4 changes: 2 additions & 2 deletions .github/workflows/jrl-cmakemodules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: true
- run: sudo apt install libboost-all-dev libeigen3-dev python3-numpy
- run: sudo apt install libboost-all-dev libeigen3-dev python3-numpy python3-scipy
- run: cmake .


Expand All @@ -21,6 +21,6 @@ jobs:
with:
submodules: false
path: eigenpy
- run: sudo apt install libboost-all-dev libeigen3-dev python3-numpy
- run: sudo apt install libboost-all-dev libeigen3-dev python3-numpy python3-scipy
- run: cmake -B build -S eigenpy
- run: grep -qvz CMAKE_PROJECT_VERSION:STATIC=0.0 build/CMakeCache.txt
4 changes: 2 additions & 2 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ jobs:
submodules: 'true'
- run: |
sudo apt-get update
sudo apt-get install cmake libboost-all-dev libeigen3-dev python*-numpy python*-dev
sudo apt-get install cmake libboost-all-dev libeigen3-dev python*-numpy python*-dev python*-scipy
echo $(sudo apt list --installed)
echo $(g++ --version)
- run: cmake -DPYTHON_EXECUTABLE=$(which python${{ matrix.python }}) .
- run: cmake . -DPYTHON_EXECUTABLE=$(which python${{ matrix.python }}) -DBUILD_TESTING_SCIPY=ON
- run: make -j2
- run: make test

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/macos-linux-conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ jobs:
-G "Ninja" \
-DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_TESTING_SCIPY=ON \
-DPYTHON_EXECUTABLE=$(which python3)
- name: Uninstall EigenPy
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/reloc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
run: git -C /RELOC/SRC clone --recursive $(pwd)

- name: install dependencies
run: sudo apt install libboost-all-dev libeigen3-dev python-is-python3 python3-numpy python3-pip ccache
run: sudo apt install libboost-all-dev libeigen3-dev python-is-python3 python3-numpy python3-pip python3-scipy ccache

- name: update CMake
run: pip install -U pip && pip install -U cmake
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [Unreleased]

### Added
- Support for `Eigen::SparseMatrix` types ([#426](https://github.com/stack-of-tasks/eigenpy/pull/426))

### Fixed
- Fix the issue of missing exposition of Eigen types with __int64 scalar type ([#426](https://github.com/stack-of-tasks/eigenpy/pull/426))

## [3.3.0] - 2024-01-23

### Fixed
Expand Down
15 changes: 6 additions & 9 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,8 @@ set(CMAKE_VERBOSE_MAKEFILE True)
# ----------------------------------------------------
option(INSTALL_DOCUMENTATION "Generate and install the documentation" OFF)
option(SUFFIX_SO_VERSION "Suffix library name with its version" OFF)

if(DEFINED BUILD_UNIT_TESTS)
message(
AUTHOR_WARNING
"BUILD_UNIT_TESTS is deprecated. Use BUILD_TESTING instead.\
If you are manually building EigenPy from source in an existing build folder,\
we suggest that you delete your build folder and make a new one.")
set(BUILD_TESTING ${BUILD_UNIT_TESTS})
endif(DEFINED BUILD_UNIT_TESTS)
option(BUILD_TESTING_SCIPY
"Build the SciPy tests (scipy should be installed on the machine)" OFF)

include("${JRL_CMAKE_MODULES}/base.cmake")
compute_project_args(PROJECT_ARGS LANGUAGES CXX)
Expand Down Expand Up @@ -170,6 +163,9 @@ set(${PROJECT_NAME}_HEADERS
include/eigenpy/pickle-vector.hpp
include/eigenpy/stride.hpp
include/eigenpy/tensor/eigen-from-python.hpp
include/eigenpy/sparse/eigen-from-python.hpp
include/eigenpy/scipy-allocator.hpp
include/eigenpy/scipy-type.hpp
include/eigenpy/swig.hpp
include/eigenpy/version.hpp)

Expand Down Expand Up @@ -209,6 +205,7 @@ set(${PROJECT_NAME}_SOURCES
src/angle-axis.cpp
src/quaternion.cpp
src/geometry-conversion.cpp
src/scipy-type.cpp
src/std-vector.cpp
src/optional.cpp
src/version.cpp)
Expand Down
2 changes: 1 addition & 1 deletion cmake
Submodule cmake updated 2 files
+0 −1 pkg-config.cmake
+26 −1 python.cmake
20 changes: 19 additions & 1 deletion include/eigenpy/details.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright 2014-2019, CNRS
* Copyright 2018-2023, INRIA
* Copyright 2018-2024, INRIA
*/

#ifndef __eigenpy_details_hpp__
Expand Down Expand Up @@ -40,6 +40,24 @@ struct expose_eigen_type_impl<MatType, Eigen::MatrixBase<MatType>, Scalar> {
}
};

template <typename MatType, typename Scalar>
struct expose_eigen_type_impl<MatType, Eigen::SparseMatrixBase<MatType>,
Scalar> {
static void run() {
if (check_registration<MatType>()) return;

// to-python
EigenToPyConverter<MatType>::registration();
// #if EIGEN_VERSION_AT_LEAST(3, 2, 0)
// EigenToPyConverter<Eigen::Ref<MatType> >::registration();
// EigenToPyConverter<const Eigen::Ref<const MatType> >::registration();
// #endif

// from-python
EigenFromPyConverter<MatType>::registration();
}
};

#ifdef EIGENPY_WITH_TENSOR_SUPPORT
template <typename TensorType, typename Scalar>
struct expose_eigen_type_impl<TensorType, Eigen::TensorBase<TensorType>,
Expand Down
4 changes: 3 additions & 1 deletion include/eigenpy/eigen-from-python.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ struct copy_if_non_const<const MatType, true> {
template <typename _RefType>
struct referent_storage_eigen_ref {
typedef _RefType RefType;
typedef typename get_eigen_ref_plain_type<RefType>::type PlainObjectType;
typedef typename get_eigen_plain_type<RefType>::type PlainObjectType;
typedef typename ::eigenpy::aligned_storage<
::boost::python::detail::referent_size<RefType &>::value>::type
AlignedStorage;
Expand Down Expand Up @@ -565,4 +565,6 @@ struct EigenFromPy<const Eigen::Ref<const MatType, Options, Stride> > {
#include "eigenpy/tensor/eigen-from-python.hpp"
#endif

#include "eigenpy/sparse/eigen-from-python.hpp"

#endif // __eigenpy_eigen_from_python_hpp__
51 changes: 49 additions & 2 deletions include/eigenpy/eigen-to-python.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (c) 2014-2023 CNRS INRIA
// Copyright (c) 2014-2024 CNRS INRIA
//

#ifndef __eigenpy_eigen_to_python_hpp__
Expand All @@ -11,7 +11,9 @@

#include "eigenpy/eigen-allocator.hpp"
#include "eigenpy/numpy-allocator.hpp"
#include "eigenpy/scipy-allocator.hpp"
#include "eigenpy/numpy-type.hpp"
#include "eigenpy/scipy-type.hpp"
#include "eigenpy/registration.hpp"

namespace boost {
Expand Down Expand Up @@ -116,6 +118,50 @@ struct eigen_to_py_impl_matrix {
// Create an instance (either np.array or np.matrix)
return NumpyType::make(pyArray).ptr();
}

static PyTypeObject const* get_pytype() { return getPyArrayType(); }
};

template <typename MatType>
struct eigen_to_py_impl_sparse_matrix;

template <typename MatType>
struct eigen_to_py_impl<MatType, Eigen::SparseMatrixBase<MatType> >
: eigen_to_py_impl_sparse_matrix<MatType> {};

template <typename MatType>
struct eigen_to_py_impl<MatType&, Eigen::SparseMatrixBase<MatType> >
: eigen_to_py_impl_sparse_matrix<MatType&> {};

template <typename MatType>
struct eigen_to_py_impl<const MatType, const Eigen::SparseMatrixBase<MatType> >
: eigen_to_py_impl_sparse_matrix<const MatType> {};

template <typename MatType>
struct eigen_to_py_impl<const MatType&, const Eigen::SparseMatrixBase<MatType> >
: eigen_to_py_impl_sparse_matrix<const MatType&> {};

template <typename MatType>
struct eigen_to_py_impl_sparse_matrix {
enum { IsRowMajor = MatType::IsRowMajor };

static PyObject* convert(
typename boost::add_reference<
typename boost::add_const<MatType>::type>::type mat) {
typedef typename boost::remove_const<
typename boost::remove_reference<MatType>::type>::type MatrixDerived;

// Allocate and perform the copy
PyObject* pyArray =
ScipyAllocator<MatType>::allocate(const_cast<MatrixDerived&>(mat));

return pyArray;
}

static PyTypeObject const* get_pytype() {
return IsRowMajor ? ScipyType::getScipyCSRMatrixType()
: ScipyType::getScipyCSCMatrixType();
}
};

#ifdef EIGENPY_WITH_TENSOR_SUPPORT
Expand Down Expand Up @@ -149,6 +195,8 @@ struct eigen_to_py_impl_tensor {
// Create an instance (either np.array or np.matrix)
return NumpyType::make(pyArray).ptr();
}

static PyTypeObject const* get_pytype() { return getPyArrayType(); }
};
#endif

Expand All @@ -163,7 +211,6 @@ template <typename EigenType, typename _Scalar>
struct EigenToPy
#endif
: eigen_to_py_impl<EigenType> {
static PyTypeObject const* get_pytype() { return getPyArrayType(); }
};

template <typename MatType>
Expand Down
4 changes: 3 additions & 1 deletion include/eigenpy/eigenpy.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright 2014-2019, CNRS
* Copyright 2018-2023, INRIA
* Copyright 2018-2024, INRIA
*/

#ifndef __eigenpy_eigenpy_hpp__
Expand Down Expand Up @@ -54,6 +54,8 @@ EIGEN_DONT_INLINE void exposeType() {
ENABLE_SPECIFIC_MATRIX_TYPE(VectorXs);
ENABLE_SPECIFIC_MATRIX_TYPE(RowVectorXs);
ENABLE_SPECIFIC_MATRIX_TYPE(MatrixXs);

enableEigenPySpecific<Eigen::SparseMatrix<Scalar, Options> >();
}

template <typename Scalar>
Expand Down
32 changes: 21 additions & 11 deletions include/eigenpy/fwd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ namespace bp = boost::python;
#undef BOOST_BIND_GLOBAL_PLACEHOLDERS

#include <Eigen/Core>
#include <Eigen/Sparse>
#include <Eigen/Geometry>

#ifdef EIGENPY_WITH_CXX11_SUPPORT
Expand All @@ -108,6 +109,12 @@ namespace bp = boost::python;

#define EIGENPY_UNUSED_VARIABLE(var) (void)(var)
#define EIGENPY_UNUSED_TYPE(type) EIGENPY_UNUSED_VARIABLE((type *)(NULL))
#ifndef NDEBUG
#define EIGENPY_USED_VARIABLE_ONLY_IN_DEBUG_MODE(var)
#else
#define EIGENPY_USED_VARIABLE_ONLY_IN_DEBUG_MODE(var) \
EIGENPY_UNUSED_VARIABLE(var)
#endif

#ifdef EIGENPY_WITH_CXX11_SUPPORT
#include <memory>
Expand Down Expand Up @@ -138,35 +145,38 @@ struct get_eigen_base_type {
typedef typename remove_const_reference<EigenType>::type EigenType_;
typedef typename boost::mpl::if_<
boost::is_base_of<Eigen::MatrixBase<EigenType_>, EigenType_>,
Eigen::MatrixBase<EigenType_>
#ifdef EIGENPY_WITH_TENSOR_SUPPORT
,
Eigen::MatrixBase<EigenType_>,
typename boost::mpl::if_<
boost::is_base_of<Eigen::TensorBase<EigenType_>, EigenType_>,
Eigen::TensorBase<EigenType_>, void>::type
boost::is_base_of<Eigen::SparseMatrixBase<EigenType_>, EigenType_>,
Eigen::SparseMatrixBase<EigenType_>
#ifdef EIGENPY_WITH_TENSOR_SUPPORT
,
typename boost::mpl::if_<
boost::is_base_of<Eigen::TensorBase<EigenType_>, EigenType_>,
Eigen::TensorBase<EigenType_>, void>::type
#else
,
void
,
void
#endif
>::type _type;
>::type>::type _type;

typedef typename boost::mpl::if_<
boost::is_const<typename boost::remove_reference<EigenType>::type>,
const _type, _type>::type type;
};

template <typename EigenType>
struct get_eigen_ref_plain_type;
struct get_eigen_plain_type;

template <typename MatType, int Options, typename Stride>
struct get_eigen_ref_plain_type<Eigen::Ref<MatType, Options, Stride> > {
struct get_eigen_plain_type<Eigen::Ref<MatType, Options, Stride> > {
typedef typename Eigen::internal::traits<
Eigen::Ref<MatType, Options, Stride> >::PlainObjectType type;
};

#ifdef EIGENPY_WITH_TENSOR_SUPPORT
template <typename TensorType>
struct get_eigen_ref_plain_type<Eigen::TensorRef<TensorType> > {
struct get_eigen_plain_type<Eigen::TensorRef<TensorType> > {
typedef TensorType type;
};
#endif
Expand Down
11 changes: 9 additions & 2 deletions include/eigenpy/numpy-type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,19 @@ bool np_type_is_convertible_into_scalar(const int np_type) {
if (NumpyEquivalentType<Scalar>::type_code == np_type) return true;

switch (np_type) {
#ifdef WIN32
case NPY_INT:
case NPY_LONG:
return FromTypeToType<int, Scalar>::value;
case NPY_LONGLONG:
return FromTypeToType<__int64, Scalar>::value;
#else
case NPY_INT:
return FromTypeToType<int, Scalar>::value;
case NPY_LONG:
case NPY_LONGLONG:
return FromTypeToType<long, Scalar>::value;
#endif
case NPY_FLOAT:
return FromTypeToType<float, Scalar>::value;
case NPY_CFLOAT:
Expand Down Expand Up @@ -56,8 +65,6 @@ struct EIGENPY_DLLAPI NumpyType {

static bool sharedMemory();

static bp::object getNumpyType();

static const PyTypeObject* getNumpyArrayType();

protected:
Expand Down
19 changes: 19 additions & 0 deletions include/eigenpy/register.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>());
Expand Down
Loading

0 comments on commit 2b550cb

Please sign in to comment.