.. note:: :class: sphx-glr-download-link-note Click :ref:`here ` to download the full example code .. rst-class:: sphx-glr-example-title .. _sphx_glr_examples_rotation3d.py: Rotation in 3D Space ==================== This tutorial demonstrates how rotate a group of points in 3D space. To begin with, we must understand that rotation is a linear transformation. The set of all rotations forms the group SO(3). It is a Lie group that each group element is described by an orthogonal 3x3 matrix. That is, if you have two points :math:`\vec{r}_1` and :math:`\vec{r}_2`, and you want to rotate the two points along the same axis for the same number of degrees, then there is a single orthogonal matrix :math:`R` that no matter the value of :math:`\vec{r}_1` and :math:`\vec{r}_2`, their rotation is always :math:`R\cdot\vec{r}_1` and :math:`R\cdot\vec{r}_2`. Let's import libraries first .. code-block:: default import math import torch import pytest import sys import nnp.so3 as so3 Let's first take a look at a special case: rotating the three unit vectors ``(1, 0, 0)``, ``(0, 1, 0)`` and ``(0, 0, 1)`` along the diagonal for 120 degree, this should permute these points: .. code-block:: default rx = torch.tensor([1, 0, 0]) ry = torch.tensor([0, 1, 0]) rz = torch.tensor([0, 0, 1]) points = torch.stack([rx, ry, rz]) Now let's compute the rotation matrix .. code-block:: default axis = torch.ones(3) / math.sqrt(3) * (math.pi * 2 / 3) R = so3.rotate_along(axis) Note that we need to do matrix-vector product for all these unit vectors so we need to do a transpose in order to use ``@`` operator. .. code-block:: default rotated = (R @ points.float().t()).t() print(rotated) .. rst-class:: sphx-glr-script-out Out: .. code-block:: none tensor([[0.0000e+00, 1.0000e+00, 3.5535e-09], [0.0000e+00, 0.0000e+00, 1.0000e+00], [1.0000e+00, 1.8456e-09, 1.8456e-09]]) After this rotation, the three vector permutes: rx->ry, ry->rz, rz->rx. Let's programmically check that. This check will be run by pytest later. .. code-block:: default def test_rotated_unit_vectors(): expected = torch.stack([ry, rz, rx]).float() assert torch.allclose(rotated, expected, atol=1e-5) Now let's run all the tests .. code-block:: default if __name__ == '__main__': pytest.main([sys.argv[0], '-v']) .. rst-class:: sphx-glr-script-out Out: .. code-block:: none ============================= test session starts ============================== platform linux -- Python 3.7.5, pytest-5.3.0, py-1.8.0, pluggy-0.13.1 -- /opt/hostedtoolcache/Python/3.7.5/x64/bin/python cachedir: .pytest_cache rootdir: /home/runner/work/nnp/nnp, inifile: setup.cfg collecting ... collected 1 item rotation3d.py::test_rotated_unit_vectors PASSED [100%] ============================== 1 passed in 0.01s =============================== .. rst-class:: sphx-glr-timing **Total running time of the script:** ( 0 minutes 0.699 seconds) .. _sphx_glr_download_examples_rotation3d.py: .. only :: html .. container:: sphx-glr-footer :class: sphx-glr-footer-example .. container:: sphx-glr-download :download:`Download Python source code: rotation3d.py ` .. container:: sphx-glr-download :download:`Download Jupyter notebook: rotation3d.ipynb ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_