Metadata-Version: 2.4
Name: quat-numpy
Version: 1.0.0b1
Summary: Quaternion algebra library with vector/matrix/tensor collections, linear algebra (SVD), interpolation (SLERP/squad), signal processing (QFFT/QConv), distance metrics, kinematics, statistics, and optimized einsum kernels.
Author-email: gealachlee <gealachlee@users.noreply.github.com>
License-Expression: Apache-2.0
Project-URL: homepage, https://github.com/gealachlee/quat
Project-URL: repository, https://github.com/gealachlee/quat
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.21
Provides-Extra: data
Requires-Dist: scipy>=1.7; extra == "data"
Requires-Dist: Pillow>=9.0; extra == "data"
Requires-Dist: scikit-learn>=1.0; extra == "data"
Dynamic: license-file

<p align="center">
  <h1 align="center">quat</h1>
  <p align="center"><b>Quaternion Algebra for NumPy</b></p>
</p>

<p align="center">
  <img src="https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue" alt="Python">
  <img src="https://img.shields.io/badge/numpy-%E2%89%A51.21-4d77cf" alt="NumPy">
  <img src="https://img.shields.io/badge/license-Apache%202.0-green" alt="License">
</p>

---

**quat** brings first-class quaternion support to NumPy.  Scalar quaternions,
vector/matrix/tensor collections, SVD, SLERP interpolation, QFFT signal
processing, serialization — with a three-tier-einsum Hamilton kernel that
picks the fastest strategy at every data size.  All in pure Python, zero C
extensions.

```
pip install quat-numpy
```

## Why quat?

Most quaternion libraries stop at rotating 3D vectors.  quat gives you the
full toolbox:

| What | How |
|---|---|
| **Scalar quaternions** | Full algebra — `+`, `*`, `/`, `exp`, `log`, `pow`, Hamilton product |
| **Collections** | `QuatVector` (1D), `QuatMatrix` (2D), `QuatTensor` (3D) — broadcast-aware |
| **Linear algebra** | SVD, rank, condition number, pseudo-inverse, determinant — on quaternion matrices |
| **Interpolation** | SLERP (shortest arc on S³), squad cubic spline, batch SLERP, angular velocity estimation |
| **Distance & statistics** | 4 geodesic/chordal distance metrics, quaternion vector mean, Karcher mean, PCA |
| **Signal processing** | 1D/2D QFFT, quaternion convolution, FIR filter design |
| **Random generation** | Reproducible generators for all four types |
| **Serialization** | JSON + compact binary roundtrip for all types; SciPy `Rotation` interop |
| **Performance** | Three-tier Hamilton kernel (component-wise → einsum), `~20x` faster SVD fast-path |

## Quickstart

```python
import numpy as np
from quat import Quaternion, QuatVector, QuatMatrix
from quat import slerp, squad, random_unit_quat

# ---- Construction -------------------------------------------------
q = Quaternion(1, 2, 3, 4)                         # a + bi + cj + dk
r = Quaternion(5.0)                                 # pure real
u = Quaternion.from_axis_angle((0,0,1), np.pi/2)    # 90 deg around z
e = Quaternion.from_euler((0.1, 0.2, 0.3))          # intrinsic ZYX

# ---- Hamilton product ---------------------------------------------
i, j, k = Quaternion(0,1,0,0), Quaternion(0,0,1,0), Quaternion(0,0,0,1)
print(i * j)           # Quaternion(0, 0, 0, 1)   — i*j = k
print(i * j * k)       # Quaternion(-1, 0, 0, 0)  — i² = j² = k² = ijk = -1

# ---- Algebra ------------------------------------------------------
q.conjugate()          # Quaternion(1, -2, -3, -4)
q.norm()               # Euclidean norm
q.inverse() * q        # ≈ identity
q.exp()                # quaternion exponential
q.log()                # quaternion logarithm

# ---- 3D rotation --------------------------------------------------
u = Quaternion.from_axis_angle((0, 0, 1), np.pi / 2)
u.rotate_vector((1, 0, 0))           # ≈ (0, 1, 0) — 90° rotation
axis, angle = u.to_axis_angle()      # roundtrip (axis, angle)

# ---- Smooth interpolation -----------------------------------------
a = Quaternion(1, 0, 0, 0)
b = Quaternion(0, 1, 0, 0)
mid = slerp(a, b, 0.5)               # shortest arc on the 3-sphere

q0, q1, q2, q3 = [random_unit_quat() for _ in range(4)]
curve = squad(q0, q1, q2, q3, 0.75)  # cubic spline (Shoemake 1987)

# ---- Collections --------------------------------------------------
v = QuatVector([
    Quaternion(1,0,0,0), Quaternion(0,1,0,0), Quaternion(0,0,1,0)
])
v.real          # array([1., 0., 0.])
v.data.shape    # (3, 4)

A = QuatMatrix.eye(3)
B = QuatMatrix(np.random.randn(3, 4, 4))
C = A @ B       # quaternion matrix multiply (A * B also works)
C.shape         # (3, 4)

# ---- Linear algebra -----------------------------------------------
from quat.linalg import svd, pseudo_inverse, solve

U, s, Vh = svd(C)
A_pinv = pseudo_inverse(C)
x = solve(A_pinv, QuatVector(np.ones((3, 4))))

# ---- Signal processing --------------------------------------------
from quat.signal import qfft, qconv, lowpass

X = qfft(np.random.randn(256, 4))            # 1D quaternion FFT
k = lowpass(16, cutoff=0.2)                  # FIR lowpass filter
y = qconv(np.random.randn(128, 4), k._data)  # quaternion convolution

# ---- Serialization ------------------------------------------------
s = q.to_json()         # → '{"type":"Quaternion","data":[...]}'
b = q.to_bytes()        # → compact binary
q2 = Quaternion.from_json(s)
q3 = Quaternion.from_bytes(b)

# ---- Basis constants ----------------------------------------------
from quat import _I, _J, _K, _R, _ZERO
print(_I * _J * _K)     # -1 (Hamilton's fundamental identity)
```

## API Reference

### `Quaternion` — scalar quaternion

| category | methods |
|---|---|
| **constructors** | `zero()`, `one_q()`, `from_axis_angle()`, `from_euler()`, `from_complex_matrix()`, `from_real_matrix_left()` |
| **components** | `.w`, `.r`, `.i`, `.j`, `.k`, `.real`, `.imag`, `.components`, `.data` |
| **arithmetic** | `+`, `-`, `*`, `/`, `-q`, `@` |
| **algebra** | `.conjugate()`, `.norm()`, `.normalize()`, `.normalized`, `.inverse()`, `.exp()`, `.log()`, `.pow(t)`, `.re_inner(q)`, `.commutator(q)`, `.minimal()` |
| **rotation** | `.rotate_vector(v)`, `.to_axis_angle()`, `.to_euler(seq='zyx')`, `.angle`, `.axis` |
| **validation** | `.isnan()`, `.isinf()`, `.isfinite()`, `.isclose(q)` |
| **serialization** | `.to_json()`, `.from_json(s)`, `.to_bytes()`, `.from_bytes(b)` |
| **matrices** | `.to_complex_matrix()`, `.to_real_matrix_left()`, `.to_real_matrix_right()` |
| **conversion** | `float(q)`, `int(q)`, `complex(q)`, `abs(q)`, `np.asarray(q)` |

### `QuatVector` / `QuatMatrix` / `QuatTensor`

|  | QuatVector | QuatMatrix | QuatTensor |
|---|---|---|---|
| ndim | 1D | 2D | 3D |
| shape | `(n,)` | `(m, n)` | `(p, q, r)` |
| create | `zeros`, `ones` | `zeros`, `eye` | `zeros` |
| access | `.data`, `.real/.i/.j/.k` | `.data`, `.real/.i/.j/.k`, `.row(i)`, `.col(j)` | `.data`, `.real/.i/.j/.k` |
| ops | `.inner(v)`, `.norm()` | `.norm()`, `.T`, `.H`, `.conjugate()` | `.inner(T)`, `.unfold(mode)`, `.mode_n_product(A)` |

### Module overview

| module | exports |
|---|---|
| `quat.random` | `random_quat`, `random_unit_quat`, `random_quat_vector`, `random_quat_matrix`, `random_quat_tensor` |
| `quat.interpolate` | `slerp`, `slerp_vector`, `squad`, `angular_velocity`, `integrate_angular_velocity`, `rotate_frame` |
| `quat.linalg` | `svd`, `svd_values`, `rank`, `condition_number`, `pseudo_inverse`, `trace`, `det`, `norm`, `solve` |
| `quat.serialization` | `to_json`, `from_json`, `to_bytes`, `from_bytes`, `to_scipy_rotation`, `from_scipy_rotation` |
| `quat.signal` | `qfft`, `iqfft`, `qfft2`, `iqfft2`, `qconv`, `qconv2`, `lowpass`, `highpass`, `bandpass`, `bandstop` |
| `quat.stats` | `rotation.intrinsic`, `rotation.chordal`, `rotor.intrinsic`, `rotor.chordal`, `mean_rotation`, `approximate_karcher_mean`, `quaternion_mean`, `quaternion_cov`, `quaternion_pca` |
| `quat.algebra` | `hamilton_einsum`, `quat_matmul`, `conjugate_batch`, `norm_squared_batch`, `normalize_batch` |

### Performance

The Hamilton product kernel uses a **three-tier dispatch** that selects the
fastest strategy based on data size:

| size | kernel | strategy |
|---|---|---|
| ≤ 500 elements | `_hamilton_component` | direct float arithmetic |
| 500 – 5000 | `_hamilton_einsum_noopt` | einsum without contraction-path optimisation |
| > 5000 | `_hamilton_einsum` | einsum with cached contraction-path optimisation |

**SVD fast-path:** `rank()`, `condition_number()`, and `norm(A, 2)` compute
only singular values via the complex (2×2) representation — **~20× faster**
than the full 4m×4n real SVD.

**Zero-copy interop:** `q.to_numpy(copy=False)` returns the internal buffer
without copying.  `.to_complex_matrix()` and `.to_real_matrix_left()` use
`np.empty` to skip zero-initialisation.

## Install

```bash
pip install quat-numpy
```

Requires **Python ≥ 3.9**, **NumPy ≥ 1.21**.  Optional `scipy` for
`to_scipy_rotation` / `from_scipy_rotation`.

For development:

```bash
git clone https://github.com/gealachlee/quat.git
cd quat
pip install -e .
pytest tests/
```
