Skip to content
Add built-in support for quaternions to numpy
Python C Jupyter Notebook Shell
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.github/ISSUE_TEMPLATE Add issue templates [skip ci] May 16, 2018
docs Successful configuration for readthedocs (#122) Jul 25, 2019
test Qualify quaternion type name by module. (#119) Jul 4, 2019
.gitignore Cache pip downloads in manylinux1 Oct 2, 2018
.readthedocs.yml Successful configuration for readthedocs (#122) Jul 25, 2019
LICENSE Add python 3.7 to appveyor tests Oct 1, 2018
MANIFEST.in Remove reference to old auto_version [skip ci] May 10, 2018
README.md Update a few basics, and be less rude Jul 25, 2019
README.txt Add README.txt to shut pypi up Aug 25, 2016
__init__.py Complete an incomplete sentence in the docstring Jul 25, 2019
calculus.py String format bug in ValueError in spline_evaluation, more info. (#121) Jul 23, 2019
math_msvc_compatibility.h Update __all__ includes [skip ci] Mar 14, 2017
means.py Remove old (and circular!) import May 24, 2019
numba_wrapper.py Remove mentions of numbapro May 16, 2018
numpy_quaternion.c Remove no-longer-relevant TODO item Jul 25, 2019
pipify.sh Simplify distribution process Jun 26, 2019
pyproject.toml Try to get more pip variants correctly installing Jun 26, 2019
quaternion.c Improve handling of sqrt for tiny values Mar 6, 2018
quaternion.h Deal with old MSVC issue correctly (conforming to C89) May 10, 2018
quaternion_time_series.py Skip .conjugate in the one place I use it, just in case... Jul 25, 2019
requirements-build-115.txt
requirements-build.txt Build pip things with numpy==1.13; require numpy>=1.13 May 17, 2018
requirements-doc.txt Successful configuration for readthedocs (#122) Jul 25, 2019
requirements.txt Build pip things with numpy==1.13; require numpy>=1.13 May 17, 2018
setup.cfg Update __all__ includes [skip ci] Mar 14, 2017
setup.py Successful configuration for readthedocs (#122) Jul 25, 2019

README.md

DOI Available from conda on Linux, MacOS, and Windows MIT license

Quaternions in numpy

This Python module adds a quaternion dtype to NumPy.

The code was originally based on code by Martin Ling (which he wrote with help from Mark Wiebe), but has been rewritten with ideas from rational to work with both python 2.x and 3.x (and to fix a few bugs), and greatly expands the applications of quaternions.

Quickstart

conda install -c conda-forge quaternion

or

pip install --user numpy numpy-quaternion

Dependencies

The basic requirements for this code are reasonably current versions of python and numpy. In particular, python versions 2.7, 3.6, and 3.7 are routinely tested. Also, any numpy version greater than 1.13.0 should work, but the tests are run on the most recent release at the time of the test.

However, certain advanced functions in this package (including squad, mean_rotor_in_intrinsic_metric, integrate_angular_velocity, and related functions) require scipy and can automatically use numba. Scipy is a standard python package for scientific computation, and implements interfaces to C and Fortran codes for optimization (among other things) need for finding mean and optimal rotors. Numba uses LLVM to compile python code to machine code, accelerating many numerical functions by factors of anywhere from 2 to 2000. It is possible to run all the code without numba, but these particular functions are roughly 4 to 400 times slower without it.

The only drawback of numba is that it is nontrivial to install on its own. Fortunately, the best python installer for scientific python, anaconda, makes it trivial. Just install the main anaconda package, which installs both numba and scipy. If you prefer the smaller download size of miniconda (which comes with minimal extras), you'll also have to run this command:

conda install numpy scipy numba

Installation

Assuming you use conda to manage your python installation (which is currently the preferred choice for science and engineering with python), you can install this package simply as

conda install -c conda-forge quaternion

If you prefer to use pip (whether or not you use conda), you can instead do

pip install numpy numpy-quaternion

If you refuse to use conda, you might want to install inside your home directory without root privileges. (Conda does this by default anyway.) This is done by adding --user to the above command:

pip install --user numpy numpy-quaternion

Note that pip will attempt to compile the code — which requires a working C compiler.

Finally, there's also the fully manual option of just downloading the code, changing to the code directory, and running

python setup.py install

This should work regardless of the installation method, as long as you have a compiler hanging around. However, note that you will need to have at least numpy installed before this can compile (because this package uses a header file provided by numpy).

Basic usage

The full documentation can be found on Read the Docs, and most functions have docstrings that should explain the relevant points. The following are mostly for the purposes of example.

>>> import numpy as np
>>> import quaternion
>>> np.quaternion(1,0,0,0)
quaternion(1, 0, 0, 0)
>>> q1 = np.quaternion(1,2,3,4)
>>> q2 = np.quaternion(5,6,7,8)
>>> q1 * q2
quaternion(-60, 12, 30, 24)
>>> a = np.array([q1, q2])
>>> a
array([quaternion(1, 2, 3, 4), quaternion(5, 6, 7, 8)], dtype=quaternion)
>>> exp(a)
array([quaternion(1.69392, -0.78956, -1.18434, -1.57912),
       quaternion(138.909, -25.6861, -29.9671, -34.2481)], dtype=quaternion)

The following ufuncs are implemented (which means they run fast on numpy arrays):

add, subtract, multiply, divide, log, exp, power, negative, conjugate,
copysign, equal, not_equal, less, less_equal, isnan, isinf, isfinite, absolute

Quaternion components are stored as doubles. Numpy arrays with dtype=quaternion can be accessed as arrays of doubles without any (slow, memory-consuming) copying of data; rather, a view of the exact same memory space can be created within a microsecond, regardless of the shape or size of the quaternion array.

Comparison operations follow the same lexicographic ordering as tuples.

The unary tests isnan and isinf return true if they would return true for any individual component; isfinite returns true if it would return true for all components.

Real types may be cast to quaternions, giving quaternions with zero for all three imaginary components. Complex types may also be cast to quaternions, with their single imaginary component becoming the first imaginary component of the quaternion. Quaternions may not be cast to real or complex types.

Several array-conversion functions are also included. For example, to convert an Nx4 array of floats to an N-dimensional array of quaternions, use as_quat_array:

>>> import numpy as np
>>> import quaternion
>>> a = np.random.rand(7, 4)
>>> a
array([[ 0.93138726,  0.46972279,  0.18706385,  0.86605021],
       [ 0.70633523,  0.69982741,  0.93303559,  0.61440879],
       [ 0.79334456,  0.65912598,  0.0711557 ,  0.46622885],
       [ 0.88185987,  0.9391296 ,  0.73670503,  0.27115149],
       [ 0.49176628,  0.56688076,  0.13216632,  0.33309146],
       [ 0.11951624,  0.86804078,  0.77968826,  0.37229404],
       [ 0.33187593,  0.53391165,  0.8577846 ,  0.18336855]])
>>> qs = quaternion.as_quat_array(a)
>>> qs
array([ quaternion(0.931387262880247, 0.469722787598354, 0.187063852060487, 0.866050210100621),
       quaternion(0.706335233363319, 0.69982740767353, 0.933035590130247, 0.614408786768725),
       quaternion(0.793344561317281, 0.659125976566815, 0.0711557025000925, 0.466228847713644),
       quaternion(0.881859869074069, 0.939129602918467, 0.736705031709562, 0.271151494174001),
       quaternion(0.491766284854505, 0.566880763189927, 0.132166320200012, 0.333091463422536),
       quaternion(0.119516238634238, 0.86804077992676, 0.779688263524229, 0.372294043850009),
       quaternion(0.331875925159073, 0.533911652483908, 0.857784598617977, 0.183368547490701)], dtype=quaternion)

[Note that quaternions are printed with full precision, unlike floats, which is why you see extra digits above. But the actual data is identical in the two cases.] To convert an N-dimensional array of quaternions to an Nx4 array of floats, use as_float_array:

>>> b = quaternion.as_float_array(qs)
>>> b
array([[ 0.93138726,  0.46972279,  0.18706385,  0.86605021],
       [ 0.70633523,  0.69982741,  0.93303559,  0.61440879],
       [ 0.79334456,  0.65912598,  0.0711557 ,  0.46622885],
       [ 0.88185987,  0.9391296 ,  0.73670503,  0.27115149],
       [ 0.49176628,  0.56688076,  0.13216632,  0.33309146],
       [ 0.11951624,  0.86804078,  0.77968826,  0.37229404],
       [ 0.33187593,  0.53391165,  0.8577846 ,  0.18336855]])

It is also possible to convert a quaternion to or from a 3x3 array of floats representing a rotation matrix, or an array of N quaternions to or from an Nx3x3 array of floats representing N rotation matrices, using as_rotation_matrix and from_rotation_matrix. Similar conversions are possible for rotation vectors using as_rotation_vector and from_rotation_vector, and for spherical coordinates using as_spherical_coords and from_spherical_coords. Finally, it is possible to derive the Euler angles from a quaternion using as_euler_angles, or create a quaternion from Euler angles using from_euler_angles — though be aware that Euler angles are basically the worst things ever.1 Before you complain about those functions using something other than your favorite conventions, please read this page.

Bug reports and feature requests

Bug reports and feature requests are entirely welcome (with very few exceptions). The best way to do this is to open an issue on this code's github page. For bug reports, please try to include a minimal working example demonstrating the problem.

Pull requests are also entirely welcome, of course, if you have an idea where the code is going wrong, or have an idea for a new feature that you know how to implement.

This code is routinely tested on recent versions of both python (2.7, 3.6, and 3.7) and numpy (>=1.13). But the test coverage is not necessarily as complete as it could be, so bugs may certainly be present, especially in the higher-level functions like mean_rotor_....

Acknowledgments

This code is, of course, hosted on github. Because it is an open-source project, the hosting is free, and all the wonderful features of github are available, including free wiki space and web page hosting, pull requests, a nice interface to the git logs, etc. Github user Hannes Ovrén (hovren) pointed out some errors in a previous version of this code and suggested some nice utility functions for rotation matrices, etc. Github user Stijn van Drongelen (rhymoid) contributed some code that makes compilation work with MSVC++. Github user Jon Long (longjon) has provided some elegant contributions to substantially improve several tricky parts of this code. Rebecca Turner (9999years) and Leo Stein (duetosymmetry) did all the work in getting the documentation onto Read the Docs.

Every change in this code is automatically tested on Travis-CI. This service integrates beautifully with github, detecting each commit and automatically re-running the tests. The code is downloaded and installed fresh each time, and then tested, on each of the five different versions of python. This ensures that no change I make to the code breaks either installation or any of the features that I have written tests for. Travis-CI also automatically builds the conda and pip versions of the code hosted on anaconda.org and pypi respectively. These are all free services for open-source projects like this one.

The work of creating this code was supported in part by the Sherman Fairchild Foundation and by NSF Grants No. PHY-1306125 and AST-1333129.



1 Euler angles are awful

Euler angles are pretty much the worst things ever and it makes me feel bad even supporting them. Quaternions are faster, more accurate, basically free of singularities, more intuitive, and generally easier to understand. You can work entirely without Euler angles (I certainly do). You absolutely never need them. But if you really can't give them up, they are mildly supported.

You can’t perform that action at this time.