Skip to content

Commit

Permalink
avoid running tests twice
Browse files Browse the repository at this point in the history
Summary: Avoid test files explicitly importing TestCase objects from each other, because doing so causes the tests to be discovered twice by unittest discover. This means moving a few static functions out of their classes. I noticed this while trying to fix failures from yesterday.

Reviewed By: nikhilaravi

Differential Revision: D28194679

fbshipit-source-id: ac6e6585603bd4ef9c098cdd56891d94f8923ba6
  • Loading branch information
bottler authored and facebook-github-bot committed May 7, 2021
1 parent e3624b4 commit 0ca839c
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 169 deletions.
4 changes: 2 additions & 2 deletions tests/test_mesh_edge_loss.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from common_testing import TestCaseMixin
from pytorch3d.loss import mesh_edge_loss
from pytorch3d.structures import Meshes
from test_sample_points_from_meshes import TestSamplePoints
from test_sample_points_from_meshes import init_meshes


class TestMeshEdgeLoss(TestCaseMixin, unittest.TestCase):
Expand Down Expand Up @@ -92,7 +92,7 @@ def test_mesh_edge_loss_output(self):

@staticmethod
def mesh_edge_loss(num_meshes: int = 10, max_v: int = 100, max_f: int = 300):
meshes = TestSamplePoints.init_meshes(num_meshes, max_v, max_f, device="cuda:0")
meshes = init_meshes(num_meshes, max_v, max_f, device="cuda:0")
torch.cuda.synchronize()

def compute_loss():
Expand Down
256 changes: 126 additions & 130 deletions tests/test_meshes.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,128 +10,126 @@
from pytorch3d.structures.meshes import Meshes


class TestMeshes(TestCaseMixin, unittest.TestCase):
def setUp(self) -> None:
np.random.seed(42)
torch.manual_seed(42)
def init_mesh(
num_meshes: int = 10,
max_v: int = 100,
max_f: int = 300,
lists_to_tensors: bool = False,
device: str = "cpu",
requires_grad: bool = False,
):
"""
Function to generate a Meshes object of N meshes with
random numbers of vertices and faces.
Args:
num_meshes: Number of meshes to generate.
max_v: Max number of vertices per mesh.
max_f: Max number of faces per mesh.
lists_to_tensors: Determines whether the generated meshes should be
constructed from lists (=False) or
a tensor (=True) of faces/verts.
Returns:
Meshes object.
"""
device = torch.device(device)

verts_list = []
faces_list = []

# Randomly generate numbers of faces and vertices in each mesh.
if lists_to_tensors:
# If we define faces/verts with tensors, f/v has to be the
# same for each mesh in the batch.
f = torch.randint(1, max_f, size=(1,), dtype=torch.int32)
v = torch.randint(3, high=max_v, size=(1,), dtype=torch.int32)
f = f.repeat(num_meshes)
v = v.repeat(num_meshes)
else:
# For lists of faces and vertices, we can sample different v/f
# per mesh.
f = torch.randint(max_f, size=(num_meshes,), dtype=torch.int32)
v = torch.randint(3, high=max_v, size=(num_meshes,), dtype=torch.int32)

# Generate the actual vertices and faces.
for i in range(num_meshes):
verts = torch.rand(
(v[i], 3),
dtype=torch.float32,
device=device,
requires_grad=requires_grad,
)
faces = torch.randint(v[i], size=(f[i], 3), dtype=torch.int64, device=device)
verts_list.append(verts)
faces_list.append(faces)

@staticmethod
def init_mesh(
num_meshes: int = 10,
max_v: int = 100,
max_f: int = 300,
lists_to_tensors: bool = False,
device: str = "cpu",
requires_grad: bool = False,
):
"""
Function to generate a Meshes object of N meshes with
random numbers of vertices and faces.
Args:
num_meshes: Number of meshes to generate.
max_v: Max number of vertices per mesh.
max_f: Max number of faces per mesh.
lists_to_tensors: Determines whether the generated meshes should be
constructed from lists (=False) or
a tensor (=True) of faces/verts.
Returns:
Meshes object.
"""
device = torch.device(device)
if lists_to_tensors:
verts_list = torch.stack(verts_list)
faces_list = torch.stack(faces_list)

verts_list = []
faces_list = []
return Meshes(verts=verts_list, faces=faces_list)

# Randomly generate numbers of faces and vertices in each mesh.
if lists_to_tensors:
# If we define faces/verts with tensors, f/v has to be the
# same for each mesh in the batch.
f = torch.randint(1, max_f, size=(1,), dtype=torch.int32)
v = torch.randint(3, high=max_v, size=(1,), dtype=torch.int32)
f = f.repeat(num_meshes)
v = v.repeat(num_meshes)
else:
# For lists of faces and vertices, we can sample different v/f
# per mesh.
f = torch.randint(max_f, size=(num_meshes,), dtype=torch.int32)
v = torch.randint(3, high=max_v, size=(num_meshes,), dtype=torch.int32)

# Generate the actual vertices and faces.
for i in range(num_meshes):
verts = torch.rand(
(v[i], 3),
dtype=torch.float32,
device=device,
requires_grad=requires_grad,
)
faces = torch.randint(
v[i], size=(f[i], 3), dtype=torch.int64, device=device
)
verts_list.append(verts)
faces_list.append(faces)

if lists_to_tensors:
verts_list = torch.stack(verts_list)
faces_list = torch.stack(faces_list)
def init_simple_mesh(device: str = "cpu"):
"""
Returns a Meshes data structure of simple mesh examples.
return Meshes(verts=verts_list, faces=faces_list)
Returns:
Meshes object.
"""
device = torch.device(device)

@staticmethod
def init_simple_mesh(device: str = "cpu"):
"""
Returns a Meshes data structure of simple mesh examples.
Returns:
Meshes object.
"""
device = torch.device(device)

verts = [
torch.tensor(
[[0.1, 0.3, 0.5], [0.5, 0.2, 0.1], [0.6, 0.8, 0.7]],
dtype=torch.float32,
device=device,
),
torch.tensor(
[[0.1, 0.3, 0.3], [0.6, 0.7, 0.8], [0.2, 0.3, 0.4], [0.1, 0.5, 0.3]],
dtype=torch.float32,
device=device,
),
torch.tensor(
[
[0.7, 0.3, 0.6],
[0.2, 0.4, 0.8],
[0.9, 0.5, 0.2],
[0.2, 0.3, 0.4],
[0.9, 0.3, 0.8],
],
dtype=torch.float32,
device=device,
),
]
faces = [
torch.tensor([[0, 1, 2]], dtype=torch.int64, device=device),
torch.tensor([[0, 1, 2], [1, 2, 3]], dtype=torch.int64, device=device),
torch.tensor(
[
[1, 2, 0],
[0, 1, 3],
[2, 3, 1],
[4, 3, 2],
[4, 0, 1],
[4, 3, 1],
[4, 2, 1],
],
dtype=torch.int64,
device=device,
),
]
return Meshes(verts=verts, faces=faces)
verts = [
torch.tensor(
[[0.1, 0.3, 0.5], [0.5, 0.2, 0.1], [0.6, 0.8, 0.7]],
dtype=torch.float32,
device=device,
),
torch.tensor(
[[0.1, 0.3, 0.3], [0.6, 0.7, 0.8], [0.2, 0.3, 0.4], [0.1, 0.5, 0.3]],
dtype=torch.float32,
device=device,
),
torch.tensor(
[
[0.7, 0.3, 0.6],
[0.2, 0.4, 0.8],
[0.9, 0.5, 0.2],
[0.2, 0.3, 0.4],
[0.9, 0.3, 0.8],
],
dtype=torch.float32,
device=device,
),
]
faces = [
torch.tensor([[0, 1, 2]], dtype=torch.int64, device=device),
torch.tensor([[0, 1, 2], [1, 2, 3]], dtype=torch.int64, device=device),
torch.tensor(
[
[1, 2, 0],
[0, 1, 3],
[2, 3, 1],
[4, 3, 2],
[4, 0, 1],
[4, 3, 1],
[4, 2, 1],
],
dtype=torch.int64,
device=device,
),
]
return Meshes(verts=verts, faces=faces)


class TestMeshes(TestCaseMixin, unittest.TestCase):
def setUp(self) -> None:
np.random.seed(42)
torch.manual_seed(42)

def test_simple(self):
mesh = TestMeshes.init_simple_mesh("cuda:0")
mesh = init_simple_mesh("cuda:0")

# Check that faces/verts per mesh are set in init:
self.assertClose(mesh._num_faces_per_mesh.cpu(), torch.tensor([1, 2, 7]))
Expand Down Expand Up @@ -168,7 +166,7 @@ def test_init_error(self):
# Check if correct errors are raised when verts/faces are on
# different devices

mesh = TestMeshes.init_mesh(10, 10, 100)
mesh = init_mesh(10, 10, 100)
verts_list = mesh.verts_list() # all tensors on cpu
verts_list = [
v.to("cuda:0") if random.uniform(0, 1) > 0.5 else v for v in verts_list
Expand All @@ -192,7 +190,7 @@ def test_simple_random_meshes(self):
# Define the test mesh object either as a list or tensor of faces/verts.
for lists_to_tensors in (False, True):
N = 10
mesh = TestMeshes.init_mesh(N, 100, 300, lists_to_tensors=lists_to_tensors)
mesh = init_mesh(N, 100, 300, lists_to_tensors=lists_to_tensors)
verts_list = mesh.verts_list()
faces_list = mesh.faces_list()

Expand Down Expand Up @@ -361,7 +359,7 @@ def test_padding(self):

def test_clone(self):
N = 5
mesh = TestMeshes.init_mesh(N, 10, 100)
mesh = init_mesh(N, 10, 100)
for force in [0, 1]:
if force:
# force mesh to have computed attributes
Expand All @@ -386,7 +384,7 @@ def test_clone(self):

def test_detach(self):
N = 5
mesh = TestMeshes.init_mesh(N, 10, 100, requires_grad=True)
mesh = init_mesh(N, 10, 100, requires_grad=True)
for force in [0, 1]:
if force:
# force mesh to have computed attributes
Expand Down Expand Up @@ -425,7 +423,7 @@ def naive_laplacian_packed(meshes):

# Note that we don't test with random meshes for this case, as the
# definition of Laplacian is defined for simple graphs (aka valid meshes)
meshes = TestMeshes.init_simple_mesh("cuda:0")
meshes = init_simple_mesh("cuda:0")

lapl_naive = naive_laplacian_packed(meshes)
lapl = meshes.laplacian_packed().to_dense()
Expand All @@ -443,7 +441,7 @@ def naive_offset_verts(mesh, vert_offsets_packed):
return Meshes(verts=new_verts_list, faces=new_faces_list)

N = 5
mesh = TestMeshes.init_mesh(N, 30, 100, lists_to_tensors=True)
mesh = init_mesh(N, 30, 100, lists_to_tensors=True)
all_v = mesh.verts_packed().size(0)
verts_per_mesh = mesh.num_verts_per_mesh()
for force, deform_shape in itertools.product([False, True], [(all_v, 3), 3]):
Expand Down Expand Up @@ -577,7 +575,7 @@ def naive_scale_verts(mesh, scale):
N = 5
for test in ["tensor", "scalar"]:
for force in (False, True):
mesh = TestMeshes.init_mesh(N, 10, 100, lists_to_tensors=True)
mesh = init_mesh(N, 10, 100, lists_to_tensors=True)
if force:
# force mesh to have computed attributes
mesh.verts_packed()
Expand Down Expand Up @@ -686,7 +684,7 @@ def naive_scale_verts(mesh, scale):

def test_extend_list(self):
N = 10
mesh = TestMeshes.init_mesh(5, 10, 100)
mesh = init_mesh(5, 10, 100)
for force in [0, 1]:
if force:
# force some computes to happen
Expand Down Expand Up @@ -721,15 +719,15 @@ def test_extend_list(self):
mesh.extend(N=-1)

def test_to(self):
mesh = TestMeshes.init_mesh(5, 10, 100, device=torch.device("cuda:0"))
mesh = init_mesh(5, 10, 100, device=torch.device("cuda:0"))
device = torch.device("cuda:1")

new_mesh = mesh.to(device)
self.assertTrue(new_mesh.device == device)
self.assertTrue(mesh.device == torch.device("cuda:0"))

def test_split_mesh(self):
mesh = TestMeshes.init_mesh(5, 10, 100)
mesh = init_mesh(5, 10, 100)
split_sizes = [2, 3]
split_meshes = mesh.split(split_sizes)
self.assertTrue(len(split_meshes[0]) == 2)
Expand All @@ -756,9 +754,7 @@ def test_update_padded(self):
N = 10
for lists_to_tensors in (False, True):
for force in (True, False):
mesh = TestMeshes.init_mesh(
N, 100, 300, lists_to_tensors=lists_to_tensors
)
mesh = init_mesh(N, 100, 300, lists_to_tensors=lists_to_tensors)
num_verts_per_mesh = mesh.num_verts_per_mesh()
if force:
# force mesh to have computed attributes
Expand Down Expand Up @@ -1166,7 +1162,7 @@ def test_compute_faces_areas_cpu_cuda(self):
num_meshes = 10
max_v = 100
max_f = 300
mesh_cpu = TestMeshes.init_mesh(num_meshes, max_v, max_f, device="cpu")
mesh_cpu = init_mesh(num_meshes, max_v, max_f, device="cpu")
device = torch.device("cuda:0")
mesh_cuda = mesh_cpu.to(device)

Expand All @@ -1187,7 +1183,7 @@ def test_compute_faces_areas_cpu_cuda(self):
def compute_packed_with_init(
num_meshes: int = 10, max_v: int = 100, max_f: int = 300, device: str = "cpu"
):
mesh = TestMeshes.init_mesh(num_meshes, max_v, max_f, device=device)
mesh = init_mesh(num_meshes, max_v, max_f, device=device)
torch.cuda.synchronize()

def compute_packed():
Expand All @@ -1200,7 +1196,7 @@ def compute_packed():
def compute_padded_with_init(
num_meshes: int = 10, max_v: int = 100, max_f: int = 300, device: str = "cpu"
):
mesh = TestMeshes.init_mesh(num_meshes, max_v, max_f, device=device)
mesh = init_mesh(num_meshes, max_v, max_f, device=device)
torch.cuda.synchronize()

def compute_padded():
Expand Down
Loading

0 comments on commit 0ca839c

Please sign in to comment.