1 """@namespace IMP.algebra.jax
2 @brief Utility functions for the JAX Python library.
4 This provides various algebraic and geometric functions that are
5 similar to those in IMP.algebra but are implemented using the
6 [JAX](https://docs.jax.dev/) Python library. They are intended to
7 support code in other IMP modules that is implemented in JAX.
12 import jax.numpy
as jnp
14 from dataclasses
import dataclass
18 """Return a random vector uniformly distributed in a 3-dimensional
19 sphere of given radius at the origin"""
21 if isinstance(shape, tuple):
23 k1, k2, k3 = jax.random.split(key, 3)
24 phi = jax.random.uniform(k1, shape=shape,
25 minval=0., maxval=2. * math.pi)
26 theta = jnp.acos(jax.random.uniform(k2, shape=shape,
27 minval=-1., maxval=1.))
28 r = radius * jnp.cbrt(jax.random.uniform(k3, shape=shape,
29 minval=0.0, maxval=1.0))
30 return jnp.array([r * jnp.sin(theta) * jnp.cos(phi),
31 r * jnp.sin(theta) * jnp.sin(phi),
32 r * jnp.cos(theta)]).T
36 """Return a random vector uniformly distributed in an n-dimensional
37 sphere of given radius at the origin"""
40 box_lbound = jnp.full(n, -radius)
41 box_ubound = jnp.full(n, radius)
43 def vector_out_radius(kv):
45 return jnp.dot(vector, vector) > radius * radius
47 def sample_vector(kv):
49 key, subkey = jax.random.split(key)
50 return (key, jax.random.uniform(subkey, shape=n, minval=box_lbound,
56 return jax.lax.while_loop(vector_out_radius, sample_vector,
57 (key, box_ubound * 2.))[1]
61 """Return a random vector uniformly distributed on the surface of
62 a 3-dimensional sphere of given radius at the origin"""
64 if isinstance(shape, tuple):
66 k1, k2 = jax.random.split(key, 2)
67 phi = jax.random.uniform(k1, shape=shape,
68 minval=0., maxval=2. * math.pi)
69 theta = jnp.acos(jax.random.uniform(k2, shape=shape,
70 minval=-1., maxval=1.))
71 return jnp.array([radius * jnp.sin(theta) * jnp.cos(phi),
72 radius * jnp.sin(theta) * jnp.sin(phi),
73 radius * jnp.cos(theta)]).T
77 """Return a quaternion from the given normalized axis and angle"""
78 return jnp.concatenate((jnp.atleast_1d(jnp.cos(angle / 2.0)),
79 axis_norm * jnp.sin(angle / 2.0)))
82 def _quaternion_to_rotation_matrix(quaternion):
83 """Convert (normalized) quaternion to rotation matrix"""
99 [[v0s + v1s - v2s - v3s, 2. * (v12 + v03), 2. * (v13 - v02)],
100 [2. * (v12 - v03), v0s - v1s + v2s - v3s, 2. * (v23 + v01)],
101 [2. * (v13 + v02), 2. * (v23 - v01), v0s - v1s - v2s + v3s]])
104 def _quaternion_multiply(q1, q2):
105 """Multiply two quaternions and return the result"""
107 [q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2] - q1[3] * q2[3],
108 q1[0] * q2[1] + q1[1] * q2[0] + q1[2] * q2[3] - q1[3] * q2[2],
109 q1[0] * q2[2] - q1[1] * q2[3] + q1[2] * q2[0] + q1[3] * q2[1],
110 q1[0] * q2[3] + q1[1] * q2[2] - q1[2] * q2[1] + q1[3] * q2[0]])
113 def _quaternion_normalize(q):
114 """Normalize and return a quaternion (or array of quaternions)"""
115 norm = jnp.linalg.norm(q, axis=-1, keepdims=
True)
116 return jnp.where(norm < 1e-3, jnp.array([1., 0., 0., 0.]), q / norm)
119 @jax.tree_util.register_dataclass
120 @dataclass(frozen=
True)
121 class Transformation3DWithMatrix:
122 """A transformation that includes the rotation matrix. Usually these
123 are created using Transformation3D.get_with_matrix(). These can be
124 composed with other transformations via multiplication."""
129 rotation_matrix: jax.Array
131 translation: jax.Array
133 def get_transformed(self, v):
134 """Transform and return all vectors v"""
135 return jnp.vecmat(v, self.rotation_matrix) + self.translation
137 def __mul__(self, other):
140 rotation=_quaternion_multiply(self.rotation, other.rotation),
141 translation=self.get_transformed(other.translation))
144 @jax.tree_util.register_dataclass
147 """A 3D transformation as a rotation quaternion plus translation."""
151 translation: jax.Array
153 def get_with_matrix(self):
154 """Calculate the 3x3 rotation matrix and return a new object that
155 includes it. This object can then be used to transform vectors
156 and can be composed with other transformations."""
157 return Transformation3DWithMatrix(
158 rotation=self.rotation, translation=self.translation,
159 rotation_matrix=_quaternion_to_rotation_matrix(self.rotation))
def get_random_vector_on_3d_sphere
Return a random vector uniformly distributed on the surface of a 3-dimensional sphere of given radius...
def get_rotation_about_normalized_axis
Return a quaternion from the given normalized axis and angle.
def get_random_vector_in_3d_sphere
Return a random vector uniformly distributed in a 3-dimensional sphere of given radius at the origin...
def get_random_vector_in_sphere
Return a random vector uniformly distributed in an n-dimensional sphere of given radius at the origin...