def test_clone(self): """ Check that cloned transformations contain different _matrix objects. Also, the clone of a composed translation and rotation has to be the same as composition of clones of translation and rotation. """ tr = Translate(torch.FloatTensor([[1.0, 2.0, 3.0]])) R = torch.FloatTensor([[0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]]) R = Rotate(R) # check that the _matrix property of clones of # both transforms are different for t in (R, tr): self.assertTrue(t._matrix is not t.clone()._matrix) # check that the _transforms lists of composition of R, tr contain # different objects t1 = Transform3d().compose(R, tr) for t, t_clone in (t1._transforms, t1.clone()._transforms): self.assertTrue(t is not t_clone) self.assertTrue(t._matrix is not t_clone._matrix) # check that all composed transforms are numerically equivalent t2 = Transform3d().compose(R.clone(), tr.clone()) t3 = t1.clone() for t_pair in ((t1, t2), (t1, t3), (t2, t3)): matrix1 = t_pair[0].get_matrix() matrix2 = t_pair[1].get_matrix() self.assertTrue(torch.allclose(matrix1, matrix2))
def test_inverse(self): xyz = torch.tensor([[0.2, 0.3, 0.4], [2.0, 3.0, 4.0]]) t = Translate(xyz) im = t.inverse()._matrix im_2 = t._matrix.inverse() im_comp = t.get_matrix().inverse() self.assertTrue(torch.allclose(im, im_comp)) self.assertTrue(torch.allclose(im, im_2))
def test_inverse(self, batch_size=5): device = torch.device("cuda:0") # generate a random chain of transforms for _ in range(10): # 10 different tries # list of transform matrices ts = [] for i in range(10): choice = float(torch.rand(1)) if choice <= 1.0 / 3.0: t_ = Translate( torch.randn((batch_size, 3), dtype=torch.float32, device=device), device=device, ) elif choice <= 2.0 / 3.0: t_ = Rotate( so3_exponential_map( torch.randn( (batch_size, 3), dtype=torch.float32, device=device, )), device=device, ) else: rand_t = torch.randn((batch_size, 3), dtype=torch.float32, device=device) rand_t = rand_t.sign() * torch.clamp(rand_t.abs(), 0.2) t_ = Scale(rand_t, device=device) ts.append(t_._matrix.clone()) if i == 0: t = t_ else: t = t.compose(t_) # generate the inverse transformation in several possible ways m1 = t.inverse(invert_composed=True).get_matrix() m2 = t.inverse(invert_composed=True)._matrix m3 = t.inverse(invert_composed=False).get_matrix() m4 = t.get_matrix().inverse() # compute the inverse explicitly ... m5 = torch.eye(4, dtype=torch.float32, device=device) m5 = m5[None].repeat(batch_size, 1, 1) for t_ in ts: m5 = torch.bmm(torch.inverse(t_), m5) # assert all same for m in (m1, m2, m3, m4): self.assertTrue(torch.allclose(m, m5, atol=1e-3))
def test_broadcast_transform_normals(self): t1 = Scale(0.1, 0.1, 0.1) N = 10 P = 7 M = 20 x = torch.tensor([0.2] * N) y = torch.tensor([0.3] * N) z = torch.tensor([0.4] * N) tN = Translate(x, y, z) p1 = t1.transform_normals(torch.randn(P, 3)) self.assertTrue(p1.shape == (P, 3)) p2 = t1.transform_normals(torch.randn(1, P, 3)) self.assertTrue(p2.shape == (1, P, 3)) p3 = t1.transform_normals(torch.randn(M, P, 3)) self.assertTrue(p3.shape == (M, P, 3)) p4 = tN.transform_normals(torch.randn(P, 3)) self.assertTrue(p4.shape == (N, P, 3)) p5 = tN.transform_normals(torch.randn(1, P, 3)) self.assertTrue(p5.shape == (N, P, 3))
def test_torch_scalar_grads(self): # Make sure backprop works if we give torch scalars x = torch.tensor(0.2, requires_grad=True) y = torch.tensor(0.3, requires_grad=True) z = torch.tensor(0.4) t = Translate(x, y, z) t._matrix.sum().backward() self.assertTrue(hasattr(x, "grad")) self.assertTrue(hasattr(y, "grad")) self.assertTrue(torch.allclose(x.grad, x.new_ones(x.shape))) self.assertTrue(torch.allclose(y.grad, y.new_ones(y.shape)))
def test_to(self): tr = Translate(torch.FloatTensor([[1.0, 2.0, 3.0]])) R = torch.FloatTensor([[0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]]) R = Rotate(R) t = Transform3d().compose(R, tr) for _ in range(3): t.cpu() t.cuda() t.cuda() t.cpu()
def test_python_scalar(self): t = Translate(0.2, 0.3, 0.4) matrix = torch.tensor( [[ [1.0, 0.0, 0.0, 0], [0.0, 1.0, 0.0, 0], [0.0, 0.0, 1.0, 0], [0.2, 0.3, 0.4, 1], ]], dtype=torch.float32, ) self.assertTrue(torch.allclose(t._matrix, matrix))
def test_broadcast_compose_fail(self): # Cannot compose two transforms which have batch dimensions N and M # other than the case where either N or M is 1 N = 10 M = 20 scale_n = torch.tensor([0.3] * N) tN = Scale(scale_n) x = torch.tensor([0.2] * M) y = torch.tensor([0.3] * M) z = torch.tensor([0.4] * M) tM = Translate(x, y, z) with self.assertRaises(ValueError): t = tN.compose(tM) t.get_matrix()
def test_mixed_broadcast_grad(self): x = 0.2 y = torch.tensor(0.3, requires_grad=True) z = torch.tensor([0.4, 4.0], requires_grad=True) t = Translate(x, y, z) t._matrix.sum().backward() self.assertTrue(hasattr(y, "grad")) self.assertTrue(hasattr(z, "grad")) y_grad = torch.tensor(2.0) z_grad = torch.tensor([1.0, 1.0]) self.assertEqual(y.grad.shape, y_grad.shape) self.assertEqual(z.grad.shape, z_grad.shape) self.assertTrue(torch.allclose(y.grad, y_grad)) self.assertTrue(torch.allclose(z.grad, z_grad))
def test_to(self): tr = Translate(torch.FloatTensor([[1.0, 2.0, 3.0]])) R = torch.FloatTensor([[0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]]) cpu_points = torch.rand(9, 3) cuda_points = cpu_points.cuda() R = Rotate(R) t = Transform3d().compose(R, tr) for _ in range(3): t = t.cpu() t.transform_points(cpu_points) t = t.cuda() t.transform_points(cuda_points) t = t.cuda() t = t.cpu()
def test_mixed_scalars(self): x = 0.2 y = torch.tensor(0.3) z = 0.4 t = Translate(x, y, z) matrix = torch.tensor( [[ [1.0, 0.0, 0.0, 0], [0.0, 1.0, 0.0, 0], [0.0, 0.0, 1.0, 0], [0.2, 0.3, 0.4, 1], ]], dtype=torch.float32, ) self.assertTrue(torch.allclose(t._matrix, matrix))
def test_matrix(self): xyz = torch.tensor([[0.2, 0.3, 0.4], [2.0, 3.0, 4.0]]) t = Translate(xyz) matrix = torch.tensor( [ [ [1.0, 0.0, 0.0, 0], [0.0, 1.0, 0.0, 0], [0.0, 0.0, 1.0, 0], [0.2, 0.3, 0.4, 1], ], [ [1.0, 0.0, 0.0, 0], [0.0, 1.0, 0.0, 0], [0.0, 0.0, 1.0, 0], [2.0, 3.0, 4.0, 1], ], ], dtype=torch.float32, ) self.assertTrue(torch.allclose(t._matrix, matrix))
def test_vector_broadcast(self): x = torch.tensor([0.2, 2.0]) y = torch.tensor([0.3, 3.0]) z = torch.tensor([0.4]) t = Translate(x, y, z) matrix = torch.tensor( [ [ [1.0, 0.0, 0.0, 0], [0.0, 1.0, 0.0, 0], [0.0, 0.0, 1.0, 0], [0.2, 0.3, 0.4, 1], ], [ [1.0, 0.0, 0.0, 0], [0.0, 1.0, 0.0, 0], [0.0, 0.0, 1.0, 0], [2.0, 3.0, 0.4, 1], ], ], dtype=torch.float32, ) self.assertTrue(torch.allclose(t._matrix, matrix))
def test_mixed_broadcast(self): x = 0.2 y = torch.tensor(0.3) z = torch.tensor([0.4, 4.0]) t = Translate(x, y, z) matrix = torch.tensor( [ [ [1.0, 0.0, 0.0, 0], [0.0, 1.0, 0.0, 0], [0.0, 0.0, 1.0, 0], [0.2, 0.3, 0.4, 1], ], [ [1.0, 0.0, 0.0, 0], [0.0, 1.0, 0.0, 0], [0.0, 0.0, 1.0, 0], [0.2, 0.3, 4.0, 1], ], ], dtype=torch.float32, ) self.assertTrue(torch.allclose(t._matrix, matrix))
def test_matrix_extra_args(self): xyz = torch.tensor([[0.2, 0.3, 0.4], [2.0, 3.0, 4.0]]) with self.assertRaises(ValueError): Translate(xyz, xyz[:, 1], xyz[:, 2])
def test_bad_broadcast(self): x = torch.tensor([0.2, 2.0, 20.0]) y = torch.tensor([0.3, 3.0]) z = torch.tensor([0.4]) with self.assertRaises(ValueError): Translate(x, y, z)
def test_to(self): tr = Translate(torch.FloatTensor([[1.0, 2.0, 3.0]])) R = torch.FloatTensor([[0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]]) R = Rotate(R) t = Transform3d().compose(R, tr) cpu_device = torch.device("cpu") cpu_t = t.to("cpu") self.assertEqual(cpu_device, cpu_t.device) self.assertEqual(cpu_device, t.device) self.assertEqual(torch.float32, cpu_t.dtype) self.assertEqual(torch.float32, t.dtype) self.assertIs(t, cpu_t) cpu_t = t.to(cpu_device) self.assertEqual(cpu_device, cpu_t.device) self.assertEqual(cpu_device, t.device) self.assertEqual(torch.float32, cpu_t.dtype) self.assertEqual(torch.float32, t.dtype) self.assertIs(t, cpu_t) cpu_t = t.to(dtype=torch.float64, device=cpu_device) self.assertEqual(cpu_device, cpu_t.device) self.assertEqual(cpu_device, t.device) self.assertEqual(torch.float64, cpu_t.dtype) self.assertEqual(torch.float32, t.dtype) self.assertIsNot(t, cpu_t) cuda_device = torch.device("cuda:0") cuda_t = t.to("cuda:0") self.assertEqual(cuda_device, cuda_t.device) self.assertEqual(cpu_device, t.device) self.assertEqual(torch.float32, cuda_t.dtype) self.assertEqual(torch.float32, t.dtype) self.assertIsNot(t, cuda_t) cuda_t = t.to(cuda_device) self.assertEqual(cuda_device, cuda_t.device) self.assertEqual(cpu_device, t.device) self.assertEqual(torch.float32, cuda_t.dtype) self.assertEqual(torch.float32, t.dtype) self.assertIsNot(t, cuda_t) cuda_t = t.to(dtype=torch.float64, device=cuda_device) self.assertEqual(cuda_device, cuda_t.device) self.assertEqual(cpu_device, t.device) self.assertEqual(torch.float64, cuda_t.dtype) self.assertEqual(torch.float32, t.dtype) self.assertIsNot(t, cuda_t) cpu_points = torch.rand(9, 3) cuda_points = cpu_points.cuda() for _ in range(3): t = t.cpu() t.transform_points(cpu_points) t = t.cuda() t.transform_points(cuda_points) t = t.cuda() t = t.cpu()