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 point-on-plane detection for NumPy 1.19.0 in Linux #202

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

paulmelnikow
Copy link
Member

@paulmelnikow paulmelnikow commented Jul 3, 2020

This fixes the tests that are failing in NumPy 1.19.0.

See https://app.circleci.com/pipelines/github/lace/polliwog/836/workflows/b88ac698-51bc-46ea-80b8-f9e57e7157d9/jobs/2618/steps

_________ test_open_starts_in_front_ends_in_back_with_vertex_on_plane __________

    def test_open_starts_in_front_ends_in_back_with_vertex_on_plane():
        signs = np.array([1, 1, 1, 0, -1, -1, -1, -1])
>       vertices = vertices_with_signs(signs)

polliwog/polyline/test_slice_by_plane.py:62: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

signs = array([ 1,  1,  1,  0, -1, -1, -1, -1])

    def vertices_with_signs(signs):
        num_verts = len(signs)
        random_points_on_plane = plane.project_point(rand_nonzero(num_verts, 3))
        random_displacement_along_normal = (
            rand_nonzero(num_verts).reshape(-1, 1) * plane_normal
        )
        vertices = (
            random_points_on_plane + signs.reshape(-1, 1) * random_displacement_along_normal
        )
        # Because of rounding, the random points don't necessarily return 0 for
        # sign, so pick one that does.
        vertices[signs == 0] = plane.reference_point
>       np.testing.assert_array_equal(plane.sign(vertices), signs)
E       AssertionError: 
E       Arrays are not equal
E       
E       Mismatched elements: 1 / 8 (12.5%)
E       Max absolute difference: 1.
E       Max relative difference: 0.
E        x: array([ 1.,  1.,  1.,  1., -1., -1., -1., -1.])
E        y: array([ 1,  1,  1,  0, -1, -1, -1, -1])

polliwog/polyline/test_slice_by_plane.py:28: AssertionError
_________ test_open_starts_in_back_ends_in_front_with_vertex_on_plane __________

    def test_open_starts_in_back_ends_in_front_with_vertex_on_plane():
        signs = np.array([-1, -1, -1, -1, -1, 0, 1, 1, 1])
>       vertices = vertices_with_signs(signs)

polliwog/polyline/test_slice_by_plane.py:88: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

signs = array([-1, -1, -1, -1, -1,  0,  1,  1,  1])

    def vertices_with_signs(signs):
        num_verts = len(signs)
        random_points_on_plane = plane.project_point(rand_nonzero(num_verts, 3))
        random_displacement_along_normal = (
            rand_nonzero(num_verts).reshape(-1, 1) * plane_normal
        )
        vertices = (
            random_points_on_plane + signs.reshape(-1, 1) * random_displacement_along_normal
        )
        # Because of rounding, the random points don't necessarily return 0 for
        # sign, so pick one that does.
        vertices[signs == 0] = plane.reference_point
>       np.testing.assert_array_equal(plane.sign(vertices), signs)
E       AssertionError: 
E       Arrays are not equal
E       
E       Mismatched elements: 1 / 9 (11.1%)
E       Max absolute difference: 1.
E       Max relative difference: 0.
E        x: array([-1., -1., -1., -1., -1.,  1.,  1.,  1.,  1.])
E        y: array([-1, -1, -1, -1, -1,  0,  1,  1,  1])

polliwog/polyline/test_slice_by_plane.py:28: AssertionError
___________________ test_open_starts_on_plane_ends_in_front ____________________

    def test_open_starts_on_plane_ends_in_front():
        signs = np.array([0, 1, 1, 1, 1])
>       vertices = vertices_with_signs(signs)

polliwog/polyline/test_slice_by_plane.py:97: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

signs = array([0, 1, 1, 1, 1])

    def vertices_with_signs(signs):
        num_verts = len(signs)
        random_points_on_plane = plane.project_point(rand_nonzero(num_verts, 3))
        random_displacement_along_normal = (
            rand_nonzero(num_verts).reshape(-1, 1) * plane_normal
        )
        vertices = (
            random_points_on_plane + signs.reshape(-1, 1) * random_displacement_along_normal
        )
        # Because of rounding, the random points don't necessarily return 0 for
        # sign, so pick one that does.
        vertices[signs == 0] = plane.reference_point
>       np.testing.assert_array_equal(plane.sign(vertices), signs)
E       AssertionError: 
E       Arrays are not equal
E       
E       Mismatched elements: 1 / 5 (20%)
E       Max absolute difference: 1.
E       Max relative difference: 0.
E        x: array([1., 1., 1., 1., 1.])
E        y: array([0, 1, 1, 1, 1])

polliwog/polyline/test_slice_by_plane.py:28: AssertionError
____________ test_open_starts_with_edges_along_plane_ends_in_front _____________

    def test_open_starts_with_edges_along_plane_ends_in_front():
        signs = np.array([0, 0, 1, 1, 1, 1])
>       vertices = vertices_with_signs(signs)

polliwog/polyline/test_slice_by_plane.py:106: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

signs = array([0, 0, 1, 1, 1, 1])

    def vertices_with_signs(signs):
        num_verts = len(signs)
        random_points_on_plane = plane.project_point(rand_nonzero(num_verts, 3))
        random_displacement_along_normal = (
            rand_nonzero(num_verts).reshape(-1, 1) * plane_normal
        )
        vertices = (
            random_points_on_plane + signs.reshape(-1, 1) * random_displacement_along_normal
        )
        # Because of rounding, the random points don't necessarily return 0 for
        # sign, so pick one that does.
        vertices[signs == 0] = plane.reference_point
>       np.testing.assert_array_equal(plane.sign(vertices), signs)
E       AssertionError: 
E       Arrays are not equal
E       
E       Mismatched elements: 2 / 6 (33.3%)
E       Max absolute difference: 1.
E       Max relative difference: 0.
E        x: array([1., 1., 1., 1., 1., 1.])
E        y: array([0, 0, 1, 1, 1, 1])

polliwog/polyline/test_slice_by_plane.py:28: AssertionError
____________________ test_open_starts_on_plane_ends_in_back ____________________

    def test_open_starts_on_plane_ends_in_back():
        signs = np.array([0, -1, -1, -1, -1])
>       vertices = vertices_with_signs(signs)

polliwog/polyline/test_slice_by_plane.py:115: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

signs = array([ 0, -1, -1, -1, -1])

    def vertices_with_signs(signs):
        num_verts = len(signs)
        random_points_on_plane = plane.project_point(rand_nonzero(num_verts, 3))
        random_displacement_along_normal = (
            rand_nonzero(num_verts).reshape(-1, 1) * plane_normal
        )
        vertices = (
            random_points_on_plane + signs.reshape(-1, 1) * random_displacement_along_normal
        )
        # Because of rounding, the random points don't necessarily return 0 for
        # sign, so pick one that does.
        vertices[signs == 0] = plane.reference_point
>       np.testing.assert_array_equal(plane.sign(vertices), signs)
E       AssertionError: 
E       Arrays are not equal
E       
E       Mismatched elements: 1 / 5 (20%)
E       Max absolute difference: 1.
E       Max relative difference: 0.
E        x: array([ 1., -1., -1., -1., -1.])
E        y: array([ 0, -1, -1, -1, -1])

polliwog/polyline/test_slice_by_plane.py:28: AssertionError
___________________ test_open_starts_in_front_ends_on_plane ____________________

    def test_open_starts_in_front_ends_on_plane():
        signs = np.array([1, 1, 1, 1, 0])
>       vertices = vertices_with_signs(signs)

polliwog/polyline/test_slice_by_plane.py:125: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

signs = array([1, 1, 1, 1, 0])

    def vertices_with_signs(signs):
        num_verts = len(signs)
        random_points_on_plane = plane.project_point(rand_nonzero(num_verts, 3))
        random_displacement_along_normal = (
            rand_nonzero(num_verts).reshape(-1, 1) * plane_normal
        )
        vertices = (
            random_points_on_plane + signs.reshape(-1, 1) * random_displacement_along_normal
        )
        # Because of rounding, the random points don't necessarily return 0 for
        # sign, so pick one that does.
        vertices[signs == 0] = plane.reference_point
>       np.testing.assert_array_equal(plane.sign(vertices), signs)
E       AssertionError: 
E       Arrays are not equal
E       
E       Mismatched elements: 1 / 5 (20%)
E       Max absolute difference: 1.
E       Max relative difference: 0.
E        x: array([1., 1., 1., 1., 1.])
E        y: array([1, 1, 1, 1, 0])

polliwog/polyline/test_slice_by_plane.py:28: AssertionError
____________ test_open_starts_in_front_ends_with_edges_along_plane _____________

    def test_open_starts_in_front_ends_with_edges_along_plane():
        signs = np.array([1, 1, 1, 1, 0, 0])
>       vertices = vertices_with_signs(signs)

polliwog/polyline/test_slice_by_plane.py:134: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

signs = array([1, 1, 1, 1, 0, 0])

    def vertices_with_signs(signs):
        num_verts = len(signs)
        random_points_on_plane = plane.project_point(rand_nonzero(num_verts, 3))
        random_displacement_along_normal = (
            rand_nonzero(num_verts).reshape(-1, 1) * plane_normal
        )
        vertices = (
            random_points_on_plane + signs.reshape(-1, 1) * random_displacement_along_normal
        )
        # Because of rounding, the random points don't necessarily return 0 for
        # sign, so pick one that does.
        vertices[signs == 0] = plane.reference_point
>       np.testing.assert_array_equal(plane.sign(vertices), signs)
E       AssertionError: 
E       Arrays are not equal
E       
E       Mismatched elements: 2 / 6 (33.3%)
E       Max absolute difference: 1.
E       Max relative difference: 0.
E        x: array([1., 1., 1., 1., 1., 1.])
E        y: array([1, 1, 1, 1, 0, 0])

polliwog/polyline/test_slice_by_plane.py:28: AssertionError
_____________________ test_open_in_back_then_ends_on_plane _____________________

    def test_open_in_back_then_ends_on_plane():
        signs = np.array([-1, -1, -1, -1, 0])
>       vertices = vertices_with_signs(signs)

polliwog/polyline/test_slice_by_plane.py:143: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

signs = array([-1, -1, -1, -1,  0])

    def vertices_with_signs(signs):
        num_verts = len(signs)
        random_points_on_plane = plane.project_point(rand_nonzero(num_verts, 3))
        random_displacement_along_normal = (
            rand_nonzero(num_verts).reshape(-1, 1) * plane_normal
        )
        vertices = (
            random_points_on_plane + signs.reshape(-1, 1) * random_displacement_along_normal
        )
        # Because of rounding, the random points don't necessarily return 0 for
        # sign, so pick one that does.
        vertices[signs == 0] = plane.reference_point
>       np.testing.assert_array_equal(plane.sign(vertices), signs)
E       AssertionError: 
E       Arrays are not equal
E       
E       Mismatched elements: 1 / 5 (20%)
E       Max absolute difference: 1.
E       Max relative difference: 0.
E        x: array([-1., -1., -1., -1.,  1.])
E        y: array([-1, -1, -1, -1,  0])

polliwog/polyline/test_slice_by_plane.py:28: AssertionError
________ test_open_starts_in_front_then_along_plane_then_in_front_again ________

    def test_open_starts_in_front_then_along_plane_then_in_front_again():
        signs = np.array([1, 1, 0, 0, 1])
>       vertices = vertices_with_signs(signs)

polliwog/polyline/test_slice_by_plane.py:153: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

signs = array([1, 1, 0, 0, 1])

    def vertices_with_signs(signs):
        num_verts = len(signs)
        random_points_on_plane = plane.project_point(rand_nonzero(num_verts, 3))
        random_displacement_along_normal = (
            rand_nonzero(num_verts).reshape(-1, 1) * plane_normal
        )
        vertices = (
            random_points_on_plane + signs.reshape(-1, 1) * random_displacement_along_normal
        )
        # Because of rounding, the random points don't necessarily return 0 for
        # sign, so pick one that does.
        vertices[signs == 0] = plane.reference_point
>       np.testing.assert_array_equal(plane.sign(vertices), signs)
E       AssertionError: 
E       Arrays are not equal
E       
E       Mismatched elements: 2 / 5 (40%)
E       Max absolute difference: 1.
E       Max relative difference: 0.
E        x: array([1., 1., 1., 1., 1.])
E        y: array([1, 1, 0, 0, 1])

polliwog/polyline/test_slice_by_plane.py:28: AssertionError
__ test_open_starts_in_back_then_along_plane_then_in_front_then_in_back_again __

    def test_open_starts_in_back_then_along_plane_then_in_front_then_in_back_again():
        signs = np.array([-1, -1, 0, 0, 1, 1, 1, -1, -1])
>       vertices = vertices_with_signs(signs)

polliwog/polyline/test_slice_by_plane.py:181: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

signs = array([-1, -1,  0,  0,  1,  1,  1, -1, -1])

    def vertices_with_signs(signs):
        num_verts = len(signs)
        random_points_on_plane = plane.project_point(rand_nonzero(num_verts, 3))
        random_displacement_along_normal = (
            rand_nonzero(num_verts).reshape(-1, 1) * plane_normal
        )
        vertices = (
            random_points_on_plane + signs.reshape(-1, 1) * random_displacement_along_normal
        )
        # Because of rounding, the random points don't necessarily return 0 for
        # sign, so pick one that does.
        vertices[signs == 0] = plane.reference_point
>       np.testing.assert_array_equal(plane.sign(vertices), signs)
E       AssertionError: 
E       Arrays are not equal
E       
E       Mismatched elements: 2 / 9 (22.2%)
E       Max absolute difference: 1.
E       Max relative difference: 0.
E        x: array([-1., -1.,  1.,  1.,  1.,  1.,  1., -1., -1.])
E        y: array([-1, -1,  0,  0,  1,  1,  1, -1, -1])

polliwog/polyline/test_slice_by_plane.py:28: AssertionError
___________ test_open_starts_in_front_then_along_plane_then_in_back ____________

    def test_open_starts_in_front_then_along_plane_then_in_back():
        signs = np.array([1, 1, 0, 0, -1])
>       vertices = vertices_with_signs(signs)

polliwog/polyline/test_slice_by_plane.py:199: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

signs = array([ 1,  1,  0,  0, -1])

    def vertices_with_signs(signs):
        num_verts = len(signs)
        random_points_on_plane = plane.project_point(rand_nonzero(num_verts, 3))
        random_displacement_along_normal = (
            rand_nonzero(num_verts).reshape(-1, 1) * plane_normal
        )
        vertices = (
            random_points_on_plane + signs.reshape(-1, 1) * random_displacement_along_normal
        )
        # Because of rounding, the random points don't necessarily return 0 for
        # sign, so pick one that does.
        vertices[signs == 0] = plane.reference_point
>       np.testing.assert_array_equal(plane.sign(vertices), signs)
E       AssertionError: 
E       Arrays are not equal
E       
E       Mismatched elements: 2 / 5 (40%)
E       Max absolute difference: 1.
E       Max relative difference: 0.
E        x: array([ 1.,  1.,  1.,  1., -1.])
E        y: array([ 1,  1,  0,  0, -1])

polliwog/polyline/test_slice_by_plane.py:28: AssertionError
____________________________ test_open_all_in_plane ____________________________

    def test_open_all_in_plane():
        signs = np.array([0, 0])
>       vertices = vertices_with_signs(signs)

polliwog/polyline/test_slice_by_plane.py:228: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

signs = array([0, 0])

    def vertices_with_signs(signs):
        num_verts = len(signs)
        random_points_on_plane = plane.project_point(rand_nonzero(num_verts, 3))
        random_displacement_along_normal = (
            rand_nonzero(num_verts).reshape(-1, 1) * plane_normal
        )
        vertices = (
            random_points_on_plane + signs.reshape(-1, 1) * random_displacement_along_normal
        )
        # Because of rounding, the random points don't necessarily return 0 for
        # sign, so pick one that does.
        vertices[signs == 0] = plane.reference_point
>       np.testing.assert_array_equal(plane.sign(vertices), signs)
E       AssertionError: 
E       Arrays are not equal
E       
E       Mismatched elements: 2 / 2 (100%)
E       Max absolute difference: 1.
E       Max relative difference: inf
E        x: array([1., 1.])
E        y: array([0, 0])

polliwog/polyline/test_slice_by_plane.py:28: AssertionError
_________________________ test_open_one_vert_on_plane __________________________

    def test_open_one_vert_on_plane():
        signs = np.array([0])
>       vertices = vertices_with_signs(signs)

polliwog/polyline/test_slice_by_plane.py:258: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

signs = array([0])

    def vertices_with_signs(signs):
        num_verts = len(signs)
        random_points_on_plane = plane.project_point(rand_nonzero(num_verts, 3))
        random_displacement_along_normal = (
            rand_nonzero(num_verts).reshape(-1, 1) * plane_normal
        )
        vertices = (
            random_points_on_plane + signs.reshape(-1, 1) * random_displacement_along_normal
        )
        # Because of rounding, the random points don't necessarily return 0 for
        # sign, so pick one that does.
        vertices[signs == 0] = plane.reference_point
>       np.testing.assert_array_equal(plane.sign(vertices), signs)
E       AssertionError: 
E       Arrays are not equal
E       
E       Mismatched elements: 1 / 1 (100%)
E       Max absolute difference: 1.
E       Max relative difference: inf
E        x: array([1.])
E        y: array([0])

polliwog/polyline/test_slice_by_plane.py:28: AssertionError

Closes #198

@paulmelnikow paulmelnikow requested a review from jbeard4 July 3, 2020 20:54
@paulmelnikow paulmelnikow changed the title Fix point on plane detection for NumPy on Linux Fix point-on-plane detection for NumPy 1.19.0 in Linux Jul 3, 2020
@jbeard4
Copy link

jbeard4 commented Jul 30, 2020

@paulmelnikow Can you confirm that the idea behind this PR is to work around small numerical precision differences in Numpy 1.19 between your local OS X development machine, and the remote Linux CI server?

@paulmelnikow
Copy link
Member Author

I forget whether or not it's reproducible on OS X. Though yes, the way I'd put it is that the goal is to make the code work the same in NumPy 1.19 on Linux as it did in earlier versions of NumPy on Linux.

These tests started failing when NumPy 1.19 was released and I had to pin the NumPy version back to 1.18 to get them working again.

I can re-check OS X tomorrow if it's helpful.

@jbeard4
Copy link

jbeard4 commented Jul 30, 2020

I am fine with this fix, but I wonder if it would be better to regard it as a workaround to a regression in numpy. Do you think this is an issue that should get reported upstream?

@@ -1,3 +1,3 @@
numpy<1.19.0
numpy
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will default to the latest published numpy. Does it make sense to tag numpy at version 1.19?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In applications I always pin NumPy for safety's sake, however in libraries, I usually don't bother, since issues like this one are fairly rare (once every couple years) and version mismatches will cause warnings downstream at install time.

In other words, it's less work to leave the NumPy version number loose.

When a library depends on a new feature in a dependency we can use >=, which of course does not cause warnings with later versions.

@paulmelnikow
Copy link
Member Author

I am fine with this fix, but I wonder if it would be better to regard it as a workaround to a regression in numpy. Do you think this is an issue that should get reported upstream?

I do not know for sure whether or not this is a regression. It's possible. Though in general, I do not expect floating point multiplication and division to produce values which exactly equal zero. However that's what NumPy used to do until 1.19.

It's not the first time a NumPy upgrade has caused unexpected regressions in downstream code. I do know that NumPy 1.19 dropped Python 2 and a bunch of legacy code along the way.

Would you like to try to isolate the issue and report it?

I do think if this is a temporary workaround for a regression we should mark it up in the codebase with TODO comments.

@paulmelnikow paulmelnikow changed the base branch from master to main October 6, 2020 03:47
@jbeard4
Copy link

jbeard4 commented Oct 25, 2020

Let's confirm with upstream numpy that this is the expected behavior going forward, then merge this PR into our codebase.

@paulmelnikow paulmelnikow marked this pull request as draft October 25, 2020 23:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fix regression in NumPy 1.19.0
2 participants