Welcome

Welcome! pyquaternion is a full-featured Python module for representing and using quaternions.

The following should get you up and running with pyquaternion in no time.

Getting started

The following aims to familiarize you with the basic functionality of quaternions in pyquaternion. It provides an entry point and a quick orientation (no pun intended) for those who want get stuck straight in. More comprehensive feature summaries can be found in the features and operations documentation.

Installation

To start, you will need to install pyquaternion into your environment.

[Optional] If you are using virtual environments, switch to or create your environment of choice now:

\$ workon <my_environment>

Now use pip to install pyquaternion and its dependencies

\$ pip install pyquaternion

Note: pyquaternion requires Numpy for the representation of arrays and matrices. Chances are if you're needing quaternions, you've been dealing with numerical computation and you're already familiar with numpy. If not, don't worry, it will be installed into your environment automatically.

Great, you now have pyquaternion installed and you're ready to roll. Or pitch. Or yaw. No judging here :)

Basic Usage

In your code, simply import the Quaternion object from the pyquaternion module:

>>> from pyquaternion import Quaternion

Next, create a Quaternion object to describe your desired rotation:

>>> my_quaternion = Quaternion(axis=[1, 0, 0], angle=3.14159265)

Note: There are many ways to create a Quaternion object. See the initialisation section for a complete guide.

Now you can do a bunch of useful things with your new quaternion object. Let's try rotating a vector:

>>> import numpy
>>> numpy.set_printoptions(suppress=True) # Suppress insignificant values for clarity
>>> v = numpy.array([0., 0., 1.]) # Unit vector in the +z direction
>>> v_prime = my_quaternion.rotate(v)
>>> v_prime
array([ 0., 0., -1.])
>>>

A cool feature of quaternions is that they can be intuitively chained together to form a composite rotation from a sequence of discrete rotations:

>>> q1 = Quaternion(axis=[1, 0, 0], angle=3.14159265) # Rotate 180 about X
>>> q2 = Quaternion(axis=[0, 1, 0], angle=3.14159265 / 2) # Rotate 90 about Y
>>> q3 = q1 * q2 # Composite rotation of q1 then q2 expressed as standard multiplication
>>> v_prime = q3.rotate(v)
>>> v_prime
array([ 1., 0., 0.])
>>>

Quaternions are used extensively in animation to describe smooth transitions between known orientations. This is known as interpolation. This is an example of an area where quaternions are preferred to rotation matrices as smooth interpolation is not possible with the latter. Here's quaternion interpolation in action:

>>> import numpy
>>> numpy.set_printoptions(suppress=True) # Suppress insignificant values for clarity
>>> v = numpy.array([0., 0., 1.]) # Unit vector in the +z direction
>>> q0 = Quaternion(axis=[1, 1, 1], angle=0.0) # Rotate 0 about x=y=z
>>> q1 = Quaternion(axis=[1, 1, 1], angle=2 * 3.14159265 / 3) # Rotate 120 about x=y=z
>>> for q in Quaternion.intermediates(q0, q1, 8, include_endpoints=True):
...     v_prime = q.rotate(v)
...     print(v_prime)
...
[ 0.  0.  1.]
[ 0.14213118 -0.12416109  0.98202991]
[ 0.29457011 -0.22365854  0.92908843]
[ 0.44909878 -0.29312841  0.84402963]
[ 0.59738651 -0.32882557  0.73143906]
[ 0.73143906 -0.32882557  0.59738651]
[ 0.84402963 -0.29312841  0.44909879]
[ 0.92908843 -0.22365854  0.29457012]
[ 0.98202991 -0.12416109  0.14213118]
[ 1. 0.  0.]

In the code above, the expression Quaternion.intermediates(q0, q1, 8, include_endpoints=True) returns an iterator over a sequence of Quaternion objects describing a set of 10 (8 + 2) rotations between q0 and q1. The printed output is then the path of the point originally at [0, 0, 1] as it is rotated through 120 degrees about x=y=z to end up at [1, 0, 0]. This could easily be plugged into a visualisation framework to show smooth animated rotation sequences. Read the full documentation on interpolation features here.

For a full demonstration of 3D interpolation and animation, run the demo2.py script included in the pyquaternion package. This will require some elements of the full SciPy package that are not required for pyquaternion itself.

Object Initialisation

A Quaternion object can be created in the following ways:

Default

Quaternion()

Creates a unit quaternion 1 + 0i + 0j + 0k: the quaternion representation of the real number 1.0, and the representation of a null rotation.

q1 = Quaternion()

For the purposes of rotation, this is a null quaternion (has no effect on the rotated vector). For the purposes of quaternion multiplication, this is a unit quaternion (has no effect when multiplying)

Copy

Quaternion(other)

Clone another quaternion object

Params:

• other must be another Quaternion instance.
q2 = Quaternion(q1)

Raises: TypeError if the provided object is not an instance of Quaternion, or any valid positional argument as outlined below.

Random

Quaternion.random()

Create a random quaternion that describes a rotation randomly chosen from a uniform distribution across the rotation space. Source.

This is a class method and is called as a method of the class itself rather than on a particular instance.

q3 = Quaternion.random() # called as a class method

From scalar

Quaternion(scalar)

Create the quaternion representation of a scalar (single real number) value.

Params:

• scalar can be a real number, or a string representing a real number.

The imaginary part of the resulting quaternion will always be 0i + 0j + 0k.

q4 = Quaternion(4.7349)
q4 = Quaternion(-3)
q4 = Quaternion("4.7349")
q4 = Quaternion("98")

Raises:

• TypeError if the provided value cannot be converted to a real number.
• ValueError if a provided string cannot be interpreted as a real number.

From elements

Quaternion(w, x, y, z)

Create a quaternion by specifying 4 real-numbered scalar elements.

Params:

• w, x, y, z can be real numbers, strings representing real numbers, or a mixture of both.
q5 = Quaternion(1, 1, 0, 0)
q5 = Quaternion("1.0", "0", ""0.347"", "0.0")
q5 = Quaternion("1.76", 0, 0, 0)

Raises:

• TypeError if any of the provided values cannot be converted to a real number.
• ValueError if any of the provided strings cannot be interpreted as a real number.

From a numpy array

Quaternion(array)

Create a quaternion from the elements of a 4-element Numpy array

Params:

• array must be a 4-element numpy array containing real valued elements.

The elements [a, b, c, d] of the array correspond the the real, and each imaginary component respectively in the order a + bi + cj + dk.

q6 = Quaternion(numpy.array([a, b, c, d]))

Raises:

• TypeError if any of the array contents cannot be converted to a real number.
• ValueError if the array contains less/more than 4 elements

From a sequence

Quaternion(seq)

Create a quaternion object from an ordered sequence containing 4 real valued scalar elements

Params:

• seq can be a list, a tuple, a generator or any iterable sequence containing 4 values, each convertible to a real number.

The sequential elements a, b, c, d of the sequence correspond the the real, and each imaginary component respectively in the order a + bi + cj + dk.

q7 = Quaternion((a, b, c, d)) // from 4-tuple
q7 = Quaternion([a, b, c, d]) // from list of 4

Raises:

• TypeError if any of the sequence contents cannot be converted to a real number.
• ValueError if the sequence contains less/more than 4 elements

Explicitly by element

Quaternion(a=w, b=x, c=y, d=z)

Specify each element, using any sequence of ordered labels

Params:

• a=w, b=x, c=y, d=z can be real numbers, strings representing real numbers, or a mixture of both.
q8a = Quaternion(a=1.0, b=0.0, c=0.0, d=0.0)
q8a = Quaternion(w=1.0, x=0.0, y=0.0, z=0.0)
q8a = Quaternion(a=1.0, i=0.0, j=0.0, k=0.0)
q8a = Quaternion(q1=1.0, q2=0.0, q3=0.0, q4=0.0)

Rasises: Exception behaviour is the same as initialisation by element as described above.

Explicitly by component

Quaternion(scalar=s, vector=v) or Quaternion(real=r, imaginary=i)

Specify the scalar (real) and vector (imaginary) parts of the desired quaternion.

Params:

• scalar=s or real=r can be a real number, or a string representing a real number.
• vector=v or imaginary=i can be a sequence or numpy array containing 3 real numbers.

Either component (but not both) may be absent, None or empty, and will be assumed to be zero in that case.

q8b = Quaternion(scalar=1.0, vector=(0.0, 0.0, 0.0)) // Using 3-tuple
q8b = Quaternion(scalar=None, vector=[1.0, 0.0, 0.0]) // Using list
q8b = Quaternion(vector=numpy.array([1.0, 0.0, 0.0])) // Using Numpy 3-array

q8b = Quaternion(real=1.0, imaginary=(0.0, 0.0, 0.0)) // Using 3-tuple
q8b = Quaternion(real=None, imaginary=[1.0, 0.0, 0.0]) // Using list
q8b = Quaternion(imaginary=numpy.array([1.0, 0.0, 0.0])) // Using Numpy 3-array

Raises: ValueError if the vector or imaginary component contains less/more than 3 elements

Explicitly by rotation parameters

Specify the angle (qualified as radians or degrees) for a rotation about an axis vector [x, y, z] to be described by the quaternion object.

Params axis=ax can be a sequence or numpy array containing 3 real numbers. It can have any magnitude except 0. radians=rad [optional] a real number, or a string representing a real number in radians. degrees=deg [optional] a real number, or a string representing a real number in degrees. angle=theta [optional] a real number, or a string representing a real number in radians.

The angle (radians/degrees/angle) keyword may be absent, None or empty, and will be assumed to be zero in that case, but the axis keyword must be provided to describe a meaningful rotation.

q8c = Quaternion(axis=(1.0, 0.0, 0.0), radians=math.pi/2) // Using radians asnd a 3-tuple
q8c = Quaternion(axis=[1.0, 0.0, 0.0], degrees=90) // Using degrees and a list
q8c = Quaternion(axis=numpy.array([1.0, 0.0, 0.0]), angle=math.pi/2) // Using radians and a Numpy 3-array

Raises:

• ValueError if axis is missing
• ValueError if axis contains less/more than 3 elements
• TypeError if radians/degrees/angle cannot be interpreted as a real number
• ZeroDivisionError if axis has 0 length.

Explicitly by rotation or transformation matrix

Quaternion(matrix=R) or Quaternion(matrix=T)

Specify the 3x3 rotation matrix (R) or 4x4 transformation matrix (T) from which the quaternion's rotation should be created.

Params:

• matrix=R can be a 3x3 numpy array or matrix
• matrix=T can be a 4x4 numpy array or matrix. In this case, the translation part will be ignored, and only the rotational component of the matrix will be encoded within the quaternion.

Important: The rotation component of the provided matrix must be a pure rotation i.e. special orthogonal.

rotation = numpy.eye(3)
transformation = numpy.eye(4)
q8d = Quaternion(matrix=rotation) // Using 3x3 rotation matrix
q8d = Quaternion(matrix=transformation) // Using 4x4 transformation matrix

This code uses a modification of the algorithm described in Converting a Rotation Matrix to a Quaternion, which is itself based on the method described here.

Note: Both matrices and quaternions avoid the singularities and discontinuities involved with rotation in 3 dimensions by adding extra dimensions. This has the effect that different values could represent the same rotation, for example quaternion q and -q represent the same rotation. It is therefore possible that, when converting a rotation sequence, the output may jump between these equivalent forms. This could cause problems where subsequent operations such as differentiation are done on this data. Programmers should be aware of this issue.

Raises:

• ValueError if the matrix is not 3x3 or 4x4 or if the matrix is not special orthogonal.
• TypeError if the matrix is of the wrong type

Explicitly by a numpy array

Quaternion(array=a)

Specify a numpy 4-array of quaternion elements to be assigned directly to the internal vector representation of the quaternion object.

This is more direct, and may be faster than feeding a numpy array as a positional argument to the initialiser.

Params:

• array=a must be a 4-element numpy array containing real valued elements.

The elements [a, b, c, d] of the array correspond the the real, and each imaginary component respectively in the order a + bi + cj + dk.

q8e = Quaternion(array=numpy.array([1.0, 0.0, 0.0, 0.0]))

Raises: ValueError if the array vector contains less/more than 4 elements

Quaternion Features

This section defines features available for pyquaternion's Quaternion objects

The code examples below assume the existence of a Quaternion object. You can recreate this by running the following in your Python interpreter of choice:

my_quaternion = Quaternion.random()

Norm

norm or magnitude

L2 norm of the quaternion 4-vector

This should be 1.0 for a unit quaternion (versor)

Returns: a scalar real number representing the square root of the sum of the squares of the elements of the quaternion.

my_quaternion.norm
my_quaternion.magnitude

is_unit(tolerance=1e-14)

Params:

• tolerance - [optional] - maximum absolute value by which the norm can differ from 1.0 for the object to be considered a unit quaternion. Defaults to 1e-14.

Returns: True if the Quaternion object is of unit length to within the specified tolerance value. False otherwise.

Inversion

inverse

Inverse of the quaternion object

For a unit quaternion, this is the inverse rotation, i.e. when combined with the original rotation, will result in the null rotation.

Returns: a new Quaternion object representing the inverse of this object

inv_quaternion = my_quaternion.inverse

Conjugation

conjugate

Quaternion conjugate

For a unit quaternion, this is the same as the inverse.

Returns: a new Quaternion object clone with its vector part negated

conj_quaternion = my_quaternion.conjugate

Normalisation

normalised or unit

Get a unit quaternion (versor) copy of this Quaternion object.

A unit quaternion has a norm of 1.0

Note: A Quaternion representing zero i.e. Quaternion(0, 0, 0, 0) cannot be normalised. In this case, the returned object will remain zero.

Returns: a new Quaternion object clone that is guaranteed to be a unit quaternion unless the original object was zero, in which case the norm will remain zero.

unit_quaternion = my_quaternion.normalised
unit_quaternion = my_quaternion.unit

Rotation

rotate(vector)

Rotate a 3D vector by the rotation stored in the Quaternion object

Params:

• vector - a 3-vector specified as any ordered sequence of 3 real numbers corresponding to x, y, and z values. Some types that are recognised are: numpy arrays, lists and tuples. A 3-vector can also be represented by a Quaternion object who's scalar part is 0 and vector part is the required 3-vector. Thus it is possible to call Quaternion.rotate(q) with another quaternion object as an input.

Returns: the rotated vector returned as the same type it was specified at input.

rotated_tuple       = my_quaternion.rotate((1, 0, 0)) # Returns a tuple
rotated_list        = my_quaternion.rotate([1.0, 0.0, 0.0]) # Returns a list
rotated_array       = my_quaternion.rotate(numpy.array([1.0, 0.0, 0.0])) # Returns a Numpy 3-array
rotated_quaternion  = my_quaternion.rotate(Quaternion(vector=[1, 0, 0])) # Returns a Quaternion object

Raises:

• TypeError if any of the vector elements cannot be converted to a real number.
• ValueError if vector cannot be interpreted as a 3-vector or a Quaternion object.

Exp and Log Maps

Quaternion.exp(q) - class method

Quaternion Exponential.

Params:

• q - the input quaternion/argument as a Quaternion object.

Returns: A quaternion amount representing the exp(q). See Source.

Note: The method can compute the exponential of any quaternion.

Quaternion.log(q) - class method

Quaternion Logarithm.

Params:

• q - the input quaternion/argument as a Quaternion object.

Returns: A quaternion amount representing log(q) := (log(|q|), v/|v|acos(w/|q|)).

Note: The method computes the logarithm of general quaternions. See Source for more details.

Quaternion.exp_map(q, eta) - class method

Quaternion exponential map.

Find the exponential map on the Riemannian manifold described by the quaternion space.

Params:

• q - the base point of the exponential map, i.e. a Quaternion object
• eta - the argument of the exponential map, a tangent vector, i.e. a Quaternion object

Returns: A quaternion p such that p is the endpoint of the geodesic starting at q in the direction of eta, having the length equal to the magnitude of eta.

Note: The exponential map plays an important role in integrating orientation variations (e.g. angular velocities). This is done by projecting quaternion tangent vectors onto the quaternion manifold.

Quaternion.sym_exp_map(q, eta) - class method

Quaternion symmetrized exponential map.

Find the symmetrized exponential map on the quaternion Riemannian manifold.

Params:

• q - the base point as a Quaternion object
• eta - the tangent vector argument of the exponential map as a Quaternion object

Returns: A quaternion p.

Note: The symmetrized exponential formulation is akin to the exponential formulation for symmetric positive definite tensors Source

Quaternion.log_map(q, p) - class method

Quaternion logarithm map.

Find the logarithm map on the quaternion Riemannian manifold.

Params:

• q - the base point at which the logarithm is computed, i.e. a Quaternion object
• p - the argument of the quaternion map, a Quaternion object

Returns: A tangent vector having the length and direction given by the geodesic joining q and p.

Quaternion.sym_log_map(q, p) - class method

Quaternion symmetrized logarithm map.

Find the symmetrized logarithm map on the quaternion Riemannian manifold.

Params:

• q - the base point at which the logarithm is computed, i.e. a Quaternion object
• p - the argument of the quaternion map, a Quaternion object

Returns: A tangent vector corresponding to the symmetrized geodesic curve formulation.

Note: Information on the symmetrized formulations given in Source.

Distance computation

Quaternion.absolute_distance(q0, q1) - class method

Quaternion absolute distance.

Find the distance between two quaternions accounting for the sign ambiguity.

Params:

• q0 - the first quaternion
• q1 - the second quaternion

Returns: A positive scalar corresponding to the chord of the shortest path/arc that connects q0 to q1.

Note: This function does not measure the distance on the hypersphere, but it takes into account the fact that q and -q encode the same rotation. It is thus a good indicator for rotation similarities.

Quaternion.distance(q0, q1) - class method

Quaternion intrinsic distance.

Find the intrinsic geodesic distance between q0 and q1.

Params:

• q0 - the first quaternion
• q1 - the second quaternion

Returns: A positive amount corresponding to the length of the geodesic arc connecting q0 to q1.

Note: Although q0^(-1)*q1 != q1^(-1)*q0, the length of the path joining them is given by the logarithm of those product quaternions, the norm of which is the same.

Quaternion.sym_distance(q0, q1) - class method

Quaternion symmetrized distance.

Find the intrinsic symmetrized geodesic distance between q0 and q1.

Params:

• q0 - the first quaternion
• q1 - the second quaternion

Returns: A positive amount corresponding to the length of the symmetrized geodesic curve connecting q0 to q1.

Note: This formulation is more numerically stable when performing iterative gradient descent on the Riemannian quaternion manifold. However, the distance between q and -q is equal to pi, rendering this formulation not useful for measuring rotation similarities when the samples are spread over a "solid" angle of more than pi/2 radians (the spread refers to quaternions as point samples on the unit hypersphere).

Interpolation

Quaternion.slerp(q0, q1, amount=0.5) - class method

Find a valid quaternion rotation at a specified distance along the minor arc of a great circle passing through any two existing quaternion endpoints lying on the unit radius hypersphere. Source

This is a class method and is called as a method of the class itself rather than on a particular instance.

Params:

• q0 - first endpoint rotation as a Quaternion object
• q1 - second endpoint rotation as a Quaternion object
• amount - interpolation parameter between 0 and 1. This describes the linear placement position of the result along the arc between endpoints; 0 being at q0 and 1 being at q1. Defaults to the midpoint (0.5).

Returns: a new Quaternion object representing the interpolated rotation. This is guaranteed to be a unit quaternion.

Note: This feature only makes sense when interpolating between unit quaternions (those lying on the unit radius hypersphere). Calling this method will implicitly normalise the endpoints to unit quaternions if they are not already unit length.

q0 = Quaternion(axis=[1, 1, 1], angle=0.0)
q1 = Quaternion(axis=[1, 1, 1], angle=3.141592)
q  = Quaternion.slerp(q0, q1, 2.0/3.0) # Rotate 120 degrees (2 * pi / 3)

Quaternion.intermediates(q_start, q_end, n, include_endpoints=False) - class method

Generator method to get an iterable sequence of n evenly spaced quaternion rotations between any two existing quaternion endpoints lying on the unit radius hypersphere. This is a convenience function that is based on Quaternion.slerp() as defined above.

This is a class method and is called as a method of the class itself rather than on a particular instance.

Params:

• q_start - initial endpoint rotation as a Quaternion object
• q_end - final endpoint rotation as a Quaternion object
• n - number of intermediate quaternion objects to include within the interval
• include_endpoints - [optional] - If set to True, the sequence of intermediates will be 'bookended' by q_start and q_end, resulting in a sequence length of n + 2. If set to False, endpoints are not included. Defaults to False.

Yields: a generator object iterating over a sequence of intermediate quaternion objects.

Note: This feature only makes sense when interpolating between unit quaternions (those lying on the unit radius hypersphere). Calling this method will implicitly normalise the endpoints to unit quaternions if they are not already unit length.

q0 = Quaternion(axis=[1, 1, 1], angle=0.0)
q1 = Quaternion(axis=[1, 1, 1], angle=2 * 3.141592 / 3)
for q in Quaternion.intermediates(q0, q1, 8, include_endpoints=True):
v = q.rotate([1, 0, 0])
print(v)

Differentiation

derivative(rate)

Get the instantaneous quaternion derivative representing a quaternion rotating at a 3D rate vector rate

Params:

• rate - numpy 3-array (or array-like) describing rotation rates about the global x, y and z axes respectively.

Returns: A unit quaternion describing the rotation rate

q_dot = my_quaternion.derivative([0, 0, 3.14159]) # Rotate about z at 0.5 rotation per second

Raises:

• TypeError if any of rate contents cannot be converted to a real number.
• ValueError if rate contains less/more than 3 elements

Integration

integrate(rate, timestep)

Advance a time varying quaternion to its value at a time timestep in the future.

The Quaternion object will be modified to its future value. It is guaranteed to remain a unit quaternion.

Params:

• rate - numpy 3-array (or array-like) describing rotation rates about the global x, y and z axes respectively.
• timestep - interval over which to integrate into the future. Assuming now is T=0, the integration occurs over the interval T=0 to T=timestep. Smaller intervals are more accurate when rate changes over time.

Note 1: This feature only makes sense when referring to a unit quaternion. Calling this method will implicitly normalise the Quaternion object to a unit quaternion if it is not already one. Many quaternion integration algorithms will have unwanted scaling effects leading a quaternion object to become non-unit over time, thus the object is re-normalised with each call to integrate(). Because this method is often called very frequently (every timestep for realtime simulation) an optimised re-normalisation is performed. See _fast_normalise() for more info.

Note 2: The solution is in closed form given the assumption that rate is constant over the interval of length timestep. This algorithm is not an exact solution to the differential equation over any interval where the angular rates are not constant. It is a second order approximation, meaning the integral error contains terms proportional to timestep ** 3 and higher powers.

>>> q = Quaternion() # null rotation
>>> q.integrate([2*pi, 0, 0], 0.25) # Rotate about x at 1 rotation per second
>>> q == Quaternion(axis=[1, 0, 0], angle=(pi/2))
True
>>>

Raises:

• TypeError if any of rate contents cannot be converted to a real number.
• ValueError if rate contains less/more than 3 elements

Accessing matrix form

rotation_matrix & transformation_matrix

Get the 3x3 rotation or 4x4 homogeneous transformation matrix equivalent of the quaternion rotation.

Returns:

• Quaternion.rotation_matrix : a 3x3 orthogonal rotation matrix as a 3x3 Numpy array
• Quaternion.transformation_matrix : a 4x4 homogeneous transformation matrix as a 4x4 Numpy array

Note 1: This feature only makes sense when referring to a unit quaternion. Calling this method will implicitly normalise the Quaternion object to a unit quaternion if it is not already one.

Note 2: Both matrices and quaternions avoid the singularities and discontinuities involved with rotation in 3 dimensions by adding extra dimensions. This has the effect that different values could represent the same rotation, for example quaternion q and -q represent the same rotation. It is therefore possible that, when converting a rotation sequence, the output may jump between different but equivalent forms. This could cause problems where subsequent operations such as differentiation are done on this data. Programmers should be aware of this issue.

R = my_quaternion.rotation_matrix         # 3x3 rotation matrix
T = my_quaternion.transformation_matrix   # 4x4 transformation matrix

Accessing rotation axis

axis or get_axis(undefined=[0,0,0])

Get the axis or vector about which the quaternion rotation occurs

For a null rotation (a purely real quaternion), the rotation angle will always be 0, but the rotation axis is undefined. It is by default assumed to be [0, 0, 0].

Note: In the case of a null rotation, retrieving the axis is geometrically meaningless, as it could be any of an infinite set of vectors. By default, ([0, 0, 0]) is returned in this instance, but should this causes undesired behaviour, please use the alternative get_axis() form, specifying the undefined keyword to return a vector of your choice.

Params:

• undefined - [optional] - specify the axis vector that should define a null rotation.

Returns: a Numpy unit 3-vector describing the Quaternion object's axis of rotation.

Note 1: This feature only makes sense when referring to a unit quaternion. Calling this method will implicitly normalise the Quaternion object to a unit quaternion if it is not already one.

Note 2: Both matrices and quaternions avoid the singularities and discontinuities involved with rotation in 3 dimensions by adding extra dimensions. This has the effect that different values could represent the same rotation, for example quaternion q and -q represent the same rotation. It is therefore possible that, when converting a rotation sequence to axis/angle representation, the output may jump between different but equivalent forms. This could cause problems where subsequent operations such as differentiation are done on this data. Programmers should be aware of this issue.

u = my_quaternion.axis # Unit vector about which rotation occurs  #or
u = my_quaternion.get_axis(undefined=[1, 0, 0]) # Prefers a custom axis vector in the case of undefined result

Accessing rotation angle

Get the angle (in radians) describing the magnitude of the quaternion rotation about its rotation axis. This is guaranteed to be within the range (-pi:pi) with the direction of rotation indicated by the sign.

When a particular rotation describes a 180 degree rotation about an arbitrary axis vector v, the conversion to axis / angle representation may jump discontinuously between all permutations of (-pi, pi) and (-v, v), each being geometrically equivalent (see Note 2 below).

Returns: a real number in the range (-pi:pi) describing the angle of rotation in radians about a Quaternion object's axis of rotation.

Note 1: This feature only makes sense when referring to a unit quaternion. Calling this method will implicitly normalise the Quaternion object to a unit quaternion if it is not already one.

Note 2: Both matrices and quaternions avoid the singularities and discontinuities involved with rotation in 3 dimensions by adding extra dimensions. This has the effect that different values could represent the same rotation, for example quaternion q and -q represent the same rotation. It is therefore possible that, when converting a rotation sequence to axis/angle representation, the output may jump between different but equivalent forms. This could cause problems where subsequent operations such as differentiation are done on this data. Programmers should be aware of this issue.

theta = my_quaternion.angle # Magnitude of rotation about the prescribed axis, in radians
theta = my_quaternion.radians # Equivalent, but explicit
theta = my_quaternion.degrees # The same, but in degrees

Accessing real components

scalar or real

Get the real or scalar component of the Quaternion object

A quaternion can be described in terms of a scalar and vector part, q = [r, v] where:

• r is the scalar coefficient of the real part of the quaternion i.e. a in a + bi + cj + dk
• v is the 3-vector of coefficients to the imaginary parts of the quaternion i.e. [b, c, d] in a + bi + cj + dk

This property returns r

Returns the scalar, real valued element of the Quaternion object

r = my_quaternion.scalar
r = my_quaternion.real

Accessing imaginary components

vector or imaginary

Get the imaginary or vector component of the Quaternion object. This can be used, for example, to extract the stored vector when a pure-imaginary quaternion object is used to describe a vector within the three-dimensional vector space.

A quaternion can be described in terms of a scalar and vector part, q = [r, v] where:

• r is the scalar coefficient of the real part of the quaternion i.e. a in a + bi + cj + dk
• v is the 3-vector of coefficients to the imaginary parts of the quaternion i.e. [b, c, d] in a + bi + cj + dk

This property returns v

Returns Numpy 3-array of the 3 imaginary elements of the Quaternion object

v = my_quaternion.vector
v = my_quaternion.imaginary

Accessing individual elements

elements

Return all four elements of the quaternion object. Result is not guaranteed to be a unit 4-vector.

Returns: a numpy 4-array of real numbered coefficients.

>>> a = my_quaternion.elements
>>> print("{} + {}i + {}j + {}k".format(a, a, a, a))
-0.6753741977725701 + 0.4624451782281068i + -0.059197245808339134j + 0.5714103921047806k

__getitem__(index)

my_quaternion[i] returns the real numbered element at the specified index i in the quaternion 4-array

Params:

• index - integer in the range [-4:3] inclusive
>>> print("{} + {}i + {}j + {}k".format(my_quaternion, my_quaternion, my_quaternion, my_quaternion))
-0.6753741977725701 + 0.4624451782281068i + -0.059197245808339134j + 0.5714103921047806k
>>> print("{} + {}i + {}j + {}k".format(my_quaternion[-4], my_quaternion[-3], my_quaternion[-2], my_quaternion[-1]))
-0.6753741977725701 + 0.4624451782281068i + -0.059197245808339134j + 0.5714103921047806k
>>>

Raises:

• IndexError if the index provided is invalid
• TypeError or ValueError if the index cannot be interpreted as an integer

Modifying individual elements

__setitem__(index, value)

my_quaternion[i] = x sets the element at the specified index i in the quaternion 4-array to the specified value x.

Params:

• index - integer in the range [-4:3] inclusive
• value - real value to be inserted into the quaternion array at index
>>> str(my_quaternion)
'-0.653 -0.127i -0.220j +0.714k'
>>> my_quaternion = 9
>>> str(my_quaternion)
'-0.653 -0.127i +9.000j +0.714k'
>>>

Raises:

• IndexError if the index provided is invalid
• TypeError or ValueError if the value cannot be interpreted as a real number

Quaternion Operations

This section defines operations applicable to pyquaternion's Quaternion objects.

The code examples below assume the existence of a Quaternion object. You can recreate this by running the following in your Python interpreter of choice:

my_quaternion = Quaternion.random()

String Representation

__str__()

str(my_quaternion) returns an informal, nicely printable string representation of the Quaternion object. Source

>>> str(my_quaternion)
'-0.810 +0.022i -0.563j -0.166k'
>>> print(my_quaternion)
-0.810 +0.022i -0.563j -0.166k
>>>

__repr__()

repr(my_quaternion) returns the 'official' string representation of the Quaternion object. This is a string representation of a valid Python expression that could be used to recreate an object with the same value (given an appropriate environment). Source

>>> repr(my_quaternion)
'Quaternion(-0.80951530224438595, 0.022231097065902788, -0.56268832802625091,-0.16604999023923223)'
>>> my_quaternion
Quaternion(-0.80951530224438595, 0.022231097065902788, -0.56268832802625091,-0.16604999023923223)
>>>

__format__(format_spec)

a_string_containing_{format_spec}_placeholders.format(my_quaternion) inserts a customisable, nicely printable string representation of the Quaternion object into the respective places in the provided string. Source

The syntax for format_spec mirrors that of the built in format specifiers for floating point types. Check out the official Python format specification mini-language for details. An empty format_spec string will result in the same behaviour as the Quaternion.__str__().

>>> "My quaternion is: {}".format(my_quaternion)
'My quaternion is: -0.810 +0.022i -0.563j -0.166k'
>>> "My quaternion is: {:+.6}".format(my_quaternion)
'My quaternion is: -0.809515 +0.0222311i -0.562688j -0.16605k'

Bool

__bool__() or __nonzero__()

Returns: False within a logical context if the Quaternion object is zero, i.e. Quaternion(0.0, 0.0, 0.0, 0.0) or True otherwise.

The bitwise not operator ~ can be used to invert the boolean value, however the keyword not (logical) is preferred.

Note: This does not evaluate the booleanity of a quaternion rotation. A non-zero Quaternion object such as Quaternion(1.0, 0.0, 0.0, 0.0) will have a boolean value of True even though it represents a null rotation.

>>> Quaternion() == True
True
>>> not Quaternion() == False
True
>>> Quaternion(scalar=0.0) == False
True

Equality

__eq__(other)

q1 == q2 returns True if all corresponding elements are equal between two Quaternion objects q1 and q2, or False otherwise.

The inequality operator != can also be used to verify inequality in a similar way.

Because comparisons are carried out on floating point elements, equality is considered True when the absolute difference between elements falls below a threshold error. This is determined by numpy.allclose() with an absolute tolerance of 1.0e-14 and a relative tolerance of 1.0e-13. As a result, objects differing by very small individual element differences may be considered equal.

Note: This does not directly evaluate the equality of a quaternion rotation. For example, unit Quaternions q and -q will have an equality of False even though they represent the equivalent rotation.

>>> Quaternion(1, 0, 1, 1) == Quaternion(scalar=1.0, vector=[0.0, 1.0, 1.0])
True
>>> Quaternion(1, 0, 1, 1) == Quaternion(scalar=1.0, vector=[0.1, 1.0, 1.0])
False
>>> Quaternion() != Quaternion(scalar=2)
True

Negation

__neg__()

-q is the quaternion formed by the element wise negation of the elements of q.

Returns: a new Quaternion object representing the negation of the single operand. If the operand is a unit quaternion, the result is guaranteed to be a unit quaternion.

>>> my_elements = my_quaternion.elements() # Numpy array of individual elements
>>> -my_quaternion == Quaternion(-my_elements)
True

q1 + q2 is the quaternion formed by element-wise sum of q1 and q2. Source

Note: If 'other' is not a Quaternion object, it will be converted to one, using the behaviour described in the [object initialisation][initialisation] section. As described therein, a TypeError or ValueError will be raised if this conversion fails.

Returns: a new Quaternion object representing the sum of the inputs. The sum is not guaranteed to be a unit quaternion.

>>> q1 = Quaternion.random()
>>> q2 = Quaternion.random()
>>> q1 + q2 == Quaternion(q1.elements() + q2.elements())
True

Subtraction

__sub__(other)

q1 - q2 is the quaternion formed by element-wise difference between q1 and q2. Source

Note: If 'other' is not a Quaternion object, it will be converted to one, using the behaviour described in the [object initialisation][initialisation] section. As described therein, a TypeError or ValueError will be raised if this conversion fails.

Returns: a new Quaternion object representing the difference of the inputs. The difference is not guaranteed to be a unit quaternion.

>>> q1 = Quaternion.random()
>>> q2 = Quaternion.random()
>>> q1 - q2 == Quaternion(q1.elements() - q2.elements())
True

Multiplication

__mul__(other)

q1 * q2 is the quaternion formed by Hamilton product of q1 and q2. Source

The Hamiltonian product is not commutative. Ensure your operands are correctly placed.

Note: If 'other' is not a Quaternion object, it will be converted to one, using the behaviour described in the [object initialisation][initialisation] section. As described therein, a TypeError or ValueError will be raised if this conversion fails. As a result this operation holds true for scalar multiplication as scalars are converted to pure real Quaternion objects.

Returns: a new Quaternion object representing the Hamilton product of the inputs. If the two multiplicands are unit quaternions, the product is guaranteed to be a unit quaternion.

>>> one = Quaternion(1, 0, 0, 0)
>>> i   = Quaternion(0, 1, 0, 0)
>>> j   = Quaternion(0, 0, 1, 0)
>>> k   = Quaternion(0, 0, 0, 1)
>>> (i * i) == (j * j) == (k * k) == (i * j * k) == -1
True

Division

__truediv__(other) or __div__(other)

q1 / q2 is the quaternion formed by Hamilton product of q1 and q2.inverse(). Source

The Hamiltonian product is not commutative. Ensure your operands are correctly placed.

Note: If 'other' is not a Quaternion object, it will be converted to one, using the behaviour described in the [object initialisation][initialisation] section. As described therein, a TypeError or ValueError will be raised if this conversion fails. As a result this operation holds true for scalar division as scalars are converted to pure real Quaternion objects.

Returns: a new Quaternion object representing the Hamilton quotient of the inputs. If the dividend and divisor are unit quaternions, the quotient is guaranteed to be a unit quaternion.

>>> my_quaternion / my_quaternion == Quaternion(1.0)
True

Exponentiation

__pow__(other)

q ** p is the quaternion formed by raising the Quaternion q1 to the power of p for any real p. Source

Returns: a new Quaternion object representing the the object raised to the power of the input. If the base object is a unit quaternion, the result is guaranteed to be a unit quaternion.

>>> one = Quaternion(1, 0, 0, 0)
>>> i   = Quaternion(0, 1, 0, 0)
>>> j   = Quaternion(0, 0, 1, 0)
>>> k   = Quaternion(0, 0, 0, 1)
>>> (i ** 2) == (j ** 2) == (k ** 2) == -1
True

Raises: TypeError if other cannot be interpreted as a real number.