diff --git a/.github/workflows/conda/environment_macos_linux.yml b/.github/workflows/conda/environment_macos_linux.yml
index 34842449fe8853be8b636d51c989f73ef93d1a90..5d8e21c5e962eedcb51514929baa082b84efb592 100644
--- a/.github/workflows/conda/environment_macos_linux.yml
+++ b/.github/workflows/conda/environment_macos_linux.yml
@@ -10,3 +10,4 @@ dependencies:
   - ccache
   - cxx-compiler
   - ninja
+  - scipy
diff --git a/.github/workflows/conda/environment_windows.yml b/.github/workflows/conda/environment_windows.yml
index beb2be2afa70dd3994d5f2ce88f3055176056f4e..af5e1c07a759d0c0c320580103fcf38fb7fc04bf 100644
--- a/.github/workflows/conda/environment_windows.yml
+++ b/.github/workflows/conda/environment_windows.yml
@@ -9,3 +9,4 @@ dependencies:
   - boost
   - ccache
   - ninja
+  - scipy
diff --git a/.github/workflows/jrl-cmakemodules.yml b/.github/workflows/jrl-cmakemodules.yml
index 9207b4b3027a0ce53a1fa9d51b106826ae0a2721..0de0d5ec9df241637d1649606131f99d492d80ac 100644
--- a/.github/workflows/jrl-cmakemodules.yml
+++ b/.github/workflows/jrl-cmakemodules.yml
@@ -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 .
 
 
@@ -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
diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index 94d5cd074d6bf12474c8ccb7882457cf5b0fb007..c3c182b817a53bdf4107f9923df5f0e70e3b3a07 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -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
 
diff --git a/.github/workflows/macos-linux-conda.yml b/.github/workflows/macos-linux-conda.yml
index 150f10f1891e12079d57af60604a51bc1c40d74c..f40513a54acae8d30824212f6d83ff5899e5e0bb 100644
--- a/.github/workflows/macos-linux-conda.yml
+++ b/.github/workflows/macos-linux-conda.yml
@@ -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
diff --git a/.github/workflows/reloc.yml b/.github/workflows/reloc.yml
index fd5cab798cf6be95e438ea0bc258dd1f73265f0f..ae271202955ecf63b93e7201fada793e071c1f21 100644
--- a/.github/workflows/reloc.yml
+++ b/.github/workflows/reloc.yml
@@ -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
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d2f5eb9c721eaa10415fdb8ac241df6148b65861..422e29f6e951ef48c06ae08d9a7bd5a6395e9122 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a280328c6ae27a7fcda1aae9262607a098aa934f..1c9024257ea76c5c90809e179234591945fe939a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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)
@@ -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)
 
@@ -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)
diff --git a/cmake b/cmake
index 24abb7409c89f3c7fb08e55cf3edc823781ff9fb..935e2154e02e84f147046278bfd64e27a5f0850a 160000
--- a/cmake
+++ b/cmake
@@ -1 +1 @@
-Subproject commit 24abb7409c89f3c7fb08e55cf3edc823781ff9fb
+Subproject commit 935e2154e02e84f147046278bfd64e27a5f0850a
diff --git a/include/eigenpy/details.hpp b/include/eigenpy/details.hpp
index e1bab2adebc08d5ff3b21f1589f6c2c68b5ed014..538812abe11e3e01fd410480769c137f43579757 100644
--- a/include/eigenpy/details.hpp
+++ b/include/eigenpy/details.hpp
@@ -1,6 +1,6 @@
 /*
  * Copyright 2014-2019, CNRS
- * Copyright 2018-2023, INRIA
+ * Copyright 2018-2024, INRIA
  */
 
 #ifndef __eigenpy_details_hpp__
@@ -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>,
diff --git a/include/eigenpy/eigen-from-python.hpp b/include/eigenpy/eigen-from-python.hpp
index f31e3512e2a00b249f2648048f82a19240cfc625..904c2dd9c8eb5ffd1e6e0624a8b4e75635ef5961 100644
--- a/include/eigenpy/eigen-from-python.hpp
+++ b/include/eigenpy/eigen-from-python.hpp
@@ -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;
@@ -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__
diff --git a/include/eigenpy/eigen-to-python.hpp b/include/eigenpy/eigen-to-python.hpp
index 2b5e039bcba0f9e203b36c53e5aa197270ab72ec..e6c4c14086e232f8e2bae0239bcf960c899ee0d1 100644
--- a/include/eigenpy/eigen-to-python.hpp
+++ b/include/eigenpy/eigen-to-python.hpp
@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2014-2023 CNRS INRIA
+// Copyright (c) 2014-2024 CNRS INRIA
 //
 
 #ifndef __eigenpy_eigen_to_python_hpp__
@@ -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 {
@@ -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
@@ -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
 
@@ -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>
diff --git a/include/eigenpy/eigenpy.hpp b/include/eigenpy/eigenpy.hpp
index 8a6cecc28be18685375feba1e47f812f54103ef0..f4edf090d36a2df10d71ad214ad0b73cfd1d8f8b 100644
--- a/include/eigenpy/eigenpy.hpp
+++ b/include/eigenpy/eigenpy.hpp
@@ -1,6 +1,6 @@
 /*
  * Copyright 2014-2019, CNRS
- * Copyright 2018-2023, INRIA
+ * Copyright 2018-2024, INRIA
  */
 
 #ifndef __eigenpy_eigenpy_hpp__
@@ -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>
diff --git a/include/eigenpy/fwd.hpp b/include/eigenpy/fwd.hpp
index aada9ac1cff8dbb1584325cc24d88ba74033861e..7f52949954b42e29335693ac29a544881c8518ae 100644
--- a/include/eigenpy/fwd.hpp
+++ b/include/eigenpy/fwd.hpp
@@ -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
@@ -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>
@@ -138,17 +145,20 @@ 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>,
@@ -156,17 +166,17 @@ struct get_eigen_base_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
diff --git a/include/eigenpy/numpy-type.hpp b/include/eigenpy/numpy-type.hpp
index 816390b47a400aa133784b26450d12fbefc46c38..25dee99f2a995dd6d531a96ba222d2c6ccc7028d 100644
--- a/include/eigenpy/numpy-type.hpp
+++ b/include/eigenpy/numpy-type.hpp
@@ -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:
@@ -56,8 +65,6 @@ struct EIGENPY_DLLAPI NumpyType {
 
   static bool sharedMemory();
 
-  static bp::object getNumpyType();
-
   static const PyTypeObject* getNumpyArrayType();
 
  protected:
diff --git a/include/eigenpy/register.hpp b/include/eigenpy/register.hpp
index 64d09d76e5d94a9cbfc94d5dd4be9fcc74f28b3f..ba3bb0674c624f3b0da231f881b126adc1932172 100644
--- a/include/eigenpy/register.hpp
+++ b/include/eigenpy/register.hpp
@@ -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>());
diff --git a/include/eigenpy/scalar-conversion.hpp b/include/eigenpy/scalar-conversion.hpp
index 1756b12a56f9ed96c658976bc2853c820227818e..8a8532d9a97d2daf1f6052b3c2e0ffdca58ccbdc 100644
--- a/include/eigenpy/scalar-conversion.hpp
+++ b/include/eigenpy/scalar-conversion.hpp
@@ -1,70 +1,18 @@
 //
-// Copyright (c) 2014-2020 CNRS INRIA
+// Copyright (c) 2014-2024 CNRS INRIA
 //
 
 #ifndef __eigenpy_scalar_conversion_hpp__
 #define __eigenpy_scalar_conversion_hpp__
 
 #include "eigenpy/config.hpp"
+#include <boost/numeric/conversion/conversion_traits.hpp>
 
 namespace eigenpy {
-template <typename SCALAR1, typename SCALAR2>
-struct FromTypeToType : public boost::false_type {};
+template <typename Source, typename Target>
+struct FromTypeToType
+    : public boost::numeric::conversion_traits<Source, Target>::subranged {};
 
-template <typename SCALAR>
-struct FromTypeToType<SCALAR, SCALAR> : public boost::true_type {};
-
-template <>
-struct FromTypeToType<int, long> : public boost::true_type {};
-template <>
-struct FromTypeToType<int, float> : public boost::true_type {};
-template <>
-struct FromTypeToType<int, std::complex<float> > : public boost::true_type {};
-template <>
-struct FromTypeToType<int, double> : public boost::true_type {};
-template <>
-struct FromTypeToType<int, std::complex<double> > : public boost::true_type {};
-template <>
-struct FromTypeToType<int, long double> : public boost::true_type {};
-template <>
-struct FromTypeToType<int, std::complex<long double> >
-    : public boost::true_type {};
-
-template <>
-struct FromTypeToType<long, float> : public boost::true_type {};
-template <>
-struct FromTypeToType<long, std::complex<float> > : public boost::true_type {};
-template <>
-struct FromTypeToType<long, double> : public boost::true_type {};
-template <>
-struct FromTypeToType<long, std::complex<double> > : public boost::true_type {};
-template <>
-struct FromTypeToType<long, long double> : public boost::true_type {};
-template <>
-struct FromTypeToType<long, std::complex<long double> >
-    : public boost::true_type {};
-
-template <>
-struct FromTypeToType<float, std::complex<float> > : public boost::true_type {};
-template <>
-struct FromTypeToType<float, double> : public boost::true_type {};
-template <>
-struct FromTypeToType<float, std::complex<double> > : public boost::true_type {
-};
-template <>
-struct FromTypeToType<float, long double> : public boost::true_type {};
-template <>
-struct FromTypeToType<float, std::complex<long double> >
-    : public boost::true_type {};
-
-template <>
-struct FromTypeToType<double, std::complex<double> > : public boost::true_type {
-};
-template <>
-struct FromTypeToType<double, long double> : public boost::true_type {};
-template <>
-struct FromTypeToType<double, std::complex<long double> >
-    : public boost::true_type {};
 }  // namespace eigenpy
 
 #endif  // __eigenpy_scalar_conversion_hpp__
diff --git a/include/eigenpy/scipy-allocator.hpp b/include/eigenpy/scipy-allocator.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..848fd62f0b60b1fd39a42a46a53bb6f5b2591ca1
--- /dev/null
+++ b/include/eigenpy/scipy-allocator.hpp
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2024 INRIA
+ */
+
+#ifndef __eigenpy_scipy_allocator_hpp__
+#define __eigenpy_scipy_allocator_hpp__
+
+#include "eigenpy/fwd.hpp"
+#include "eigenpy/eigen-allocator.hpp"
+#include "eigenpy/scipy-type.hpp"
+#include "eigenpy/register.hpp"
+
+namespace eigenpy {
+
+template <typename EigenType, typename BaseType>
+struct scipy_allocator_impl;
+
+template <typename EigenType>
+struct scipy_allocator_impl_sparse_matrix;
+
+template <typename MatType>
+struct scipy_allocator_impl<
+    MatType,
+    Eigen::SparseMatrixBase<typename remove_const_reference<MatType>::type> >
+    : scipy_allocator_impl_sparse_matrix<MatType> {};
+
+template <typename MatType>
+struct scipy_allocator_impl<
+    const MatType, const Eigen::SparseMatrixBase<
+                       typename remove_const_reference<MatType>::type> >
+    : scipy_allocator_impl_sparse_matrix<const MatType> {};
+
+// template <typename MatType>
+// struct scipy_allocator_impl<MatType &, Eigen::MatrixBase<MatType> > :
+// scipy_allocator_impl_sparse_matrix<MatType &>
+//{};
+
+template <typename MatType>
+struct scipy_allocator_impl<const MatType &,
+                            const Eigen::SparseMatrixBase<MatType> >
+    : scipy_allocator_impl_sparse_matrix<const MatType &> {};
+
+template <typename EigenType,
+          typename BaseType = typename get_eigen_base_type<EigenType>::type>
+struct ScipyAllocator : scipy_allocator_impl<EigenType, BaseType> {};
+
+template <typename MatType>
+struct scipy_allocator_impl_sparse_matrix {
+  template <typename SimilarMatrixType>
+  static PyObject *allocate(
+      const Eigen::SparseCompressedBase<SimilarMatrixType> &mat_,
+      bool copy = false) {
+    EIGENPY_UNUSED_VARIABLE(copy);
+    typedef typename SimilarMatrixType::Scalar Scalar;
+    typedef typename SimilarMatrixType::StorageIndex StorageIndex;
+
+    enum { IsRowMajor = SimilarMatrixType::IsRowMajor };
+
+    typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1> DataVector;
+    typedef const Eigen::Map<const DataVector> MapDataVector;
+    typedef Eigen::Matrix<StorageIndex, Eigen::Dynamic, 1> StorageIndexVector;
+    typedef Eigen::Matrix<int32_t, Eigen::Dynamic, 1> ScipyStorageIndexVector;
+    typedef const Eigen::Map<const StorageIndexVector> MapStorageIndexVector;
+
+    SimilarMatrixType &mat = mat_.const_cast_derived();
+    bp::object scipy_sparse_matrix_type =
+        ScipyType::get_pytype_object<SimilarMatrixType>();
+
+    MapDataVector data(mat.valuePtr(), mat.nonZeros());
+    MapStorageIndexVector outer_indices(
+        mat.outerIndexPtr(), (IsRowMajor ? mat.rows() : mat.cols()) + 1);
+    MapStorageIndexVector inner_indices(mat.innerIndexPtr(), mat.nonZeros());
+
+    bp::object scipy_sparse_matrix;
+
+    if (mat.rows() == 0 &&
+        mat.cols() == 0)  // handle the specific case of empty matrix
+    {
+      //      PyArray_Descr* npy_type =
+      //      Register::getPyArrayDescrFromScalarType<Scalar>(); bp::dict args;
+      //      args["dtype"] =
+      //      bp::object(bp::handle<>(bp::borrowed(npy_type->typeobj)));
+      //      args["shape"] = bp::object(bp::handle<>(bp::borrowed(Py_None)));
+      //      scipy_sparse_matrix =
+      //      scipy_sparse_matrix_type(*bp::make_tuple(0,0),**args);
+      scipy_sparse_matrix = scipy_sparse_matrix_type(
+          Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>(0, 0));
+    } else {
+      scipy_sparse_matrix = scipy_sparse_matrix_type(bp::make_tuple(
+          DataVector(data),
+          ScipyStorageIndexVector(inner_indices.template cast<int32_t>()),
+          ScipyStorageIndexVector(
+              outer_indices.template cast<int32_t>())));  //,
+      //                                                              bp::make_tuple(mat.rows(),
+      //                                                              mat.cols())));
+    }
+    Py_INCREF(scipy_sparse_matrix.ptr());
+    return scipy_sparse_matrix.ptr();
+  }
+};
+
+// template <typename MatType>
+// struct scipy_allocator_impl_sparse_matrix<MatType &> {
+//   template <typename SimilarMatrixType>
+//   static PyArrayObject *allocate(Eigen::PlainObjectBase<SimilarMatrixType>
+//   &mat,
+//                                  npy_intp nd, npy_intp *shape) {
+//     typedef typename SimilarMatrixType::Scalar Scalar;
+//     enum {
+//       NPY_ARRAY_MEMORY_CONTIGUOUS =
+//           SimilarMatrixType::IsRowMajor ? NPY_ARRAY_CARRAY : NPY_ARRAY_FARRAY
+//     };
+//
+//     if (NumpyType::sharedMemory()) {
+//       const int Scalar_type_code = Register::getTypeCode<Scalar>();
+//       PyArrayObject *pyArray = (PyArrayObject *)call_PyArray_New(
+//           getPyArrayType(), static_cast<int>(nd), shape, Scalar_type_code,
+//           mat.data(), NPY_ARRAY_MEMORY_CONTIGUOUS | NPY_ARRAY_ALIGNED);
+//
+//       return pyArray;
+//     } else {
+//       return NumpyAllocator<MatType>::allocate(mat, nd, shape);
+//     }
+//   }
+// };
+
+#if EIGEN_VERSION_AT_LEAST(3, 2, 0)
+
+// template <typename MatType, int Options, typename Stride>
+// struct scipy_allocator_impl_sparse_matrix<Eigen::Ref<MatType, Options,
+// Stride> > {
+//   typedef Eigen::Ref<MatType, Options, Stride> RefType;
+//
+//   static PyArrayObject *allocate(RefType &mat, npy_intp nd, npy_intp *shape)
+//   {
+//     typedef typename RefType::Scalar Scalar;
+//     enum {
+//       NPY_ARRAY_MEMORY_CONTIGUOUS =
+//           RefType::IsRowMajor ? NPY_ARRAY_CARRAY : NPY_ARRAY_FARRAY
+//     };
+//
+//     if (NumpyType::sharedMemory()) {
+//       const int Scalar_type_code = Register::getTypeCode<Scalar>();
+//       const bool reverse_strides = MatType::IsRowMajor || (mat.rows() == 1);
+//       Eigen::DenseIndex inner_stride = reverse_strides ? mat.outerStride()
+//                                                        : mat.innerStride(),
+//                         outer_stride = reverse_strides ? mat.innerStride()
+//                                                        : mat.outerStride();
+//
+//       const int elsize =
+//       call_PyArray_DescrFromType(Scalar_type_code)->elsize; npy_intp
+//       strides[2] = {elsize * inner_stride, elsize * outer_stride};
+//
+//       PyArrayObject *pyArray = (PyArrayObject *)call_PyArray_New(
+//           getPyArrayType(), static_cast<int>(nd), shape, Scalar_type_code,
+//           strides, mat.data(), NPY_ARRAY_MEMORY_CONTIGUOUS |
+//           NPY_ARRAY_ALIGNED);
+//
+//       return pyArray;
+//     } else {
+//       return NumpyAllocator<MatType>::allocate(mat, nd, shape);
+//     }
+//   }
+// };
+
+#endif
+
+// template <typename MatType>
+// struct scipy_allocator_impl_sparse_matrix<const MatType &> {
+//   template <typename SimilarMatrixType>
+//   static PyArrayObject *allocate(
+//       const Eigen::PlainObjectBase<SimilarMatrixType> &mat, npy_intp nd,
+//       npy_intp *shape) {
+//     typedef typename SimilarMatrixType::Scalar Scalar;
+//     enum {
+//       NPY_ARRAY_MEMORY_CONTIGUOUS_RO = SimilarMatrixType::IsRowMajor
+//                                            ? NPY_ARRAY_CARRAY_RO
+//                                            : NPY_ARRAY_FARRAY_RO
+//     };
+//
+//     if (NumpyType::sharedMemory()) {
+//       const int Scalar_type_code = Register::getTypeCode<Scalar>();
+//       PyArrayObject *pyArray = (PyArrayObject *)call_PyArray_New(
+//           getPyArrayType(), static_cast<int>(nd), shape, Scalar_type_code,
+//           const_cast<Scalar *>(mat.data()),
+//           NPY_ARRAY_MEMORY_CONTIGUOUS_RO | NPY_ARRAY_ALIGNED);
+//
+//       return pyArray;
+//     } else {
+//       return NumpyAllocator<MatType>::allocate(mat, nd, shape);
+//     }
+//   }
+// };
+
+#if EIGEN_VERSION_AT_LEAST(3, 2, 0)
+
+// template <typename MatType, int Options, typename Stride>
+// struct scipy_allocator_impl_sparse_matrix<
+//     const Eigen::Ref<const MatType, Options, Stride> > {
+//   typedef const Eigen::Ref<const MatType, Options, Stride> RefType;
+//
+//   static PyArrayObject *allocate(RefType &mat, npy_intp nd, npy_intp *shape)
+//   {
+//     typedef typename RefType::Scalar Scalar;
+//     enum {
+//       NPY_ARRAY_MEMORY_CONTIGUOUS_RO =
+//           RefType::IsRowMajor ? NPY_ARRAY_CARRAY_RO : NPY_ARRAY_FARRAY_RO
+//     };
+//
+//     if (NumpyType::sharedMemory()) {
+//       const int Scalar_type_code = Register::getTypeCode<Scalar>();
+//
+//       const bool reverse_strides = MatType::IsRowMajor || (mat.rows() == 1);
+//       Eigen::DenseIndex inner_stride = reverse_strides ? mat.outerStride()
+//                                                        : mat.innerStride(),
+//                         outer_stride = reverse_strides ? mat.innerStride()
+//                                                        : mat.outerStride();
+//
+//       const int elsize =
+//       call_PyArray_DescrFromType(Scalar_type_code)->elsize; npy_intp
+//       strides[2] = {elsize * inner_stride, elsize * outer_stride};
+//
+//       PyArrayObject *pyArray = (PyArrayObject *)call_PyArray_New(
+//           getPyArrayType(), static_cast<int>(nd), shape, Scalar_type_code,
+//           strides, const_cast<Scalar *>(mat.data()),
+//           NPY_ARRAY_MEMORY_CONTIGUOUS_RO | NPY_ARRAY_ALIGNED);
+//
+//       return pyArray;
+//     } else {
+//       return NumpyAllocator<MatType>::allocate(mat, nd, shape);
+//     }
+//   }
+// };
+
+#endif
+
+}  // namespace eigenpy
+
+#endif  // ifndef __eigenpy_scipy_allocator_hpp__
diff --git a/include/eigenpy/scipy-type.hpp b/include/eigenpy/scipy-type.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7d53e1148c6b8f4a1953cf8e94cc07ce2717a33b
--- /dev/null
+++ b/include/eigenpy/scipy-type.hpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2024 INRIA
+ */
+
+#ifndef __eigenpy_scipy_type_hpp__
+#define __eigenpy_scipy_type_hpp__
+
+#include "eigenpy/fwd.hpp"
+#include "eigenpy/register.hpp"
+#include "eigenpy/scalar-conversion.hpp"
+#include "eigenpy/numpy-type.hpp"
+
+namespace eigenpy {
+
+struct EIGENPY_DLLAPI ScipyType {
+  static ScipyType& getInstance();
+
+  static void sharedMemory(const bool value);
+
+  static bool sharedMemory();
+
+  static bp::object getScipyType();
+
+  static const PyTypeObject* getScipyCSRMatrixType();
+  static const PyTypeObject* getScipyCSCMatrixType();
+
+  template <typename SparseMatrix>
+  static bp::object get_pytype_object(
+      const Eigen::SparseMatrixBase<SparseMatrix>* ptr = nullptr) {
+    EIGENPY_UNUSED_VARIABLE(ptr);
+    return SparseMatrix::IsRowMajor ? getInstance().csr_matrix_obj
+                                    : getInstance().csc_matrix_obj;
+  }
+
+  template <typename SparseMatrix>
+  static PyTypeObject const* get_pytype(
+      const Eigen::SparseMatrixBase<SparseMatrix>* ptr = nullptr) {
+    EIGENPY_UNUSED_VARIABLE(ptr);
+    return SparseMatrix::IsRowMajor ? getInstance().csr_matrix_type
+                                    : getInstance().csc_matrix_type;
+  }
+
+  static int get_numpy_type_num(const bp::object& obj) {
+    const PyTypeObject* type = Py_TYPE(obj.ptr());
+    EIGENPY_USED_VARIABLE_ONLY_IN_DEBUG_MODE(type);
+    assert(type == getInstance().csr_matrix_type ||
+           type == getInstance().csc_matrix_type);
+
+    bp::object dtype = obj.attr("dtype");
+
+    const PyArray_Descr* npy_type =
+        reinterpret_cast<PyArray_Descr*>(dtype.ptr());
+    return npy_type->type_num;
+  }
+
+ protected:
+  ScipyType();
+
+  bp::object sparse_module;
+
+  // SciPy types
+  bp::object csr_matrix_obj, csc_matrix_obj;
+  PyTypeObject *csr_matrix_type, *csc_matrix_type;
+
+  bool shared_memory;
+};
+}  // namespace eigenpy
+
+#endif  // ifndef __eigenpy_scipy_type_hpp__
diff --git a/include/eigenpy/sparse/eigen-from-python.hpp b/include/eigenpy/sparse/eigen-from-python.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f5158a53f2e77cf08b6875e8568a9ee1958729ba
--- /dev/null
+++ b/include/eigenpy/sparse/eigen-from-python.hpp
@@ -0,0 +1,267 @@
+//
+// Copyright (c) 2024 INRIA
+//
+
+#ifndef __eigenpy_sparse_eigen_from_python_hpp__
+#define __eigenpy_sparse_eigen_from_python_hpp__
+
+#include "eigenpy/fwd.hpp"
+#include "eigenpy/eigen-allocator.hpp"
+#include "eigenpy/scipy-type.hpp"
+#include "eigenpy/scalar-conversion.hpp"
+
+namespace eigenpy {
+
+template <typename SparseMatrixType>
+struct expected_pytype_for_arg<SparseMatrixType,
+                               Eigen::SparseMatrixBase<SparseMatrixType> > {
+  static PyTypeObject const *get_pytype() {
+    PyTypeObject const *py_type = ScipyType::get_pytype<SparseMatrixType>();
+    return py_type;
+  }
+};
+
+}  // namespace eigenpy
+
+namespace boost {
+namespace python {
+namespace converter {
+
+template <typename Scalar, int Options, typename StorageIndex>
+struct expected_pytype_for_arg<
+    Eigen::SparseMatrix<Scalar, Options, StorageIndex> >
+    : eigenpy::expected_pytype_for_arg<
+          Eigen::SparseMatrix<Scalar, Options, StorageIndex> > {};
+
+template <typename Scalar, int Options, typename StorageIndex>
+struct rvalue_from_python_data<
+    Eigen::SparseMatrix<Scalar, Options, StorageIndex> const &>
+    : ::eigenpy::rvalue_from_python_data<
+          Eigen::SparseMatrix<Scalar, Options, StorageIndex> const &> {
+  typedef Eigen::SparseMatrix<Scalar, Options, StorageIndex> T;
+  EIGENPY_RVALUE_FROM_PYTHON_DATA_INIT(T const &)
+};
+
+template <typename Derived>
+struct rvalue_from_python_data<Eigen::SparseMatrixBase<Derived> const &>
+    : ::eigenpy::rvalue_from_python_data<Derived const &> {
+  EIGENPY_RVALUE_FROM_PYTHON_DATA_INIT(Derived const &)
+};
+
+}  // namespace converter
+}  // namespace python
+}  // namespace boost
+
+namespace boost {
+namespace python {
+namespace detail {
+// template <typename TensorType>
+// struct referent_storage<Eigen::TensorRef<TensorType> &> {
+//   typedef Eigen::TensorRef<TensorType> RefType;
+//   typedef ::eigenpy::details::referent_storage_eigen_ref<RefType>
+//   StorageType; typedef typename ::eigenpy::aligned_storage<
+//       referent_size<StorageType &>::value>::type type;
+// };
+
+// template <typename TensorType>
+// struct referent_storage<const Eigen::TensorRef<const TensorType> &> {
+//   typedef Eigen::TensorRef<const TensorType> RefType;
+//   typedef ::eigenpy::details::referent_storage_eigen_ref<RefType>
+//   StorageType; typedef typename ::eigenpy::aligned_storage<
+//       referent_size<StorageType &>::value>::type type;
+// };
+}  // namespace detail
+}  // namespace python
+}  // namespace boost
+
+namespace eigenpy {
+
+template <typename SparseMatrixType>
+struct eigen_from_py_impl<SparseMatrixType,
+                          Eigen::SparseMatrixBase<SparseMatrixType> > {
+  typedef typename SparseMatrixType::Scalar Scalar;
+
+  /// \brief Determine if pyObj can be converted into a MatType object
+  static void *convertible(PyObject *pyObj);
+
+  /// \brief Allocate memory and copy pyObj in the new storage
+  static void construct(PyObject *pyObj,
+                        bp::converter::rvalue_from_python_stage1_data *memory);
+
+  static void registration();
+};
+
+template <typename SparseMatrixType>
+void *eigen_from_py_impl<
+    SparseMatrixType,
+    Eigen::SparseMatrixBase<SparseMatrixType> >::convertible(PyObject *pyObj) {
+  const PyTypeObject *type = Py_TYPE(pyObj);
+  const PyTypeObject *sparse_matrix_py_type =
+      ScipyType::get_pytype<SparseMatrixType>();
+  typedef typename SparseMatrixType::Scalar Scalar;
+
+  if (type != sparse_matrix_py_type) return 0;
+
+  bp::object obj(bp::handle<>(bp::borrowed(pyObj)));
+
+  const int type_num = ScipyType::get_numpy_type_num(obj);
+
+  if (!np_type_is_convertible_into_scalar<Scalar>(type_num)) return 0;
+
+  return pyObj;
+}
+
+template <typename MatOrRefType>
+void eigen_sparse_matrix_from_py_construct(
+    PyObject *pyObj, bp::converter::rvalue_from_python_stage1_data *memory) {
+  typedef typename MatOrRefType::Scalar Scalar;
+  typedef typename MatOrRefType::StorageIndex StorageIndex;
+
+  typedef Eigen::Map<MatOrRefType> MapMatOrRefType;
+
+  bp::converter::rvalue_from_python_storage<MatOrRefType> *storage =
+      reinterpret_cast<
+          bp::converter::rvalue_from_python_storage<MatOrRefType> *>(
+          reinterpret_cast<void *>(memory));
+  void *raw_ptr = storage->storage.bytes;
+
+  bp::object obj(bp::handle<>(bp::borrowed(pyObj)));
+
+  const int type_num_python_sparse_matrix = ScipyType::get_numpy_type_num(obj);
+  const int type_num_eigen_sparse_matrix = Register::getTypeCode<Scalar>();
+
+  if (type_num_eigen_sparse_matrix == type_num_python_sparse_matrix) {
+    typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1> DataVector;
+    //    typedef const Eigen::Ref<const DataVector> RefDataVector;
+    DataVector data = bp::extract<DataVector>(obj.attr("data"));
+    bp::tuple shape = bp::extract<bp::tuple>(obj.attr("shape"));
+    typedef Eigen::Matrix<StorageIndex, Eigen::Dynamic, 1> StorageIndexVector;
+    //    typedef const Eigen::Ref<const StorageIndexVector>
+    //    RefStorageIndexVector;
+    StorageIndexVector indices =
+        bp::extract<StorageIndexVector>(obj.attr("indices"));
+    StorageIndexVector indptr =
+        bp::extract<StorageIndexVector>(obj.attr("indptr"));
+
+    const Eigen::Index m = bp::extract<Eigen::Index>(shape[0]),
+                       n = bp::extract<Eigen::Index>(shape[1]),
+                       nnz = bp::extract<Eigen::Index>(obj.attr("nnz"));
+    MapMatOrRefType sparse_map(m, n, nnz, indptr.data(), indices.data(),
+                               data.data());
+
+    new (raw_ptr) MatOrRefType(sparse_map);
+  }
+
+  memory->convertible = storage->storage.bytes;
+}
+
+template <typename SparseMatrixType>
+void eigen_from_py_impl<SparseMatrixType,
+                        Eigen::SparseMatrixBase<SparseMatrixType> >::
+    construct(PyObject *pyObj,
+              bp::converter::rvalue_from_python_stage1_data *memory) {
+  eigen_sparse_matrix_from_py_construct<SparseMatrixType>(pyObj, memory);
+}
+
+template <typename SparseMatrixType>
+void eigen_from_py_impl<
+    SparseMatrixType,
+    Eigen::SparseMatrixBase<SparseMatrixType> >::registration() {
+  bp::converter::registry::push_back(
+      reinterpret_cast<void *(*)(_object *)>(&eigen_from_py_impl::convertible),
+      &eigen_from_py_impl::construct, bp::type_id<SparseMatrixType>()
+#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
+                                          ,
+      &eigenpy::expected_pytype_for_arg<SparseMatrixType>::get_pytype
+#endif
+  );
+}
+
+template <typename SparseMatrixType>
+struct eigen_from_py_converter_impl<
+    SparseMatrixType, Eigen::SparseMatrixBase<SparseMatrixType> > {
+  static void registration() {
+    EigenFromPy<SparseMatrixType>::registration();
+
+    // Add conversion to Eigen::SparseMatrixBase<SparseMatrixType>
+    typedef Eigen::SparseMatrixBase<SparseMatrixType> SparseMatrixBase;
+    EigenFromPy<SparseMatrixBase>::registration();
+
+    //    // Add conversion to Eigen::Ref<SparseMatrixType>
+    //    typedef Eigen::Ref<SparseMatrixType> RefType;
+    //    EigenFromPy<SparseMatrixType>::registration();
+    //
+    //    // Add conversion to Eigen::Ref<const SparseMatrixType>
+    //    typedef const Eigen::Ref<const SparseMatrixType> ConstRefType;
+    //    EigenFromPy<ConstRefType>::registration();
+  }
+};
+
+template <typename SparseMatrixType>
+struct EigenFromPy<Eigen::SparseMatrixBase<SparseMatrixType> >
+    : EigenFromPy<SparseMatrixType> {
+  typedef EigenFromPy<SparseMatrixType> EigenFromPyDerived;
+  typedef Eigen::SparseMatrixBase<SparseMatrixType> Base;
+
+  static void registration() {
+    bp::converter::registry::push_back(
+        reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
+        &EigenFromPy::construct, bp::type_id<Base>()
+#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
+                                     ,
+        &eigenpy::expected_pytype_for_arg<SparseMatrixType>::get_pytype
+#endif
+    );
+  }
+};
+//
+// template <typename TensorType>
+// struct EigenFromPy<Eigen::TensorRef<TensorType> > {
+//  typedef Eigen::TensorRef<TensorType> RefType;
+//  typedef typename TensorType::Scalar Scalar;
+//
+//  /// \brief Determine if pyObj can be converted into a MatType object
+//  static void *convertible(PyObject *pyObj) {
+//    if (!call_PyArray_Check(pyObj)) return 0;
+//    PyArrayObject *pyArray = reinterpret_cast<PyArrayObject *>(pyObj);
+//    if (!PyArray_ISWRITEABLE(pyArray)) return 0;
+//    return EigenFromPy<TensorType>::convertible(pyObj);
+//  }
+//
+//  static void registration() {
+//    bp::converter::registry::push_back(
+//        reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
+//        &eigen_from_py_construct<RefType>, bp::type_id<RefType>()
+// #ifndef BOOST_PYTHON_NO_PY_SIGNATURES
+//                                               ,
+//        &eigenpy::expected_pytype_for_arg<TensorType>::get_pytype
+// #endif
+//    );
+//  }
+//};
+
+// template <typename TensorType>
+// struct EigenFromPy<const Eigen::TensorRef<const TensorType> > {
+//   typedef const Eigen::TensorRef<const TensorType> ConstRefType;
+//   typedef typename TensorType::Scalar Scalar;
+//
+//   /// \brief Determine if pyObj can be converted into a MatType object
+//   static void *convertible(PyObject *pyObj) {
+//     return EigenFromPy<TensorType>::convertible(pyObj);
+//   }
+//
+//   static void registration() {
+//     bp::converter::registry::push_back(
+//         reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
+//         &eigen_from_py_construct<ConstRefType>, bp::type_id<ConstRefType>()
+// #ifndef BOOST_PYTHON_NO_PY_SIGNATURES
+//                                                     ,
+//         &eigenpy::expected_pytype_for_arg<TensorType>::get_pytype
+// #endif
+//     );
+//   }
+// };
+
+}  // namespace eigenpy
+
+#endif  // __eigenpy_sparse_eigen_from_python_hpp__
diff --git a/src/matrix-long.cpp b/src/matrix-long.cpp
index db29afd4bd3d9ff2c251f0b475fb66cc1ace53c5..f52e89f1aabbf2715366764ce689ff8a1f0465ce 100644
--- a/src/matrix-long.cpp
+++ b/src/matrix-long.cpp
@@ -6,7 +6,12 @@
 
 namespace eigenpy {
 void exposeMatrixLong() {
+#ifdef WIN32
+  exposeType<__int64>();
+  exposeType<__int64, Eigen::RowMajor>();
+#else
   exposeType<long>();
   exposeType<long, Eigen::RowMajor>();
+#endif
 }
 }  // namespace eigenpy
diff --git a/src/register.cpp b/src/register.cpp
index ec22decec96d057134761e0e87b11121c019d0af..84e84f645d028b81672dd5e84911c7ff58536238 100644
--- a/src/register.cpp
+++ b/src/register.cpp
@@ -14,6 +14,17 @@ PyArray_Descr* Register::getPyArrayDescr(PyTypeObject* py_type_ptr) {
     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;
diff --git a/src/scipy-type.cpp b/src/scipy-type.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e189e636fa124bfee202a283da5dbb308c689bd9
--- /dev/null
+++ b/src/scipy-type.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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() {
+  sparse_module = bp::import("scipy.sparse");
+
+#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
diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt
index 88b07f496f39cf232a4040da0d75e9b1371ba470..dab2bfba7596b7ceff0927e21e622b62ba1e8e22 100644
--- a/unittest/CMakeLists.txt
+++ b/unittest/CMakeLists.txt
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2014-2019 CNRS Copyright (c) 2018-2023 INRIA
+# Copyright (c) 2014-2019 CNRS Copyright (c) 2018-2024 INRIA
 #
 
 macro(ADD_LIB_UNIT_TEST test)
@@ -25,6 +25,10 @@ macro(ADD_LIB_UNIT_TEST test)
 endmacro(ADD_LIB_UNIT_TEST)
 
 add_lib_unit_test(matrix)
+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)
@@ -63,6 +67,10 @@ endif()
 add_lib_unit_test(bind_virtual_factory)
 
 add_python_unit_test("py-matrix" "unittest/python/test_matrix.py" "unittest")
+if(BUILD_TESTING_SCIPY)
+  add_python_unit_test("py-sparse-matrix"
+                       "unittest/python/test_sparse_matrix.py" "unittest")
+endif()
 
 add_python_unit_test("py-tensor" "unittest/python/test_tensor.py" "unittest")
 add_python_unit_test("py-geometry" "unittest/python/test_geometry.py"
diff --git a/unittest/python/test_sparse_matrix.py b/unittest/python/test_sparse_matrix.py
new file mode 100644
index 0000000000000000000000000000000000000000..428c3bede04d128226ab42a0677ab0bc21770022
--- /dev/null
+++ b/unittest/python/test_sparse_matrix.py
@@ -0,0 +1,28 @@
+from __future__ import print_function
+
+import numpy as np
+import sparse_matrix
+from scipy.sparse import csc_matrix, csr_matrix
+
+m = sparse_matrix.emptyMatrix()
+assert m.shape == (0, 0)
+
+v = sparse_matrix.emptyVector()
+assert v.shape == (0, 0)
+
+m = sparse_matrix.matrix1x1(2)
+assert m.toarray() == np.array([2])
+
+v = sparse_matrix.vector1x1(2)
+assert v.toarray() == np.array([2])
+
+size = 100
+diag_values = np.random.rand(100)
+diag_mat = sparse_matrix.diagonal(diag_values)
+assert (diag_mat.toarray() == np.diag(diag_values)).all()
+
+diag_mat_copy = sparse_matrix.copy(diag_mat)
+assert (diag_mat_copy != diag_mat).nnz == 0
+
+diag_mat_csr = csr_matrix(diag_mat)
+assert (sparse_matrix.copy(diag_mat_csr) != diag_mat_csr).nnz == 0
diff --git a/unittest/python/test_tensor.py b/unittest/python/test_tensor.py
index b82d34f91fb57877e628cce4842ebbbd31312955..ff641cfec9bb868af03cb7043cd9ec9565a3eb78 100644
--- a/unittest/python/test_tensor.py
+++ b/unittest/python/test_tensor.py
@@ -3,7 +3,7 @@ from __future__ import print_function
 import numpy as np
 import tensor
 
-dim = np.array([10, 20, 30], dtype=np.int32)
+dim = np.array([10, 20, 30], dtype=np.int64)
 t = tensor.TensorContainer3(dim)
 r = t.get_ref()
 r[:] = 0.0
diff --git a/unittest/sparse_matrix.cpp b/unittest/sparse_matrix.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e8e4b2d6048c4462da17a946eb95ab9135dfd8c8
--- /dev/null
+++ b/unittest/sparse_matrix.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2024 CNRS INRIA
+ */
+
+#include <iostream>
+
+#include "eigenpy/eigenpy.hpp"
+
+template <typename Scalar, int Options>
+Eigen::SparseMatrix<Scalar, Options> vector1x1(const Scalar& value) {
+  typedef Eigen::SparseMatrix<Scalar, Options> ReturnType;
+  ReturnType mat(1, 1);
+  mat.coeffRef(0, 0) = value;
+  mat.makeCompressed();
+  return mat;
+}
+
+template <typename Scalar, int Options>
+Eigen::SparseMatrix<Scalar, Options> matrix1x1(const Scalar& value) {
+  typedef Eigen::SparseMatrix<Scalar, Options> ReturnType;
+  ReturnType mat(1, 1);
+  mat.coeffRef(0, 0) = value;
+  mat.makeCompressed();
+  return mat;
+}
+
+template <typename Scalar, int Options>
+Eigen::SparseMatrix<Scalar, Options> diagonal(
+    const Eigen::Ref<Eigen::Matrix<Scalar, Eigen::Dynamic, 1> >& diag_values) {
+  typedef Eigen::SparseMatrix<Scalar, Options> ReturnType;
+  ReturnType mat(diag_values.size(), diag_values.size());
+  for (Eigen::Index k = 0; k < diag_values.size(); ++k)
+    mat.coeffRef(k, k) = diag_values[k];
+  mat.makeCompressed();
+  return mat;
+}
+
+template <typename Scalar, int Options>
+Eigen::SparseMatrix<Scalar, Options> emptyVector() {
+  return Eigen::SparseMatrix<Scalar, Options>();
+}
+
+template <typename Scalar, int Options>
+Eigen::SparseMatrix<Scalar, Options> emptyMatrix() {
+  return Eigen::SparseMatrix<Scalar, Options>();
+}
+
+template <typename Scalar, int Options>
+void print(const Eigen::SparseMatrix<Scalar, Options>& mat) {
+  std::cout << mat << std::endl;
+}
+
+template <typename Scalar, int Options>
+Eigen::SparseMatrix<Scalar, Options> copy(
+    const Eigen::SparseMatrix<Scalar, Options>& mat) {
+  return mat;
+}
+
+template <typename Scalar, int Options>
+void expose_functions() {
+  namespace bp = boost::python;
+  bp::def("vector1x1", vector1x1<Scalar, Options>);
+  bp::def("matrix1x1", matrix1x1<Scalar, Options>);
+
+  bp::def("print", print<Scalar, Options>);
+  bp::def("copy", copy<Scalar, Options>);
+  bp::def("diagonal", diagonal<Scalar, Options>);
+
+  bp::def("emptyVector", emptyVector<Scalar, Options>);
+  bp::def("emptyMatrix", emptyMatrix<Scalar, Options>);
+}
+
+BOOST_PYTHON_MODULE(sparse_matrix) {
+  namespace bp = boost::python;
+  eigenpy::enableEigenPy();
+
+  expose_functions<double, Eigen::ColMajor>();
+  expose_functions<double, Eigen::RowMajor>();
+}
diff --git a/unittest/tensor.cpp b/unittest/tensor.cpp
index 80838ebfc06d0a810d4d252f3c9322220a4850bb..7a7ef6d3e0110dc7c22c74a081d287a2557344df 100644
--- a/unittest/tensor.cpp
+++ b/unittest/tensor.cpp
@@ -122,7 +122,7 @@ template <typename Scalar, int Rank>
 struct TensorContainer {
   typedef Eigen::Tensor<Scalar, Rank> Tensor;
   typedef Eigen::TensorRef<Tensor> TensorRef;
-  typedef Eigen::Matrix<Scalar, Rank, 1> Dimensions;
+  typedef Eigen::Matrix<typename Tensor::Index, Rank, 1> Dimensions;
 
   Tensor m_tensor;
   TensorContainer(const Dimensions& dims) {