{"id":6995,"date":"2021-08-02T14:47:34","date_gmt":"2021-08-02T13:47:34","guid":{"rendered":"https:\/\/www.blopig.com\/blog\/?p=6995"},"modified":"2021-08-03T16:12:52","modified_gmt":"2021-08-03T15:12:52","slug":"uniformly-sampled-3d-rotation-matrices","status":"publish","type":"post","link":"https:\/\/www.blopig.com\/blog\/2021\/08\/uniformly-sampled-3d-rotation-matrices\/","title":{"rendered":"Uniformly sampled 3D rotation matrices"},"content":{"rendered":"\n<p><strong>It&#8217;s not as simple as you&#8217;d think.<\/strong><\/p>\n\n\n\n<p><strong>If you want to skip the small talk, the code is at the bottom. <\/strong>Sampling 2D rotations uniformly is simple: rotate by an angle from the uniform distribution <img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/s0.wp.com\/latex.php?latex=%5Ctheta+%5Csim+U%280%2C+2%5Cpi%29&#038;bg=ffffff&#038;fg=000&#038;s=0&#038;c=20201002\" alt=\"&#92;theta &#92;sim U(0, 2&#92;pi)\" class=\"latex\" \/>. Extending this idea to 3D rotations, we could sample each of the three Euler angles from the same uniform distribution <img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/s0.wp.com\/latex.php?latex=%5Cphi%2C+%5Ctheta%2C+%5Cpsi+%5Csim+U%280%2C+2%5Cpi%29&#038;bg=ffffff&#038;fg=000&#038;s=0&#038;c=20201002\" alt=\"&#92;phi, &#92;theta, &#92;psi &#92;sim U(0, 2&#92;pi)\" class=\"latex\" \/>. This, however, gives more probability density to transformations which are clustered towards the poles:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/i0.wp.com\/www.blopig.com\/blog\/wp-content\/uploads\/2021\/08\/sphere.jpg?ssl=1\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"480\" height=\"360\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/www.blopig.com\/blog\/wp-content\/uploads\/2021\/08\/sphere.jpg?resize=480%2C360&#038;ssl=1\" alt=\"\" class=\"wp-image-7235\" srcset=\"https:\/\/i0.wp.com\/www.blopig.com\/blog\/wp-content\/uploads\/2021\/08\/sphere.jpg?w=480&amp;ssl=1 480w, https:\/\/i0.wp.com\/www.blopig.com\/blog\/wp-content\/uploads\/2021\/08\/sphere.jpg?resize=300%2C225&amp;ssl=1 300w\" sizes=\"auto, (max-width: 480px) 100vw, 480px\" \/><\/a><figcaption>Sampling Euler angles uniformly does not give an even distribution across the sphere.<\/figcaption><\/figure><\/div>\n\n\n\n<p>In <a href=\"http:\/\/citeseerx.ist.psu.edu\/viewdoc\/download?doi=10.1.1.53.1357&amp;rep=rep1&amp;type=pdf\">Fast Random Rotation Matrices (James Avro, 1992)<\/a>, a method for uniform random 3D rotation matrices is outlined, the main steps being:<\/p>\n\n\n\n<!--more-->\n\n\n\n<ol class=\"wp-block-list\"><li>A random rotation about the <em>z<\/em> axis<\/li><li>Rotate the (unmoved) <em>z<\/em> unit vector to a random position on the unit sphere<\/li><\/ol>\n\n\n\n<p>The first of these is a simple 2D rotation matrix with a random angle, parameterised by  <img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/s0.wp.com\/latex.php?latex=x_1+%5Csim+U%280%2C+1%29&#038;bg=ffffff&#038;fg=000&#038;s=0&#038;c=20201002\" alt=\"x_1 &#92;sim U(0, 1)\" class=\"latex\" \/><\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/i0.wp.com\/www.blopig.com\/blog\/wp-content\/uploads\/2021\/08\/image-2.png?ssl=1\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"327\" height=\"136\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/www.blopig.com\/blog\/wp-content\/uploads\/2021\/08\/image-2.png?resize=327%2C136&#038;ssl=1\" alt=\"\" class=\"wp-image-7238\" srcset=\"https:\/\/i0.wp.com\/www.blopig.com\/blog\/wp-content\/uploads\/2021\/08\/image-2.png?w=327&amp;ssl=1 327w, https:\/\/i0.wp.com\/www.blopig.com\/blog\/wp-content\/uploads\/2021\/08\/image-2.png?resize=300%2C125&amp;ssl=1 300w\" sizes=\"auto, (max-width: 327px) 100vw, 327px\" \/><\/a><figcaption>J. Avro, 1992<\/figcaption><\/figure><\/div>\n\n\n\n<p>The second point is more tricky: it uses the Householder matrix with certain properties, given by:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/i0.wp.com\/www.blopig.com\/blog\/wp-content\/uploads\/2021\/08\/image.png?ssl=1\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"206\" height=\"129\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/www.blopig.com\/blog\/wp-content\/uploads\/2021\/08\/image.png?resize=206%2C129&#038;ssl=1\" alt=\"\" class=\"wp-image-7236\"\/><\/a><figcaption>J. Avro, 1992<\/figcaption><\/figure><\/div>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/i0.wp.com\/www.blopig.com\/blog\/wp-content\/uploads\/2021\/08\/image-1.png?ssl=1\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"121\" height=\"29\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/www.blopig.com\/blog\/wp-content\/uploads\/2021\/08\/image-1.png?resize=121%2C29&#038;ssl=1\" alt=\"\" class=\"wp-image-7237\"\/><\/a><figcaption>J. Avro, 1992<\/figcaption><\/figure><\/div>\n\n\n\n<p>where <img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/s0.wp.com\/latex.php?latex=x_2&#038;bg=ffffff&#038;fg=000&#038;s=0&#038;c=20201002\" alt=\"x_2\" class=\"latex\" \/> and <img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/s0.wp.com\/latex.php?latex=x_3&#038;bg=ffffff&#038;fg=000&#038;s=0&#038;c=20201002\" alt=\"x_3\" class=\"latex\" \/> are also drawn from <img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/s0.wp.com\/latex.php?latex=U%280%2C+1%29&#038;bg=ffffff&#038;fg=000&#038;s=0&#038;c=20201002\" alt=\"U(0, 1)\" class=\"latex\" \/>. The final, completed random 3D rotation matrix is then (including a reflection through the origin to turn the Householder matrix transformation from a reflection into a rotation):<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/i0.wp.com\/www.blopig.com\/blog\/wp-content\/uploads\/2021\/08\/image-3.png?ssl=1\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"105\" height=\"30\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/www.blopig.com\/blog\/wp-content\/uploads\/2021\/08\/image-3.png?resize=105%2C30&#038;ssl=1\" alt=\"\" class=\"wp-image-7239\"\/><\/a><figcaption>J. Avro, 1992<\/figcaption><\/figure><\/div>\n\n\n\n<p>I have included a python implementation which rotates any vector about the origin with the uniform 3D rotation matrix:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import numpy as np\n\ndef uniform_random_rotation(x):\n    \"\"\"Apply a random rotation in 3D, with a distribution uniform over the\n    sphere.\n\n    Arguments:\n        x: vector or set of vectors with dimension (n, 3), where n is the\n            number of vectors\n\n    Returns:\n        Array of shape (n, 3) containing the randomly rotated vectors of x,\n        about the mean coordinate of x.\n\n    Algorithm taken from \"Fast Random Rotation Matrices\" (James Avro, 1992):\n    https:\/\/doi.org\/10.1016\/B978-0-08-050755-2.50034-8\n    \"\"\"\n\n    def generate_random_z_axis_rotation():\n        \"\"\"Generate random rotation matrix about the z axis.\"\"\"\n        R = np.eye(3)\n        x1 = np.random.rand()\n        R[0, 0] = R[1, 1] = np.cos(2 * np.pi * x1)\n        R[0, 1] = -np.sin(2 * np.pi * x1)\n        R[1, 0] = np.sin(2 * np.pi * x1)\n        return R\n\n    # There are two random variables in [0, 1) here (naming is same as paper)\n    x2 = 2 * np.pi * np.random.rand()\n    x3 = np.random.rand()\n\n    # Rotation of all points around x axis using matrix\n    R = generate_random_z_axis_rotation()\n    v = np.array([\n        np.cos(x2) * np.sqrt(x3),\n        np.sin(x2) * np.sqrt(x3),\n        np.sqrt(1 - x3)\n    ])\n    H = np.eye(3) - (2 * np.outer(v, v))\n    M = -(H @ R)\n    x = x.reshape((-1, 3))\n    mean_coord = np.mean(x, axis=0)\n    return ((x - mean_coord) @ M) + mean_coord @ M<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>It&#8217;s not as simple as you&#8217;d think. If you want to skip the small talk, the code is at the bottom. Sampling 2D rotations uniformly is simple: rotate by an angle from the uniform distribution . Extending this idea to 3D rotations, we could sample each of the three Euler angles from the same uniform [&hellip;]<\/p>\n","protected":false},"author":61,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"nf_dc_page":"","wikipediapreview_detectlinks":true,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"ngg_post_thumbnail":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[29,14,221,227,15],"tags":[416,418,420,421,422,415],"ppma_author":[541],"class_list":["post-6995","post","type-post","status-publish","format-standard","hentry","category-code","category-howto","category-python","category-python-code","category-technical","tag-matrices","tag-matrix","tag-rotation","tag-sample","tag-sphere","tag-vision"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"authors":[{"term_id":541,"user_id":61,"is_guest":0,"slug":"jack","display_name":"Jack Scantlebury","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/2d30962dbe9d08db0ac110abbf9ffd5bd52f4eb7da79636d286fb584280feb2c?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/posts\/6995","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/users\/61"}],"replies":[{"embeddable":true,"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/comments?post=6995"}],"version-history":[{"count":5,"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/posts\/6995\/revisions"}],"predecessor-version":[{"id":7265,"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/posts\/6995\/revisions\/7265"}],"wp:attachment":[{"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/media?parent=6995"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/categories?post=6995"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/tags?post=6995"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=6995"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}