FastQuat 0.5.2__tar.gz → 1.0b2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fastquat-0.5.2 → fastquat-1.0b2}/.github/workflows/ci.yml +2 -2
- {fastquat-0.5.2 → fastquat-1.0b2}/.pre-commit-config.yaml +7 -0
- {fastquat-0.5.2 → fastquat-1.0b2}/.readthedocs.yaml +0 -6
- {fastquat-0.5.2 → fastquat-1.0b2}/PKG-INFO +25 -9
- {fastquat-0.5.2 → fastquat-1.0b2}/README.md +21 -6
- fastquat-1.0b2/docs/README.md +27 -0
- {fastquat-0.5.2 → fastquat-1.0b2}/docs/source/api/quaternion.md +1 -1
- {fastquat-0.5.2 → fastquat-1.0b2}/docs/source/conf.py +3 -0
- {fastquat-0.5.2 → fastquat-1.0b2}/docs/source/development.md +40 -18
- {fastquat-0.5.2 → fastquat-1.0b2}/docs/source/index.md +23 -26
- fastquat-1.0b2/docs/source/user-guide/getting-started.ipynb +293 -0
- {fastquat-0.5.2/docs/source → fastquat-1.0b2/docs/source/user-guide}/installation.md +13 -19
- fastquat-0.5.2/docs/source/examples/1-spherical-cap.ipynb → fastquat-1.0b2/docs/source/user-guide/tutorial-rotations.ipynb +21 -22
- fastquat-0.5.2/docs/source/examples/2-slerp-animation.ipynb → fastquat-1.0b2/docs/source/user-guide/tutorial-slerp.ipynb +5 -6
- fastquat-1.0b2/pyproject.toml +87 -0
- fastquat-1.0b2/src/fastquat/__init__.py +6 -0
- {fastquat-0.5.2 → fastquat-1.0b2}/src/fastquat/quaternion.py +150 -90
- fastquat-1.0b2/tests/conftest.py +13 -0
- fastquat-1.0b2/tests/test_base.py +418 -0
- fastquat-1.0b2/tests/test_indexing.py +208 -0
- fastquat-1.0b2/tests/test_math.py +591 -0
- fastquat-1.0b2/tests/test_pow.py +388 -0
- {fastquat-0.5.2 → fastquat-1.0b2}/tests/test_rotation.py +179 -272
- fastquat-1.0b2/tests/test_tensor.py +269 -0
- fastquat-1.0b2/uv.lock +3870 -0
- fastquat-0.5.2/docs/README.md +0 -96
- fastquat-0.5.2/docs/source/api/index.md +0 -9
- fastquat-0.5.2/docs/source/examples/index.md +0 -31
- fastquat-0.5.2/docs/source/quickstart.md +0 -164
- fastquat-0.5.2/pyproject.toml +0 -76
- fastquat-0.5.2/src/fastquat/__init__.py +0 -3
- fastquat-0.5.2/tests/test_base.py +0 -480
- fastquat-0.5.2/tests/test_math.py +0 -762
- fastquat-0.5.2/tests/test_pow.py +0 -496
- fastquat-0.5.2/tests/test_tensor.py +0 -426
- fastquat-0.5.2/uv.lock +0 -3048
- {fastquat-0.5.2 → fastquat-1.0b2}/.github/workflows/release.yml +0 -0
- {fastquat-0.5.2 → fastquat-1.0b2}/.gitignore +0 -0
- {fastquat-0.5.2 → fastquat-1.0b2}/docs/.gitignore +0 -0
- {fastquat-0.5.2 → fastquat-1.0b2}/docs/Makefile +0 -0
|
@@ -35,7 +35,7 @@ jobs:
|
|
|
35
35
|
runs-on: ubuntu-latest
|
|
36
36
|
strategy:
|
|
37
37
|
matrix:
|
|
38
|
-
python-version: ['3.10', '3.11', '3.12', '3.13']
|
|
38
|
+
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
|
|
39
39
|
|
|
40
40
|
steps:
|
|
41
41
|
- name: Checkout code
|
|
@@ -58,7 +58,7 @@ jobs:
|
|
|
58
58
|
|
|
59
59
|
- name: Install project's wheel and dependencies
|
|
60
60
|
run: |
|
|
61
|
-
uv sync --
|
|
61
|
+
uv sync --locked --no-install-project
|
|
62
62
|
uv pip install dist/fastquat-*.whl
|
|
63
63
|
|
|
64
64
|
- name: Run tests
|
|
@@ -30,6 +30,13 @@ repos:
|
|
|
30
30
|
- id: end-of-file-fixer
|
|
31
31
|
- id: check-merge-conflict
|
|
32
32
|
|
|
33
|
+
- repo: https://github.com/kynan/nbstripout
|
|
34
|
+
rev: 0.6.1
|
|
35
|
+
hooks:
|
|
36
|
+
- id: nbstripout
|
|
37
|
+
name: notebook stripping
|
|
38
|
+
args: [--extra-keys=metadata.language_info.version]
|
|
39
|
+
|
|
33
40
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
34
41
|
rev: 'v1.19.0'
|
|
35
42
|
hooks:
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# Read the Docs configuration file
|
|
2
2
|
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
|
3
3
|
|
|
4
|
-
# Required
|
|
5
4
|
version: 2
|
|
6
5
|
|
|
7
|
-
# Set the OS, Python version, and other tools you might need
|
|
8
6
|
build:
|
|
9
7
|
os: ubuntu-24.04
|
|
10
8
|
tools:
|
|
@@ -20,10 +18,6 @@ sphinx:
|
|
|
20
18
|
configuration: docs/source/conf.py
|
|
21
19
|
fail_on_warning: false
|
|
22
20
|
|
|
23
|
-
# Optionally build your docs in additional formats such as PDF and ePub
|
|
24
|
-
# formats:
|
|
25
|
-
# - pdf
|
|
26
|
-
|
|
27
21
|
# Set the version of Python and other tools you might need
|
|
28
22
|
submodules:
|
|
29
23
|
include: all
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: FastQuat
|
|
3
|
-
Version:
|
|
3
|
+
Version: 1.0b2
|
|
4
4
|
Summary: High-performance quaternions with JAX support
|
|
5
5
|
Project-URL: homepage, https://fastquat.readthedocs.io
|
|
6
6
|
Project-URL: repository, https://github.com/CMBSciPol/fastquat
|
|
@@ -12,12 +12,13 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
16
|
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
16
17
|
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
17
18
|
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
18
19
|
Requires-Python: >=3.10
|
|
19
20
|
Requires-Dist: jax>=0.4.0
|
|
20
|
-
Requires-Dist:
|
|
21
|
+
Requires-Dist: typing-extensions; python_version < '3.11'
|
|
21
22
|
Description-Content-Type: text/markdown
|
|
22
23
|
|
|
23
24
|
# FastQuat - High-Performance Quaternions with JAX
|
|
@@ -52,13 +53,28 @@ import jax.numpy as jnp
|
|
|
52
53
|
from fastquat import Quaternion
|
|
53
54
|
|
|
54
55
|
# Create quaternions
|
|
55
|
-
q1 = Quaternion
|
|
56
|
+
q1 = Quaternion(1) # Identity quaternion
|
|
56
57
|
q2 = Quaternion(0.7071, 0.7071, 0.0, 0.0) # 90° rotation around x-axis
|
|
58
|
+
q = Quaternion(1.0, 0.1, 0.2, 0.3)
|
|
59
|
+
p = 2
|
|
57
60
|
|
|
58
|
-
#
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
# Arithmetic
|
|
62
|
+
q_sum = q1 + q2
|
|
63
|
+
q_diff = q1 - q2
|
|
64
|
+
q_product = q1 * q2
|
|
65
|
+
q_power = q**p
|
|
66
|
+
|
|
67
|
+
# Normalization
|
|
68
|
+
norm = abs(q) # Quaternion norm
|
|
69
|
+
q_unit = q.normalize() # Unit quaternion
|
|
70
|
+
|
|
71
|
+
# Conjugation and inverse
|
|
72
|
+
q_conj = q.conj() # Conjugate
|
|
73
|
+
q_inv = 1 / q # Inverse, or q ** -1
|
|
74
|
+
|
|
75
|
+
# Other operations
|
|
76
|
+
q_log = q.log()
|
|
77
|
+
q_exp = q.exp()
|
|
62
78
|
|
|
63
79
|
# Rotate vectors
|
|
64
80
|
vector = jnp.array([1.0, 0.0, 0.0])
|
|
@@ -120,7 +136,7 @@ rotated_batch = batch_rotate(q_batch, vectors)
|
|
|
120
136
|
### SLERP
|
|
121
137
|
```python
|
|
122
138
|
# Smooth rotation interpolation
|
|
123
|
-
q_start = Quaternion.
|
|
139
|
+
q_start = Quaternion(1.0)
|
|
124
140
|
q_end = Quaternion.from_rotation_matrix(rotation_matrix)
|
|
125
141
|
|
|
126
142
|
# Generate smooth interpolation
|
|
@@ -30,13 +30,28 @@ import jax.numpy as jnp
|
|
|
30
30
|
from fastquat import Quaternion
|
|
31
31
|
|
|
32
32
|
# Create quaternions
|
|
33
|
-
q1 = Quaternion
|
|
33
|
+
q1 = Quaternion(1) # Identity quaternion
|
|
34
34
|
q2 = Quaternion(0.7071, 0.7071, 0.0, 0.0) # 90° rotation around x-axis
|
|
35
|
+
q = Quaternion(1.0, 0.1, 0.2, 0.3)
|
|
36
|
+
p = 2
|
|
35
37
|
|
|
36
|
-
#
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
# Arithmetic
|
|
39
|
+
q_sum = q1 + q2
|
|
40
|
+
q_diff = q1 - q2
|
|
41
|
+
q_product = q1 * q2
|
|
42
|
+
q_power = q**p
|
|
43
|
+
|
|
44
|
+
# Normalization
|
|
45
|
+
norm = abs(q) # Quaternion norm
|
|
46
|
+
q_unit = q.normalize() # Unit quaternion
|
|
47
|
+
|
|
48
|
+
# Conjugation and inverse
|
|
49
|
+
q_conj = q.conj() # Conjugate
|
|
50
|
+
q_inv = 1 / q # Inverse, or q ** -1
|
|
51
|
+
|
|
52
|
+
# Other operations
|
|
53
|
+
q_log = q.log()
|
|
54
|
+
q_exp = q.exp()
|
|
40
55
|
|
|
41
56
|
# Rotate vectors
|
|
42
57
|
vector = jnp.array([1.0, 0.0, 0.0])
|
|
@@ -98,7 +113,7 @@ rotated_batch = batch_rotate(q_batch, vectors)
|
|
|
98
113
|
### SLERP
|
|
99
114
|
```python
|
|
100
115
|
# Smooth rotation interpolation
|
|
101
|
-
q_start = Quaternion.
|
|
116
|
+
q_start = Quaternion(1.0)
|
|
102
117
|
q_end = Quaternion.from_rotation_matrix(rotation_matrix)
|
|
103
118
|
|
|
104
119
|
# Generate smooth interpolation
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Documentation Build Instructions
|
|
2
|
+
|
|
3
|
+
This documentation uses **PEP 735 dependency groups** instead of separate requirements files to avoid duplication.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Building Documentation Locally
|
|
7
|
+
|
|
8
|
+
The dependency group `docs` is included in the group `dev` which is installed by default when using ̀uv` (recommended).
|
|
9
|
+
No extra step other than setting up and activating the virtual environment is required:
|
|
10
|
+
```bash
|
|
11
|
+
# From the docs/ directory
|
|
12
|
+
uv sync --locked --group cuda12
|
|
13
|
+
source .venv/bin/activate
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
If you're not using uv, you can install the docs dependencies manually:
|
|
17
|
+
```bash
|
|
18
|
+
# Install the project with docs dependencies (pip >= 25.1)
|
|
19
|
+
pip install --upgrade pip
|
|
20
|
+
pip install -e . --group dev
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Then build the docs
|
|
24
|
+
```bash
|
|
25
|
+
cd docs
|
|
26
|
+
make html
|
|
27
|
+
```
|
|
@@ -39,7 +39,7 @@ compilation, automatic differentiation, and vectorization.
|
|
|
39
39
|
## Core Operations
|
|
40
40
|
|
|
41
41
|
```{eval-rst}
|
|
42
|
-
.. automethod:: fastquat.Quaternion.
|
|
42
|
+
.. automethod:: fastquat.Quaternion.__abs__
|
|
43
43
|
.. automethod:: fastquat.Quaternion.normalize
|
|
44
44
|
.. automethod:: fastquat.Quaternion.conjugate
|
|
45
45
|
.. automethod:: fastquat.Quaternion.conj
|
|
@@ -105,36 +105,58 @@ FastQuat is designed for high performance. When contributing:
|
|
|
105
105
|
Here's how to add a new method to the Quaternion class:
|
|
106
106
|
|
|
107
107
|
```python
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
108
|
+
from typing import Self
|
|
109
|
+
|
|
110
|
+
import jax.numpy as jnp
|
|
111
|
+
from jax.typing import ArrayLike
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class Quaternion:
|
|
115
|
+
...
|
|
116
|
+
def new_method(self, parameter: ArrayLike) -> Self:
|
|
117
|
+
"""Brief description of what the method does.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
parameter: Description of the parameter
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Description of the return value
|
|
124
|
+
"""
|
|
125
|
+
# Implementation using JAX operations
|
|
126
|
+
parameter = jnp.asarray(parameter)
|
|
127
|
+
result = jnp.some_operation(self.wxyz, parameter)
|
|
128
|
+
return Quaternion.from_array(result)
|
|
120
129
|
```
|
|
121
130
|
|
|
122
131
|
Then add tests:
|
|
123
132
|
|
|
124
133
|
```python
|
|
134
|
+
import jax
|
|
135
|
+
import jax.numpy as jnp
|
|
136
|
+
import pytest
|
|
137
|
+
|
|
138
|
+
from fastquat import Quaternion
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@pytest.mark.parametrize(
|
|
142
|
+
'parameter, expected_values',
|
|
143
|
+
[
|
|
144
|
+
(..., ...),
|
|
145
|
+
]
|
|
146
|
+
)
|
|
125
147
|
@pytest.mark.parametrize('do_jit', [False, True])
|
|
126
|
-
def test_new_method(do_jit):
|
|
148
|
+
def test_new_method(parameter, expected_values, do_jit):
|
|
127
149
|
"""Test the new method."""
|
|
128
|
-
def test_fn(
|
|
129
|
-
return
|
|
150
|
+
def test_fn(q_, parameter_):
|
|
151
|
+
return q_.new_method(parameter_)
|
|
130
152
|
|
|
131
153
|
if do_jit:
|
|
132
|
-
test_fn = jax.jit(test_fn)
|
|
154
|
+
test_fn = jax.jit(test_fn, static_argnums=1)
|
|
133
155
|
|
|
134
156
|
# Test implementation
|
|
135
|
-
q = Quaternion.
|
|
157
|
+
q = Quaternion(1.0)
|
|
136
158
|
result = test_fn(q, parameter)
|
|
137
|
-
assert jnp.allclose(result.wxyz,
|
|
159
|
+
assert jnp.allclose(result.wxyz, expected_values)
|
|
138
160
|
```
|
|
139
161
|
|
|
140
162
|
## Documentation
|
|
@@ -19,55 +19,52 @@ FastQuat provides optimized quaternion operations with full JAX compatibility, f
|
|
|
19
19
|
## Quick Start
|
|
20
20
|
|
|
21
21
|
```python
|
|
22
|
-
import jax.numpy as jnp
|
|
23
22
|
from fastquat import Quaternion
|
|
24
23
|
|
|
25
24
|
# Create quaternions
|
|
26
|
-
q1 = Quaternion.
|
|
25
|
+
q1 = Quaternion(1.0) # Identity quaternion
|
|
27
26
|
q2 = Quaternion(0.7071, 0.7071, 0.0, 0.0) # 90° rotation around x-axis
|
|
28
|
-
|
|
29
|
-
# Quaternion operations
|
|
30
|
-
q3 = q1 * q2 # Multiplication
|
|
31
|
-
q_inv = 1 / q1 # Inverse
|
|
32
|
-
q_norm = q1.normalize() # Normalization
|
|
33
|
-
|
|
34
|
-
# Rotate vectors
|
|
35
|
-
vector = jnp.array([1.0, 0.0, 0.0])
|
|
36
|
-
rotated = q2.rotate_vector(vector)
|
|
37
|
-
|
|
38
|
-
# Spherical interpolation (SLERP)
|
|
39
|
-
interpolated = q1.slerp(q2, t=0.5) # Halfway between q1 and q2
|
|
40
27
|
```
|
|
41
28
|
|
|
42
|
-
##
|
|
29
|
+
## Quaternion Operations
|
|
30
|
+
|
|
31
|
+
| Operation | Syntax | Description |
|
|
32
|
+
|-----------|--------|-------------|
|
|
33
|
+
| Addition | `q1 + q2` | Component-wise addition |
|
|
34
|
+
| Subtraction | `q1 - q2` | Component-wise subtraction |
|
|
35
|
+
| Multiplication | `q1 * q2` | Hamilton product |
|
|
36
|
+
| Exponentiation | `q ** p` | Quaternion power |
|
|
37
|
+
| Inverse | `1 / q` | Multiplicative inverse |
|
|
38
|
+
| Norm | `abs(q)` | Quaternion magnitude |
|
|
39
|
+
| Normalization | `q.normalize()` | Unit quaternion |
|
|
40
|
+
| Conjugate | `q.conj()` | Quaternion conjugate |
|
|
41
|
+
| Rotation | `q.rotate_vector(v)` | Rotate 3D vector |
|
|
42
|
+
| SLERP | `q1.slerp(q2, t)` | Spherical interpolation |
|
|
43
|
+
| Log | `q.log()` | Quaternion logarithm |
|
|
44
|
+
| Exp | `q.exp()` | Quaternion exponential |
|
|
43
45
|
|
|
44
46
|
```{toctree}
|
|
45
47
|
:maxdepth: 2
|
|
46
48
|
:caption: User Guide
|
|
47
49
|
|
|
48
|
-
installation
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
```{toctree}
|
|
53
|
-
:maxdepth: 2
|
|
54
|
-
:caption: Examples
|
|
55
|
-
|
|
56
|
-
examples/index.md
|
|
50
|
+
user-guide/installation
|
|
51
|
+
user-guide/getting-started
|
|
52
|
+
user-guide/tutorial-rotations
|
|
53
|
+
user-guide/tutorial-slerp
|
|
57
54
|
```
|
|
58
55
|
|
|
59
56
|
```{toctree}
|
|
60
57
|
:maxdepth: 2
|
|
61
58
|
:caption: API Reference
|
|
62
59
|
|
|
63
|
-
api/
|
|
60
|
+
api/quaternion
|
|
64
61
|
```
|
|
65
62
|
|
|
66
63
|
```{toctree}
|
|
67
64
|
:maxdepth: 1
|
|
68
65
|
:caption: Development
|
|
69
66
|
|
|
70
|
-
development
|
|
67
|
+
development
|
|
71
68
|
```
|
|
72
69
|
|
|
73
70
|
## Indices and tables
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"metadata": {},
|
|
6
|
+
"source": [
|
|
7
|
+
"# Getting Started\n",
|
|
8
|
+
"\n",
|
|
9
|
+
"This guide will get you started with FastQuat's core functionality."
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"cell_type": "markdown",
|
|
14
|
+
"metadata": {},
|
|
15
|
+
"source": [
|
|
16
|
+
"## Creating Quaternions\n",
|
|
17
|
+
"\n",
|
|
18
|
+
"There are several ways to create quaternions:"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"cell_type": "code",
|
|
23
|
+
"execution_count": null,
|
|
24
|
+
"metadata": {},
|
|
25
|
+
"outputs": [],
|
|
26
|
+
"source": [
|
|
27
|
+
"import jax\n",
|
|
28
|
+
"import jax.numpy as jnp\n",
|
|
29
|
+
"import jax.random as jr\n",
|
|
30
|
+
"\n",
|
|
31
|
+
"from fastquat import Quaternion\n",
|
|
32
|
+
"\n",
|
|
33
|
+
"# From components (w, x, y, z)\n",
|
|
34
|
+
"q1 = Quaternion(0.0, -1.0, -1.0, -1.0)\n",
|
|
35
|
+
"\n",
|
|
36
|
+
"identity = Quaternion(1.0)\n",
|
|
37
|
+
"i = Quaternion(x=1)\n",
|
|
38
|
+
"j = Quaternion(y=1)\n",
|
|
39
|
+
"k = Quaternion(z=1)\n",
|
|
40
|
+
"\n",
|
|
41
|
+
"# Convenience constructors\n",
|
|
42
|
+
"q_zeros = Quaternion.zeros((2, 3))\n",
|
|
43
|
+
"q_ones = Quaternion.ones((2, 3))\n",
|
|
44
|
+
"q_twos = Quaternion.full((2, 3), 2)\n",
|
|
45
|
+
"\n",
|
|
46
|
+
"# From arrays\n",
|
|
47
|
+
"array = jnp.array([1.0, 0.0, 0.0, 0.0])\n",
|
|
48
|
+
"q2 = Quaternion.from_array(array)\n",
|
|
49
|
+
"\n",
|
|
50
|
+
"# Random normalized quaternions\n",
|
|
51
|
+
"key = jr.key(42)\n",
|
|
52
|
+
"q_random = Quaternion.random(key)"
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"cell_type": "markdown",
|
|
57
|
+
"metadata": {},
|
|
58
|
+
"source": [
|
|
59
|
+
"## Basic Operations\n",
|
|
60
|
+
"\n",
|
|
61
|
+
"Quaternions support standard mathematical operations:"
|
|
62
|
+
]
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"cell_type": "code",
|
|
66
|
+
"execution_count": null,
|
|
67
|
+
"metadata": {},
|
|
68
|
+
"outputs": [],
|
|
69
|
+
"source": [
|
|
70
|
+
"q = Quaternion(1.0, 0.1, 0.2, 0.3)\n",
|
|
71
|
+
"p = 2\n",
|
|
72
|
+
"\n",
|
|
73
|
+
"# Arithmetic\n",
|
|
74
|
+
"q_sum = q1 + q2\n",
|
|
75
|
+
"q_diff = q1 - q2\n",
|
|
76
|
+
"q_product = q1 * q2\n",
|
|
77
|
+
"q_power = q**p\n",
|
|
78
|
+
"\n",
|
|
79
|
+
"# Normalization\n",
|
|
80
|
+
"norm = abs(q) # Quaternion norm\n",
|
|
81
|
+
"q_unit = q.normalize() # Unit quaternion\n",
|
|
82
|
+
"\n",
|
|
83
|
+
"# Conjugation and inverse\n",
|
|
84
|
+
"q_conj = q.conj() # Conjugate\n",
|
|
85
|
+
"q_inv = 1 / q # Inverse, or q ** -1\n",
|
|
86
|
+
"\n",
|
|
87
|
+
"# Other operations\n",
|
|
88
|
+
"q_log = q.log()\n",
|
|
89
|
+
"q_exp = q.exp()"
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"cell_type": "markdown",
|
|
94
|
+
"metadata": {},
|
|
95
|
+
"source": [
|
|
96
|
+
"## Vector Rotation\n",
|
|
97
|
+
"\n",
|
|
98
|
+
"One of the most common uses of quaternions is rotating 3D vectors:"
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"cell_type": "code",
|
|
103
|
+
"execution_count": null,
|
|
104
|
+
"metadata": {},
|
|
105
|
+
"outputs": [],
|
|
106
|
+
"source": [
|
|
107
|
+
"# Create a 90° rotation around the z-axis\n",
|
|
108
|
+
"angle = jnp.pi / 2\n",
|
|
109
|
+
"q_rot = Quaternion(jnp.cos(angle / 2), 0.0, 0.0, jnp.sin(angle / 2))\n",
|
|
110
|
+
"\n",
|
|
111
|
+
"# Rotate a vector\n",
|
|
112
|
+
"vector = jnp.array([1.0, 0.0, 0.0]) # Unit vector along x\n",
|
|
113
|
+
"rotated = q_rot.rotate_vector(vector)\n",
|
|
114
|
+
"print(rotated) # Should be approximately [0, 1, 0]"
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"cell_type": "markdown",
|
|
119
|
+
"metadata": {},
|
|
120
|
+
"source": [
|
|
121
|
+
"## Conversion to/from Rotation Matrices\n",
|
|
122
|
+
"\n",
|
|
123
|
+
"FastQuat can convert between quaternions and rotation matrices:"
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"cell_type": "code",
|
|
128
|
+
"execution_count": null,
|
|
129
|
+
"metadata": {},
|
|
130
|
+
"outputs": [],
|
|
131
|
+
"source": [
|
|
132
|
+
"# Quaternion to rotation matrix\n",
|
|
133
|
+
"R = q_rot.to_rotation_matrix()\n",
|
|
134
|
+
"print(R.shape) # (3, 3)\n",
|
|
135
|
+
"\n",
|
|
136
|
+
"# Rotation matrix to quaternion\n",
|
|
137
|
+
"q_from_matrix = Quaternion.from_rotation_matrix(R)"
|
|
138
|
+
]
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"cell_type": "markdown",
|
|
142
|
+
"metadata": {},
|
|
143
|
+
"source": [
|
|
144
|
+
"## Spherical Linear Interpolation (SLERP)\n",
|
|
145
|
+
"\n",
|
|
146
|
+
"SLERP provides smooth interpolation between quaternions:"
|
|
147
|
+
]
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"cell_type": "code",
|
|
151
|
+
"execution_count": null,
|
|
152
|
+
"metadata": {},
|
|
153
|
+
"outputs": [],
|
|
154
|
+
"source": [
|
|
155
|
+
"# Two different orientations\n",
|
|
156
|
+
"q_start = Quaternion(1.0) # Identity\n",
|
|
157
|
+
"q_end = Quaternion(0.7071, 0.7071, 0.0, 0.0) # 90° around x\n",
|
|
158
|
+
"\n",
|
|
159
|
+
"# Interpolate between them\n",
|
|
160
|
+
"t = 0.5 # Halfway point\n",
|
|
161
|
+
"q_mid = q_start.slerp(q_end, t)\n",
|
|
162
|
+
"\n",
|
|
163
|
+
"# Batch interpolation\n",
|
|
164
|
+
"t_values = jnp.linspace(0, 1, 10)\n",
|
|
165
|
+
"interpolated = q_start.slerp(q_end, t_values)\n",
|
|
166
|
+
"print(interpolated.shape) # (10,) - 10 quaternions"
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"cell_type": "markdown",
|
|
171
|
+
"metadata": {},
|
|
172
|
+
"source": [
|
|
173
|
+
"## JAX Integration\n",
|
|
174
|
+
"\n",
|
|
175
|
+
"FastQuat is fully compatible with JAX transformations:"
|
|
176
|
+
]
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"cell_type": "code",
|
|
180
|
+
"execution_count": null,
|
|
181
|
+
"metadata": {},
|
|
182
|
+
"outputs": [],
|
|
183
|
+
"source": [
|
|
184
|
+
"# JIT compilation\n",
|
|
185
|
+
"@jax.jit\n",
|
|
186
|
+
"def rotate_and_normalize(q, v):\n",
|
|
187
|
+
" rotated = q.rotate_vector(v)\n",
|
|
188
|
+
" return rotated / jnp.linalg.norm(rotated)\n",
|
|
189
|
+
"\n",
|
|
190
|
+
"\n",
|
|
191
|
+
"# Vectorization\n",
|
|
192
|
+
"batch_rotate = jax.vmap(lambda q, v: q.rotate_vector(v))\n",
|
|
193
|
+
"\n",
|
|
194
|
+
"# Create batches\n",
|
|
195
|
+
"q_batch = Quaternion.random(key, shape=(100,))\n",
|
|
196
|
+
"v_batch = jax.random.normal(key, (100, 3))\n",
|
|
197
|
+
"\n",
|
|
198
|
+
"# Process entire batch at once\n",
|
|
199
|
+
"rotated_batch = batch_rotate(q_batch, v_batch)\n",
|
|
200
|
+
"\n",
|
|
201
|
+
"\n",
|
|
202
|
+
"# Automatic differentiation\n",
|
|
203
|
+
"def loss_function(q_params):\n",
|
|
204
|
+
" q = Quaternion.from_array(q_params)\n",
|
|
205
|
+
" rotated = q.rotate_vector(vector)\n",
|
|
206
|
+
" return jnp.sum(rotated**2)\n",
|
|
207
|
+
"\n",
|
|
208
|
+
"\n",
|
|
209
|
+
"grad_fn = jax.grad(loss_function)\n",
|
|
210
|
+
"gradients = grad_fn(jnp.array([1.0, 0.1, 0.1, 0.1]))"
|
|
211
|
+
]
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"cell_type": "markdown",
|
|
215
|
+
"metadata": {},
|
|
216
|
+
"source": [
|
|
217
|
+
"## Performance Tips\n",
|
|
218
|
+
"\n",
|
|
219
|
+
"1. **Use JIT compilation** for repeated operations:"
|
|
220
|
+
]
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"cell_type": "code",
|
|
224
|
+
"execution_count": null,
|
|
225
|
+
"metadata": {},
|
|
226
|
+
"outputs": [],
|
|
227
|
+
"source": [
|
|
228
|
+
"@jax.jit\n",
|
|
229
|
+
"def batch_operation(quaternions):\n",
|
|
230
|
+
" return quaternions.normalize()"
|
|
231
|
+
]
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"cell_type": "markdown",
|
|
235
|
+
"metadata": {},
|
|
236
|
+
"source": [
|
|
237
|
+
"2. **Prefer batch operations** over loops:"
|
|
238
|
+
]
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"cell_type": "code",
|
|
242
|
+
"execution_count": null,
|
|
243
|
+
"metadata": {},
|
|
244
|
+
"outputs": [],
|
|
245
|
+
"source": [
|
|
246
|
+
"# Good: vectorized operation\n",
|
|
247
|
+
"results = q_batch.rotate_vector(v_batch)\n",
|
|
248
|
+
"\n",
|
|
249
|
+
"# Avoid: Python loops\n",
|
|
250
|
+
"# results = [q.rotate_vector(v) for q, v in zip(q_batch, v_batch)]"
|
|
251
|
+
]
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
"cell_type": "markdown",
|
|
255
|
+
"metadata": {},
|
|
256
|
+
"source": [
|
|
257
|
+
"3. **Normalize quaternions** when needed for rotations:"
|
|
258
|
+
]
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
"cell_type": "code",
|
|
262
|
+
"execution_count": null,
|
|
263
|
+
"metadata": {},
|
|
264
|
+
"outputs": [],
|
|
265
|
+
"source": [
|
|
266
|
+
"q_unit = q.normalize() # Ensure unit quaternion for rotations"
|
|
267
|
+
]
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
"cell_type": "markdown",
|
|
271
|
+
"metadata": {},
|
|
272
|
+
"source": [
|
|
273
|
+
"## Next Steps\n",
|
|
274
|
+
"\n",
|
|
275
|
+
"* Explore the tutorials for detailed use cases\n",
|
|
276
|
+
"* Check the [API reference](../api/quaternion.md) for complete API documentation\n",
|
|
277
|
+
"* See advanced interpolation techniques with SLERP"
|
|
278
|
+
]
|
|
279
|
+
}
|
|
280
|
+
],
|
|
281
|
+
"metadata": {
|
|
282
|
+
"kernelspec": {
|
|
283
|
+
"display_name": "Python 3",
|
|
284
|
+
"language": "python",
|
|
285
|
+
"name": "python3"
|
|
286
|
+
},
|
|
287
|
+
"language_info": {
|
|
288
|
+
"name": "python"
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
"nbformat": 4,
|
|
292
|
+
"nbformat_minor": 4
|
|
293
|
+
}
|