Return cartesian vectors, after rotation by specified angles about
    each degree of freedom.

    Parameters
    ----------
    a: array, shape (n, d)
        Input d-dimensional cartesian vectors, left unchanged.
    th: array, shape (n, m)
        Angles by which to rotate about each m rotational degree of freedom
        (m=1 in 2 dimensions, m=3 in 3 dimensions).

    Returns
    -------
    ar: array, shape of a
        Rotated cartesian vectors.

def rotate(a, th):
    """Return cartesian vectors, after rotation by specified angles about
    each degree of freedom.

    Parameters
    ----------
    a: array, shape (n, d)
        Input d-dimensional cartesian vectors, left unchanged.
    th: array, shape (n, m)
        Angles by which to rotate about each m rotational degree of freedom
        (m=1 in 2 dimensions, m=3 in 3 dimensions).

    Returns
    -------
    ar: array, shape of a
        Rotated cartesian vectors.
    """
    return np.sum(a[..., np.newaxis] * R_rot(th), axis=-2)