IMP logo
IMP Reference Guide  develop.0e3808d870,2026/10/23
The Integrative Modeling Platform
algebra/jax.py
1 """@namespace IMP.algebra.jax
2  @brief Utility functions for the JAX Python library.
3 
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.
8 """
9 
10 import math
11 import jax.lax
12 import jax.numpy as jnp
13 import jax.random
14 from dataclasses import dataclass
15 
16 
17 def get_random_vector_in_3d_sphere(key, radius, shape=1):
18  """Return a random vector uniformly distributed in a 3-dimensional
19  sphere of given radius at the origin"""
20  # Need to reverse the shape since we return the transpose
21  if isinstance(shape, tuple):
22  shape = shape[::-1]
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
33 
34 
35 def get_random_vector_in_sphere(key, n, radius):
36  """Return a random vector uniformly distributed in an n-dimensional
37  sphere of given radius at the origin"""
38  # Simply sample vectors in a cube, and discard any that fall outside
39  # of the sphere:
40  box_lbound = jnp.full(n, -radius)
41  box_ubound = jnp.full(n, radius)
42 
43  def vector_out_radius(kv):
44  key, vector = kv
45  return jnp.dot(vector, vector) > radius * radius
46 
47  def sample_vector(kv):
48  key, vector = kv
49  key, subkey = jax.random.split(key)
50  return (key, jax.random.uniform(subkey, shape=n, minval=box_lbound,
51  maxval=box_ubound))
52 
53  if n == 3:
54  return get_random_vector_in_3d_sphere(key, radius)
55  else:
56  return jax.lax.while_loop(vector_out_radius, sample_vector,
57  (key, box_ubound * 2.))[1]
58 
59 
60 def get_random_vector_on_3d_sphere(key, radius, shape=1):
61  """Return a random vector uniformly distributed on the surface of
62  a 3-dimensional sphere of given radius at the origin"""
63  # Need to reverse the shape since we return the transpose
64  if isinstance(shape, tuple):
65  shape = shape[::-1]
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
74 
75 
76 def get_rotation_about_normalized_axis(axis_norm, angle):
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)))
80 
81 
82 def _quaternion_to_rotation_matrix(quaternion):
83  """Convert (normalized) quaternion to rotation matrix"""
84  v0 = quaternion[0]
85  v1 = quaternion[1]
86  v2 = quaternion[2]
87  v3 = quaternion[3]
88  v0s = v0**2
89  v1s = v1**2
90  v2s = v2**2
91  v3s = v3**2
92  v12 = v1 * v2
93  v01 = v0 * v1
94  v02 = v0 * v2
95  v23 = v2 * v3
96  v03 = v0 * v3
97  v13 = v1 * v3
98  return jnp.array(
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]])
102 
103 
104 def _quaternion_multiply(q1, q2):
105  """Multiply two quaternions and return the result"""
106  return jnp.array(
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]])
111 
112 
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)
117 
118 
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."""
125 
126  # Rotation (as a quaternion)
127  rotation: jax.Array
128  # Rotation matrix (3x3 array)
129  rotation_matrix: jax.Array
130  # Translation vector
131  translation: jax.Array
132 
133  def get_transformed(self, v):
134  """Transform and return all vectors v"""
135  return jnp.vecmat(v, self.rotation_matrix) + self.translation
136 
137  def __mul__(self, other):
138  # Compose with another transformation
139  return Transformation3D(
140  rotation=_quaternion_multiply(self.rotation, other.rotation),
141  translation=self.get_transformed(other.translation))
142 
143 
144 @jax.tree_util.register_dataclass
145 @dataclass
146 class Transformation3D:
147  """A 3D transformation as a rotation quaternion plus translation."""
148  # Rotation (as a quaternion)
149  rotation: jax.Array
150  # Translation vector
151  translation: jax.Array
152 
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))
Simple 3D transformation class.
def get_random_vector_on_3d_sphere
Return a random vector uniformly distributed on the surface of a 3-dimensional sphere of given radius...
Definition: algebra/jax.py:60
def get_rotation_about_normalized_axis
Return a quaternion from the given normalized axis and angle.
Definition: algebra/jax.py:76
def get_random_vector_in_3d_sphere
Return a random vector uniformly distributed in a 3-dimensional sphere of given radius at the origin...
Definition: algebra/jax.py:17
def get_random_vector_in_sphere
Return a random vector uniformly distributed in an n-dimensional sphere of given radius at the origin...
Definition: algebra/jax.py:35