avoid running tests twice

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
This commit is contained in:
Jeremy Reizenstein 2021-05-07 05:03:10 -07:00 committed by Facebook GitHub Bot
parent e3624b4e9d
commit 0ca839cc32
4 changed files with 165 additions and 171 deletions

View File

@ -6,7 +6,7 @@ import torch
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):
@ -92,7 +92,7 @@ class TestMeshEdgeLoss(TestCaseMixin, unittest.TestCase):
@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():

View File

@ -10,128 +10,126 @@ from common_testing import TestCaseMixin
from pytorch3d.structures.meshes import Meshes
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)
if lists_to_tensors:
verts_list = torch.stack(verts_list)
faces_list = torch.stack(faces_list)
return Meshes(verts=verts_list, faces=faces_list)
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)
class TestMeshes(TestCaseMixin, unittest.TestCase):
def setUp(self) -> None:
np.random.seed(42)
torch.manual_seed(42)
@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)
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)
if lists_to_tensors:
verts_list = torch.stack(verts_list)
faces_list = torch.stack(faces_list)
return Meshes(verts=verts_list, faces=faces_list)
@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)
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]))
@ -168,7 +166,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
# 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
@ -192,7 +190,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
# 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()
@ -361,7 +359,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
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
@ -386,7 +384,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
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
@ -425,7 +423,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
# 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()
@ -443,7 +441,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
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]):
@ -577,7 +575,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
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()
@ -686,7 +684,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
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
@ -721,7 +719,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
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)
@ -729,7 +727,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
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)
@ -756,9 +754,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
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
@ -1166,7 +1162,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
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)
@ -1187,7 +1183,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
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():
@ -1200,7 +1196,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
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():

View File

@ -33,41 +33,41 @@ DEBUG = False
DATA_DIR = get_tests_dir() / "data"
def init_meshes(
num_meshes: int = 10,
num_verts: int = 1000,
num_faces: int = 3000,
device: str = "cpu",
add_texture: bool = False,
):
device = torch.device(device)
verts_list = []
faces_list = []
texts_list = []
for _ in range(num_meshes):
verts = torch.rand((num_verts, 3), dtype=torch.float32, device=device)
faces = torch.randint(
num_verts, size=(num_faces, 3), dtype=torch.int64, device=device
)
texts = torch.rand((num_verts, 3), dtype=torch.float32, device=device)
verts_list.append(verts)
faces_list.append(faces)
texts_list.append(texts)
# create textures
textures = None
if add_texture:
textures = TexturesVertex(texts_list)
meshes = Meshes(verts=verts_list, faces=faces_list, textures=textures)
return meshes
class TestSamplePoints(TestCaseMixin, unittest.TestCase):
def setUp(self) -> None:
super().setUp()
torch.manual_seed(1)
@staticmethod
def init_meshes(
num_meshes: int = 10,
num_verts: int = 1000,
num_faces: int = 3000,
device: str = "cpu",
add_texture: bool = False,
):
device = torch.device(device)
verts_list = []
faces_list = []
texts_list = []
for _ in range(num_meshes):
verts = torch.rand((num_verts, 3), dtype=torch.float32, device=device)
faces = torch.randint(
num_verts, size=(num_faces, 3), dtype=torch.int64, device=device
)
texts = torch.rand((num_verts, 3), dtype=torch.float32, device=device)
verts_list.append(verts)
faces_list.append(faces)
texts_list.append(texts)
# create textures
textures = None
if add_texture:
textures = TexturesVertex(texts_list)
meshes = Meshes(verts=verts_list, faces=faces_list, textures=textures)
return meshes
def test_all_empty_meshes(self):
"""
Check sample_points_from_meshes raises an exception if all meshes are
@ -298,9 +298,7 @@ class TestSamplePoints(TestCaseMixin, unittest.TestCase):
def test_outputs(self):
for add_texture in (True, False):
meshes = TestSamplePoints.init_meshes(
device=torch.device("cuda:0"), add_texture=add_texture
)
meshes = init_meshes(device=torch.device("cuda:0"), add_texture=add_texture)
out1 = sample_points_from_meshes(meshes, num_samples=100)
self.assertTrue(torch.is_tensor(out1))

View File

@ -15,7 +15,7 @@ from pytorch3d.renderer.mesh.textures import (
pack_rectangles,
)
from pytorch3d.structures import Meshes, list_to_packed, packed_to_list
from test_meshes import TestMeshes
from test_meshes import init_mesh
def tryindex(self, index, tex, meshes, source):
@ -173,7 +173,7 @@ class TestTexturesVertex(TestCaseMixin, unittest.TestCase):
def test_extend(self):
B = 10
mesh = TestMeshes.init_mesh(B, 30, 50)
mesh = init_mesh(B, 30, 50)
V = mesh._V
tex_uv = TexturesVertex(verts_features=torch.randn((B, V, 3)))
tex_mesh = Meshes(
@ -379,7 +379,7 @@ class TestTexturesAtlas(TestCaseMixin, unittest.TestCase):
def test_extend(self):
B = 10
mesh = TestMeshes.init_mesh(B, 30, 50)
mesh = init_mesh(B, 30, 50)
F = mesh._F
tex_uv = TexturesAtlas(atlas=torch.randn((B, F, 2, 2, 3)))
tex_mesh = Meshes(
@ -667,7 +667,7 @@ class TestTexturesUV(TestCaseMixin, unittest.TestCase):
def test_extend(self):
B = 5
mesh = TestMeshes.init_mesh(B, 30, 50)
mesh = init_mesh(B, 30, 50)
V = mesh._V
num_faces = mesh.num_faces_per_mesh()
num_verts = mesh.num_verts_per_mesh()