Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix common issues in custom types #525

Merged
merged 10 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Fixed

- Fix Python library linkage for Debug build on Windows ([#514](https://github.com/stack-of-tasks/eigenpy/pull/514))
- Fix np.ones when dtype is a custom user type ([#525](https://github.com/stack-of-tasks/eigenpy/pull/525))

## [3.10.1] - 2024-10-30

Expand Down
4 changes: 3 additions & 1 deletion include/eigenpy/ufunc.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (c) 2020-2021 INRIA
// Copyright (c) 2020-2025 INRIA
// code aptapted from
// https://github.com/numpy/numpy/blob/41977b24ae011a51f64faa75cb524c7350fdedd9/numpy/core/src/umath/_rational_tests.c.src
//
Expand Down Expand Up @@ -151,6 +151,7 @@ EIGENPY_REGISTER_BINARY_OPERATOR(greater_equal, >=)
}

EIGENPY_REGISTER_UNARY_OPERATOR(negative, -)
EIGENPY_REGISTER_UNARY_OPERATOR(square, x *)

} // namespace internal

Expand Down Expand Up @@ -258,6 +259,7 @@ void registerCommonUfunc() {

// Unary operators
EIGENPY_REGISTER_UNARY_UFUNC(negative, type_code, Scalar, Scalar);
EIGENPY_REGISTER_UNARY_UFUNC(square, type_code, Scalar, Scalar);

Py_DECREF(numpy);
}
Expand Down
47 changes: 28 additions & 19 deletions include/eigenpy/user-type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,34 +132,43 @@ struct SpecialMethods<T, NPY_USERDEF> {
eigenpy::Exception("Cannot retrieve the type stored in the array.");
return -1;
}

PyArrayObject* py_array = static_cast<PyArrayObject*>(array);
PyArray_Descr* descr = PyArray_DTYPE(py_array);
PyTypeObject* array_scalar_type = descr->typeobj;
PyTypeObject* src_obj_type = Py_TYPE(src_obj);

T& dest = *static_cast<T*>(dest_ptr);
if (array_scalar_type != src_obj_type) {
std::stringstream ss;
ss << "The input type is of wrong type. ";
ss << "The expected type is " << bp::type_info(typeid(T)).name()
<< std::endl;
eigenpy::Exception(ss.str());
return -1;
}
long long src_value = PyLong_AsLongLong(src_obj);
if (src_value == -1 && PyErr_Occurred()) {
std::stringstream ss;
ss << "The input type is of wrong type. ";
ss << "The expected type is " << bp::type_info(typeid(T)).name()
<< std::endl;
eigenpy::Exception(ss.str());
return -1;
}

dest = T(src_value);

bp::extract<T&> extract_src_obj(src_obj);
if (!extract_src_obj.check()) {
std::stringstream ss;
ss << "The input type is of wrong type. ";
ss << "The expected type is " << bp::type_info(typeid(T)).name()
<< std::endl;
eigenpy::Exception(ss.str());
return -1;
} else {
bp::extract<T&> extract_src_obj(src_obj);
if (!extract_src_obj.check()) {
std::cout << "if (!extract_src_obj.check())" << std::endl;
std::stringstream ss;
ss << "The input type is of wrong type. ";
ss << "The expected type is " << bp::type_info(typeid(T)).name()
<< std::endl;
eigenpy::Exception(ss.str());
return -1;
}

const T& src = extract_src_obj();
T& dest = *static_cast<T*>(dest_ptr);
dest = src;
}

const T& src = extract_src_obj();
T& dest = *static_cast<T*>(dest_ptr);
dest = src;

return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion src/register.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ int Register::registerNewType(
}

PyArray_DescrProto* descr_ptr = new PyArray_DescrProto();
Py_SET_TYPE(descr_ptr, &PyArrayDescr_Type);
PyArray_DescrProto& descr = *descr_ptr;
descr.typeobj = py_type_ptr;
descr.kind = 'V';
Expand Down Expand Up @@ -92,6 +91,7 @@ int Register::registerNewType(
funcs.fill = fill;
funcs.fillwithscalar = fillwithscalar;
// f->cast = cast;
Py_SET_TYPE(descr_ptr, &PyArrayDescr_Type);

const int code = call_PyArray_RegisterDataType(descr_ptr);
assert(code >= 0 && "The return code should be positive");
Expand Down
20 changes: 17 additions & 3 deletions unittest/python/test_user_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

def test(dtype):
rng = np.random.default_rng()
mat = np.array(np.ones((rows, cols)).astype(np.int32), dtype=dtype)
mat = np.ones((rows, cols), dtype=dtype)
mat = rng.random((rows, cols)).astype(dtype)
mat_copy = mat.copy()
assert (mat == mat_copy).all()
Expand Down Expand Up @@ -44,6 +44,11 @@ def test(dtype):
mat2 = np.matmul(mat, mat.T)
assert np.isclose(mat2.astype(np.double), mat2_ref).all()

vec = np.ones((rows,), dtype=dtype)
norm = np.linalg.norm(vec)
norm_ref = np.linalg.norm(vec.astype(np.double))
assert norm == norm_ref


def test_cast(from_dtype, to_dtype):
np.can_cast(from_dtype, to_dtype)
Expand All @@ -63,8 +68,17 @@ def test_cast(from_dtype, to_dtype):
test_cast(user_type.CustomDouble, np.int32)
test_cast(np.int32, user_type.CustomDouble)

test(user_type.CustomFloat)

v = user_type.CustomDouble(1)
a = np.array(v)
assert type(v) is a.dtype.type

test(user_type.CustomFloat)

test_cast(user_type.CustomFloat, np.float32)
test_cast(np.double, user_type.CustomFloat)

test_cast(user_type.CustomFloat, np.int64)
test_cast(np.int64, user_type.CustomFloat)

test_cast(user_type.CustomFloat, np.int32)
test_cast(np.int32, user_type.CustomFloat)
5 changes: 5 additions & 0 deletions unittest/user_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,14 +201,19 @@ BOOST_PYTHON_MODULE(user_type) {

eigenpy::registerCast<DoubleType, double>(true);
eigenpy::registerCast<double, DoubleType>(true);
eigenpy::registerCast<DoubleType, float>(false);
eigenpy::registerCast<float, DoubleType>(true);
eigenpy::registerCast<DoubleType, int>(false);
eigenpy::registerCast<int, DoubleType>(true);
eigenpy::registerCast<DoubleType, long long>(false);
eigenpy::registerCast<long long, DoubleType>(true);
eigenpy::registerCast<DoubleType, long>(false);
eigenpy::registerCast<long, DoubleType>(true);

eigenpy::registerCast<FloatType, double>(true);
eigenpy::registerCast<double, FloatType>(false);
eigenpy::registerCast<FloatType, float>(true);
eigenpy::registerCast<float, FloatType>(true);
eigenpy::registerCast<FloatType, long long>(false);
eigenpy::registerCast<long long, FloatType>(true);
eigenpy::registerCast<FloatType, int>(false);
Expand Down
Loading