From d5deec5186171cf8390ac84002e94f149ec2825b Mon Sep 17 00:00:00 2001 From: Florian Pfaff <6773539+FlorianPfaff@users.noreply.github.com> Date: Fri, 28 Aug 2026 20:55:56 +0800 Subject: [PATCH 1/2] Validate point-set reflection flags --- src/pyrecest/utils/point_set_registration.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/pyrecest/utils/point_set_registration.py b/src/pyrecest/utils/point_set_registration.py index 942847bdf..b0482196b 100644 --- a/src/pyrecest/utils/point_set_registration.py +++ b/src/pyrecest/utils/point_set_registration.py @@ -262,6 +262,22 @@ def _validate_positive_integer(value, name: str, *, minimum: int = 1) -> int: return value_int +def _validate_boolean_flag(value, name: str) -> bool: + """Return a real boolean flag without accepting merely truthy values.""" + error_message = f"{name} must be a boolean." + if np.ma.is_masked(value): + raise TypeError(error_message) + if isinstance(value, (bool, np.bool_)): + return bool(value) + try: + value_array = np.asarray(value) + except (TypeError, ValueError, RuntimeError, OverflowError) as exc: + raise TypeError(error_message) from exc + if value_array.shape == () and value_array.dtype == np.bool_: + return bool(value_array.item()) + raise TypeError(error_message) + + def estimate_transform( # pylint: disable=too-many-locals source_points, target_points, @@ -289,6 +305,7 @@ def estimate_transform( # pylint: disable=too-many-locals "estimate_transform is not supported on the JAX backend." ) + allow_reflection = _validate_boolean_flag(allow_reflection, "allow_reflection") source, target = _validate_pair(source_points, target_points) n_points, dim = source.shape min_matches = _minimum_required_matches(model, dim) @@ -405,6 +422,7 @@ def joint_registration_assignment( # pylint: disable=too-many-arguments,too-man "joint_registration_assignment is not supported on the JAX backend." ) + allow_reflection = _validate_boolean_flag(allow_reflection, "allow_reflection") max_iterations = _validate_positive_integer(max_iterations, "max_iterations") reference = _as_point_array(reference_points) From cf1e05ee8289f31efb80c758d5cec02e44a35b93 Mon Sep 17 00:00:00 2001 From: Florian Pfaff <6773539+FlorianPfaff@users.noreply.github.com> Date: Fri, 28 Aug 2026 20:56:11 +0800 Subject: [PATCH 2/2] Add reflection flag validation regressions --- ..._point_set_registration_reflection_flag.py | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 tests/test_point_set_registration_reflection_flag.py diff --git a/tests/test_point_set_registration_reflection_flag.py b/tests/test_point_set_registration_reflection_flag.py new file mode 100644 index 000000000..7205f40a3 --- /dev/null +++ b/tests/test_point_set_registration_reflection_flag.py @@ -0,0 +1,49 @@ +import numpy as np +import pytest +import pyrecest.backend + +from pyrecest.backend import array +from pyrecest.utils.point_set_registration import ( + estimate_transform, + joint_registration_assignment, +) + +pytestmark = pytest.mark.skipif( + pyrecest.backend.__backend_name__ == "jax", # pylint: disable=no-member + reason="Point-set registration is not supported on JAX.", +) + +_SOURCE = array([[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]) +_REFLECTED = array([[0.0, 0.0], [-1.0, 0.0], [0.0, 1.0]]) + + +@pytest.mark.parametrize("invalid_flag", ["False", "True", 0, 1, None]) +def test_estimate_transform_rejects_non_boolean_allow_reflection(invalid_flag): + with pytest.raises(TypeError, match="allow_reflection must be a boolean"): + estimate_transform( + _SOURCE, + _REFLECTED, + model="rigid", + allow_reflection=invalid_flag, + ) + + +def test_joint_registration_rejects_truthy_string_allow_reflection(): + with pytest.raises(TypeError, match="allow_reflection must be a boolean"): + joint_registration_assignment( + _SOURCE, + _REFLECTED, + model="rigid", + allow_reflection="False", + ) + + +def test_numpy_boolean_allow_reflection_is_accepted(): + transform = estimate_transform( + _SOURCE, + _REFLECTED, + model="rigid", + allow_reflection=np.bool_(True), + ) + + assert float(np.linalg.det(np.asarray(transform.matrix))) < 0.0