def test_so3_log_singularity(self, batch_size: int = 100): """ Tests whether the `so3_log_map` is robust to the input matrices who's rotation angles are close to the numerically unstable region (i.e. matrices with low rotation angles). """ # generate random rotations with a tiny angle device = torch.device("cuda:0") identity = torch.eye(3, device=device) rot180 = identity * torch.tensor([[1.0, -1.0, -1.0]], device=device) r = [identity, rot180] # add random rotations and random almost orthonormal matrices r.extend([ qr(identity + torch.randn_like(identity) * 1e-4)[0] + float(i > batch_size // 2) * (0.5 - torch.rand_like(identity)) * 1e-3 # this adds random noise to the second half # of the random orthogonal matrices to generate # near-orthogonal matrices for i in range(batch_size - 2) ]) r = torch.stack(r) r.requires_grad = True # the log of the rotation matrix r r_log = so3_log_map(r, cos_bound=1e-4, eps=1e-2) # tests whether all outputs are finite self.assertTrue(torch.isfinite(r_log).all()) # tests whether the gradient is not None and all finite loss = r.sum() loss.backward() self.assertIsNotNone(r.grad) self.assertTrue(torch.isfinite(r.grad).all())
def init_rot(batch_size: int = 10): """ Randomly generate a batch of `batch_size` 3x3 rotation matrices. """ device = torch.device("cuda:0") # TODO(dnovotny): replace with random_rotation from random_rotation.py rot = [] for _ in range(batch_size): r = qr(torch.randn((3, 3), device=device))[0] f = torch.randint(2, (3, ), device=device, dtype=torch.float32) if f.sum() % 2 == 0: f = 1 - f rot.append(r * (2 * f - 1).float()) rot = torch.stack(rot) return rot
def test_se3_log_singularity(self, batch_size: int = 100): """ Tests whether the `se3_log_map` is robust to the input matrices whose rotation angles and translations are close to the numerically unstable region (i.e. matrices with low rotation angles and 0 translation). """ # generate random rotations with a tiny angle device = torch.device("cuda:0") identity = torch.eye(3, device=device) rot180 = identity * torch.tensor([[1.0, -1.0, -1.0]], device=device) r = [identity, rot180] r.extend([ qr(identity + torch.randn_like(identity) * 1e-6)[0] + float(i > batch_size // 2) * (0.5 - torch.rand_like(identity)) * 1e-8 # this adds random noise to the second half # of the random orthogonal matrices to generate # near-orthogonal matrices for i in range(batch_size - 2) ]) r = torch.stack(r) # tiny translations t = torch.randn(batch_size, 3, dtype=r.dtype, device=device) * 1e-6 # create the transform matrix transform = torch.zeros(batch_size, 4, 4, dtype=torch.float32, device=device) transform[:, :3, :3] = r transform[:, 3, :3] = t transform[:, 3, 3] = 1.0 transform.requires_grad = True # the log of the transform log_transform = se3_log_map(transform, eps=1e-4, cos_bound=1e-4) # tests whether all outputs are finite self.assertTrue(torch.isfinite(log_transform).all()) # tests whether all gradients are finite and not None loss = log_transform.sum() loss.backward() self.assertIsNotNone(transform.grad) self.assertTrue(torch.isfinite(transform.grad).all())
def test_so3_cos_bound(self, batch_size: int = 100): """ Checks that for an identity rotation `R=I`, the so3_rotation_angle returns non-finite gradients when `cos_bound=None` and finite gradients for `cos_bound > 0.0`. """ # generate random rotations with a tiny angle to generate cases # with the gradient singularity device = torch.device("cuda:0") identity = torch.eye(3, device=device) rot180 = identity * torch.tensor([[1.0, -1.0, -1.0]], device=device) r = [identity, rot180] r.extend([ qr(identity + torch.randn_like(identity) * 1e-4)[0] for _ in range(batch_size - 2) ]) r = torch.stack(r) r.requires_grad = True for is_grad_finite in (True, False): # clear the gradients and decide the cos_bound: # for is_grad_finite we run so3_rotation_angle with cos_bound # set to a small float, otherwise we set to 0.0 r.grad = None cos_bound = 1e-4 if is_grad_finite else 0.0 # compute the angles of r angles = so3_rotation_angle(r, cos_bound=cos_bound) # tests whether all outputs are finite in both cases self.assertTrue(torch.isfinite(angles).all()) # compute the gradients loss = angles.sum() loss.backward() # tests whether the gradient is not None for both cases self.assertIsNotNone(r.grad) if is_grad_finite: # all grad values have to be finite self.assertTrue(torch.isfinite(r.grad).all())