diff --git a/CHANGELOG.md b/CHANGELOG.md index 156a578d9581d1a6eb7e55f56dac1fc83826efed..0a38a1c3137b5c4f7627f8bebe3aa91fc77ead37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added - Added a deprecation call policy shortcut ([#466](https://github.com/stack-of-tasks/eigenpy/pull/466)) +- Added id() helper to retrieve unique object identifier in Python ([#477](https://github.com/stack-of-tasks/eigenpy/pull/477)) ### Fixed - Fix register_symbolic_link_to_registered_type() for multiple successive registrations ([#471](https://github.com/stack-of-tasks/eigenpy/pull/471)) diff --git a/CMakeLists.txt b/CMakeLists.txt index bcc8f9897f45aeb0ef2b2ebbb720f82ec45cde4f..94bf5d85acf9716dc37d2e407748fc7de0aa9998 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -232,6 +232,7 @@ set(${PROJECT_NAME}_HEADERS include/eigenpy/eigen-to-python.hpp include/eigenpy/eigen-from-python.hpp include/eigenpy/eigen-typedef.hpp + include/eigenpy/id.hpp include/eigenpy/numpy-map.hpp include/eigenpy/geometry.hpp include/eigenpy/geometry-conversion.hpp diff --git a/include/eigenpy/angle-axis.hpp b/include/eigenpy/angle-axis.hpp index 3f0377c8030177b0ab32da468304b729bd45083d..d0600c79bf907263754157032fd361c2c728728d 100644 --- a/include/eigenpy/angle-axis.hpp +++ b/include/eigenpy/angle-axis.hpp @@ -118,7 +118,8 @@ class AngleAxisVisitor : public bp::def_visitor<AngleAxisVisitor<AngleAxis> > { static void expose() { bp::class_<AngleAxis>( "AngleAxis", "AngleAxis representation of a rotation.\n\n", bp::no_init) - .def(AngleAxisVisitor<AngleAxis>()); + .def(AngleAxisVisitor<AngleAxis>()) + .def(IdVisitor<AngleAxis>()); // Cast to Eigen::RotationBase bp::implicitly_convertible<AngleAxis, RotationBase>(); diff --git a/include/eigenpy/decompositions/EigenSolver.hpp b/include/eigenpy/decompositions/EigenSolver.hpp index 3bc447d6a2d12975b01405db41c57e71e71a9439..14583b2e3e9cade3d0bd2d42f7a9b8a162eaa824 100644 --- a/include/eigenpy/decompositions/EigenSolver.hpp +++ b/include/eigenpy/decompositions/EigenSolver.hpp @@ -75,7 +75,9 @@ struct EigenSolverVisitor } static void expose(const std::string& name) { - bp::class_<Solver>(name.c_str(), bp::no_init).def(EigenSolverVisitor()); + bp::class_<Solver>(name.c_str(), bp::no_init) + .def(EigenSolverVisitor()) + .def(IdVisitor<Solver>()); } private: diff --git a/include/eigenpy/decompositions/LDLT.hpp b/include/eigenpy/decompositions/LDLT.hpp index 2a635a217b028e3d6c10eac97bac814b26d6469a..b148a23a2ddbc652cdb7a75e743c632b33a4931c 100644 --- a/include/eigenpy/decompositions/LDLT.hpp +++ b/include/eigenpy/decompositions/LDLT.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 INRIA + * Copyright 2020-2024 INRIA */ #ifndef __eigenpy_decomposition_ldlt_hpp__ @@ -119,6 +119,7 @@ struct LDLTSolverVisitor "have zeros in the bottom right rank(A) - n submatrix. Avoiding the " "square root on D also stabilizes the computation.", bp::no_init) + .def(IdVisitor<Solver>()) .def(LDLTSolverVisitor()); } diff --git a/include/eigenpy/decompositions/LLT.hpp b/include/eigenpy/decompositions/LLT.hpp index 1996d6ccbe3657a44f6a258b77b9adf75eebe08a..695c37300cc2072fe2ca7f96a26c9ce8aecbf85a 100644 --- a/include/eigenpy/decompositions/LLT.hpp +++ b/include/eigenpy/decompositions/LLT.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 INRIA + * Copyright 2020-2024 INRIA */ #ifndef __eigenpy_decomposition_llt_hpp__ @@ -115,6 +115,7 @@ struct LLTSolverVisitor "remains useful in many other situations like generalised eigen " "problems with hermitian matrices.", bp::no_init) + .def(IdVisitor<Solver>()) .def(LLTSolverVisitor()); } diff --git a/include/eigenpy/decompositions/PermutationMatrix.hpp b/include/eigenpy/decompositions/PermutationMatrix.hpp index 48d8ce7b88cfb2422a1d61aea171f1b0cf3ec86c..094bdbaf1144c0258c9e2ba36aa4910550f4ab72 100644 --- a/include/eigenpy/decompositions/PermutationMatrix.hpp +++ b/include/eigenpy/decompositions/PermutationMatrix.hpp @@ -97,6 +97,7 @@ struct PermutationMatrixVisitor "This class represents a permutation matrix, " "internally stored as a vector of integers.", bp::no_init) + .def(IdVisitor<PermutationMatrix>()) .def(PermutationMatrixVisitor()); } }; diff --git a/include/eigenpy/decompositions/SelfAdjointEigenSolver.hpp b/include/eigenpy/decompositions/SelfAdjointEigenSolver.hpp index cd1ddd42b4160788d1dd53e3b910afb56e531ff7..b2587a802459c192b6358a6abf3d4daf75292828 100644 --- a/include/eigenpy/decompositions/SelfAdjointEigenSolver.hpp +++ b/include/eigenpy/decompositions/SelfAdjointEigenSolver.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2020 INRIA + * Copyright 2020-2024 INRIA */ #ifndef __eigenpy_decomposition_self_adjoint_eigen_solver_hpp__ @@ -84,6 +84,7 @@ struct SelfAdjointEigenSolverVisitor static void expose(const std::string& name) { bp::class_<Solver>(name.c_str(), bp::no_init) + .def(IdVisitor<Solver>()) .def(SelfAdjointEigenSolverVisitor()); } diff --git a/include/eigenpy/decompositions/minres.hpp b/include/eigenpy/decompositions/minres.hpp index f2de4e0ca1963bd1cdee18c9e93d80f1fec0c872..7f5019c4b61f708f546c6cdbfc0638305fa6e65c 100644 --- a/include/eigenpy/decompositions/minres.hpp +++ b/include/eigenpy/decompositions/minres.hpp @@ -165,7 +165,8 @@ struct MINRESSolverVisitor "defaults are the size of the problem for the maximal number of " "iterations and NumTraits<Scalar>::epsilon() for the tolerance.\n", bp::no_init) - .def(MINRESSolverVisitor()); + .def(MINRESSolverVisitor()) + .def(IdVisitor<Solver>()); } private: diff --git a/include/eigenpy/decompositions/sparse/LDLT.hpp b/include/eigenpy/decompositions/sparse/LDLT.hpp index d282442cb717fe9885e3678a489e725f52e6f66e..0d552e4e86cfb2f7c2b342d6e0ddbd34a826dac6 100644 --- a/include/eigenpy/decompositions/sparse/LDLT.hpp +++ b/include/eigenpy/decompositions/sparse/LDLT.hpp @@ -59,7 +59,8 @@ struct SimplicialLDLTVisitor "prior to the factorization such that the factorized matrix is P A " "P^-1.", bp::no_init) - .def(SimplicialLDLTVisitor()); + .def(SimplicialLDLTVisitor()) + .def(IdVisitor<Solver>()); } private: diff --git a/include/eigenpy/decompositions/sparse/LLT.hpp b/include/eigenpy/decompositions/sparse/LLT.hpp index 199c2573480dbd2a824c78ee8600d74a388e4527..3d3fa0d562e99fc468bfb976cd19dfe0e4fd8d4e 100644 --- a/include/eigenpy/decompositions/sparse/LLT.hpp +++ b/include/eigenpy/decompositions/sparse/LLT.hpp @@ -57,7 +57,8 @@ struct SimplicialLLTVisitor "prior to the factorization such that the factorized matrix is P A " "P^-1.", bp::no_init) - .def(SimplicialLLTVisitor()); + .def(SimplicialLLTVisitor()) + .def(IdVisitor<Solver>()); } }; diff --git a/include/eigenpy/fwd.hpp b/include/eigenpy/fwd.hpp index c511b569097978a51e659340a5fb13ddacfdac44..acc8edc1100f8d34496eb2426c4590b48452cc25 100644 --- a/include/eigenpy/fwd.hpp +++ b/include/eigenpy/fwd.hpp @@ -200,5 +200,6 @@ struct has_operator_equal : internal::has_operator_equal_impl<T1, T2>::type {}; } // namespace eigenpy #include "eigenpy/alignment.hpp" +#include "eigenpy/id.hpp" #endif // ifndef __eigenpy_fwd_hpp__ diff --git a/include/eigenpy/id.hpp b/include/eigenpy/id.hpp new file mode 100644 index 0000000000000000000000000000000000000000..af56d79f1ee2daf2c02cebf79bd8afb71eb016a9 --- /dev/null +++ b/include/eigenpy/id.hpp @@ -0,0 +1,33 @@ +// +// Copyright (c) 2024 INRIA +// + +#ifndef __eigenpy_id_hpp__ +#define __eigenpy_id_hpp__ + +#include <boost/python.hpp> +#include <boost/cstdint.hpp> + +namespace eigenpy { + +/// +/// \brief Add the Python method id to retrieving a unique id for a given object +/// exposed with Boost.Python +/// +template <class C> +struct IdVisitor : public bp::def_visitor<IdVisitor<C> > { + template <class PyClass> + void visit(PyClass& cl) const { + cl.def("id", &id, bp::arg("self"), + "Returns the unique identity of an object.\n" + "For object held in C++, it corresponds to its memory address."); + } + + private: + static boost::int64_t id(const C& self) { + return boost::int64_t(reinterpret_cast<const void*>(&self)); + } +}; +} // namespace eigenpy + +#endif // ifndef __eigenpy_id_hpp__ diff --git a/include/eigenpy/quaternion.hpp b/include/eigenpy/quaternion.hpp index a5051c154317ad9d4ff559a3c7aa813fc1777c96..69a72b9eae2de95286a302edca7e366db7b58f86 100644 --- a/include/eigenpy/quaternion.hpp +++ b/include/eigenpy/quaternion.hpp @@ -364,7 +364,8 @@ class QuaternionVisitor "'q*v' (rotating 'v' by 'q'), " "'q==q', 'q!=q', 'q[0..3]'.", bp::no_init) - .def(QuaternionVisitor<Quaternion>()); + .def(QuaternionVisitor<Quaternion>()) + .def(IdVisitor<Quaternion>()); // Cast to Eigen::QuaternionBase and vice-versa bp::implicitly_convertible<Quaternion, QuaternionBase>(); diff --git a/include/eigenpy/solvers/BFGSPreconditioners.hpp b/include/eigenpy/solvers/BFGSPreconditioners.hpp index 2fd36833994863299e366aee7e4cd7c731143bc6..53c4ad433a71fae871141514e0c00f9587328b15 100644 --- a/include/eigenpy/solvers/BFGSPreconditioners.hpp +++ b/include/eigenpy/solvers/BFGSPreconditioners.hpp @@ -1,5 +1,6 @@ /* * Copyright 2017 CNRS + * Copyright 2024 Inria */ #ifndef __eigenpy_bfgs_preconditioners_hpp__ @@ -37,6 +38,7 @@ struct BFGSPreconditionerBaseVisitor static void expose(const std::string& name) { bp::class_<Preconditioner>(name, bp::no_init) + .def(IdVisitor<Preconditioner>()) .def(BFGSPreconditionerBaseVisitor<Preconditioner>()); } }; @@ -56,6 +58,7 @@ struct LimitedBFGSPreconditionerBaseVisitor static void expose(const std::string& name) { bp::class_<Preconditioner>(name.c_str(), bp::no_init) + .def(IdVisitor<Preconditioner>()) .def(LimitedBFGSPreconditionerBaseVisitor<Preconditioner>()); } }; diff --git a/include/eigenpy/solvers/BasicPreconditioners.hpp b/include/eigenpy/solvers/BasicPreconditioners.hpp index d74ee3f225e89ec8b89c611bfa54fa181e3ef25a..fb65f2028e9559df5785666bfb3795d0e37a4aab 100644 --- a/include/eigenpy/solvers/BasicPreconditioners.hpp +++ b/include/eigenpy/solvers/BasicPreconditioners.hpp @@ -1,5 +1,6 @@ /* * Copyright 2017 CNRS + * Copyright 2024 Inria */ #ifndef __eigenpy_basic_preconditioners_hpp__ @@ -69,7 +70,8 @@ struct DiagonalPreconditionerVisitor "A preconditioner based on the digonal entrie.\n" "This class allows to approximately solve for A.x = b problems " "assuming A is a diagonal matrix.", - bp::no_init); + bp::no_init) + .def(IdVisitor<Preconditioner>()); } }; @@ -91,7 +93,8 @@ struct LeastSquareDiagonalPreconditionerVisitor "his class allows to approximately solve for A' A x = A' b problems " "assuming A' A is a diagonal matrix.", bp::no_init) - .def(DiagonalPreconditionerVisitor<Scalar>()); + .def(DiagonalPreconditionerVisitor<Scalar>()) + .def(IdVisitor<Preconditioner>()); } }; #endif @@ -105,7 +108,8 @@ struct IdentityPreconditionerVisitor static void expose() { bp::class_<Preconditioner>("IdentityPreconditioner", bp::no_init) - .def(PreconditionerBaseVisitor<Preconditioner>()); + .def(PreconditionerBaseVisitor<Preconditioner>()) + .def(IdVisitor<Preconditioner>()); } }; diff --git a/include/eigenpy/solvers/ConjugateGradient.hpp b/include/eigenpy/solvers/ConjugateGradient.hpp index 6f41e9d788c0136452352a8b224a105aa0982ded..1d3d71afff6c65a35f15c8bf5df2e45bf82a468f 100644 --- a/include/eigenpy/solvers/ConjugateGradient.hpp +++ b/include/eigenpy/solvers/ConjugateGradient.hpp @@ -31,7 +31,8 @@ struct ConjugateGradientVisitor static void expose(const std::string& name = "ConjugateGradient") { bp::class_<ConjugateGradient, boost::noncopyable>(name.c_str(), bp::no_init) - .def(ConjugateGradientVisitor<ConjugateGradient>()); + .def(ConjugateGradientVisitor<ConjugateGradient>()) + .def(IdVisitor<ConjugateGradient>()); } }; diff --git a/include/eigenpy/solvers/LeastSquaresConjugateGradient.hpp b/include/eigenpy/solvers/LeastSquaresConjugateGradient.hpp index 3107270e0eb4d127057c46fd37af0a07e8c93511..21464ae6100cc8e980402453bb5b0203aff2d5cd 100644 --- a/include/eigenpy/solvers/LeastSquaresConjugateGradient.hpp +++ b/include/eigenpy/solvers/LeastSquaresConjugateGradient.hpp @@ -34,7 +34,8 @@ struct LeastSquaresConjugateGradientVisitor "LeastSquaresConjugateGradient", bp::no_init) .def(IterativeSolverVisitor<LeastSquaresConjugateGradient>()) .def(LeastSquaresConjugateGradientVisitor< - LeastSquaresConjugateGradient>()); + LeastSquaresConjugateGradient>()) + .def(IdVisitor<LeastSquaresConjugateGradient>()); } }; diff --git a/include/eigenpy/std-array.hpp b/include/eigenpy/std-array.hpp index 492e003201f68a9be4e857c99a221ee5c42c27aa..19ea821b495707fa06a8e43a90bf89237ff5a056 100644 --- a/include/eigenpy/std-array.hpp +++ b/include/eigenpy/std-array.hpp @@ -135,6 +135,7 @@ struct StdArrayPythonVisitor { bp::class_<array_type> cl(class_name.c_str(), doc_string.c_str()); cl.def(bp::init<const array_type &>(bp::args("self", "other"), "Copy constructor")); + cl.def(IdVisitor<array_type>()); array_indexing_suite<array_type, NoProxy, SliceAllocator> indexing_suite; cl.def(indexing_suite) diff --git a/include/eigenpy/std-vector.hpp b/include/eigenpy/std-vector.hpp index 0c5e3bf6e7d1e38394747c03b939f4e1d9457070..2dbdabfd2f8e9bd03c642b0e14466bc5d2648b33 100644 --- a/include/eigenpy/std-vector.hpp +++ b/include/eigenpy/std-vector.hpp @@ -454,6 +454,7 @@ struct StdVectorPythonVisitor { if (!register_symbolic_link_to_registered_type<vector_type>( add_std_visitor)) { bp::class_<vector_type> cl(class_name.c_str(), doc_string.c_str()); + cl.def(IdVisitor<vector_type>()); // Standard vector indexing definition boost::python::vector_indexing_suite< diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index 72bfb1bb33e19ab97a0476fe265d576059969635..38f75745f9f7672bf953bb8bba2ccd36190c6059 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -134,6 +134,8 @@ add_python_eigenpy_lib_unit_test("py-LLT" "unittest/python/test_LLT.py") add_python_eigenpy_lib_unit_test("py-LDLT" "unittest/python/test_LDLT.py") +add_python_eigenpy_lib_unit_test("py-id" "unittest/python/test_id.py") + if(NOT WIN32) add_python_eigenpy_lib_unit_test("py-MINRES" "unittest/python/test_MINRES.py") endif(NOT WIN32) diff --git a/unittest/python/test_id.py b/unittest/python/test_id.py new file mode 100644 index 0000000000000000000000000000000000000000..c6d87bb5e611bc248cbd39bbfbf79009bbb80589 --- /dev/null +++ b/unittest/python/test_id.py @@ -0,0 +1,11 @@ +import eigenpy + +ldlt1 = eigenpy.LDLT() +ldlt2 = eigenpy.LDLT() + +id1 = ldlt1.id() +id2 = ldlt2.id() + +assert id1 != id2 +assert id1 == ldlt1.id() +assert id2 == ldlt2.id()