Skip to content

Checks: screws.testing

Helpers for checking your own implementation against the library's.

The course pattern: write the function yourself, then compare it with the screws version on random inputs. Nothing here does robotics; it makes the comparison short to write.

random_rotation

random_rotation(rng=None)

A random rotation matrix: the exponential of a random axis-angle with theta in [0, pi).

Source code in screws/testing.py
23
24
25
26
27
28
def random_rotation(rng=None) -> np.ndarray:
    """A random rotation matrix: the exponential of a random axis-angle with theta in [0, pi)."""
    rng = _rng(rng)
    axis = rng.normal(size=3)
    axis /= np.linalg.norm(axis)
    return exp3(vec_to_so3(axis * rng.uniform(0.0, np.pi)))

random_transform

random_transform(rng=None, *, scale=1.0)

A random homogeneous transformation: random rotation, position uniform in [-scale, scale]^3.

Source code in screws/testing.py
31
32
33
34
def random_transform(rng=None, *, scale: float = 1.0) -> np.ndarray:
    """A random homogeneous transformation: random rotation, position uniform in [-scale, scale]^3."""
    rng = _rng(rng)
    return rp_to_transform(random_rotation(rng), rng.uniform(-scale, scale, size=3))

random_theta

random_theta(robot, rng=None)

A random joint vector within robot.joint_limits; [-pi, pi) for joints without a finite bound.

Source code in screws/testing.py
37
38
39
40
41
42
43
44
def random_theta(robot, rng=None) -> np.ndarray:
    """A random joint vector within robot.joint_limits; [-pi, pi) for joints without a finite bound."""
    rng = _rng(rng)
    if robot.joint_limits is None:
        return rng.uniform(-np.pi, np.pi, size=robot.n)
    lo = np.where(np.isfinite(robot.joint_limits[:, 0]), robot.joint_limits[:, 0], -np.pi)
    hi = np.where(np.isfinite(robot.joint_limits[:, 1]), robot.joint_limits[:, 1], np.pi)
    return rng.uniform(lo, hi)

assert_close

assert_close(a, b, *, atol=1e-09, what='arrays')

Assert a and b agree to atol, or fail naming the largest discrepancy and where it is.

Source code in screws/testing.py
47
48
49
50
51
52
53
54
55
56
57
58
def assert_close(a, b, *, atol: float = 1e-9, what: str = "arrays") -> None:
    """Assert a and b agree to atol, or fail naming the largest discrepancy and where it is."""
    a, b = np.asarray(a, dtype=float), np.asarray(b, dtype=float)
    if a.shape != b.shape:
        raise AssertionError(f"{what} differ in shape: {a.shape} vs {b.shape}")
    diff = np.abs(a - b)
    if diff.size and diff.max() > atol:
        idx = np.unravel_index(int(np.argmax(diff)), diff.shape)
        raise AssertionError(
            f"{what} differ by {diff.max():.3e} at index {tuple(int(i) for i in idx)}: "
            f"{a[idx]!r} vs {b[idx]!r} (atol {atol:g})"
        )

check

check(mine, reference, cases, *, atol=1e-09)

Run mine and reference on each case and fail on the first disagreement.

A tuple case is unpacked as positional arguments; anything else is the single argument.

Source code in screws/testing.py
61
62
63
64
65
66
67
68
def check(mine: Callable, reference: Callable, cases: Iterable, *, atol: float = 1e-9) -> None:
    """Run mine and reference on each case and fail on the first disagreement.

    A tuple case is unpacked as positional arguments; anything else is the single argument.
    """
    for k, case in enumerate(cases):
        args = case if isinstance(case, tuple) else (case,)
        assert_close(mine(*args), reference(*args), atol=atol, what=f"case {k}: mine vs reference")