From f0b8598e445f50fdbf9bbdfc887b9b7c6cb4a08d Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Wed, 25 Jan 2023 18:30:33 -0500 Subject: [PATCH 01/30] test: Fix assumption on quaternions. Only test quaternions that are not effectively zero. --- tests/test_polygon.py | 4 ++-- tests/test_spheropolygon.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_polygon.py b/tests/test_polygon.py index 4c6d78be..9973f785 100644 --- a/tests/test_polygon.py +++ b/tests/test_polygon.py @@ -189,7 +189,7 @@ def test_convex_area(points): @given(random_quat=arrays(np.float64, (4,), elements=floats(-1, 1, width=64))) def test_rotation_signed_area(random_quat): """Ensure that rotating does not change the signed area.""" - assume(not np.all(random_quat == 0)) + assume(not np.allclose(random_quat, 0)) random_quat = rowan.normalize(random_quat) rotated_points = rowan.rotate(random_quat, get_square_points()) poly = Polygon(rotated_points) @@ -253,7 +253,7 @@ def test_bounding_circle_radius_random_hull(points): ) def test_bounding_circle_radius_random_hull_rotation(points, rotation): """Test that rotating vertices does not change the bounding radius.""" - assume(not np.all(rotation == 0)) + assume(not np.allclose(rotation, 0)) hull = ConvexHull(points) poly = Polygon(points[hull.vertices]) diff --git a/tests/test_spheropolygon.py b/tests/test_spheropolygon.py index a9df4a0e..a1d34b6e 100644 --- a/tests/test_spheropolygon.py +++ b/tests/test_spheropolygon.py @@ -152,7 +152,7 @@ def test_convex_signed_area(square_points): ) ) def testfun(random_quat): - assume(not np.all(random_quat == 0)) + assume(not np.allclose(random_quat, 0)) random_quat = rowan.normalize(random_quat) rotated_points = rowan.rotate(random_quat, square_points) r = 1 From ab212791e2f016a71a4b6e582813326527d28d10 Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 6 Feb 2023 11:01:17 -0500 Subject: [PATCH 02/30] Added new classes to return the edges of polyhedra The new class ``polyhedron.edges`` returns the a list of the edges of a polyhedron as vertex-index pairs, similar to the current ``polyhedron.faces`` class. The class ``polyhedron.get_edge_vectors`` returns a list of edges as vectors in 3D space. --- .gitignore | 3 ++- coxeter/shapes/polyhedron.py | 28 +++++++++++++++++++++++++++- tests/test_polyhedron.py | 18 ++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 028cfa56..22b6b6b8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ *~ *.swp - +.DS_Store # Packages *.egg *.egg-info* @@ -17,6 +17,7 @@ lib lib64 __pycache__ .hypothesis +.coverage *.pyc doc/source/bibtex.json .ipynb_checkpoints diff --git a/coxeter/shapes/polyhedron.py b/coxeter/shapes/polyhedron.py index 074db4b6..13481c42 100644 --- a/coxeter/shapes/polyhedron.py +++ b/coxeter/shapes/polyhedron.py @@ -356,9 +356,35 @@ def vertices(self): @property def faces(self): - """list(:class:`numpy.ndarray`): Get the polyhedron's faces.""" + """list(:class:`numpy.ndarray`): Get the polyhedron's faces. + + Results returned as vertex index lists. + """ return self._faces + @property + def edges(self): + """list(:class:`numpy.ndarray`): Get the polyhedron's edges. + + Results returned as vertex index pairs. + """ + edges = [] + for face in self.faces: + [ + edges.append((i, j)) + for i, j in zip(np.append(face, face[0]), np.append(face[1:], face[0])) + if (j, i) not in edges + ] + return edges + + @property + def get_edge_vectors(self): + """list(:class:`numpy.ndarray`): Get the polyhedron's edges as vectors.""" + vecs = [] + vertices = self.vertices + for edge in self.edges: + vecs.append(vertices[edge[1]] - vertices[edge[0]]) + @property def volume(self): """float: Get or set the polyhedron's volume.""" diff --git a/tests/test_polyhedron.py b/tests/test_polyhedron.py index 0f3286c7..017a6208 100644 --- a/tests/test_polyhedron.py +++ b/tests/test_polyhedron.py @@ -224,6 +224,24 @@ def test_dihedrals(): assert np.isclose(poly.get_dihedral(i, j), dihedral) +def test_edges(): + # The shapes in the PlatonicFamily are normalized to unit volume + known_shapes = { + "Tetrahedron": np.sqrt(2) * np.cbrt(3), + "Cube": 1, + "Octahedron": np.power(2, 5 / 6) * np.cbrt(3 / 8), + "Dodecahedron": np.power(2, 2 / 3) * np.cbrt(1 / (15 + np.sqrt(245))), + "Icosahedron": np.cbrt(9 / 5 - 3 / 5 * np.sqrt(5)), + } + for name, edgelength in known_shapes.items(): + poly = PlatonicFamily.get_shape(name) + if name == "Dodecahedron": + poly.merge_faces(rtol=1) + for edge in poly.get_edge_vectors: + # rtol must be lowered to accomodate small inaccuracies in vertex locations + assert np.isclose(np.linalg.norm(edge), edgelength, rtol=1e-4) + + def test_curvature(): """Regression test against values computed with older method.""" # The shapes in the PlatonicFamily are normalized to unit volume. From 22399d8cfc3912dc9ad6fdcda4e2f3588f43293c Mon Sep 17 00:00:00 2001 From: janbridley Date: Tue, 7 Feb 2023 14:11:05 -0500 Subject: [PATCH 03/30] Fixed return for get_edge_vectors method. --- coxeter/shapes/polyhedron.py | 1 + 1 file changed, 1 insertion(+) diff --git a/coxeter/shapes/polyhedron.py b/coxeter/shapes/polyhedron.py index 13481c42..f8dcda73 100644 --- a/coxeter/shapes/polyhedron.py +++ b/coxeter/shapes/polyhedron.py @@ -384,6 +384,7 @@ def get_edge_vectors(self): vertices = self.vertices for edge in self.edges: vecs.append(vertices[edge[1]] - vertices[edge[0]]) + return vecs @property def volume(self): From 7c941004342f69b80d53fe4514fb80177899ee09 Mon Sep 17 00:00:00 2001 From: Jen Bradley <55467578+janbridley@users.noreply.github.com> Date: Fri, 24 Feb 2023 13:50:58 -0500 Subject: [PATCH 04/30] Renamed get_edge_vectors property to edge_vectors Co-authored-by: Vyas Ramasubramani --- coxeter/shapes/polyhedron.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coxeter/shapes/polyhedron.py b/coxeter/shapes/polyhedron.py index f8dcda73..42dd8c80 100644 --- a/coxeter/shapes/polyhedron.py +++ b/coxeter/shapes/polyhedron.py @@ -378,7 +378,7 @@ def edges(self): return edges @property - def get_edge_vectors(self): + def edge_vectors(self): """list(:class:`numpy.ndarray`): Get the polyhedron's edges as vectors.""" vecs = [] vertices = self.vertices From 0e6501a19c23980dfb98b911b3a1116d42b9ec3c Mon Sep 17 00:00:00 2001 From: Jen Bradley <55467578+janbridley@users.noreply.github.com> Date: Mon, 27 Feb 2023 11:03:58 -0500 Subject: [PATCH 05/30] Vectorized edge_vectors return with numpy Co-authored-by: Vyas Ramasubramani --- coxeter/shapes/polyhedron.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/coxeter/shapes/polyhedron.py b/coxeter/shapes/polyhedron.py index 42dd8c80..87e7146a 100644 --- a/coxeter/shapes/polyhedron.py +++ b/coxeter/shapes/polyhedron.py @@ -380,11 +380,8 @@ def edges(self): @property def edge_vectors(self): """list(:class:`numpy.ndarray`): Get the polyhedron's edges as vectors.""" - vecs = [] - vertices = self.vertices - for edge in self.edges: - vecs.append(vertices[edge[1]] - vertices[edge[0]]) - return vecs + edges = np.asarray(self.edges) + return list(self.vertices[edges[:, 1]] - self.vertices[edges[:, 0]]) @property def volume(self): From fdec57c7e76d028296487a7a28382ea702c80681 Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 27 Feb 2023 11:12:38 -0500 Subject: [PATCH 06/30] Updated test_polyhedron to be compatible with the renamed edge_vectors property --- tests/test_polyhedron.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_polyhedron.py b/tests/test_polyhedron.py index 621f841f..dddda5b5 100644 --- a/tests/test_polyhedron.py +++ b/tests/test_polyhedron.py @@ -238,7 +238,7 @@ def test_edges(): poly = PlatonicFamily.get_shape(name) if name == "Dodecahedron": poly.merge_faces(rtol=1) - for edge in poly.get_edge_vectors: + for edge in poly.edge_vectors: # rtol must be lowered to accomodate small inaccuracies in vertex locations assert np.isclose(np.linalg.norm(edge), edgelength, rtol=1e-4) From dd47ed2cb8c5416e8e5281a91dc5eeaa96ba0cff Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 27 Feb 2023 11:24:49 -0500 Subject: [PATCH 07/30] Updated test_polyhedron to explicitly cover the edges property --- tests/test_polyhedron.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_polyhedron.py b/tests/test_polyhedron.py index dddda5b5..750c5811 100644 --- a/tests/test_polyhedron.py +++ b/tests/test_polyhedron.py @@ -238,10 +238,17 @@ def test_edges(): poly = PlatonicFamily.get_shape(name) if name == "Dodecahedron": poly.merge_faces(rtol=1) + # Test edge_vectors property for edge in poly.edge_vectors: # rtol must be lowered to accomodate small inaccuracies in vertex locations assert np.isclose(np.linalg.norm(edge), edgelength, rtol=1e-4) + # Test edges property + edges = np.asarray(poly.edges) + vertices = poly.vertices + veclens = np.linalg.norm(vertices[edges[:, 1]] - vertices[edges[:, 0]], axis=1) + assert np.allclose(veclens, edgelength) + def test_curvature(): """Regression test against values computed with older method.""" From 210f0099e04ce5b69ad77d53c458ea18b6333abf Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 27 Feb 2023 13:05:44 -0500 Subject: [PATCH 08/30] Updated rtol for edge property test Until the precision improvements in #177 are added, a decrease in rtol and the merge_faces call are required for pytest to succeed on the dodecahedron's edges. Once the precision is increased, these temporary solutions can be reverted --- tests/test_polyhedron.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_polyhedron.py b/tests/test_polyhedron.py index 750c5811..1c9c0469 100644 --- a/tests/test_polyhedron.py +++ b/tests/test_polyhedron.py @@ -247,7 +247,7 @@ def test_edges(): edges = np.asarray(poly.edges) vertices = poly.vertices veclens = np.linalg.norm(vertices[edges[:, 1]] - vertices[edges[:, 0]], axis=1) - assert np.allclose(veclens, edgelength) + assert np.allclose(veclens, edgelength, rtol=1e-4) def test_curvature(): From a2cfd3ca839aa6527695fe98ddc4f32631f2fa20 Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 27 Feb 2023 13:14:48 -0500 Subject: [PATCH 09/30] Reverted small fixes that account for inaccuracies in polyhedron stored data Currently, the stored data for polyhedra is not accurate enough for the ``edges`` and ``edge_vectors`` properties to function as expected. Once #177 is merged, the accuracy increase should fix the issue and assertion tolerances for testing can be tightened. In addition, ``merge_faces`` should no longer be required for any of the polyhedra included with this package. --- tests/test_polyhedron.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/test_polyhedron.py b/tests/test_polyhedron.py index 1c9c0469..71540de1 100644 --- a/tests/test_polyhedron.py +++ b/tests/test_polyhedron.py @@ -236,18 +236,16 @@ def test_edges(): } for name, edgelength in known_shapes.items(): poly = PlatonicFamily.get_shape(name) - if name == "Dodecahedron": - poly.merge_faces(rtol=1) # Test edge_vectors property for edge in poly.edge_vectors: # rtol must be lowered to accomodate small inaccuracies in vertex locations - assert np.isclose(np.linalg.norm(edge), edgelength, rtol=1e-4) + assert np.isclose(np.linalg.norm(edge), edgelength) # Test edges property edges = np.asarray(poly.edges) vertices = poly.vertices veclens = np.linalg.norm(vertices[edges[:, 1]] - vertices[edges[:, 0]], axis=1) - assert np.allclose(veclens, edgelength, rtol=1e-4) + assert np.allclose(veclens, edgelength) def test_curvature(): From 1f4a10673d0928aa1ab8e7126f37855ccfafe3c7 Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 27 Feb 2023 22:42:10 -0500 Subject: [PATCH 10/30] Removed unnecessary comment from ``test_edges`` --- tests/test_polyhedron.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_polyhedron.py b/tests/test_polyhedron.py index 71540de1..375e492b 100644 --- a/tests/test_polyhedron.py +++ b/tests/test_polyhedron.py @@ -238,7 +238,6 @@ def test_edges(): poly = PlatonicFamily.get_shape(name) # Test edge_vectors property for edge in poly.edge_vectors: - # rtol must be lowered to accomodate small inaccuracies in vertex locations assert np.isclose(np.linalg.norm(edge), edgelength) # Test edges property From d387fca343149839412d6994ff1c832c95b31d72 Mon Sep 17 00:00:00 2001 From: Jen Bradley <55467578+janbridley@users.noreply.github.com> Date: Mon, 27 Feb 2023 22:50:50 -0500 Subject: [PATCH 11/30] Changed ``edge_vectors`` property to return a numpy array Co-authored-by: Bradley Dice --- coxeter/shapes/polyhedron.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coxeter/shapes/polyhedron.py b/coxeter/shapes/polyhedron.py index 87e7146a..b19e6513 100644 --- a/coxeter/shapes/polyhedron.py +++ b/coxeter/shapes/polyhedron.py @@ -380,8 +380,8 @@ def edges(self): @property def edge_vectors(self): """list(:class:`numpy.ndarray`): Get the polyhedron's edges as vectors.""" - edges = np.asarray(self.edges) - return list(self.vertices[edges[:, 1]] - self.vertices[edges[:, 0]]) + edges = self.edges + return self.vertices[edges[:, 1]] - self.vertices[edges[:, 0]] @property def volume(self): From f473e27c380adb96da4e1f6bdb33cd6d7112d32e Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 22 Mar 2023 14:39:48 -0400 Subject: [PATCH 12/30] Rewrote ``edges`` method and updated ``edge_vectors`` for compatibility After discussing possible options for returning polyhedral edges, it was determined that a set of i Date: Wed, 22 Mar 2023 15:48:02 -0400 Subject: [PATCH 13/30] Updated ``test_polyhedron`` to work with set edges --- tests/test_polyhedron.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_polyhedron.py b/tests/test_polyhedron.py index 375e492b..03dc8a06 100644 --- a/tests/test_polyhedron.py +++ b/tests/test_polyhedron.py @@ -234,6 +234,7 @@ def test_edges(): "Dodecahedron": np.power(2, 2 / 3) * np.cbrt(1 / (15 + np.sqrt(245))), "Icosahedron": np.cbrt(9 / 5 - 3 / 5 * np.sqrt(5)), } + for name, edgelength in known_shapes.items(): poly = PlatonicFamily.get_shape(name) # Test edge_vectors property @@ -241,7 +242,11 @@ def test_edges(): assert np.isclose(np.linalg.norm(edge), edgelength) # Test edges property - edges = np.asarray(poly.edges) + edges = np.asarray( + [ + *poly.edges, + ] + ) vertices = poly.vertices veclens = np.linalg.norm(vertices[edges[:, 1]] - vertices[edges[:, 0]], axis=1) assert np.allclose(veclens, edgelength) From 01077ccd3d29c08a21ee198cfa0a674beb7ff268 Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 22 Mar 2023 17:47:06 -0400 Subject: [PATCH 14/30] Fixed docstring formatting for ``edges`` and ``edge_vectors`` --- coxeter/shapes/polyhedron.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/coxeter/shapes/polyhedron.py b/coxeter/shapes/polyhedron.py index 6e0f34d4..7eefdf3e 100644 --- a/coxeter/shapes/polyhedron.py +++ b/coxeter/shapes/polyhedron.py @@ -364,15 +364,14 @@ def faces(self): @property def edges(self): - """set(:class:`tuple`): Get the polyhedron's edges. + """set(tuple(int,int)): Get the polyhedron's edges. Results returned as vertex index pairs, with each edge of the polyhedron included exactly once. Edge (i,j) pairs are ordered by vertex index with i Date: Thu, 25 May 2023 10:45:21 -0400 Subject: [PATCH 15/30] Updated edges method to return Numpy array It was determined that edges should be returned as an ordered Numpy array. This commit ensures the final output is a numpy array, sorted first by the (i) element and then by the (j) element where i Date: Thu, 25 May 2023 11:12:29 -0400 Subject: [PATCH 16/30] test_polyhedron::test_edges now checks edge count --- tests/test_polyhedron.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_polyhedron.py b/tests/test_polyhedron.py index 03dc8a06..4c5eccfe 100644 --- a/tests/test_polyhedron.py +++ b/tests/test_polyhedron.py @@ -234,9 +234,20 @@ def test_edges(): "Dodecahedron": np.power(2, 2 / 3) * np.cbrt(1 / (15 + np.sqrt(245))), "Icosahedron": np.cbrt(9 / 5 - 3 / 5 * np.sqrt(5)), } + number_of_edges = { + "Tetrahedron": 6, + "Cube": 12, + "Octahedron": 12, + "Dodecahedron": 30, + "Icosahedron": 30, + } for name, edgelength in known_shapes.items(): poly = PlatonicFamily.get_shape(name) + # Test that the correct number of edges has been found + assert(number_of_edges[name]==len(poly.edges)) + assert(number_of_edges[name]==len(poly.edge_vectors)) + # Test edge_vectors property for edge in poly.edge_vectors: assert np.isclose(np.linalg.norm(edge), edgelength) From 770199b5cabd2355bcede1574158a05800963bd9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 25 May 2023 15:27:21 +0000 Subject: [PATCH 17/30] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- coxeter/shapes/polyhedron.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/coxeter/shapes/polyhedron.py b/coxeter/shapes/polyhedron.py index 5efd66f5..f93e1b49 100644 --- a/coxeter/shapes/polyhedron.py +++ b/coxeter/shapes/polyhedron.py @@ -381,19 +381,21 @@ def edges(self): for i, j in zip(face, np.roll(face, -1)) if i < j ], - key=lambda x: (x[0],x[1]), + key=lambda x: (x[0], x[1]), ) ) @property def edge_vectors(self): """list(tuple(float,float,float)): Get the polyhedron's edges as vectors.""" - return np.array([ - np.subtract( - *self.vertices[[j, i]], - ) - for (i, j) in self.edges - ]) + return np.array( + [ + np.subtract( + *self.vertices[[j, i]], + ) + for (i, j) in self.edges + ] + ) @property def volume(self): From b4f60f9e9c5f2b5c64ccd455916241f70f772a6c Mon Sep 17 00:00:00 2001 From: janbridley Date: Fri, 4 Aug 2023 12:21:18 -0400 Subject: [PATCH 18/30] Updated edges property to cache result --- coxeter/shapes/polyhedron.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/coxeter/shapes/polyhedron.py b/coxeter/shapes/polyhedron.py index f93e1b49..530e370e 100644 --- a/coxeter/shapes/polyhedron.py +++ b/coxeter/shapes/polyhedron.py @@ -368,22 +368,12 @@ def edges(self): Results returned as vertex index pairs, with each edge of the polyhedron included exactly once. Edge (i,j) pairs are ordered by vertex index with i Date: Mon, 7 Aug 2023 10:40:53 -0400 Subject: [PATCH 19/30] Updated edges and edge_vectors to make better use of numpy functions and functools caching --- coxeter/shapes/polyhedron.py | 45 +++++++++++++++--------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/coxeter/shapes/polyhedron.py b/coxeter/shapes/polyhedron.py index 530e370e..05ab3d34 100644 --- a/coxeter/shapes/polyhedron.py +++ b/coxeter/shapes/polyhedron.py @@ -4,6 +4,7 @@ """Defines a polyhedron.""" import warnings +from functools import cached_property import numpy as np import rowan @@ -362,43 +363,33 @@ def faces(self): """ return self._faces - @property + @cached_property def edges(self): """set(tuple(int,int)): Get the polyhedron's edges. Results returned as vertex index pairs, with each edge of the polyhedron included exactly once. Edge (i,j) pairs are ordered by vertex index with i Date: Mon, 7 Aug 2023 14:03:33 -0400 Subject: [PATCH 20/30] Added num_edges method --- coxeter/shapes/polyhedron.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/coxeter/shapes/polyhedron.py b/coxeter/shapes/polyhedron.py index 05ab3d34..3d48a081 100644 --- a/coxeter/shapes/polyhedron.py +++ b/coxeter/shapes/polyhedron.py @@ -391,6 +391,11 @@ def edge_vectors(self): """list(tuple(float,float,float)): Get the polyhedron's edges as vectors.""" return self.vertices[self.edges[:, 1]] - self.vertices[self.edges[:, 0]] + @property + def num_edges(self): + """int: Get the number of edges.""" + return len(self.edges) + @property def volume(self): """float: Get or set the polyhedron's volume.""" From ced2fe6fe338f3da63a21cf1b828cb189a311260 Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 7 Aug 2023 14:04:01 -0400 Subject: [PATCH 21/30] Updated credits --- Credits.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Credits.rst b/Credits.rst index e52b4872..527bcb66 100644 --- a/Credits.rst +++ b/Credits.rst @@ -112,6 +112,7 @@ Jen Bradley * Added shape families for Archimedean, Catalan, and Johnson solids. * Added shape family for prisms and antiprisms. * Added shape family for equilateral pyramids and dipyramids. +* Added edges, edge_vectors, and num_edges methods Source code ----------- From 20a8518a7551a2ff8e669edc419308c83190b7e0 Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 7 Aug 2023 15:15:41 -0400 Subject: [PATCH 22/30] Updated test_polyhedron to test num_edges property --- tests/test_polyhedron.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_polyhedron.py b/tests/test_polyhedron.py index 34aab06f..63bfea4d 100644 --- a/tests/test_polyhedron.py +++ b/tests/test_polyhedron.py @@ -359,7 +359,7 @@ def test_edges(): for name, edgelength in known_shapes.items(): poly = PlatonicFamily.get_shape(name) # Test that the correct number of edges has been found - assert number_of_edges[name] == len(poly.edges) + assert number_of_edges[name] == poly.num_edges assert number_of_edges[name] == len(poly.edge_vectors) # Test edge_vectors property From 3a2f336151d3825dff9e358ac388ba0c078e5e52 Mon Sep 17 00:00:00 2001 From: Jen Bradley <55467578+janbridley@users.noreply.github.com> Date: Mon, 7 Aug 2023 20:03:52 -0400 Subject: [PATCH 23/30] Updated edges documentation Co-authored-by: Bradley Dice --- coxeter/shapes/polyhedron.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coxeter/shapes/polyhedron.py b/coxeter/shapes/polyhedron.py index 3d48a081..f2738874 100644 --- a/coxeter/shapes/polyhedron.py +++ b/coxeter/shapes/polyhedron.py @@ -365,7 +365,7 @@ def faces(self): @cached_property def edges(self): - """set(tuple(int,int)): Get the polyhedron's edges. + """:class:`numpy.ndarray`: Get the polyhedron's edges. Results returned as vertex index pairs, with each edge of the polyhedron included exactly once. Edge (i,j) pairs are ordered by vertex index with i Date: Mon, 7 Aug 2023 20:04:33 -0400 Subject: [PATCH 24/30] Updated edge_vectors documentation Co-authored-by: Bradley Dice --- coxeter/shapes/polyhedron.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coxeter/shapes/polyhedron.py b/coxeter/shapes/polyhedron.py index f2738874..3eafe706 100644 --- a/coxeter/shapes/polyhedron.py +++ b/coxeter/shapes/polyhedron.py @@ -388,7 +388,7 @@ def edges(self): @property def edge_vectors(self): - """list(tuple(float,float,float)): Get the polyhedron's edges as vectors.""" + """:class:`numpy.ndarray`: Get the polyhedron's edges as vectors.""" return self.vertices[self.edges[:, 1]] - self.vertices[self.edges[:, 0]] @property From 986fa8d2132fb99d43e47bdf2ad08c59e2620078 Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 7 Aug 2023 20:52:34 -0400 Subject: [PATCH 25/30] Refactored test_edges to be more comprehensive Added explicit tests for sorting, double checks for edge length, and an additional test for the number of edges based on the euler characteristic --- tests/test_polyhedron.py | 72 ++++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/tests/test_polyhedron.py b/tests/test_polyhedron.py index 63bfea4d..02d1092a 100644 --- a/tests/test_polyhedron.py +++ b/tests/test_polyhedron.py @@ -339,8 +339,37 @@ def test___repr__(): repr(icosidodecahedron) -def test_edges(): - # The shapes in the PlatonicFamily are normalized to unit volume +@combine_marks( + named_platonic_mark, + named_archimedean_mark, + named_catalan_mark, + named_johnson_mark, + named_prismantiprism_mark, + named_pyramiddipyramid_mark, +) +def test_edges(poly): + # Check that the first column is in ascending order. + assert np.all(np.diff(poly.edges[:, 0]) >= 0) + + # Check that all items in the first column are greater than those in the second. + assert np.all(np.diff(poly.edges, axis=1) >= 0) + + # Check the second column is in ascending order for each unique item in the first. + # For example, [[0,1],[0,3],[1,2]] is permitted but [[0,1],[0,3],[0,2]] is not. + edges = poly.edges + unique_values = unique_values = np.unique(edges[:, 0]) + assert all( + [ + np.all(np.diff(edges[edges[:, 0] == value, 1]) >= 0) + for value in unique_values + ] + ) + + # Check that there are no duplicate edges. This also double-checks the sorting + assert np.all(np.unique(poly.edges, axis=1) == poly.edges) + + +def test_edge_lengths(): known_shapes = { "Tetrahedron": np.sqrt(2) * np.cbrt(3), "Cube": 1, @@ -348,33 +377,26 @@ def test_edges(): "Dodecahedron": np.power(2, 2 / 3) * np.cbrt(1 / (15 + np.sqrt(245))), "Icosahedron": np.cbrt(9 / 5 - 3 / 5 * np.sqrt(5)), } - number_of_edges = { - "Tetrahedron": 6, - "Cube": 12, - "Octahedron": 12, - "Dodecahedron": 30, - "Icosahedron": 30, - } - for name, edgelength in known_shapes.items(): poly = PlatonicFamily.get_shape(name) - # Test that the correct number of edges has been found - assert number_of_edges[name] == poly.num_edges - assert number_of_edges[name] == len(poly.edge_vectors) - - # Test edge_vectors property - for edge in poly.edge_vectors: - assert np.isclose(np.linalg.norm(edge), edgelength) - - # Test edges property - edges = np.asarray( - [ - *poly.edges, - ] + # Check that edge lengths are correct + veclens = np.linalg.norm( + poly.vertices[poly.edges[:, 1]] - poly.vertices[poly.edges[:, 0]], axis=1 ) - vertices = poly.vertices - veclens = np.linalg.norm(vertices[edges[:, 1]] - vertices[edges[:, 0]], axis=1) assert np.allclose(veclens, edgelength) + assert np.allclose(veclens, np.linalg.norm(poly.edge_vectors, axis=1)) + + +@given( + EllipsoidSurfaceStrategy, +) +def test_num_edges(points): + hull = ConvexHull(points) + poly = ConvexPolyhedron(points[hull.vertices]) + + # Calculate correct number of edges from euler characteristic + euler_characteristic_edge_count = poly.num_vertices + poly.num_faces - 2 + assert poly.num_edges == euler_characteristic_edge_count def test_curvature(): From d3f520379977f239033fb7351039d9336884b3ef Mon Sep 17 00:00:00 2001 From: janbridley Date: Tue, 8 Aug 2023 09:16:30 -0400 Subject: [PATCH 26/30] Added fast num_edges calculation for convex polyhedron and improved pytests --- coxeter/shapes/convex_polyhedron.py | 6 ++++++ tests/test_polyhedron.py | 31 ++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/coxeter/shapes/convex_polyhedron.py b/coxeter/shapes/convex_polyhedron.py index bb3472e0..2b3bcac6 100644 --- a/coxeter/shapes/convex_polyhedron.py +++ b/coxeter/shapes/convex_polyhedron.py @@ -126,6 +126,12 @@ def asphericity(self): """float: Get the asphericity as defined in :cite:`Irrgang2017`.""" return self.mean_curvature * self.surface_area / (3 * self.volume) + @property + def num_edges(self): + """int: Get the number of edges.""" + # Calculate number of edges from Euler Characteristic + return self.num_vertices + self.num_faces - 2 + def is_inside(self, points): """Determine whether points are contained in this polyhedron. diff --git a/tests/test_polyhedron.py b/tests/test_polyhedron.py index 02d1092a..df5d12d6 100644 --- a/tests/test_polyhedron.py +++ b/tests/test_polyhedron.py @@ -28,7 +28,7 @@ named_pyramiddipyramid_mark, sphere_isclose, ) -from coxeter.families import DOI_SHAPE_REPOSITORIES, PlatonicFamily +from coxeter.families import DOI_SHAPE_REPOSITORIES, ArchimedeanFamily, PlatonicFamily from coxeter.shapes import ConvexPolyhedron, Polyhedron from coxeter.shapes.utils import rotate_order2_tensor, translate_inertia_tensor from utils import compute_centroid_mc, compute_inertia_mc @@ -368,6 +368,14 @@ def test_edges(poly): # Check that there are no duplicate edges. This also double-checks the sorting assert np.all(np.unique(poly.edges, axis=1) == poly.edges) + # Check that the edges are immutable + try: + poly.edges[1] = [99, 99] + # If the assignment works, catch that: + assert poly.edges[1] != [99, 99] + except ValueError as ve: + assert "read-only" in str(ve) + def test_edge_lengths(): known_shapes = { @@ -387,6 +395,27 @@ def test_edge_lengths(): assert np.allclose(veclens, np.linalg.norm(poly.edge_vectors, axis=1)) +def test_num_edges_archimedean(): + known_shapes = { + "Cuboctahedron": 24, + "Icosidodecahedron": 60, + "Truncated Tetrahedron": 18, + "Truncated Octahedron": 36, + "Truncated Cube": 36, + "Truncated Icosahedron": 90, + "Truncated Dodecahedron": 90, + "Rhombicuboctahedron": 48, + "Rhombicosidodecahedron": 120, + "Truncated Cuboctahedron": 72, + "Truncated Icosidodecahedron": 180, + "Snub Cuboctahedron": 60, + "Snub Icosidodecahedron": 150, + } + for name, num_edges in known_shapes.items(): + poly = ArchimedeanFamily.get_shape(name) + assert poly.num_edges == num_edges + + @given( EllipsoidSurfaceStrategy, ) From 2a325e6116932890d4272c62df975bbfa7449ed5 Mon Sep 17 00:00:00 2001 From: janbridley Date: Wed, 9 Aug 2023 10:07:48 -0400 Subject: [PATCH 27/30] test_num_edges now covers nonconvex Polyhedron class --- tests/test_polyhedron.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_polyhedron.py b/tests/test_polyhedron.py index df5d12d6..45639c98 100644 --- a/tests/test_polyhedron.py +++ b/tests/test_polyhedron.py @@ -419,13 +419,14 @@ def test_num_edges_archimedean(): @given( EllipsoidSurfaceStrategy, ) -def test_num_edges(points): +def test_num_edges_polyhedron(points): hull = ConvexHull(points) poly = ConvexPolyhedron(points[hull.vertices]) + ppoly = Polyhedron(poly.vertices, poly.faces) # Calculate correct number of edges from euler characteristic - euler_characteristic_edge_count = poly.num_vertices + poly.num_faces - 2 - assert poly.num_edges == euler_characteristic_edge_count + euler_characteristic_edge_count = ppoly.num_vertices + ppoly.num_faces - 2 + assert ppoly.num_edges == euler_characteristic_edge_count def test_curvature(): From ec798c7c940b8afe3a8c12d2ba99d157cbbf6e30 Mon Sep 17 00:00:00 2001 From: Jen Bradley <55467578+janbridley@users.noreply.github.com> Date: Fri, 11 Aug 2023 18:47:39 -0400 Subject: [PATCH 28/30] Update Credits.rst Co-authored-by: Bradley Dice --- Credits.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Credits.rst b/Credits.rst index f77d8a04..2da7c61f 100644 --- a/Credits.rst +++ b/Credits.rst @@ -112,7 +112,7 @@ Jen Bradley * Added shape families for Archimedean, Catalan, and Johnson solids. * Added shape family for prisms and antiprisms. * Added shape family for equilateral pyramids and dipyramids. -* Added edges, edge_vectors, and num_edges methods +* Added edges, edge_vectors, and num_edges methods. Domagoj Fijan * Rewrote point in polygon check to use NumPy vectorized operations. From 8d4b3619d5b99be78f668dd3dcf9031f899655de Mon Sep 17 00:00:00 2001 From: Jen Bradley <55467578+janbridley@users.noreply.github.com> Date: Mon, 14 Aug 2023 12:44:34 -0400 Subject: [PATCH 29/30] Test i-i edges Co-authored-by: Bradley Dice --- tests/test_polyhedron.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_polyhedron.py b/tests/test_polyhedron.py index 45639c98..c5ad049b 100644 --- a/tests/test_polyhedron.py +++ b/tests/test_polyhedron.py @@ -352,7 +352,7 @@ def test_edges(poly): assert np.all(np.diff(poly.edges[:, 0]) >= 0) # Check that all items in the first column are greater than those in the second. - assert np.all(np.diff(poly.edges, axis=1) >= 0) + assert np.all(np.diff(poly.edges, axis=1) > 0) # Check the second column is in ascending order for each unique item in the first. # For example, [[0,1],[0,3],[1,2]] is permitted but [[0,1],[0,3],[0,2]] is not. From 341cd536a60cd953270c36b5781cd911536fd248 Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 14 Aug 2023 18:51:59 -0700 Subject: [PATCH 30/30] Removed changes to .gitignore --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 22b6b6b8..028cfa56 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ *~ *.swp -.DS_Store + # Packages *.egg *.egg-info* @@ -17,7 +17,6 @@ lib lib64 __pycache__ .hypothesis -.coverage *.pyc doc/source/bibtex.json .ipynb_checkpoints