From 670a147cf73760b2400b16022609bf57771e0a6a Mon Sep 17 00:00:00 2001 From: Joris Vaillant <joris.vaillant@inria.fr> Date: Wed, 3 Apr 2024 14:23:46 +0200 Subject: [PATCH] int_conv: Fix specific scalar conversion and support more scalar type int_conv: Add more integer type binding and add a unit test changelog: Add changelog entry int_conv: Manage int8_t and long on Windows int_conv: Try to use stdint type to manage long hack on windows int_conv: Fix Windows build int_conv: Try to fix Mac build and don't compile twice 64 bits integer on Mac and Windows int_conv: Don't use np.dtypes (introduced in numpy 1.25) int_conv: Test long and int difference in unit test [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci int_conv: Expose long in MacOS int_conv: Don't use deprecated type np.intc int_conv: Fix Mac build int_conv: Remove int128 (doesn't exists) int_conv: Disable long long test on Mac because the type is not reachable int_conv: Begin to work on promotion int_conv: Add unsigned promotion int_conv: Manage complex promotion int_conv: Split matrix binding to avoid memory issue while building int_conv: Split decomposition binding to avoid memory issue while building int_conv: Use clongdouble instead of complex256 int_conv: Avoid out of heap space error on Windows int_conv: Fix test for Mac int_conv: Try to manage longlong on Mac and int on Windows, also reduce compile time int_conv: Manage Windows and Mac int and long long when casting int_conv: Allow scalar to complex conversion int_conv: Manage casting from numpy to eigen with tensor int_conv: Add changelog entries int_conv: Remove conversion from Eigen to Numpy int_conv: Add reference to this PR int_conv: Activate SciPy tests by default --- .github/workflows/windows-conda.yml | 2 +- CHANGELOG.md | 10 + CMakeLists.txt | 30 ++- include/eigenpy/eigen-allocator.hpp | 241 +++++++----------- include/eigenpy/numpy-type.hpp | 56 +++- include/eigenpy/numpy.hpp | 114 +++++++-- include/eigenpy/scalar-conversion.hpp | 15 +- src/decompositions/decompositions.cpp | 40 ++- src/decompositions/eigen-solver.cpp | 13 + src/decompositions/ldlt-solver.cpp | 12 + src/decompositions/llt-solver.cpp | 12 + src/decompositions/minres-solver.cpp | 12 + src/decompositions/permutation-matrix.cpp | 12 + src/decompositions/seigen-solver.cpp | 13 + .../self-adjoint-eigen-solver.cpp | 13 + src/decompositions/simplicial-ldlt-solver.cpp | 13 + src/decompositions/simplicial-llt-solver.cpp | 13 + src/eigenpy.cpp | 34 ++- src/matrix-char.cpp | 14 + src/matrix-int.cpp | 12 - src/matrix-int16.cpp | 14 + src/matrix-int32.cpp | 14 + src/matrix-int64.cpp | 14 + src/matrix-int8.cpp | 14 + src/matrix-linux-long-long.cpp | 15 ++ src/matrix-linux-ulong-long.cpp | 15 ++ src/matrix-mac-long.cpp | 15 ++ src/matrix-mac-ulong.cpp | 15 ++ src/matrix-uint16.cpp | 14 + src/matrix-uint32.cpp | 14 + src/matrix-uint64.cpp | 14 + src/matrix-uint8.cpp | 14 + ...atrix-long.cpp => matrix-windows-long.cpp} | 10 +- src/matrix-windows-ulong.cpp | 15 ++ unittest/matrix.cpp | 40 +++ unittest/python/test_matrix.py | 166 ++++++++++++ unittest/python/test_user_type.py | 2 +- 37 files changed, 849 insertions(+), 237 deletions(-) create mode 100644 src/decompositions/eigen-solver.cpp create mode 100644 src/decompositions/ldlt-solver.cpp create mode 100644 src/decompositions/llt-solver.cpp create mode 100644 src/decompositions/minres-solver.cpp create mode 100644 src/decompositions/permutation-matrix.cpp create mode 100644 src/decompositions/seigen-solver.cpp create mode 100644 src/decompositions/self-adjoint-eigen-solver.cpp create mode 100644 src/decompositions/simplicial-ldlt-solver.cpp create mode 100644 src/decompositions/simplicial-llt-solver.cpp create mode 100644 src/matrix-char.cpp delete mode 100644 src/matrix-int.cpp create mode 100644 src/matrix-int16.cpp create mode 100644 src/matrix-int32.cpp create mode 100644 src/matrix-int64.cpp create mode 100644 src/matrix-int8.cpp create mode 100644 src/matrix-linux-long-long.cpp create mode 100644 src/matrix-linux-ulong-long.cpp create mode 100644 src/matrix-mac-long.cpp create mode 100644 src/matrix-mac-ulong.cpp create mode 100644 src/matrix-uint16.cpp create mode 100644 src/matrix-uint32.cpp create mode 100644 src/matrix-uint64.cpp create mode 100644 src/matrix-uint8.cpp rename src/{matrix-long.cpp => matrix-windows-long.cpp} (51%) create mode 100644 src/matrix-windows-ulong.cpp diff --git a/.github/workflows/windows-conda.yml b/.github/workflows/windows-conda.yml index b3f8ad81..6315c0f4 100644 --- a/.github/workflows/windows-conda.yml +++ b/.github/workflows/windows-conda.yml @@ -58,7 +58,7 @@ jobs: if errorlevel 1 exit 1 :: Build and Install - cmake --build . --config Release --target install + cmake --build . -j3 --config Release --target install if errorlevel 1 exit 1 :: Testing diff --git a/CHANGELOG.md b/CHANGELOG.md index 18791046..12eeb536 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added - Allow use of installed JRL-cmakemodule ([#446](https://github.com/stack-of-tasks/eigenpy/pull/446) - Support of Numpy 2.0.0b1 ([#448](https://github.com/stack-of-tasks/eigenpy/pull/448)) +- Support new primitive type (char, int8_t, uint8_t, int16_t, uint16_t, uint32_t, uint64_t) ([#455]()https://github.com/stack-of-tasks/eigenpy/pull/455) +- Support conversion between signed <-> unsigned integers ([#455](https://github.com/stack-of-tasks/eigenpy/pull/455)) +- Support conversion between complex numbers ([#455](https://github.com/stack-of-tasks/eigenpy/pull/455)) ### Fixed - Fix unit test build in C++11 ([#442](https://github.com/stack-of-tasks/eigenpy/pull/442)) - Fix unit test function signature [#443](https://github.com/stack-of-tasks/eigenpy/pull/443)) - Fix CMake export ([#446](https://github.com/stack-of-tasks/eigenpy/pull/446) +- Fix `int` management on Windows ([#455](https://github.com/stack-of-tasks/eigenpy/pull/455)) +- Fix `long long` management on Mac ([#455](https://github.com/stack-of-tasks/eigenpy/pull/455)) + +### Removed +- Remove casting when converting from Eigen scalar to Numpy scalar. + This should not remove any functionality since Numpy array are created from the Eigen scalar type + ([#455](https://github.com/stack-of-tasks/eigenpy/pull/455)) ## [3.4.0] - 2024-02-26 diff --git a/CMakeLists.txt b/CMakeLists.txt index 1fc16e5d..47502c23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,7 +54,7 @@ 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) option(BUILD_TESTING_SCIPY - "Build the SciPy tests (scipy should be installed on the machine)" OFF) + "Build the SciPy tests (scipy should be installed on the machine)" ON) include("${JRL_CMAKE_MODULES}/base.cmake") compute_project_args(PROJECT_ARGS LANGUAGES CXX) @@ -267,7 +267,16 @@ set(${PROJECT_NAME}_SOLVERS_SOURCES src/solvers/preconditioners.cpp src/solvers/solvers.cpp) set(${PROJECT_NAME}_DECOMPOSITIONS_SOURCES - src/decompositions/decompositions.cpp) + src/decompositions/decompositions.cpp + src/decompositions/eigen-solver.cpp + src/decompositions/llt-solver.cpp + src/decompositions/ldlt-solver.cpp + src/decompositions/minres-solver.cpp + src/decompositions/eigen-solver.cpp + src/decompositions/self-adjoint-eigen-solver.cpp + src/decompositions/permutation-matrix.cpp + src/decompositions/simplicial-llt-solver.cpp + src/decompositions/simplicial-ldlt-solver.cpp) if(BUILD_WITH_CHOLMOD_SUPPORT) list(APPEND ${PROJECT_NAME}_DECOMPOSITIONS_SOURCES @@ -294,8 +303,21 @@ set(${PROJECT_NAME}_SOURCES src/matrix-long-double.cpp src/matrix-complex-long-double.cpp src/matrix-bool.cpp - src/matrix-int.cpp - src/matrix-long.cpp + src/matrix-char.cpp + src/matrix-int8.cpp + src/matrix-uint8.cpp + src/matrix-int16.cpp + src/matrix-uint16.cpp + src/matrix-int32.cpp + src/matrix-uint32.cpp + src/matrix-windows-long.cpp + src/matrix-windows-ulong.cpp + src/matrix-mac-long.cpp + src/matrix-mac-ulong.cpp + src/matrix-int64.cpp + src/matrix-uint64.cpp + src/matrix-linux-long-long.cpp + src/matrix-linux-ulong-long.cpp src/angle-axis.cpp src/quaternion.cpp src/geometry-conversion.cpp diff --git a/include/eigenpy/eigen-allocator.hpp b/include/eigenpy/eigen-allocator.hpp index 7c31c56f..3b47a9a1 100644 --- a/include/eigenpy/eigen-allocator.hpp +++ b/include/eigenpy/eigen-allocator.hpp @@ -198,6 +198,91 @@ struct cast<Scalar, NewScalar, EigenBase, false> { mat, NumpyMap<MatType, NewScalar>::map( \ pyArray, details::check_swap(pyArray, mat))) +// Define specific cast for Windows and Mac +#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 +#define EIGENPY_CAST_FROM_NUMPY_TO_EIGEN_SWITCH_OS_SPECIFIC( \ + MatType, Scalar, pyArray, mat, CAST_MACRO) \ + case NPY_INT: \ + CAST_MACRO(MatType, int32_t, Scalar, pyArray, mat); \ + break; \ + case NPY_UINT: \ + CAST_MACRO(MatType, uint32_t, Scalar, pyArray, mat); \ + break; +#elif 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 +#define EIGENPY_CAST_FROM_NUMPY_TO_EIGEN_SWITCH_OS_SPECIFIC( \ + MatType, Scalar, pyArray, mat, CAST_MACRO) \ + case NPY_LONGLONG: \ + CAST_MACRO(MatType, int64_t, Scalar, pyArray, mat); \ + break; \ + case NPY_ULONGLONG: \ + CAST_MACRO(MatType, uint64_t, Scalar, pyArray, mat); \ + break; +#else +#define EIGENPY_CAST_FROM_NUMPY_TO_EIGEN_SWITCH_OS_SPECIFIC( \ + MatType, Scalar, pyArray, mat, CAST_MACRO) +#endif + +/// Define casting between Numpy matrix type to Eigen type. +#define EIGENPY_CAST_FROM_NUMPY_TO_EIGEN_SWITCH( \ + pyArray_type_code, MatType, Scalar, pyArray, mat, CAST_MACRO) \ + switch (pyArray_type_code) { \ + case NPY_BOOL: \ + CAST_MACRO(MatType, bool, Scalar, pyArray, mat); \ + break; \ + case NPY_INT8: \ + CAST_MACRO(MatType, int8_t, Scalar, pyArray, mat); \ + break; \ + case NPY_INT16: \ + CAST_MACRO(MatType, int16_t, Scalar, pyArray, mat); \ + break; \ + case NPY_INT32: \ + CAST_MACRO(MatType, int32_t, Scalar, pyArray, mat); \ + break; \ + case NPY_INT64: \ + CAST_MACRO(MatType, int64_t, Scalar, pyArray, mat); \ + break; \ + case NPY_UINT8: \ + CAST_MACRO(MatType, uint8_t, Scalar, pyArray, mat); \ + break; \ + case NPY_UINT16: \ + CAST_MACRO(MatType, uint16_t, Scalar, pyArray, mat); \ + break; \ + case NPY_UINT32: \ + CAST_MACRO(MatType, uint32_t, Scalar, pyArray, mat); \ + break; \ + case NPY_UINT64: \ + CAST_MACRO(MatType, uint64_t, Scalar, pyArray, mat); \ + break; \ + case NPY_FLOAT: \ + CAST_MACRO(MatType, float, Scalar, pyArray, mat); \ + break; \ + case NPY_CFLOAT: \ + CAST_MACRO(MatType, std::complex<float>, Scalar, pyArray, mat); \ + break; \ + case NPY_DOUBLE: \ + CAST_MACRO(MatType, double, Scalar, pyArray, mat); \ + break; \ + case NPY_CDOUBLE: \ + CAST_MACRO(MatType, std::complex<double>, Scalar, pyArray, mat); \ + break; \ + case NPY_LONGDOUBLE: \ + CAST_MACRO(MatType, long double, Scalar, pyArray, mat); \ + break; \ + case NPY_CLONGDOUBLE: \ + CAST_MACRO(MatType, std::complex<long double>, Scalar, pyArray, mat); \ + break; \ + EIGENPY_CAST_FROM_NUMPY_TO_EIGEN_SWITCH_OS_SPECIFIC( \ + MatType, Scalar, pyArray, mat, CAST_MACRO) \ + default: \ + throw Exception("You asked for a conversion which is not implemented."); \ + } + template <typename EigenType> struct EigenAllocator; @@ -247,43 +332,9 @@ struct eigen_allocator_impl_matrix { pyArray, details::check_swap(pyArray, mat)); // avoid useless cast return; } - - switch (pyArray_type_code) { - case NPY_INT: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType, int, Scalar, pyArray, - mat); - break; - case NPY_LONG: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType, long, Scalar, - pyArray, mat); - break; - case NPY_FLOAT: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType, float, Scalar, - pyArray, mat); - break; - case NPY_CFLOAT: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType, std::complex<float>, - Scalar, pyArray, mat); - break; - case NPY_DOUBLE: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType, double, Scalar, - pyArray, mat); - break; - case NPY_CDOUBLE: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType, std::complex<double>, - Scalar, pyArray, mat); - break; - case NPY_LONGDOUBLE: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType, long double, Scalar, - pyArray, mat); - break; - case NPY_CLONGDOUBLE: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX( - MatType, std::complex<long double>, Scalar, pyArray, mat); - break; - default: - throw Exception("You asked for a conversion which is not implemented."); - } + EIGENPY_CAST_FROM_NUMPY_TO_EIGEN_SWITCH( + pyArray_type_code, MatType, Scalar, pyArray, mat, + EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX); } /// \brief Copy mat into the Python array using Eigen::Map @@ -301,43 +352,8 @@ struct eigen_allocator_impl_matrix { details::check_swap(pyArray, mat)) = mat; return; } - - switch (pyArray_type_code) { - case NPY_INT: - EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType, Scalar, int, mat, - pyArray); - break; - case NPY_LONG: - EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType, Scalar, long, mat, - pyArray); - break; - case NPY_FLOAT: - EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType, Scalar, float, mat, - pyArray); - break; - case NPY_CFLOAT: - EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY( - MatType, Scalar, std::complex<float>, mat, pyArray); - break; - case NPY_DOUBLE: - EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType, Scalar, double, mat, - pyArray); - break; - case NPY_CDOUBLE: - EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY( - MatType, Scalar, std::complex<double>, mat, pyArray); - break; - case NPY_LONGDOUBLE: - EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType, Scalar, long double, - mat, pyArray); - break; - case NPY_CLONGDOUBLE: - EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY( - MatType, Scalar, std::complex<long double>, mat, pyArray); - break; - default: - throw Exception("You asked for a conversion which is not implemented."); - } + throw Exception( + "Scalar conversion from Eigen to Numpy is not implemented."); } }; @@ -394,42 +410,9 @@ struct eigen_allocator_impl_tensor { return; } - switch (pyArray_type_code) { - case NPY_INT: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_TENSOR(TensorType, int, Scalar, - pyArray, tensor); - break; - case NPY_LONG: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_TENSOR(TensorType, long, Scalar, - pyArray, tensor); - break; - case NPY_FLOAT: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_TENSOR(TensorType, float, Scalar, - pyArray, tensor); - break; - case NPY_CFLOAT: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_TENSOR( - TensorType, std::complex<float>, Scalar, pyArray, tensor); - break; - case NPY_DOUBLE: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_TENSOR(TensorType, double, Scalar, - pyArray, tensor); - break; - case NPY_CDOUBLE: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_TENSOR( - TensorType, std::complex<double>, Scalar, pyArray, tensor); - break; - case NPY_LONGDOUBLE: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_TENSOR(TensorType, long double, - Scalar, pyArray, tensor); - break; - case NPY_CLONGDOUBLE: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_TENSOR( - TensorType, std::complex<long double>, Scalar, pyArray, tensor); - break; - default: - throw Exception("You asked for a conversion which is not implemented."); - } + EIGENPY_CAST_FROM_NUMPY_TO_EIGEN_SWITCH( + pyArray_type_code, TensorType, Scalar, pyArray, tensor, + EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_TENSOR); } #define EIGENPY_CAST_FROM_EIGEN_TENSOR_TO_PYARRAY(TensorType, Scalar, \ @@ -454,42 +437,8 @@ struct eigen_allocator_impl_tensor { return; } - switch (pyArray_type_code) { - case NPY_INT: - EIGENPY_CAST_FROM_EIGEN_TENSOR_TO_PYARRAY(TensorType, Scalar, int, - tensor, pyArray); - break; - case NPY_LONG: - EIGENPY_CAST_FROM_EIGEN_TENSOR_TO_PYARRAY(TensorType, Scalar, long, - tensor, pyArray); - break; - case NPY_FLOAT: - EIGENPY_CAST_FROM_EIGEN_TENSOR_TO_PYARRAY(TensorType, Scalar, float, - tensor, pyArray); - break; - case NPY_CFLOAT: - EIGENPY_CAST_FROM_EIGEN_TENSOR_TO_PYARRAY( - TensorType, Scalar, std::complex<float>, tensor, pyArray); - break; - case NPY_DOUBLE: - EIGENPY_CAST_FROM_EIGEN_TENSOR_TO_PYARRAY(TensorType, Scalar, double, - tensor, pyArray); - break; - case NPY_CDOUBLE: - EIGENPY_CAST_FROM_EIGEN_TENSOR_TO_PYARRAY( - TensorType, Scalar, std::complex<double>, tensor, pyArray); - break; - case NPY_LONGDOUBLE: - EIGENPY_CAST_FROM_EIGEN_TENSOR_TO_PYARRAY(TensorType, Scalar, - long double, tensor, pyArray); - break; - case NPY_CLONGDOUBLE: - EIGENPY_CAST_FROM_EIGEN_TENSOR_TO_PYARRAY( - TensorType, Scalar, std::complex<long double>, tensor, pyArray); - break; - default: - throw Exception("You asked for a conversion which is not implemented."); - } + throw Exception( + "Scalar conversion from Eigen to Numpy is not implemented."); } }; #endif diff --git a/include/eigenpy/numpy-type.hpp b/include/eigenpy/numpy-type.hpp index 25dee99f..c57a914d 100644 --- a/include/eigenpy/numpy-type.hpp +++ b/include/eigenpy/numpy-type.hpp @@ -17,26 +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) { -#ifdef WIN32 - case NPY_INT: - case NPY_LONG: - return FromTypeToType<int, Scalar>::value; - case NPY_LONGLONG: - return FromTypeToType<__int64, Scalar>::value; -#else + 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<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<long, Scalar>::value; -#endif + 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: diff --git a/include/eigenpy/numpy.hpp b/include/eigenpy/numpy.hpp index 3529ec25..3786b091 100644 --- a/include/eigenpy/numpy.hpp +++ b/include/eigenpy/numpy.hpp @@ -61,57 +61,119 @@ struct NumpyEquivalentType { }; 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<int32_t> { + enum { type_code = NPY_INT32 }; }; template <> -struct NumpyEquivalentType<bool> { - enum { type_code = NPY_BOOL }; +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<int> { - enum { type_code = NPY_INT }; +struct NumpyEquivalentType<long> { + enum { type_code = NPY_INT32 }; }; template <> -struct NumpyEquivalentType<unsigned int> { - enum { type_code = NPY_UINT }; +struct NumpyEquivalentType<unsigned long> { + enum { type_code = NPY_UINT32 }; }; + +#endif // WIN32 + +template <> +struct NumpyEquivalentType<int64_t> { + enum { type_code = NPY_INT64 }; +}; +template <> +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<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__ + template <> struct NumpyEquivalentType<long long> { enum { type_code = NPY_LONGLONG }; }; -// #else -// template <> struct NumpyEquivalentType<long long> { enum { type_code = -// NPY_LONGLONG };}; -// #endif template <> -struct NumpyEquivalentType<unsigned long> { - enum { type_code = NPY_ULONG }; +struct NumpyEquivalentType<unsigned long long> { + enum { type_code = NPY_ULONGLONG }; +}; + +#endif // Linux + +template <> +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> diff --git a/include/eigenpy/scalar-conversion.hpp b/include/eigenpy/scalar-conversion.hpp index 8a8532d9..18a50a21 100644 --- a/include/eigenpy/scalar-conversion.hpp +++ b/include/eigenpy/scalar-conversion.hpp @@ -7,11 +7,24 @@ #include "eigenpy/config.hpp" #include <boost/numeric/conversion/conversion_traits.hpp> +#include <complex> namespace eigenpy { + template <typename Source, typename Target> struct FromTypeToType - : public boost::numeric::conversion_traits<Source, Target>::subranged {}; + : 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 {}; } // namespace eigenpy diff --git a/src/decompositions/decompositions.cpp b/src/decompositions/decompositions.cpp index 2b58c03d..a497db1e 100644 --- a/src/decompositions/decompositions.cpp +++ b/src/decompositions/decompositions.cpp @@ -4,29 +4,27 @@ #include "eigenpy/decompositions/decompositions.hpp" -#include "eigenpy/decompositions/EigenSolver.hpp" -#include "eigenpy/decompositions/LDLT.hpp" -#include "eigenpy/decompositions/LLT.hpp" -#include "eigenpy/decompositions/PermutationMatrix.hpp" -#include "eigenpy/decompositions/sparse/LLT.hpp" -#include "eigenpy/decompositions/sparse/LDLT.hpp" -#include "eigenpy/decompositions/SelfAdjointEigenSolver.hpp" -#include "eigenpy/decompositions/minres.hpp" #include "eigenpy/fwd.hpp" namespace eigenpy { -void exposeDecompositions() { - using namespace Eigen; - typedef Eigen::SparseMatrix<double, Eigen::ColMajor> ColMajorSparseMatrix; - // typedef Eigen::SparseMatrix<double,Eigen::RowMajor> RowMajorSparseMatrix; +void exposeEigenSolver(); +void exposeSelfAdjointEigenSolver(); +void exposeLLTSolver(); +void exposeLDLTSolver(); +void exposeMINRESSolver(); +void exposeSimplicialLLTSolver(); +void exposeSimplicialLDLTSolver(); +void exposePermutationMatrix(); - EigenSolverVisitor<MatrixXd>::expose("EigenSolver"); - SelfAdjointEigenSolverVisitor<MatrixXd>::expose("SelfAdjointEigenSolver"); - LLTSolverVisitor<MatrixXd>::expose("LLT"); - LDLTSolverVisitor<MatrixXd>::expose("LDLT"); +void exposeDecompositions() { + using namespace Eigen; - MINRESSolverVisitor<MatrixXd>::expose("MINRES"); + exposeEigenSolver(); + exposeSelfAdjointEigenSolver(); + exposeLLTSolver(); + exposeLDLTSolver(); + exposeMINRESSolver(); { bp::enum_<DecompositionOptions>("DecompositionOptions") @@ -42,12 +40,10 @@ void exposeDecompositions() { } // Expose sparse decompositions - { - SimplicialLLTVisitor<ColMajorSparseMatrix>::expose("SimplicialLLT"); - SimplicialLDLTVisitor<ColMajorSparseMatrix>::expose("SimplicialLDLT"); - } + exposeSimplicialLLTSolver(); + exposeSimplicialLDLTSolver(); - PermutationMatrixVisitor<Eigen::Dynamic>::expose("PermutationMatrix"); + exposePermutationMatrix(); #ifdef EIGENPY_WITH_CHOLMOD_SUPPORT exposeCholmod(); diff --git a/src/decompositions/eigen-solver.cpp b/src/decompositions/eigen-solver.cpp new file mode 100644 index 00000000..8fdfbf4a --- /dev/null +++ b/src/decompositions/eigen-solver.cpp @@ -0,0 +1,13 @@ + +/* + * Copyright 2024 INRIA + */ + +#include "eigenpy/decompositions/EigenSolver.hpp" + +namespace eigenpy { +void exposeEigenSolver() { + using namespace Eigen; + EigenSolverVisitor<MatrixXd>::expose("EigenSolver"); +} +} // namespace eigenpy diff --git a/src/decompositions/ldlt-solver.cpp b/src/decompositions/ldlt-solver.cpp new file mode 100644 index 00000000..6f3a2a60 --- /dev/null +++ b/src/decompositions/ldlt-solver.cpp @@ -0,0 +1,12 @@ +/* + * Copyright 2024 INRIA + */ + +#include "eigenpy/decompositions/LDLT.hpp" + +namespace eigenpy { +void exposeLDLTSolver() { + using namespace Eigen; + LDLTSolverVisitor<MatrixXd>::expose("LDLT"); +} +} // namespace eigenpy diff --git a/src/decompositions/llt-solver.cpp b/src/decompositions/llt-solver.cpp new file mode 100644 index 00000000..a58db9b4 --- /dev/null +++ b/src/decompositions/llt-solver.cpp @@ -0,0 +1,12 @@ +/* + * Copyright 2024 INRIA + */ + +#include "eigenpy/decompositions/LLT.hpp" + +namespace eigenpy { +void exposeLLTSolver() { + using namespace Eigen; + LLTSolverVisitor<MatrixXd>::expose("LLT"); +} +} // namespace eigenpy diff --git a/src/decompositions/minres-solver.cpp b/src/decompositions/minres-solver.cpp new file mode 100644 index 00000000..44a12cea --- /dev/null +++ b/src/decompositions/minres-solver.cpp @@ -0,0 +1,12 @@ +/* + * Copyright 2024 INRIA + */ + +#include "eigenpy/decompositions/minres.hpp" + +namespace eigenpy { +void exposeMINRESSolver() { + using namespace Eigen; + MINRESSolverVisitor<MatrixXd>::expose("MINRES"); +} +} // namespace eigenpy diff --git a/src/decompositions/permutation-matrix.cpp b/src/decompositions/permutation-matrix.cpp new file mode 100644 index 00000000..082ac809 --- /dev/null +++ b/src/decompositions/permutation-matrix.cpp @@ -0,0 +1,12 @@ +/* + * Copyright 2024 INRIA + */ + +#include "eigenpy/decompositions/PermutationMatrix.hpp" + +namespace eigenpy { +void exposePermutationMatrix() { + using namespace Eigen; + PermutationMatrixVisitor<Eigen::Dynamic>::expose("PermutationMatrix"); +} +} // namespace eigenpy diff --git a/src/decompositions/seigen-solver.cpp b/src/decompositions/seigen-solver.cpp new file mode 100644 index 00000000..8fdfbf4a --- /dev/null +++ b/src/decompositions/seigen-solver.cpp @@ -0,0 +1,13 @@ + +/* + * Copyright 2024 INRIA + */ + +#include "eigenpy/decompositions/EigenSolver.hpp" + +namespace eigenpy { +void exposeEigenSolver() { + using namespace Eigen; + EigenSolverVisitor<MatrixXd>::expose("EigenSolver"); +} +} // namespace eigenpy diff --git a/src/decompositions/self-adjoint-eigen-solver.cpp b/src/decompositions/self-adjoint-eigen-solver.cpp new file mode 100644 index 00000000..1497f3be --- /dev/null +++ b/src/decompositions/self-adjoint-eigen-solver.cpp @@ -0,0 +1,13 @@ + +/* + * Copyright 2024 INRIA + */ + +#include "eigenpy/decompositions/SelfAdjointEigenSolver.hpp" + +namespace eigenpy { +void exposeSelfAdjointEigenSolver() { + using namespace Eigen; + SelfAdjointEigenSolverVisitor<MatrixXd>::expose("SelfAdjointEigenSolver"); +} +} // namespace eigenpy diff --git a/src/decompositions/simplicial-ldlt-solver.cpp b/src/decompositions/simplicial-ldlt-solver.cpp new file mode 100644 index 00000000..c9a3e7b9 --- /dev/null +++ b/src/decompositions/simplicial-ldlt-solver.cpp @@ -0,0 +1,13 @@ +/* + * Copyright 2024 INRIA + */ + +#include "eigenpy/decompositions/sparse/LDLT.hpp" + +namespace eigenpy { +void exposeSimplicialLDLTSolver() { + using namespace Eigen; + typedef SparseMatrix<double, ColMajor> ColMajorSparseMatrix; + SimplicialLDLTVisitor<ColMajorSparseMatrix>::expose("SimplicialLDLT"); +} +} // namespace eigenpy diff --git a/src/decompositions/simplicial-llt-solver.cpp b/src/decompositions/simplicial-llt-solver.cpp new file mode 100644 index 00000000..f46e36f2 --- /dev/null +++ b/src/decompositions/simplicial-llt-solver.cpp @@ -0,0 +1,13 @@ +/* + * Copyright 2024 INRIA + */ + +#include "eigenpy/decompositions/sparse/LLT.hpp" + +namespace eigenpy { +void exposeSimplicialLLTSolver() { + using namespace Eigen; + typedef SparseMatrix<double, ColMajor> ColMajorSparseMatrix; + SimplicialLLTVisitor<ColMajorSparseMatrix>::expose("SimplicialLLT"); +} +} // namespace eigenpy diff --git a/src/eigenpy.cpp b/src/eigenpy.cpp index 6a74bc6a..cc2dcefb 100644 --- a/src/eigenpy.cpp +++ b/src/eigenpy.cpp @@ -12,8 +12,21 @@ namespace eigenpy { void seed(unsigned int seed_value) { srand(seed_value); } void exposeMatrixBool(); -void exposeMatrixInt(); -void exposeMatrixLong(); +void exposeMatrixInt8(); +void exposeMatrixChar(); +void exposeMatrixUInt8(); +void exposeMatrixInt16(); +void exposeMatrixUInt16(); +void exposeMatrixInt32(); +void exposeMatrixUInt32(); +void exposeMatrixWindowsLong(); +void exposeMatrixWindowsULong(); +void exposeMatrixMacLong(); +void exposeMatrixMacULong(); +void exposeMatrixInt64(); +void exposeMatrixUInt64(); +void exposeMatrixLinuxLongLong(); +void exposeMatrixLinuxULongLong(); void exposeMatrixFloat(); void exposeMatrixDouble(); void exposeMatrixLongDouble(); @@ -47,8 +60,21 @@ void enableEigenPy() { "seed_value."); exposeMatrixBool(); - exposeMatrixInt(); - exposeMatrixLong(); + exposeMatrixInt8(); + exposeMatrixChar(); + exposeMatrixUInt8(); + exposeMatrixInt16(); + exposeMatrixUInt16(); + exposeMatrixInt32(); + exposeMatrixUInt32(); + exposeMatrixWindowsLong(); + exposeMatrixWindowsULong(); + exposeMatrixMacLong(); + exposeMatrixMacULong(); + exposeMatrixInt64(); + exposeMatrixUInt64(); + exposeMatrixLinuxLongLong(); + exposeMatrixLinuxULongLong(); exposeMatrixFloat(); exposeMatrixDouble(); exposeMatrixLongDouble(); diff --git a/src/matrix-char.cpp b/src/matrix-char.cpp new file mode 100644 index 00000000..3c1a3a1c --- /dev/null +++ b/src/matrix-char.cpp @@ -0,0 +1,14 @@ +/* + * Copyright 2024 INRIA + */ + +#include "eigenpy/eigenpy.hpp" + +#include <cstdint> + +namespace eigenpy { +void exposeMatrixChar() { + exposeType<char>(); + exposeType<char, Eigen::RowMajor>(); +} +} // namespace eigenpy diff --git a/src/matrix-int.cpp b/src/matrix-int.cpp deleted file mode 100644 index 26dbe3ca..00000000 --- a/src/matrix-int.cpp +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2020 INRIA - */ - -#include "eigenpy/eigenpy.hpp" - -namespace eigenpy { -void exposeMatrixInt() { - exposeType<int>(); - exposeType<int, Eigen::RowMajor>(); -} -} // namespace eigenpy diff --git a/src/matrix-int16.cpp b/src/matrix-int16.cpp new file mode 100644 index 00000000..8e75cfdf --- /dev/null +++ b/src/matrix-int16.cpp @@ -0,0 +1,14 @@ +/* + * Copyright 2024 INRIA + */ + +#include "eigenpy/eigenpy.hpp" + +#include <cstdint> + +namespace eigenpy { +void exposeMatrixInt16() { + exposeType<int16_t>(); + exposeType<int16_t, Eigen::RowMajor>(); +} +} // namespace eigenpy diff --git a/src/matrix-int32.cpp b/src/matrix-int32.cpp new file mode 100644 index 00000000..f6f1bdcb --- /dev/null +++ b/src/matrix-int32.cpp @@ -0,0 +1,14 @@ +/* + * Copyright 2020 INRIA + */ + +#include "eigenpy/eigenpy.hpp" + +#include <cstdint> + +namespace eigenpy { +void exposeMatrixInt32() { + exposeType<int32_t>(); + exposeType<int32_t, Eigen::RowMajor>(); +} +} // namespace eigenpy diff --git a/src/matrix-int64.cpp b/src/matrix-int64.cpp new file mode 100644 index 00000000..9bbe6899 --- /dev/null +++ b/src/matrix-int64.cpp @@ -0,0 +1,14 @@ +/* + * Copyright 2020 INRIA + */ + +#include "eigenpy/eigenpy.hpp" + +#include <cstdint> + +namespace eigenpy { +void exposeMatrixInt64() { + exposeType<int64_t>(); + exposeType<int64_t, Eigen::RowMajor>(); +} +} // namespace eigenpy diff --git a/src/matrix-int8.cpp b/src/matrix-int8.cpp new file mode 100644 index 00000000..7d4ca642 --- /dev/null +++ b/src/matrix-int8.cpp @@ -0,0 +1,14 @@ +/* + * Copyright 2020 INRIA + */ + +#include "eigenpy/eigenpy.hpp" + +#include <cstdint> + +namespace eigenpy { +void exposeMatrixInt8() { + exposeType<int8_t>(); + exposeType<int8_t, Eigen::RowMajor>(); +} +} // namespace eigenpy diff --git a/src/matrix-linux-long-long.cpp b/src/matrix-linux-long-long.cpp new file mode 100644 index 00000000..7084ea4e --- /dev/null +++ b/src/matrix-linux-long-long.cpp @@ -0,0 +1,15 @@ +/* + * Copyright 2024 INRIA + */ + +#include "eigenpy/eigenpy.hpp" + +namespace eigenpy { +void exposeMatrixLinuxLongLong() { +// On Linux, long long is a 64 bytes type but it's a different type than int64_t +#ifdef __linux__ + exposeType<long long>(); + exposeType<long long, Eigen::RowMajor>(); +#endif // linux +} +} // namespace eigenpy diff --git a/src/matrix-linux-ulong-long.cpp b/src/matrix-linux-ulong-long.cpp new file mode 100644 index 00000000..bac418ee --- /dev/null +++ b/src/matrix-linux-ulong-long.cpp @@ -0,0 +1,15 @@ +/* + * Copyright 2024 INRIA + */ + +#include "eigenpy/eigenpy.hpp" + +namespace eigenpy { +void exposeMatrixLinuxULongLong() { +// On Linux, long long is a 64 bytes type but it's a different type than int64_t +#ifdef __linux__ + exposeType<unsigned long long>(); + exposeType<unsigned long long, Eigen::RowMajor>(); +#endif // linux +} +} // namespace eigenpy diff --git a/src/matrix-mac-long.cpp b/src/matrix-mac-long.cpp new file mode 100644 index 00000000..f2956f36 --- /dev/null +++ b/src/matrix-mac-long.cpp @@ -0,0 +1,15 @@ +/* + * Copyright 2024 INRIA + */ + +#include "eigenpy/eigenpy.hpp" + +namespace eigenpy { +void exposeMatrixMacLong() { +// On Mac, long is a 64 bytes type but it's a different type than int64_t +#ifdef __APPLE__ + exposeType<long>(); + exposeType<long, Eigen::RowMajor>(); +#endif // Mac +} +} // namespace eigenpy diff --git a/src/matrix-mac-ulong.cpp b/src/matrix-mac-ulong.cpp new file mode 100644 index 00000000..99374011 --- /dev/null +++ b/src/matrix-mac-ulong.cpp @@ -0,0 +1,15 @@ +/* + * Copyright 2024 INRIA + */ + +#include "eigenpy/eigenpy.hpp" + +namespace eigenpy { +void exposeMatrixMacULong() { +// On Mac, long is a 64 bytes type but it's a different type than int64_t +#ifdef __APPLE__ + exposeType<unsigned long>(); + exposeType<unsigned long, Eigen::RowMajor>(); +#endif // Mac +} +} // namespace eigenpy diff --git a/src/matrix-uint16.cpp b/src/matrix-uint16.cpp new file mode 100644 index 00000000..fd89e868 --- /dev/null +++ b/src/matrix-uint16.cpp @@ -0,0 +1,14 @@ +/* + * Copyright 2024 INRIA + */ + +#include "eigenpy/eigenpy.hpp" + +#include <cstdint> + +namespace eigenpy { +void exposeMatrixUInt16() { + exposeType<uint16_t>(); + exposeType<uint16_t, Eigen::RowMajor>(); +} +} // namespace eigenpy diff --git a/src/matrix-uint32.cpp b/src/matrix-uint32.cpp new file mode 100644 index 00000000..c8c227d8 --- /dev/null +++ b/src/matrix-uint32.cpp @@ -0,0 +1,14 @@ +/* + * Copyright 2024 INRIA + */ + +#include "eigenpy/eigenpy.hpp" + +#include <cstdint> + +namespace eigenpy { +void exposeMatrixUInt32() { + exposeType<uint32_t>(); + exposeType<uint32_t, Eigen::RowMajor>(); +} +} // namespace eigenpy diff --git a/src/matrix-uint64.cpp b/src/matrix-uint64.cpp new file mode 100644 index 00000000..26a6df99 --- /dev/null +++ b/src/matrix-uint64.cpp @@ -0,0 +1,14 @@ +/* + * Copyright 2020 INRIA + */ + +#include "eigenpy/eigenpy.hpp" + +#include <cstdint> + +namespace eigenpy { +void exposeMatrixUInt64() { + exposeType<uint64_t>(); + exposeType<uint64_t, Eigen::RowMajor>(); +} +} // namespace eigenpy diff --git a/src/matrix-uint8.cpp b/src/matrix-uint8.cpp new file mode 100644 index 00000000..a2cf3449 --- /dev/null +++ b/src/matrix-uint8.cpp @@ -0,0 +1,14 @@ +/* + * Copyright 2020 INRIA + */ + +#include "eigenpy/eigenpy.hpp" + +#include <cstdint> + +namespace eigenpy { +void exposeMatrixUInt8() { + exposeType<std::uint8_t>(); + exposeType<std::uint8_t, Eigen::RowMajor>(); +} +} // namespace eigenpy diff --git a/src/matrix-long.cpp b/src/matrix-windows-long.cpp similarity index 51% rename from src/matrix-long.cpp rename to src/matrix-windows-long.cpp index f52e89f1..44871047 100644 --- a/src/matrix-long.cpp +++ b/src/matrix-windows-long.cpp @@ -1,17 +1,15 @@ /* - * Copyright 2020 INRIA + * Copyright 2024 INRIA */ #include "eigenpy/eigenpy.hpp" namespace eigenpy { -void exposeMatrixLong() { +void exposeMatrixWindowsLong() { +// On Windows, long is a 32 bytes type but it's a different type than int #ifdef WIN32 - exposeType<__int64>(); - exposeType<__int64, Eigen::RowMajor>(); -#else exposeType<long>(); exposeType<long, Eigen::RowMajor>(); -#endif +#endif // WIN32 } } // namespace eigenpy diff --git a/src/matrix-windows-ulong.cpp b/src/matrix-windows-ulong.cpp new file mode 100644 index 00000000..a657ece1 --- /dev/null +++ b/src/matrix-windows-ulong.cpp @@ -0,0 +1,15 @@ +/* + * Copyright 2024 INRIA + */ + +#include "eigenpy/eigenpy.hpp" + +namespace eigenpy { +void exposeMatrixWindowsULong() { +// On Windows, long is a 32 bytes type but it's a different type than int +#ifdef WIN32 + exposeType<unsigned long>(); + exposeType<unsigned long, Eigen::RowMajor>(); +#endif // WIN32 +} +} // namespace eigenpy diff --git a/unittest/matrix.cpp b/unittest/matrix.cpp index e202c572..e9fcf194 100644 --- a/unittest/matrix.cpp +++ b/unittest/matrix.cpp @@ -132,6 +132,11 @@ ReturnMatrix copy(const Eigen::MatrixBase<Matrix>& mat) { return mat; } +template <typename Matrix> +Matrix copy_same(const Eigen::MatrixBase<Matrix>& mat) { + return mat; +} + BOOST_PYTHON_MODULE(matrix) { using namespace Eigen; namespace bp = boost::python; @@ -190,4 +195,39 @@ BOOST_PYTHON_MODULE(matrix) { copy<RowMajorMatrixXd, RowMajorMatrixXd>); bp::def("asRowMajorFromRowMajorVector", copy<Eigen::RowVectorXd, Eigen::RowVectorXd>); + + bp::def("copyBoolToBool", copy_same<Eigen::Matrix<bool, -1, -1> >); + + bp::def("copyInt8ToInt8", copy_same<Eigen::Matrix<int8_t, -1, -1> >); + bp::def("copyCharToChar", copy_same<Eigen::Matrix<char, -1, -1> >); + bp::def("copyUCharToUChar", copy_same<Eigen::Matrix<unsigned char, -1, -1> >); + + bp::def("copyInt16ToInt16", copy_same<Eigen::Matrix<int16_t, -1, -1> >); + bp::def("copyUInt16ToUInt16", copy_same<Eigen::Matrix<uint16_t, -1, -1> >); + + bp::def("copyInt32ToInt32", copy_same<Eigen::Matrix<int32_t, -1, -1> >); + bp::def("copyUInt32ToUInt32", copy_same<Eigen::Matrix<uint32_t, -1, -1> >); + + bp::def("copyInt64ToInt64", copy_same<Eigen::Matrix<int64_t, -1, -1> >); + bp::def("copyUInt64ToUInt64", copy_same<Eigen::Matrix<uint64_t, -1, -1> >); + + bp::def("copyLongToLong", copy_same<Eigen::Matrix<long, -1, -1> >); + bp::def("copyULongToULong", copy_same<Eigen::Matrix<unsigned long, -1, -1> >); + + bp::def("copyLongLongToLongLong", + copy_same<Eigen::Matrix<long long, -1, -1> >); + bp::def("copyULongLongToULongLong", + copy_same<Eigen::Matrix<unsigned long long, -1, -1> >); + + bp::def("copyFloatToFloat", copy_same<Eigen::Matrix<float, -1, -1> >); + bp::def("copyDoubleToDouble", copy_same<Eigen::Matrix<double, -1, -1> >); + bp::def("copyLongDoubleToLongDouble", + copy_same<Eigen::Matrix<long double, -1, -1> >); + + bp::def("copyCFloatToCFloat", + copy_same<Eigen::Matrix<std::complex<float>, -1, -1> >); + bp::def("copyCDoubleToCDouble", + copy_same<Eigen::Matrix<std::complex<double>, -1, -1> >); + bp::def("copyCLongDoubleToCLongDouble", + copy_same<Eigen::Matrix<std::complex<long double>, -1, -1> >); } diff --git a/unittest/python/test_matrix.py b/unittest/python/test_matrix.py index 87b3fd10..9c14ad93 100644 --- a/unittest/python/test_matrix.py +++ b/unittest/python/test_matrix.py @@ -1,3 +1,5 @@ +import platform + import matrix as eigenpy import numpy as np @@ -180,3 +182,167 @@ assert (eigenpy.asRowMajorFromColMajorMatrix(vec) == vec).all() assert (eigenpy.asRowMajorFromColMajorVector(vec) == vec).all() assert (eigenpy.asRowMajorFromRowMajorMatrix(vec) == vec).all() assert (eigenpy.asRowMajorFromRowMajorVector(vec) == vec).all() + +# Test numpy -> Eigen -> numpy for all same type + + +def test_conversion(function, dtype): + input_array = np.array([1, 0], dtype=dtype) + assert input_array.dtype == dtype + output_array = function(input_array) + assert output_array.dtype == dtype + assert (output_array == input_array).all() + + +bool_t = np.dtype(np.bool_) +int8_t = np.dtype(np.int8) +uint8_t = np.dtype(np.uint8) +int16_t = np.dtype(np.int16) +uint16_t = np.dtype(np.uint16) +int32_t = np.dtype(np.int32) +uint32_t = np.dtype(np.uint32) +int64_t = np.dtype(np.int64) +uint64_t = np.dtype(np.uint64) +# On Windows long is a 32 bits integer but is a different type than int32. +long_t = np.dtype(np.int32 if platform.system() == "Windows" else np.int64) +ulong_t = np.dtype(np.uint32 if platform.system() == "Windows" else np.uint64) +longlong_t = np.dtype(np.longlong) +ulonglong_t = np.dtype(np.ulonglong) + +float32_t = np.dtype(np.float32) +float64_t = np.dtype(np.float64) + +complex64_t = np.dtype(np.complex64) +complex128_t = np.dtype(np.complex128) +complex256_t = np.dtype(np.clongdouble) + + +test_conversion(eigenpy.copyBoolToBool, bool_t) + +test_conversion(eigenpy.copyInt8ToInt8, int8_t) +test_conversion(eigenpy.copyCharToChar, int8_t) +test_conversion(eigenpy.copyUCharToUChar, uint8_t) + +test_conversion(eigenpy.copyInt16ToInt16, int16_t) +test_conversion(eigenpy.copyUInt16ToUInt16, uint16_t) + +test_conversion(eigenpy.copyInt32ToInt32, int32_t) +test_conversion(eigenpy.copyUInt32ToUInt32, uint32_t) + +test_conversion(eigenpy.copyInt64ToInt64, int64_t) +test_conversion(eigenpy.copyUInt64ToUInt64, uint64_t) + +test_conversion(eigenpy.copyLongToLong, long_t) +test_conversion(eigenpy.copyULongToULong, ulong_t) + +# On Windows long long is an int64_t alias. +# The numpy dtype match between longlong and int64. + +# On Linux long long is a 64 bits integer but is a different type than int64_t. +# The numpy dtype doesn't match and it's not an issue since C++ type are different. + +# On Mac long long is an int64_t and long alias. +# This is an issue because longlong numpy dtype is different than long numpy dtype +# but long and long long are the same type in C++. +# The test should pass thanks to the promotion code. +test_conversion(eigenpy.copyLongLongToLongLong, longlong_t) +test_conversion(eigenpy.copyULongLongToULongLong, ulonglong_t) + +test_conversion(eigenpy.copyFloatToFloat, float32_t) +test_conversion(eigenpy.copyDoubleToDouble, float64_t) +# On Windows and Mac longdouble is 64 bits +test_conversion(eigenpy.copyLongDoubleToLongDouble, np.dtype(np.longdouble)) + +test_conversion(eigenpy.copyCFloatToCFloat, complex64_t) +test_conversion(eigenpy.copyCDoubleToCDouble, complex128_t) +# On Windows and Mac clongdouble is 128 bits +test_conversion(eigenpy.copyCLongDoubleToCLongDouble, complex256_t) + + +# Test numpy -> Eigen -> numpy promotion + + +def test_conversion_promotion(function, input_dtype, output_dtype): + input_array = np.array([1, 0], dtype=input_dtype) + assert input_array.dtype == input_dtype + output_array = function(input_array) + assert output_array.dtype == output_dtype + assert (output_array == input_array).all() + + +# Test bool to other type +test_conversion_promotion(eigenpy.copyInt8ToInt8, bool_t, int8_t) +test_conversion_promotion(eigenpy.copyUCharToUChar, bool_t, uint8_t) +test_conversion_promotion(eigenpy.copyInt16ToInt16, bool_t, int16_t) +test_conversion_promotion(eigenpy.copyUInt16ToUInt16, bool_t, uint16_t) +test_conversion_promotion(eigenpy.copyInt32ToInt32, bool_t, int32_t) +test_conversion_promotion(eigenpy.copyUInt32ToUInt32, bool_t, uint32_t) +test_conversion_promotion(eigenpy.copyInt64ToInt64, bool_t, int64_t) +test_conversion_promotion(eigenpy.copyUInt64ToUInt64, bool_t, uint64_t) +test_conversion_promotion(eigenpy.copyLongToLong, bool_t, long_t) +test_conversion_promotion(eigenpy.copyULongToULong, bool_t, ulong_t) +test_conversion_promotion(eigenpy.copyLongLongToLongLong, bool_t, int64_t) +test_conversion_promotion(eigenpy.copyULongLongToULongLong, bool_t, uint64_t) + +# Test int8 to other type +test_conversion_promotion(eigenpy.copyInt16ToInt16, int8_t, int16_t) +test_conversion_promotion(eigenpy.copyInt16ToInt16, uint8_t, int16_t) +test_conversion_promotion(eigenpy.copyUInt16ToUInt16, uint8_t, uint16_t) +test_conversion_promotion(eigenpy.copyInt32ToInt32, int8_t, int32_t) +test_conversion_promotion(eigenpy.copyInt32ToInt32, uint8_t, int32_t) +test_conversion_promotion(eigenpy.copyUInt32ToUInt32, uint8_t, uint32_t) +test_conversion_promotion(eigenpy.copyInt64ToInt64, int8_t, int64_t) +test_conversion_promotion(eigenpy.copyInt64ToInt64, uint8_t, int64_t) +test_conversion_promotion(eigenpy.copyUInt64ToUInt64, uint8_t, uint64_t) +test_conversion_promotion(eigenpy.copyLongToLong, int8_t, long_t) +test_conversion_promotion(eigenpy.copyLongToLong, uint8_t, long_t) +test_conversion_promotion(eigenpy.copyULongToULong, uint8_t, ulong_t) +test_conversion_promotion(eigenpy.copyLongLongToLongLong, int8_t, int64_t) +test_conversion_promotion(eigenpy.copyLongLongToLongLong, uint8_t, int64_t) +test_conversion_promotion(eigenpy.copyULongLongToULongLong, uint8_t, uint64_t) + +# Test int16 to other type +test_conversion_promotion(eigenpy.copyInt32ToInt32, int16_t, int32_t) +test_conversion_promotion(eigenpy.copyInt32ToInt32, uint16_t, int32_t) +test_conversion_promotion(eigenpy.copyUInt32ToUInt32, uint16_t, uint32_t) +test_conversion_promotion(eigenpy.copyInt64ToInt64, int16_t, int64_t) +test_conversion_promotion(eigenpy.copyInt64ToInt64, uint16_t, int64_t) +test_conversion_promotion(eigenpy.copyUInt64ToUInt64, uint16_t, uint64_t) +test_conversion_promotion(eigenpy.copyLongToLong, int16_t, long_t) +test_conversion_promotion(eigenpy.copyLongToLong, uint16_t, long_t) +test_conversion_promotion(eigenpy.copyULongToULong, uint16_t, ulong_t) +test_conversion_promotion(eigenpy.copyLongLongToLongLong, int16_t, int64_t) +test_conversion_promotion(eigenpy.copyLongLongToLongLong, uint16_t, int64_t) +test_conversion_promotion(eigenpy.copyULongLongToULongLong, uint16_t, uint64_t) + +# Test int32 to other type +test_conversion_promotion(eigenpy.copyInt64ToInt64, int32_t, int64_t) +test_conversion_promotion(eigenpy.copyInt64ToInt64, uint32_t, int64_t) +test_conversion_promotion(eigenpy.copyUInt64ToUInt64, uint32_t, uint64_t) +test_conversion_promotion(eigenpy.copyLongToLong, int32_t, long_t) +test_conversion_promotion(eigenpy.copyLongToLong, uint32_t, long_t) +test_conversion_promotion(eigenpy.copyULongToULong, uint32_t, ulong_t) +test_conversion_promotion(eigenpy.copyLongLongToLongLong, int32_t, int64_t) +test_conversion_promotion(eigenpy.copyLongLongToLongLong, uint32_t, int64_t) +test_conversion_promotion(eigenpy.copyULongLongToULongLong, uint32_t, uint64_t) + +# Test float to double +test_conversion_promotion(eigenpy.copyDoubleToDouble, float32_t, float64_t) + +# Test complex to other type +test_conversion_promotion(eigenpy.copyCDoubleToCDouble, complex64_t, complex128_t) + +test_conversion_promotion( + eigenpy.copyCLongDoubleToCLongDouble, + complex64_t, + complex256_t, +) + +# Only Linux store complex double into 258 bits. +# Then, 128 bits complex can be promoted. +if platform.system() == "Linux": + test_conversion_promotion( + eigenpy.copyCLongDoubleToCLongDouble, + complex128_t, + complex256_t, + ) diff --git a/unittest/python/test_user_type.py b/unittest/python/test_user_type.py index e22b534c..76f8591f 100644 --- a/unittest/python/test_user_type.py +++ b/unittest/python/test_user_type.py @@ -9,7 +9,7 @@ cols = 20 def test(dtype): rng = np.random.default_rng() - mat = np.array(np.ones((rows, cols)).astype(np.intc), dtype=dtype) + mat = np.array(np.ones((rows, cols)).astype(np.int32), dtype=dtype) mat = rng.random((rows, cols)).astype(dtype) mat_copy = mat.copy() assert (mat == mat_copy).all() -- GitLab