Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • jcarpent/eigenpy
  • gsaurel/eigenpy
  • stack-of-tasks/eigenpy
3 results
Show changes
Showing
with 712 additions and 601 deletions
//
// Copyright (c) 2019 INRIA
// Copyright (c) 2019-2023 INRIA
//
#include "eigenpy/version.hpp"
#include "eigenpy/config.hpp"
#include <sstream>
#include "eigenpy/config.hpp"
#include <Eigen/Core>
namespace eigenpy {
......@@ -17,6 +17,13 @@ std::string printVersion(const std::string& delimiter) {
return oss.str();
}
std::string printEigenVersion(const std::string& delimiter) {
std::ostringstream oss;
oss << EIGEN_MAJOR_VERSION << delimiter << EIGEN_MINOR_VERSION << delimiter
<< EIGEN_MINOR_VERSION;
return oss.str();
}
bool checkVersionAtLeast(unsigned int major_version, unsigned int minor_version,
unsigned int patch_version) {
return EIGENPY_VERSION_AT_LEAST(major_version, minor_version, patch_version);
......
#
# Copyright (c) 2014-2019 CNRS Copyright (c) 2018-2021 INRIA
# Copyright (c) 2014-2019 CNRS Copyright (c) 2018-2024 INRIA
#
macro(ADD_LIB_UNIT_TEST test)
function(ADD_LIB_UNIT_TEST test)
create_ctest_build_tests_target()
set(test_target ${PROJECT_NAME}-${test})
if(BUILD_TESTING)
add_library(${test} SHARED "${test}.cpp")
else(BUILD_TESTING)
add_library(${test} SHARED EXCLUDE_FROM_ALL "${test}.cpp")
endif(BUILD_TESTING)
target_link_libraries(${test} PUBLIC ${PROJECT_NAME})
set_target_properties(${test} PROPERTIES PREFIX "")
set_target_properties(${test} PROPERTIES SUFFIX ${PYTHON_EXT_SUFFIX})
add_test(NAME ${test} COMMAND ${PYTHON_EXECUTABLE} -c "import ${test}")
add_dependencies(build_tests ${test})
add_library(${test_target} SHARED "${test}.cpp")
else()
add_library(${test_target} SHARED EXCLUDE_FROM_ALL "${test}.cpp")
endif()
set_standard_output_directory(${test_target})
target_link_libraries(${test_target} PUBLIC ${PROJECT_NAME})
set_target_properties(
${test_target}
PROPERTIES PREFIX ""
LIBRARY_OUTPUT_NAME ${test}
RUNTIME_OUTPUT_NAME ${test})
set_target_properties(${test_target} PROPERTIES SUFFIX ${PYTHON_EXT_SUFFIX})
add_test(
NAME ${test_target}
COMMAND ${PYTHON_EXECUTABLE} -c "import ${test}"
WORKING_DIRECTORY $<TARGET_FILE_DIR:${test_target}>)
add_dependencies(build_tests ${test_target})
if(NOT BUILD_TESTING)
set_tests_properties(${test} PROPERTIES DEPENDS ctest_build_tests)
set_tests_properties(${test_target} PROPERTIES DEPENDS ctest_build_tests)
endif(NOT BUILD_TESTING)
endmacro(ADD_LIB_UNIT_TEST)
endfunction()
add_dependencies(build_tests ${PYWRAP})
add_lib_unit_test(matrix)
add_lib_unit_test(type_info)
add_lib_unit_test(multiple_registration)
if(BUILD_TESTING_SCIPY)
find_scipy()
add_lib_unit_test(sparse_matrix)
endif()
add_lib_unit_test(tensor)
add_lib_unit_test(geometry)
add_lib_unit_test(complex)
add_lib_unit_test(deprecation_policy)
add_lib_unit_test(return_by_ref)
add_lib_unit_test(include)
if(NOT ${EIGEN3_VERSION} VERSION_LESS "3.2.0")
......@@ -37,55 +55,154 @@ if(NOT NUMPY_WITH_BROKEN_UFUNC_SUPPORT)
add_lib_unit_test(user_type)
endif()
add_lib_unit_test(std_vector)
add_lib_unit_test(std_array)
add_lib_unit_test(std_pair)
add_lib_unit_test(std_map)
add_lib_unit_test(user_struct)
if(CMAKE_CXX_STANDARD GREATER 14 AND CMAKE_CXX_STANDARD LESS 98)
add_lib_unit_test(std_unique_ptr)
endif()
add_python_unit_test("py-matrix" "unittest/python/test_matrix.py" "unittest")
add_python_unit_test("py-geometry" "unittest/python/test_geometry.py"
"unittest")
add_python_unit_test("py-complex" "unittest/python/test_complex.py" "unittest")
add_python_unit_test("py-return-by-ref" "unittest/python/test_return_by_ref.py"
"unittest")
add_python_unit_test("py-eigen-ref" "unittest/python/test_eigen_ref.py"
"unittest")
function(add_python_lib_unit_test name source)
set(test_target ${PROJECT_NAME}-${name})
add_python_unit_test(${test_target} ${source} "lib" "bin")
endfunction()
function(add_python_eigenpy_lib_unit_test name source)
set(test_target ${PROJECT_NAME}-${name})
add_python_unit_test(${test_target} ${source} "lib" "bin" "python")
set_tests_properties(${test_target} PROPERTIES DEPENDS ${PYWRAP})
endfunction()
function(add_python_eigenpy_unit_test name source)
set(test_target ${PROJECT_NAME}-${name})
add_python_unit_test(${test_target} ${source} "python")
set_tests_properties(${test_target} PROPERTIES DEPENDS ${PYWRAP})
endfunction()
function(config_test test tagname opttype)
set(MODNAME ${test}_${tagname})
set(TEST_TYPE ${opttype})
configure_file(${test}.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/${MODNAME}.cpp)
set(py_file test_${test}_${tagname}.py)
configure_file(python/test_${test}.py.in
${CMAKE_CURRENT_BINARY_DIR}/python/${py_file})
add_lib_unit_test(${MODNAME})
set(PYTHON_TEST_NAME "${PROJECT_NAME}-py-${test}-${tagname}")
add_test(NAME ${PYTHON_TEST_NAME}
COMMAND ${PYTHON_EXECUTABLE}
"${CMAKE_CURRENT_BINARY_DIR}/python/${py_file}")
compute_pythonpath(ENV_VARIABLES "lib" "bin")
set_tests_properties(${PYTHON_TEST_NAME} PROPERTIES ENVIRONMENT
"${ENV_VARIABLES}")
endfunction()
config_test(variant boost "boost::variant")
if(CMAKE_CXX_STANDARD GREATER 14 AND CMAKE_CXX_STANDARD LESS 98)
config_test(variant std "std::variant")
endif()
if(NOT NUMPY_WITH_BROKEN_UFUNC_SUPPORT)
add_python_unit_test("py-user-type" "unittest/python/test_user_type.py"
"unittest")
config_test(bind_optional boost "boost::optional")
if(CMAKE_CXX_STANDARD GREATER 14 AND CMAKE_CXX_STANDARD LESS 98)
config_test(bind_optional std "std::optional")
endif()
add_python_unit_test("py-switch" "unittest/python/test_switch.py"
"python;unittest")
set_tests_properties("py-switch" PROPERTIES DEPENDS ${PYWRAP})
add_lib_unit_test(bind_virtual_factory)
add_python_lib_unit_test("py-matrix" "unittest/python/test_matrix.py")
add_python_lib_unit_test("py-type-info" "unittest/python/test_type_info.py")
add_python_lib_unit_test("py-multiple-registration"
"unittest/python/test_multiple_registration.py")
add_python_lib_unit_test("py-tensor" "unittest/python/test_tensor.py")
add_python_lib_unit_test("py-geometry" "unittest/python/test_geometry.py")
add_python_lib_unit_test("py-complex" "unittest/python/test_complex.py")
add_python_lib_unit_test("py-deprecation-policy"
"unittest/python/test_deprecation_policy.py")
add_python_lib_unit_test("py-return-by-ref"
"unittest/python/test_return_by_ref.py")
add_python_lib_unit_test("py-eigen-ref" "unittest/python/test_eigen_ref.py")
if(NOT NUMPY_WITH_BROKEN_UFUNC_SUPPORT)
add_python_lib_unit_test("py-user-type" "unittest/python/test_user_type.py")
endif()
add_python_unit_test("py-dimensions" "unittest/python/test_dimensions.py"
"python;unittest")
set_tests_properties("py-dimensions" PROPERTIES DEPENDS ${PYWRAP})
add_python_eigenpy_lib_unit_test("py-dimensions"
"unittest/python/test_dimensions.py")
add_python_unit_test("py-version" "unittest/python/test_version.py"
"python;unittest")
set_tests_properties("py-version" PROPERTIES DEPENDS ${PYWRAP})
add_python_eigenpy_lib_unit_test("py-version" "unittest/python/test_version.py")
add_python_unit_test("py-eigen-solver" "unittest/python/test_eigen_solver.py"
"python;unittest")
set_tests_properties("py-eigen-solver" PROPERTIES DEPENDS ${PYWRAP})
add_python_eigenpy_lib_unit_test("py-eigen-solver"
"unittest/python/test_eigen_solver.py")
add_python_unit_test(
add_python_eigenpy_lib_unit_test(
"py-self-adjoint-eigen-solver"
"unittest/python/test_self_adjoint_eigen_solver.py" "python;unittest")
set_tests_properties("py-self-adjoint-eigen-solver" PROPERTIES DEPENDS
${PYWRAP})
"unittest/python/test_self_adjoint_eigen_solver.py")
add_python_eigenpy_lib_unit_test("py-LLT" "unittest/python/test_LLT.py")
add_python_unit_test("py-LLT" "unittest/python/test_LLT.py" "python;unittest")
set_tests_properties("py-LLT" PROPERTIES DEPENDS ${PYWRAP})
add_python_eigenpy_lib_unit_test("py-LDLT" "unittest/python/test_LDLT.py")
add_python_unit_test("py-LDLT" "unittest/python/test_LDLT.py" "python;unittest")
set_tests_properties("py-LDLT" PROPERTIES DEPENDS ${PYWRAP})
add_python_eigenpy_lib_unit_test("py-id" "unittest/python/test_id.py")
add_python_eigenpy_lib_unit_test("py-QR" "unittest/python/test_QR.py")
if(NOT WIN32)
add_python_unit_test("py-MINRES" "unittest/python/test_MINRES.py"
"python;unittest")
set_tests_properties("py-MINRES" PROPERTIES DEPENDS ${PYWRAP})
add_python_eigenpy_lib_unit_test("py-MINRES" "unittest/python/test_MINRES.py")
endif(NOT WIN32)
add_python_unit_test("py-std-vector" "unittest/python/test_std_vector.py"
"python;unittest")
set_tests_properties("py-std-vector" PROPERTIES DEPENDS ${PYWRAP})
add_python_eigenpy_lib_unit_test("py-std-vector"
"unittest/python/test_std_vector.py")
add_python_lib_unit_test("py-std-array" "unittest/python/test_std_array.py")
add_python_lib_unit_test("py-std-map" "unittest/python/test_std_map.py")
add_python_lib_unit_test("py-std-pair" "unittest/python/test_std_pair.py")
add_python_lib_unit_test("py-user-struct" "unittest/python/test_user_struct.py")
if(CMAKE_CXX_STANDARD GREATER 14 AND CMAKE_CXX_STANDARD LESS 98)
add_python_lib_unit_test("py-std-unique-ptr"
"unittest/python/test_std_unique_ptr.py")
endif()
add_python_lib_unit_test("py-bind-virtual"
"unittest/python/test_bind_virtual.py")
if(BUILD_TESTING_SCIPY)
add_python_lib_unit_test("py-sparse-matrix"
"unittest/python/test_sparse_matrix.py")
add_python_eigenpy_unit_test(
"py-SimplicialLLT"
"unittest/python/decompositions/sparse/test_SimplicialLLT.py")
add_python_eigenpy_unit_test(
"py-SimplicialLDLT"
"unittest/python/decompositions/sparse/test_SimplicialLDLT.py")
if(BUILD_WITH_CHOLMOD_SUPPORT)
add_python_eigenpy_unit_test(
"py-CholmodSimplicialLLT"
"unittest/python/decompositions/sparse/cholmod/test_CholmodSimplicialLLT.py"
)
add_python_eigenpy_unit_test(
"py-CholmodSimplicialLDLT"
"unittest/python/decompositions/sparse/cholmod/test_CholmodSimplicialLDLT.py"
)
add_python_eigenpy_unit_test(
"py-CholmodSupernodalLLT"
"unittest/python/decompositions/sparse/cholmod/test_CholmodSupernodalLLT.py"
)
endif(BUILD_WITH_CHOLMOD_SUPPORT)
if(BUILD_WITH_ACCELERATE_SUPPORT)
add_python_eigenpy_unit_test(
"py-Accelerate"
"unittest/python/decompositions/sparse/test_Accelerate.py")
endif(BUILD_WITH_ACCELERATE_SUPPORT)
endif()
/* Simple test using the boost::numpy interface: return an array and a matrix.
*/
#include "boost/numpy.hpp"
#include "eigenpy/fwd.hpp"
namespace bp = boost::python;
namespace bn = boost::numpy;
/* Return an dim-1 array with 5 elements. */
bn::ndarray array() {
std::vector<double> v(5);
v[0] = 56;
Py_intptr_t shape[1] = {v.size()};
bn::ndarray result = bn::zeros(1, shape, bn::dtype::get_builtin<double>());
std::copy(v.begin(), v.end(), reinterpret_cast<double*>(result.get_data()));
return result;
}
/* Return a dim-1 matrix with five elements. */
boost::python::object matrix() {
std::vector<double> v(5);
v[0] = 56;
Py_intptr_t shape[1] = {v.size()};
bn::matrix t(bn::zeros(1, shape, bn::dtype::get_builtin<double>()));
std::copy(v.begin(), v.end(), reinterpret_cast<double*>(t.get_data()));
return t;
}
BOOST_PYTHON_MODULE(libbnpy) {
bn::initialize();
bp::def("array", array);
bp::def("matrix", matrix);
}
#include <boost/numpy.hpp>
#include "eigenpy/fwd.hpp"
namespace boopy {
namespace bpn = boost::numpy;
namespace bp = boost::python;
struct Eigenvec_to_python_matrix {
static PyObject* convert(Eigen::VectorXd const& v) {
Py_intptr_t shape[1] = {v.size()};
bpn::matrix result(bpn::zeros(1, shape, bpn::dtype::get_builtin<double>()));
std::copy(v.data(), v.data() + v.size(),
reinterpret_cast<double*>(result.get_data()));
return bp::incref(result.ptr());
}
};
struct Eigenvec_from_python_array {
Eigenvec_from_python_array() {
bp::converter::registry ::push_back(&convertible, &construct,
bp::type_id<Eigen::VectorXd>());
}
// Determine if obj_ptr can be converted in a Eigenvec
static void* convertible(PyObject* obj_ptr) {
try {
bp::object obj(bp::handle<>(bp::borrowed(obj_ptr)));
std::auto_ptr<bpn::ndarray> array(new bpn::ndarray(bpn::from_object(
obj, bpn::dtype::get_builtin<double>(), bpn::ndarray::V_CONTIGUOUS)));
if ((array->get_nd() == 1) ||
((array->get_nd() == 2) && (array->get_shape()[1] == 1)))
return array.release();
else
return 0;
} catch (bp::error_already_set& err) {
bp::handle_exception();
return 0;
}
}
// Convert obj_ptr into a Eigenvec
static void construct(PyObject*,
bp::converter::rvalue_from_python_stage1_data* memory) {
// Recover the pointer created in <convertible>
std::auto_ptr<bpn::ndarray> array(
reinterpret_cast<bpn::ndarray*>(memory->convertible));
const int nrow = array->get_shape()[0];
std::cout << "nrow = " << nrow << std::endl;
// Get the memory where to create the vector
void* storage =
((bp::converter::rvalue_from_python_storage<Eigen::VectorXd>*)memory)
->storage.bytes;
// Create the vector
Eigen::VectorXd& res = *new (storage) Eigen::VectorXd(nrow);
// Copy the data
double* data = (double*)array->get_data();
for (int i = 0; i < nrow; ++i) res[i] = data[i];
// Stash the memory chunk pointer for later use by boost.python
memory->convertible = storage;
}
};
} // namespace boopy
Eigen::VectorXd test() {
Eigen::VectorXd v = Eigen::VectorXd::Random(5);
std::cout << v.transpose() << std::endl;
return v;
}
void test2(Eigen::VectorXd v) {
std::cout << "test2: dim = " << v.size() << " ||| v[0] = " << v[0]
<< std::endl;
}
BOOST_PYTHON_MODULE(libeigen) {
namespace bpn = boost::numpy;
namespace bp = boost::python;
bpn::initialize();
bp::to_python_converter<Eigen::VectorXd, boopy::Eigenvec_to_python_matrix>();
boopy::Eigenvec_from_python_array();
bp::def("test", test);
bp::def("test2", test2);
}
#include <numpy/arrayobject.h>
#include "eigenpy/fwd.hpp"
namespace boopy {
namespace bp = boost::python;
template <typename SCALAR>
struct NumpyEquivalentType {};
template <>
struct NumpyEquivalentType<double> {
enum { type_code = NPY_DOUBLE };
};
template <>
struct NumpyEquivalentType<int> {
enum { type_code = NPY_INT };
};
template <>
struct NumpyEquivalentType<float> {
enum { type_code = NPY_FLOAT };
};
struct EigenMatrix_to_python_matrix {
static PyObject* convert(Eigen::MatrixXd const& mat) {
typedef Eigen::MatrixXd::Scalar T;
npy_intp shape[2] = {mat.rows(), mat.cols()};
PyArrayObject* pyArray = (PyArrayObject*)PyArray_SimpleNew(
2, shape, NumpyEquivalentType<T>::type_code);
T* pyData = (T*)PyArray_DATA(pyArray);
for (int i = 0; i < mat.rows(); ++i)
for (int j = 0; j < mat.cols(); ++j)
pyData[i * mat.cols() + j] = mat(i, j);
return ((PyObject*)pyArray);
}
};
struct EigenMatrix_from_python_array {
EigenMatrix_from_python_array() {
bp::converter::registry ::push_back(&convertible, &construct,
bp::type_id<Eigen::MatrixXd>());
}
// Determine if obj_ptr can be converted in a Eigenvec
static void* convertible(PyObject* obj_ptr) {
typedef Eigen::MatrixXd::Scalar T;
if (!PyArray_Check(obj_ptr)) {
return 0;
}
if (PyArray_NDIM(obj_ptr) > 2) {
return 0;
}
if (PyArray_ObjectType(obj_ptr, 0) != NumpyEquivalentType<T>::type_code) {
return 0;
}
int flags = PyArray_FLAGS(obj_ptr);
if (!(flags & NPY_C_CONTIGUOUS)) {
return 0;
}
if (!(flags & NPY_ALIGNED)) {
return 0;
}
return obj_ptr;
}
// Convert obj_ptr into a Eigenvec
static void construct(PyObject* pyObj,
bp::converter::rvalue_from_python_stage1_data* memory) {
typedef Eigen::MatrixXd::Scalar T;
using namespace Eigen;
PyArrayObject* pyArray = reinterpret_cast<PyArrayObject*>(pyObj);
int ndims = PyArray_NDIM(pyArray);
assert(ndims == 2);
int dtype_size = (PyArray_DESCR(pyArray))->elsize;
int s1 = PyArray_STRIDE(pyArray, 0);
assert(s1 % dtype_size == 0);
int R = MatrixXd::RowsAtCompileTime;
int C = MatrixXd::ColsAtCompileTime;
if (R == Eigen::Dynamic)
R = PyArray_DIMS(pyArray)[0];
else
assert(PyArray_DIMS(pyArray)[0] == R);
if (C == Eigen::Dynamic)
C = PyArray_DIMS(pyArray)[1];
else
assert(PyArray_DIMS(pyArray)[1] == C);
T* pyData = reinterpret_cast<T*>(PyArray_DATA(pyArray));
void* storage =
((bp::converter::rvalue_from_python_storage<MatrixXd>*)(memory))
->storage.bytes;
MatrixXd& mat = *new (storage) MatrixXd(R, C);
for (int i = 0; i < R; ++i)
for (int j = 0; j < C; ++j) mat(i, j) = pyData[i * C + j];
memory->convertible = storage;
}
};
} // namespace boopy
Eigen::MatrixXd test() {
Eigen::MatrixXd mat = Eigen::MatrixXd::Random(5, 5);
std::cout << "EigenMAt = " << mat << std::endl;
return mat;
}
void test2(Eigen::MatrixXd mat) {
std::cout << "test2: dim = " << mat.rows() << " ||| m[0,0] = " << mat(0, 0)
<< std::endl;
}
BOOST_PYTHON_MODULE(libeigenc) {
import_array();
namespace bp = boost::python;
bp::to_python_converter<Eigen::MatrixXd,
boopy::EigenMatrix_to_python_matrix>();
boopy::EigenMatrix_from_python_array();
bp::def("test", test);
bp::def("test2", test2);
}
#include <numpy/arrayobject.h>
#include "eigenpy/fwd.hpp"
namespace boopy {
namespace bp = boost::python;
template <typename SCALAR>
struct NumpyEquivalentType {};
template <>
struct NumpyEquivalentType<double> {
enum { type_code = NPY_DOUBLE };
};
template <>
struct NumpyEquivalentType<int> {
enum { type_code = NPY_INT };
};
template <>
struct NumpyEquivalentType<float> {
enum { type_code = NPY_FLOAT };
};
/* --- TO PYTHON --------------------------------------------------------------
*/
template <typename MatType>
struct EigenMatrix_to_python_matrix {
static PyObject* convert(MatType const& mat) {
typedef typename MatType::Scalar T;
const int R = mat.rows(), C = mat.cols();
npy_intp shape[2] = {R, C};
PyArrayObject* pyArray = (PyArrayObject*)PyArray_SimpleNew(
2, shape, NumpyEquivalentType<T>::type_code);
T* pyData = (T*)PyArray_DATA(pyArray);
Eigen::Map<
Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> >
pyMatrix(pyData, R, C);
pyMatrix = mat;
return (PyObject*)pyArray;
}
};
/* --- FROM PYTHON ------------------------------------------------------------
*/
template <typename MatType>
struct EigenMatrix_from_python_array {
EigenMatrix_from_python_array() {
bp::converter::registry ::push_back(&convertible, &construct,
bp::type_id<MatType>());
}
// Determine if obj_ptr can be converted in a Eigenvec
static void* convertible(PyObject* obj_ptr) {
typedef typename MatType::Scalar T;
if (!PyArray_Check(obj_ptr)) return 0;
std::cout << "Until here ok. ndim = " << PyArray_NDIM(obj_ptr)
<< " isvec " << MatType::IsVectorAtCompileTime << std::endl;
if (PyArray_NDIM(obj_ptr) != 2)
if ((PyArray_NDIM(obj_ptr) != 1) || (!MatType::IsVectorAtCompileTime))
return 0;
std::cout << "Until here ok." << std::endl;
if (PyArray_ObjectType(obj_ptr, 0) != NumpyEquivalentType<T>::type_code)
return 0;
if (!(PyArray_FLAGS(obj_ptr) & NPY_ALIGNED)) {
std::cerr << "NPY non-aligned matrices are not implemented." << std::endl;
return 0;
}
return obj_ptr;
}
// Convert obj_ptr into a Eigenvec
static void construct(PyObject* pyObj,
bp::converter::rvalue_from_python_stage1_data* memory) {
typedef typename MatType::Scalar T;
using namespace Eigen;
std::cout << "Until here ok. Constructing..." << std::endl;
PyArrayObject* pyArray = reinterpret_cast<PyArrayObject*>(pyObj);
if (PyArray_NDIM(pyArray) == 2) {
int R = MatType::RowsAtCompileTime;
int C = MatType::ColsAtCompileTime;
if (R == Eigen::Dynamic)
R = PyArray_DIMS(pyArray)[0];
else
assert(PyArray_DIMS(pyArray)[0] == R);
if (C == Eigen::Dynamic)
C = PyArray_DIMS(pyArray)[1];
else
assert(PyArray_DIMS(pyArray)[1] == C);
T* pyData = reinterpret_cast<T*>(PyArray_DATA(pyArray));
int itemsize = PyArray_ITEMSIZE(pyArray);
int stride1 = PyArray_STRIDE(pyArray, 0) / itemsize;
int stride2 = PyArray_STRIDE(pyArray, 1) / itemsize;
std::cout << "STRIDE = " << stride1 << " x " << stride2 << std::endl;
Eigen::Map<MatType, 0, Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic> >
pyMap(
pyData, R, C,
Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic>(stride2, stride1));
std::cout << "Map = " << pyMap << std::endl;
void* storage =
((bp::converter::rvalue_from_python_storage<MatType>*)(memory))
->storage.bytes;
MatType& mat = *new (storage) MatType(R, C);
mat = pyMap;
memory->convertible = storage;
} else {
int R = MatType::MaxSizeAtCompileTime, C = 1;
if (R == Eigen::Dynamic)
R = PyArray_DIMS(pyArray)[0];
else
assert(PyArray_DIMS(pyArray)[0] == R);
T* pyData = reinterpret_cast<T*>(PyArray_DATA(pyArray));
int itemsize = PyArray_ITEMSIZE(pyArray);
int stride = PyArray_STRIDE(pyArray, 0) / itemsize;
Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic> s(stride, 0);
Eigen::Map<MatType, 0, Eigen::InnerStride<Eigen::Dynamic> > pyMap(
pyData, R, 1, Eigen::InnerStride<Eigen::Dynamic>(stride));
std::cout << "Map = " << pyMap << std::endl;
void* storage =
((bp::converter::rvalue_from_python_storage<MatType>*)(memory))
->storage.bytes;
MatType& mat = *new (storage) MatType(R, C);
mat = pyMap;
memory->convertible = storage;
}
}
};
} // namespace boopy
Eigen::MatrixXd test() {
Eigen::MatrixXd mat = Eigen::MatrixXd::Random(3, 6);
std::cout << "EigenMAt = " << mat << std::endl;
return mat;
}
Eigen::VectorXd testVec() {
Eigen::VectorXd mat = Eigen::VectorXd::Random(6);
std::cout << "EigenVec = " << mat << std::endl;
return mat;
}
void test2(Eigen::MatrixXd mat) {
std::cout << "Test2 mat = " << mat << std::endl;
}
void test2Vec(Eigen::VectorXd v) {
std::cout << "Test2 vec = " << v << std::endl;
}
BOOST_PYTHON_MODULE(libeigentemplate) {
import_array();
namespace bp = boost::python;
bp::to_python_converter<
Eigen::MatrixXd, boopy::EigenMatrix_to_python_matrix<Eigen::MatrixXd> >();
boopy::EigenMatrix_from_python_array<Eigen::MatrixXd>();
bp::to_python_converter<
Eigen::VectorXd, boopy::EigenMatrix_to_python_matrix<Eigen::VectorXd> >();
boopy::EigenMatrix_from_python_array<Eigen::VectorXd>();
bp::def("test", test);
bp::def("testVec", testVec);
bp::def("test2", test2);
bp::def("test2Vec", test2Vec);
}
/* Tutorial with boost::python. Using the converter to access a home-made
* string class and bind it to the python strings. */
#include <boost/python/def.hpp>
#include <boost/python/module.hpp>
#include <boost/python/to_python_converter.hpp>
namespace homemadestring {
/* This is the home-made string class. */
class custom_string {
public:
custom_string() {}
custom_string(std::string const& value) : value_(value) {}
std::string const& value() const { return value_; }
private:
std::string value_;
};
/* Two simple functions with this class */
custom_string hello() { return custom_string("Hello world."); }
std::size_t size(custom_string const& s) { return s.value().size(); }
/* From c to python converter */
struct custom_string_to_python_str {
static PyObject* convert(custom_string const& s) {
return boost::python::incref(boost::python::object(s.value()).ptr());
}
};
struct custom_string_from_python_str {
custom_string_from_python_str() {
boost::python::converter::registry ::push_back(
&convertible, &construct, boost::python::type_id<custom_string>());
}
static void* convertible(PyObject* obj_ptr) {
if (!PyString_Check(obj_ptr)) return 0;
return obj_ptr;
}
static void construct(
PyObject* obj_ptr,
boost::python::converter::rvalue_from_python_stage1_data* data) {
const char* value = PyString_AsString(obj_ptr);
if (value == 0) boost::python::throw_error_already_set();
void* storage =
((boost::python::converter::rvalue_from_python_storage<custom_string>*)
data)
->storage.bytes;
new (storage) custom_string(value);
data->convertible = storage;
}
};
void init_module() {
using namespace boost::python;
boost::python::to_python_converter<custom_string,
custom_string_to_python_str>();
custom_string_from_python_str();
def("hello", hello);
def("size", size);
}
} // namespace homemadestring
BOOST_PYTHON_MODULE(libmystring) { homemadestring::init_module(); }
/* Simple test with boost::python.
* Declare and bind three function, returning char*, string, and Eigen::Vector.
* The last function raises and error at runtime due to inadequate binding.
*/
#include <string>
#include "eigenpy/fwd.hpp"
char const* testchar() { return "Yay char!"; }
std::string teststr() { return "Yay str!"; }
Eigen::VectorXd testeigenvec() {
Eigen::VectorXd v(555);
return v;
}
BOOST_PYTHON_MODULE(libsimple) {
using namespace boost::python;
def("char", testchar);
def("str", teststr);
def("eigenvec", testeigenvec);
}
///
/// Copyright (c) 2023 CNRS INRIA
///
#include "eigenpy/eigenpy.hpp"
#include "eigenpy/optional.hpp"
#ifdef EIGENPY_WITH_CXX17_SUPPORT
#include <optional>
#endif
#cmakedefine TEST_TYPE @TEST_TYPE@
#define OPTIONAL TEST_TYPE
typedef eigenpy::detail::nullopt_helper<OPTIONAL> none_helper;
static auto OPT_NONE = none_helper::value();
typedef OPTIONAL<double> opt_dbl;
struct mystruct {
OPTIONAL<int> a;
opt_dbl b;
OPTIONAL<std::string> msg{"i am struct"};
mystruct() : a(OPT_NONE), b(OPT_NONE) {}
mystruct(int a, const opt_dbl &b = OPT_NONE) : a(a), b(b) {}
};
OPTIONAL<int> none_if_zero(int i) {
if (i == 0)
return OPT_NONE;
else
return i;
}
OPTIONAL<mystruct> create_if_true(bool flag, opt_dbl b = OPT_NONE) {
if (flag) {
return mystruct(0, b);
} else {
return OPT_NONE;
}
}
OPTIONAL<Eigen::MatrixXd> random_mat_if_true(bool flag) {
if (flag)
return Eigen::MatrixXd(Eigen::MatrixXd::Random(4, 4));
else
return OPT_NONE;
}
BOOST_PYTHON_MODULE(@MODNAME@) {
using namespace eigenpy;
OptionalConverter<int, OPTIONAL>::registration();
OptionalConverter<double, OPTIONAL>::registration();
OptionalConverter<std::string, OPTIONAL>::registration();
OptionalConverter<mystruct, OPTIONAL>::registration();
OptionalConverter<Eigen::MatrixXd, OPTIONAL>::registration();
enableEigenPy();
bp::class_<mystruct>("mystruct", bp::no_init)
.def(bp::init<>(bp::args("self")))
.def(bp::init<int, bp::optional<const opt_dbl &> >(
bp::args("self", "a", "b")))
.add_property(
"a",
bp::make_getter(&mystruct::a,
bp::return_value_policy<bp::return_by_value>()),
bp::make_setter(&mystruct::a))
.add_property(
"b",
bp::make_getter(&mystruct::b,
bp::return_value_policy<bp::return_by_value>()),
bp::make_setter(&mystruct::b))
.add_property(
"msg",
bp::make_getter(&mystruct::msg,
bp::return_value_policy<bp::return_by_value>()),
bp::make_setter(&mystruct::msg));
bp::def("none_if_zero", none_if_zero, bp::args("i"));
bp::def("create_if_true", create_if_true,
(bp::arg("flag"), bp::arg("b") = OPT_NONE));
bp::def("random_mat_if_true", random_mat_if_true, bp::args("flag"));
}
/// Copyright 2023 LAAS-CNRS, INRIA
#include <eigenpy/eigenpy.hpp>
using std::shared_ptr;
namespace bp = boost::python;
// fwd declaration
struct MyVirtualData;
/// A virtual class with two pure virtual functions taking different signatures,
/// and a polymorphic factory function.
struct MyVirtualClass {
MyVirtualClass() {}
virtual ~MyVirtualClass() {}
// polymorphic fn taking arg by shared_ptr
virtual void doSomethingPtr(shared_ptr<MyVirtualData> const &data) const = 0;
// polymorphic fn taking arg by reference
virtual void doSomethingRef(MyVirtualData &data) const = 0;
virtual shared_ptr<MyVirtualData> createData() const {
return std::make_shared<MyVirtualData>(*this);
}
};
struct MyVirtualData {
MyVirtualData(MyVirtualClass const &) {}
virtual ~MyVirtualData() {} // virtual dtor to mark class as polymorphic
};
shared_ptr<MyVirtualData> callDoSomethingPtr(const MyVirtualClass &obj) {
auto d = obj.createData();
printf("Created MyVirtualData with address %p\n", (void *)d.get());
obj.doSomethingPtr(d);
return d;
}
shared_ptr<MyVirtualData> callDoSomethingRef(const MyVirtualClass &obj) {
auto d = obj.createData();
printf("Created MyVirtualData with address %p\n", (void *)d.get());
obj.doSomethingRef(*d);
return d;
}
void throw_virtual_not_implemented_error() {
throw std::runtime_error("Called C++ virtual function.");
}
/// Wrapper classes
struct VirtualClassWrapper : MyVirtualClass, bp::wrapper<MyVirtualClass> {
void doSomethingPtr(shared_ptr<MyVirtualData> const &data) const override {
if (bp::override fo = this->get_override("doSomethingPtr")) {
/// shared_ptr HAS to be passed by value.
/// Boost.Python's argument converter has the wrong behaviour for
/// reference_wrapper<shared_ptr<T>>, so boost::ref(data) does not work.
fo(data);
return;
}
throw_virtual_not_implemented_error();
}
/// The data object is passed by mutable reference to this function,
/// and wrapped in a @c boost::reference_wrapper when passed to the override.
/// Otherwise, Boost.Python's argument converter will convert to Python by
/// value and create a copy.
void doSomethingRef(MyVirtualData &data) const override {
if (bp::override fo = this->get_override("doSomethingRef")) {
fo(boost::ref(data));
return;
}
throw_virtual_not_implemented_error();
}
shared_ptr<MyVirtualData> createData() const override {
if (bp::override fo = this->get_override("createData")) {
bp::object result = fo().as<bp::object>();
return bp::extract<shared_ptr<MyVirtualData> >(result);
}
return default_createData();
}
shared_ptr<MyVirtualData> default_createData() const {
return MyVirtualClass::createData();
}
};
/// This "trampoline class" does nothing but is ABSOLUTELY required to ensure
/// downcasting works properly with non-smart ptr signatures. Otherwise,
/// there is no handle to the original Python object ( @c PyObject *).
/// Every single polymorphic type exposed to Python should be exposed through
/// such a trampoline. Users can also create their own wrapper classes by taking
/// inspiration from boost::python::wrapper<T>.
struct DataWrapper : MyVirtualData, bp::wrapper<MyVirtualData> {
/// we have to use-declare non-defaulted constructors
/// (see https://en.cppreference.com/w/cpp/language/default_constructor)
/// or define them manually.
using MyVirtualData::MyVirtualData;
};
/// Take and return a const reference
const MyVirtualData &iden_ref(const MyVirtualData &d) {
// try cast to holder
return d;
}
/// Take a shared_ptr (by const reference or value, doesn't matter), return by
/// const reference
const MyVirtualData &iden_shared(const shared_ptr<MyVirtualData> &d) {
// get boost.python's custom deleter
// boost.python hides the handle to the original object in there
// dter being nonzero indicates shared_ptr was wrapped by Boost.Python
auto *dter = std::get_deleter<bp::converter::shared_ptr_deleter>(d);
if (dter != 0) printf("> input shared_ptr has a deleter\n");
return *d;
}
/// Take and return a shared_ptr
shared_ptr<MyVirtualData> copy_shared(const shared_ptr<MyVirtualData> &d) {
auto *dter = std::get_deleter<bp::converter::shared_ptr_deleter>(d);
if (dter != 0) printf("> input shared_ptr has a deleter\n");
return d;
}
BOOST_PYTHON_MODULE(bind_virtual_factory) {
assert(std::is_polymorphic<MyVirtualClass>::value &&
"MyVirtualClass should be polymorphic!");
assert(std::is_polymorphic<MyVirtualData>::value &&
"MyVirtualData should be polymorphic!");
bp::class_<VirtualClassWrapper, boost::noncopyable>(
"MyVirtualClass", bp::init<>(bp::args("self")))
.def("doSomething", bp::pure_virtual(&MyVirtualClass::doSomethingPtr),
bp::args("self", "data"))
.def("doSomethingRef", bp::pure_virtual(&MyVirtualClass::doSomethingRef),
bp::args("self", "data"))
.def("createData", &MyVirtualClass::createData,
&VirtualClassWrapper::default_createData, bp::args("self"));
bp::register_ptr_to_python<shared_ptr<MyVirtualData> >();
/// Trampoline used as 1st argument
/// otherwise if passed as "HeldType", we need to define
/// the constructor and call initializer manually.
bp::class_<DataWrapper, boost::noncopyable>("MyVirtualData", bp::no_init)
.def(bp::init<MyVirtualClass const &>(bp::args("self", "model")));
bp::def("callDoSomethingPtr", callDoSomethingPtr, bp::args("obj"));
bp::def("callDoSomethingRef", callDoSomethingRef, bp::args("obj"));
bp::def("iden_ref", iden_ref, bp::return_internal_reference<>());
bp::def("iden_shared", iden_shared, bp::return_internal_reference<>());
bp::def("copy_shared", copy_shared);
}
......@@ -69,17 +69,19 @@ BOOST_PYTHON_MODULE(complex) {
bp::def("ascomplex",
ascomplex<long double, Eigen::Dynamic, Eigen::Dynamic, 0>);
bp::def("real", (MatrixXf(*)(const Eigen::MatrixBase<MatrixXcf> &)) &
real<MatrixXcf>);
bp::def("real", (MatrixXd(*)(const Eigen::MatrixBase<MatrixXcd> &)) &
real<MatrixXcd>);
bp::def("real", (MatrixXld(*)(const Eigen::MatrixBase<MatrixXcld> &)) &
real<MatrixXcld>);
bp::def("real",
(MatrixXf(*)(const Eigen::MatrixBase<MatrixXcf> &))&real<MatrixXcf>);
bp::def("real",
(MatrixXd(*)(const Eigen::MatrixBase<MatrixXcd> &))&real<MatrixXcd>);
bp::def(
"real",
(MatrixXld(*)(const Eigen::MatrixBase<MatrixXcld> &))&real<MatrixXcld>);
bp::def("imag", (MatrixXf(*)(const Eigen::MatrixBase<MatrixXcf> &)) &
imag<MatrixXcf>);
bp::def("imag", (MatrixXd(*)(const Eigen::MatrixBase<MatrixXcd> &)) &
imag<MatrixXcd>);
bp::def("imag", (MatrixXld(*)(const Eigen::MatrixBase<MatrixXcld> &)) &
imag<MatrixXcld>);
bp::def("imag",
(MatrixXf(*)(const Eigen::MatrixBase<MatrixXcf> &))&imag<MatrixXcf>);
bp::def("imag",
(MatrixXd(*)(const Eigen::MatrixBase<MatrixXcd> &))&imag<MatrixXcd>);
bp::def(
"imag",
(MatrixXld(*)(const Eigen::MatrixBase<MatrixXcld> &))&imag<MatrixXcld>);
}
#include "eigenpy/eigenpy.hpp"
#include "eigenpy/deprecation-policy.hpp"
#include <iostream>
namespace bp = boost::python;
using eigenpy::DeprecationType;
void some_deprecated_function() {
std::cout << "Calling this should produce a warning" << std::endl;
}
void some_future_deprecated_function() {
std::cout
<< "Calling this should produce a warning about a future deprecation"
<< std::endl;
}
class X {
public:
void deprecated_member_function() {}
};
BOOST_PYTHON_MODULE(deprecation_policy) {
bp::def("some_deprecated_function", some_deprecated_function,
eigenpy::deprecated_function<DeprecationType::DEPRECATION>());
bp::def("some_future_deprecated_function", some_future_deprecated_function,
eigenpy::deprecated_function<DeprecationType::FUTURE>());
bp::class_<X>("X", bp::init<>(bp::args("self")))
.def("deprecated_member_function", &X::deprecated_member_function,
eigenpy::deprecated_member<>());
}
......@@ -19,6 +19,11 @@ Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> matrix1x1(
return ReturnType::Constant(1, 1, value);
}
template <typename Scalar>
void matrix1x1_input(const Eigen::Matrix<Scalar, 1, 1>& mat) {
std::cout << mat << std::endl;
}
Eigen::VectorXd emptyVector() {
Eigen::VectorXd vec;
vec.resize(0);
......@@ -127,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;
......@@ -146,6 +156,8 @@ BOOST_PYTHON_MODULE(matrix) {
bp::def("vector1x1", vector1x1<double>);
bp::def("matrix1x1", matrix1x1<double>);
bp::def("matrix1x1", matrix1x1_input<double>);
bp::def("matrix1x1_int", matrix1x1_input<int>);
bp::def("naturals", naturalsXX);
bp::def("naturalsX", naturalsX);
......@@ -183,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> >);
}
#include "eigenpy/registration.hpp"
#include <cstdio>
namespace bp = boost::python;
class X {
public:
X() {}
void operator()() { printf("DOOT\n"); }
};
class X_wrapper : public X, bp::wrapper<X> {
public:
static void expose() {
if (!eigenpy::register_symbolic_link_to_registered_type<X>()) {
bp::class_<X>("X", bp::init<>()).def("__call__", &X::operator());
}
}
};
BOOST_PYTHON_MODULE(multiple_registration) {
X_wrapper::expose();
X_wrapper::expose();
X_wrapper::expose();
X_wrapper::expose();
X_wrapper::expose();
X_wrapper::expose();
}
import numpy as np
from scipy.sparse import csc_matrix
import eigenpy
dim = 100
rng = np.random.default_rng()
A = rng.random((dim, dim))
A = (A + A.T) * 0.5 + np.diag(10.0 + rng.random(dim))
A = csc_matrix(A)
llt = eigenpy.CholmodSimplicialLDLT(A)
assert llt.info() == eigenpy.ComputationInfo.Success
X = rng.random((dim, 20))
B = A.dot(X)
X_est = llt.solve(B)
assert eigenpy.is_approx(X, X_est)
assert eigenpy.is_approx(A.dot(X_est), B)
llt.analyzePattern(A)
llt.factorize(A)
import numpy as np
from scipy.sparse import csc_matrix
import eigenpy
dim = 100
rng = np.random.default_rng()
A = rng.random((dim, dim))
A = (A + A.T) * 0.5 + np.diag(10.0 + rng.random(dim))
A = csc_matrix(A)
llt = eigenpy.CholmodSimplicialLLT(A)
assert llt.info() == eigenpy.ComputationInfo.Success
X = rng.random((dim, 20))
B = A.dot(X)
X_est = llt.solve(B)
assert eigenpy.is_approx(X, X_est)
assert eigenpy.is_approx(A.dot(X_est), B)
llt.analyzePattern(A)
llt.factorize(A)
import numpy as np
from scipy.sparse import csc_matrix
import eigenpy
dim = 100
rng = np.random.default_rng()
A = rng.random((dim, dim))
A = (A + A.T) * 0.5 + np.diag(10.0 + rng.random(dim))
A = csc_matrix(A)
llt = eigenpy.CholmodSupernodalLLT(A)
assert llt.info() == eigenpy.ComputationInfo.Success
X = rng.random((dim, 20))
B = A.dot(X)
X_est = llt.solve(B)
assert eigenpy.is_approx(X, X_est)
assert eigenpy.is_approx(A.dot(X_est), B)
llt.analyzePattern(A)
llt.factorize(A)
import numpy as np
from scipy.sparse import csc_matrix
import eigenpy
rng = np.random.default_rng()
def test(SolverType: type):
dim = 100
A = rng.random((dim, dim))
A = (A + A.T) * 0.5 + np.diag(10.0 + rng.random(dim))
A = csc_matrix(A)
llt = SolverType(A)
assert llt.info() == eigenpy.ComputationInfo.Success
X = rng.random((dim, 20))
B = A.dot(X)
X_est = llt.solve(B)
# import pdb; pdb.set_trace()
assert eigenpy.is_approx(X, X_est)
assert eigenpy.is_approx(A.dot(X_est), B)
llt.analyzePattern(A)
llt.factorize(A)
test(eigenpy.AccelerateLLT)
test(eigenpy.AccelerateLDLT)
test(eigenpy.AccelerateLDLTUnpivoted)
test(eigenpy.AccelerateLDLTSBK)
test(eigenpy.AccelerateLDLTTPP)
test(eigenpy.AccelerateQR)
# test(eigenpy.AccelerateCholeskyAtA) # This test is not passing. Seems there is a bug in Eigen with the support of Accelerate.
import numpy as np
from scipy.sparse import csc_matrix
import eigenpy
dim = 100
rng = np.random.default_rng()
A = rng.random((dim, dim))
A = (A + A.T) * 0.5 + np.diag(10.0 + rng.random(dim))
A = csc_matrix(A)
ldlt = eigenpy.SimplicialLDLT(A)
assert ldlt.info() == eigenpy.ComputationInfo.Success
L = ldlt.matrixL()
U = ldlt.matrixU()
D = csc_matrix(np.diag(ldlt.vectorD()))
LDU = L @ D @ U
assert eigenpy.is_approx(LDU.toarray(), A.toarray())
X = rng.random((dim, 20))
B = A.dot(X)
X_est = ldlt.solve(B)
assert eigenpy.is_approx(X, X_est)
assert eigenpy.is_approx(A.dot(X_est), B)
ldlt.analyzePattern(A)
ldlt.factorize(A)
permutation = ldlt.permutationP()
import numpy as np
from scipy.sparse import csc_matrix
import eigenpy
dim = 100
rng = np.random.default_rng()
A = rng.random((dim, dim))
A = (A + A.T) * 0.5 + np.diag(10.0 + rng.random(dim))
A = csc_matrix(A)
llt = eigenpy.SimplicialLLT(A)
assert llt.info() == eigenpy.ComputationInfo.Success
L = llt.matrixL()
U = llt.matrixU()
LU = L @ U
assert eigenpy.is_approx(LU.toarray(), A.toarray())
X = rng.random((dim, 20))
B = A.dot(X)
X_est = llt.solve(B)
assert eigenpy.is_approx(X, X_est)
assert eigenpy.is_approx(A.dot(X_est), B)
llt.analyzePattern(A)
llt.factorize(A)
permutation = llt.permutationP()