# sym-quaternions

A Python library for symbolic quaternion operations using SymPy, designed for 3D rotation algebra and analysis.

## Description

`sym-quaternions` provides a powerful symbolic implementation of quaternions for representing and manipulating 3D rotations. Built on top of SymPy, the library enables symbolic algebra, automatic differentiation, and analytical solutions for rotation problems. It offers methods for:

- Creating rotation quaternions from angle-axis representation (with symbolic or numeric parameters)
- Rotating vectors and coordinate bases symbolically
- Converting quaternions to rotation matrices
- Quaternion arithmetic (multiplication, conjugation, inversion, normalization)
- Symbolic simplification and evaluation
- Converting between angle-axis and quaternion representations

## Installation

Install from PyPI:

```bash
pip install sym-quaternions
```

Or install from source:

```bash
git clone https://github.com/avabr/sym-quaternions.git
cd sym-quaternions
pip install -e .
```

## Usage

### Basic Example

```python
import sympy as sp
from sym_quaternions import SymQuaternion

# Create a quaternion representing 90-degree rotation around x-axis
q = SymQuaternion(angle=sp.pi/2, axis=[1, 0, 0])

# Define a vector
v = sp.Matrix([0, 1, 0])

# Rotate the vector
rotated = q.rotate_vector(v)
print(rotated)  # Output: Matrix([[0], [0], [1]])
```

### Creating Quaternions

```python
import sympy as sp

# Identity quaternion (no rotation)
q = SymQuaternion()

# From angle-axis representation
q = SymQuaternion(angle=sp.pi/4, axis=[0, 0, 1])

# From quaternion components
q = SymQuaternion(w=1, x=0, y=0, z=0)

# With symbolic parameters
theta = sp.Symbol('theta', real=True)
q = SymQuaternion(angle=theta, axis=[0, 0, 1])

# Parameters:
# - angle: rotation angle (numeric or symbolic)
# - axis: rotation axis as list, tuple, or sp.Matrix (will be normalized)
# - w, x, y, z: quaternion components (alternative to angle-axis)
```

### Symbolic Parameters

One of the key features of `sym-quaternions` is the ability to work with symbolic parameters:

```python
import sympy as sp

# Create symbolic quaternion
theta = sp.Symbol('theta', real=True)
q = SymQuaternion(angle=theta, axis=[0, 0, 1])

# Get rotation matrix symbolically
M = q.to_rotation_matrix()
print(M)
# Output: symbolic 3x3 matrix in terms of theta

# Simplify expressions
M_simplified = sp.simplify(M)

# Substitute specific values
M_45deg = M.subs(theta, sp.pi/4)

# Or evaluate the quaternion directly
q_45deg = q.eval(theta=sp.pi/4)
```

### Rotating Vectors

```python
import sympy as sp

q = SymQuaternion(angle=sp.pi/2, axis=[1, 0, 0])

# Rotate a vector in space (q * v * q⁻¹)
v = sp.Matrix([0, 1, 0])
v_rotated = q.rotate_vector(v)

# Rotate the coordinate basis (inverse rotation: q⁻¹ * v * q)
v_basis = q.rotate_basis(v)

# Works with symbolic vectors too
x, y, z = sp.symbols('x y z', real=True)
v_sym = sp.Matrix([x, y, z])
v_rotated_sym = q.rotate_vector(v_sym)
```

### Getting Rotation Matrix

```python
# Get the 3x3 rotation matrix
M = q.to_rotation_matrix()
print(M)

# The matrix can be used for standard matrix operations
v_matrix = sp.Matrix([1, 0, 0])
v_rotated = M * v_matrix

# Simplify the result
v_rotated = sp.simplify(v_rotated)
```

### Quaternion Operations

```python
import sympy as sp

q = SymQuaternion(angle=sp.pi/4, axis=[0, 0, 1])

# Quaternion conjugate
q_conj = q.conjugate()

# Quaternion norm (squared magnitude)
norm_sq = q.norm()

# Quaternion inverse
q_inv = q.inv()

# Normalize quaternion (make it unit quaternion)
q_unit = q.normalize()

# Quaternion multiplication
q1 = SymQuaternion(angle=sp.pi/4, axis=[0, 0, 1])
q2 = SymQuaternion(angle=sp.pi/4, axis=[0, 0, 1])
q_result = q1 * q2  # 90-degree rotation

# Scalar multiplication
q_scaled = q * 2
q_scaled = 2 * q  # Also works

# Simplify quaternion components
q_simple = q.simplify()

# Expand quaternion components
q_expanded = q.expand()
```

### Angle-Axis Conversion

```python
import sympy as sp

# Create from angle-axis
q = SymQuaternion(angle=sp.pi/3, axis=[1, 1, 1])

# Convert back to angle-axis
angle, axis = q.to_angle_axis()
print(f"Angle: {angle}")
print(f"Axis: {axis}")

# Get axis as sp.Matrix instead of list
angle, axis = q.to_angle_axis(return_matrix=True)
```

### Complete Example: Symbolic Rotation Analysis

```python
import sympy as sp
from sym_quaternions import SymQuaternion

# Define symbolic rotation angle
theta = sp.Symbol('theta', real=True)

# Create a rotation around the z-axis
q = SymQuaternion(angle=theta, axis=[0, 0, 1])

# Define a point
point = sp.Matrix([1, 0, 0])

# Rotate the point symbolically
rotated_point = q.rotate_vector(point)
print("Rotated point (symbolic):")
print(rotated_point)
# Output: Matrix([[cos(theta)], [sin(theta)], [0]])

# Get the rotation matrix
matrix = q.to_rotation_matrix()
print("\nRotation matrix (symbolic):")
print(sp.simplify(matrix))

# Evaluate at specific angle (45 degrees)
q_45 = q.eval(theta=sp.pi/4)
rotated_45 = q_45.rotate_vector(sp.Matrix([1, 0, 0]))
print("\nRotated point at 45 degrees:")
print(rotated_45)
print(f"Numerical: [{float(rotated_45[0]):.3f}, {float(rotated_45[1]):.3f}, {float(rotated_45[2]):.3f}]")
```

### Integration with NumPy

For numerical evaluation, you can convert symbolic results to NumPy:

```python
import sympy as sp
import numpy as np

theta = sp.Symbol('theta')
q = SymQuaternion(angle=theta, axis=[0, 0, 1])

# Get rotation matrix symbolically
M_sym = q.to_rotation_matrix()

# Convert to numerical function
M_func = sp.lambdify(theta, M_sym, 'numpy')

# Evaluate at multiple angles
angles = np.linspace(0, 2*np.pi, 10)
for angle in angles:
    M_num = M_func(angle)
    print(f"Angle: {angle:.2f}, Matrix:\n{M_num}\n")
```

## API Reference

### Constructor

- `SymQuaternion(angle=None, axis=None, w=None, x=None, y=None, z=None)` - Create a quaternion

### Rotation Methods

- `rotate_vector(vector)` - Rotate a vector in space (q * v * q⁻¹)
- `rotate_basis(vector)` - Rotate the coordinate basis (q⁻¹ * v * q)
- `to_rotation_matrix()` - Get the 3x3 rotation matrix representation

### Quaternion Operations

- `conjugate()` - Get the quaternion conjugate (w, -x, -y, -z)
- `norm()` - Calculate the quaternion norm squared (w² + x² + y² + z²)
- `inv()` - Get the quaternion inverse
- `normalize()` - Return normalized (unit) quaternion
- `simplify()` - Simplify all components using SymPy
- `expand()` - Expand all components using SymPy
- `eval(subs_dict=None, **kwargs)` - Evaluate/substitute values for symbols

### Conversion Methods

- `to_angle_axis(return_matrix=False)` - Convert to angle-axis representation
- `from_rotation_matrix(matrix)` - Create quaternion from 3x3 rotation matrix (classmethod)

### Properties

- `scalar` or `w` - Scalar (real) part of quaternion
- `vector` - Vector part as list [x, y, z]
- `vector_as_matrix` - Vector part as sp.Matrix(3,1)
- `as_matrix` - Full quaternion as sp.Matrix(4,1)

### Operators

- `q1 * q2` - Quaternion multiplication (Hamilton product)
- `q * scalar` - Scalar multiplication
- `scalar * q` - Scalar multiplication (reverse)

## Running Tests

To run the test suite:

```bash
# Install development dependencies
pip install pytest numpy num-quaternions

# Run all tests
pytest tests/

# Run tests with verbose output
pytest -v tests/

# Run comparison tests with num-quaternions
pytest tests/test_comparison_with_num_quaternions.py -v

# Run specific test
pytest tests/test_comparison_with_num_quaternions.py::TestSymbolicEvaluation::test_symbolic_angle_substitution -v
```

## Requirements

- Python >= 3.7
- sympy >= 1.8.0
- numpy (for testing and numerical evaluation)
- num-quaternions (for comparison tests)

## Comparison with num-quaternions

`sym-quaternions` is the symbolic counterpart to `num-quaternions`:

| Feature | sym-quaternions | num-quaternions |
|---------|----------------|-----------------|
| Backend | SymPy (symbolic) | NumPy (numerical) |
| Parameters | Symbolic or numeric | Numeric only |
| Use case | Analytical derivations, symbolic algebra | Fast numerical computation |
| Differentiation | Automatic (via SymPy) | Manual implementation needed |
| Performance | Slower (symbolic) | Fast (compiled NumPy) |
| Simplification | Built-in (SymPy) | N/A |

Both libraries share the same API design and produce identical numerical results when symbolic expressions are evaluated.

## License

MIT License - see LICENSE file for details.

## Author

Alexander Abramov (extremal.ru@gmail.com)

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## Links

- GitHub: https://github.com/avabr/sym-quaternions
- PyPI: https://pypi.org/project/sym-quaternions/
- Issues: https://github.com/avabr/sym-quaternions/issues
- Related: [num-quaternions](https://github.com/avabr/num-quaternions) (NumPy-based numerical quaternions)
