diff --git a/pytransform3d/rotations/__init__.py b/pytransform3d/rotations/__init__.py index 59dc7bc49..539f4afa9 100644 --- a/pytransform3d/rotations/__init__.py +++ b/pytransform3d/rotations/__init__.py @@ -9,11 +9,14 @@ vector_projection, perpendicular_to_vectors, norm_axis_angle, norm_compact_axis_angle, norm_matrix, plane_basis_from_normal, - random_vector, random_axis_angle, random_compact_axis_angle, - random_quaternion, check_skew_symmetric_matrix, check_matrix, check_quaternion, check_quaternions, check_axis_angle, check_compact_axis_angle, check_rotor, check_mrp) +from ._random import ( + random_vector, + random_axis_angle, + random_compact_axis_angle, + random_quaternion) from ._conversions import ( quaternion_from_axis_angle, quaternion_from_compact_axis_angle, quaternion_from_matrix, quaternion_wxyz_from_xyzw, diff --git a/pytransform3d/rotations/_random.py b/pytransform3d/rotations/_random.py new file mode 100644 index 000000000..fd44cc244 --- /dev/null +++ b/pytransform3d/rotations/_random.py @@ -0,0 +1,86 @@ +import numpy as np + +from ._utils import norm_vector + + +def random_vector(rng=np.random.default_rng(0), n=3): + r"""Generate an nd vector with normally distributed components. + + Each component will be sampled from :math:`\mathcal{N}(\mu=0, \sigma=1)`. + + Parameters + ---------- + rng : np.random.Generator, optional (default: random seed 0) + Random number generator + + n : int, optional (default: 3) + Number of vector components + + Returns + ------- + v : array, shape (n,) + Random vector + """ + return rng.standard_normal(size=n) + + +def random_axis_angle(rng=np.random.default_rng(0)): + r"""Generate random axis-angle. + + The angle will be sampled uniformly from the interval :math:`[0, \pi)` + and each component of the rotation axis will be sampled from + :math:`\mathcal{N}(\mu=0, \sigma=1)` and then the axis will be normalized + to length 1. + + Parameters + ---------- + rng : np.random.Generator, optional (default: random seed 0) + Random number generator + + Returns + ------- + a : array, shape (4,) + Axis of rotation and rotation angle: (x, y, z, angle) + """ + angle = np.pi * rng.random() + a = np.array([0, 0, 0, angle]) + a[:3] = norm_vector(rng.standard_normal(size=3)) + return a + + +def random_compact_axis_angle(rng=np.random.default_rng(0)): + r"""Generate random compact axis-angle. + + The angle will be sampled uniformly from the interval :math:`[0, \pi)` + and each component of the rotation axis will be sampled from + :math:`\mathcal{N}(\mu=0, \sigma=1)` and then the axis will be normalized + to length 1. + + Parameters + ---------- + rng : np.random.Generator, optional (default: random seed 0) + Random number generator + + Returns + ------- + a : array, shape (3,) + Axis of rotation and rotation angle: angle * (x, y, z) + """ + a = random_axis_angle(rng) + return a[:3] * a[3] + + +def random_quaternion(rng=np.random.default_rng(0)): + """Generate random quaternion. + + Parameters + ---------- + rng : np.random.Generator, optional (default: random seed 0) + Random number generator + + Returns + ------- + q : array, shape (4,) + Unit quaternion to represent rotation: (w, x, y, z) + """ + return norm_vector(rng.standard_normal(size=4)) diff --git a/pytransform3d/rotations/_random.pyi b/pytransform3d/rotations/_random.pyi new file mode 100644 index 000000000..59df9af25 --- /dev/null +++ b/pytransform3d/rotations/_random.pyi @@ -0,0 +1,14 @@ +import numpy as np +import numpy.typing as npt + + +def random_vector(rng: np.random.Generator = ..., n: int = ...) -> np.ndarray: ... + + +def random_axis_angle(rng: np.random.Generator = ...) -> np.ndarray: ... + + +def random_compact_axis_angle(rng: np.random.Generator = ...) -> np.ndarray: ... + + +def random_quaternion(rng: np.random.Generator = ...) -> np.ndarray: ... diff --git a/pytransform3d/rotations/_utils.py b/pytransform3d/rotations/_utils.py index 815ff4cb4..ac3a4a95d 100644 --- a/pytransform3d/rotations/_utils.py +++ b/pytransform3d/rotations/_utils.py @@ -293,89 +293,6 @@ def plane_basis_from_normal(plane_normal): return x_axis, y_axis -def random_vector(rng=np.random.default_rng(0), n=3): - r"""Generate an nd vector with normally distributed components. - - Each component will be sampled from :math:`\mathcal{N}(\mu=0, \sigma=1)`. - - Parameters - ---------- - rng : np.random.Generator, optional (default: random seed 0) - Random number generator - - n : int, optional (default: 3) - Number of vector components - - Returns - ------- - v : array, shape (n,) - Random vector - """ - return rng.standard_normal(size=n) - - -def random_axis_angle(rng=np.random.default_rng(0)): - r"""Generate random axis-angle. - - The angle will be sampled uniformly from the interval :math:`[0, \pi)` - and each component of the rotation axis will be sampled from - :math:`\mathcal{N}(\mu=0, \sigma=1)` and then the axis will be normalized - to length 1. - - Parameters - ---------- - rng : np.random.Generator, optional (default: random seed 0) - Random number generator - - Returns - ------- - a : array, shape (4,) - Axis of rotation and rotation angle: (x, y, z, angle) - """ - angle = np.pi * rng.random() - a = np.array([0, 0, 0, angle]) - a[:3] = norm_vector(rng.standard_normal(size=3)) - return a - - -def random_compact_axis_angle(rng=np.random.default_rng(0)): - r"""Generate random compact axis-angle. - - The angle will be sampled uniformly from the interval :math:`[0, \pi)` - and each component of the rotation axis will be sampled from - :math:`\mathcal{N}(\mu=0, \sigma=1)` and then the axis will be normalized - to length 1. - - Parameters - ---------- - rng : np.random.Generator, optional (default: random seed 0) - Random number generator - - Returns - ------- - a : array, shape (3,) - Axis of rotation and rotation angle: angle * (x, y, z) - """ - a = random_axis_angle(rng) - return a[:3] * a[3] - - -def random_quaternion(rng=np.random.default_rng(0)): - """Generate random quaternion. - - Parameters - ---------- - rng : np.random.Generator, optional (default: random seed 0) - Random number generator - - Returns - ------- - q : array, shape (4,) - Unit quaternion to represent rotation: (w, x, y, z) - """ - return norm_vector(rng.standard_normal(size=4)) - - def check_skew_symmetric_matrix(V, tolerance=1e-6, strict_check=True): """Input validation of a skew-symmetric matrix. diff --git a/pytransform3d/rotations/_utils.pyi b/pytransform3d/rotations/_utils.pyi index f4dfb8923..be099814d 100644 --- a/pytransform3d/rotations/_utils.pyi +++ b/pytransform3d/rotations/_utils.pyi @@ -34,19 +34,6 @@ def plane_basis_from_normal( plane_normal: npt.ArrayLike) -> Tuple[np.ndarray, np.ndarray]: ... - -def random_vector(rng: np.random.Generator = ..., n: int = ...) -> np.ndarray: ... - - -def random_axis_angle(rng: np.random.Generator = ...) -> np.ndarray: ... - - -def random_compact_axis_angle(rng: np.random.Generator = ...) -> np.ndarray: ... - - -def random_quaternion(rng: np.random.Generator = ...) -> np.ndarray: ... - - def check_skew_symmetric_matrix(V: npt.ArrayLike, tolerance: float = ..., strict_check: bool = ...) -> np.ndarray: ...