Skip to content

Commit 8c9f35f

Browse files
committed
test: add regression coverage for trlog near-identity division
trlog's general-case branch divides by sin(theta), computed from trace(R) via acos. The only existing test used R = np.eye(3) exactly, never a numerically near-identity matrix as produced by real computation -- the case #63 (unmerged) reported hitting a divide-by-zero on. Confirmed against the pre-a9fc08a code (rework code to be more robust to nearly identity rotation matrix) that this exact input raised FloatingPointError there; current code's crisp `st == 0` guard handles it cleanly.
1 parent 8b83c1f commit 8c9f35f

1 file changed

Lines changed: 28 additions & 0 deletions

File tree

tests/base/test_transforms3d.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,34 @@ def test_trlog(self):
319319
T = transl(1, 2, 3) @ rpy2tr(0.1, 0.2, 0.3)
320320
nt.assert_array_almost_equal(logm(T), trlog(T))
321321

322+
def test_trlog_near_identity(self):
323+
# Regression test for #63: trlog's general-case branch divides by
324+
# sin(theta), computed from trace(R) via acos. A near-identity R
325+
# that isn't *exactly* np.eye(3) (as produced by real computation,
326+
# not hand-constructed) must not raise or blow up.
327+
328+
# theta small enough that cos(theta) underflows to exactly 1.0 in
329+
# float64, so trace(R) rounds to exactly 3 and acos(1.0) == 0.0
330+
# exactly: this is the case that used to divide by sin(theta) == 0
331+
# with no guard. R itself is not bitwise np.eye(3) (it still has
332+
# sin(theta) noise off the diagonal), so this also exercises the
333+
# "is this actually the identity" branch on a matrix that isn't one.
334+
R = rotx(1e-9)
335+
assert not np.array_equal(R, np.eye(3))
336+
nt.assert_array_almost_equal(trlog(R, twist=True), [0, 0, 0])
337+
nt.assert_array_almost_equal(trlog(R), skew([0, 0, 0]))
338+
339+
# theta small enough to be well within the near-identity regime, but
340+
# not small enough to clamp: the general-case formula must stay
341+
# accurate here rather than falling back to a coarse zero. This is
342+
# the case a fuzzy identity-tolerance (checking R against I before
343+
# computing theta, as originally proposed in #63) risks getting
344+
# wrong in the other direction, by discarding a real small rotation.
345+
theta = 1e-7
346+
for R in (rotx(theta), roty(theta), rotz(theta)):
347+
v = trlog(R, twist=True)
348+
nt.assert_almost_equal(np.linalg.norm(v), theta, decimal=12)
349+
322350
def test_trexp(self):
323351
R = trexp(skew([0.5, 0, 0]))
324352
nt.assert_array_almost_equal(R, rotx(0.5))

0 commit comments

Comments
 (0)