# Interfacing Other Mathematical Systems¶

Geometric Algebra is known as a *universal algebra* because it subsumes several other mathematical systems. Two algebras commonly used by engineers and scientists are complex numbers and quaternions. These algebras can be subsumed as the even sub-algebras of 2 and 3 dimensional geometric algebras, respectively. This notebook demonstrates how `clifford`

can be used to incorporate data created with these systems into geometric algebra.

## Complex Numbers¶

Given a two dimensional GA with the orthonormal basis,

The geometric algebra consists of scalars, two vectors, and a bivector ,

A complex number can be directly associated with a 2D spinor in the \(e_{12}\) -plane,

The even subalgebra of a two dimensional geometric algebra is isomorphic to the complex numbers. We can setup translating functions which converts a 2D spinor into a complex number and vice-versa. In two dimensions the spinor can be also be mapped into vectors if desired.

Below is an illustration of the three different planes, the later two being contained within the geometric algebra of two dimensions, \(G_2\). Both spinors and vectors in \(G_2\) can be modeled as points on a plane, but they have distinct algebraic properties.

```
[1]:
```

```
from IPython.display import Image
Image(url='_static/2dmap.svg')
```

```
[1]:
```

```
[2]:
```

```
from numpy import pi,e
import clifford as cf
layout, blades = cf.Cl(2) # instantiate a 2D- GA
locals().update(blades) # put all blades into local namespace
def c2s(z):
'''convert a complex number to a spinor'''
return z.real + z.imag*e12
def s2c(S):
'''convert a spinor to a complex number'''
S0 = float(S(0))
S2 = float(-S|e12)
return S0 + S2*1j
```

Convert a complex number to a spinor

```
[3]:
```

```
c2s(1+2j)
```

```
[3]:
```

```
1.0 + (2.0^e12)
```

Convert a spinor to a complex number

```
[4]:
```

```
s2c(1+2*e12)
```

```
[4]:
```

```
(1+2j)
```

Make sure we get what we started with when we make a round trip

```
[5]:
```

```
s2c(c2s(1+2j)) == 1+2j
```

```
[5]:
```

```
True
```

The spinor is then mapped to a vector by choosing a reference direction. This may be done by left multiplying with \(e_{1}\) .

```
[6]:
```

```
s = 1+2*e12
e1*s
```

```
[6]:
```

```
(1.0^e1) + (2.0^e2)
```

Geometrically, this is interpreted as having the spinor rotate a specific vector, in this case \(e_1\). Building off of the previously defined functions

```
[7]:
```

```
def c2v(c):
'''convert a complex number to a vector'''
return e1*c2s(c)
def v2c(v):
'''convert a vector to a complex number'''
return s2c(e1*v)
```

```
[8]:
```

```
c2v(1+2j)
```

```
[8]:
```

```
(1.0^e1) + (2.0^e2)
```

```
[9]:
```

```
v2c(1*e1+2*e2)
```

```
[9]:
```

```
(1+2j)
```

Depending on your applications, you may wish to have the bivector be an argument to the `c2s`

and `s2c`

functions. This allows you to map input data given in the form of complex number onto the planes of your choice. For example, in three dimensional space there are three bivector-planes; \(e_{12}, e_{23}\) and \(e_{13}\), so there are many bivectors which could be interpreted as the unit imaginary.

Complex numbers mapped in this way can be used to enact rotations within the specified planes.

```
[10]:
```

```
import clifford as cf
layout, blades = cf.Cl(3)
locals().update(blades)
def c2s(z,B):
'''convert a complex number to a spinor'''
return z.real + z.imag*B
def s2c(S,B):
'''convert a spinor to a complex number'''
S0 = float(S(0))
S2 = float(-S|B)
return S0 + S2*1j
```

```
[11]:
```

```
c2s(1+2j,e23)
```

```
[11]:
```

```
1.0 + (2.0^e23)
```

```
[12]:
```

```
c2s(3+4j,e13)
```

```
[12]:
```

```
3.0 + (4.0^e13)
```

This brings us to the subject of quaternions, which are used to handle rotations in three dimensions much like complex numbers do in two dimensions. With geometric algebra, they are just spinors acting in a different geometry.

## Quaternions¶

**Note:**

There is support for quaternions in numpy through the package quaternion.

For some reason people think quaternions (wiki page) are mystical or something. They are just spinors in a three dimensional geometric algebra.

In either case, we can pass the `names`

parameters to `Cl()`

to explicitly label the bivectors `i,j,`

and `k`

.

```
[13]:
```

```
import clifford as cf
# the vector/bivector order is reversed because Hamilton defined quaternions using a
# left handed frame. wtf.
names = ['','z','y','x','k','j','i','I']
layout, blades = cf.Cl(3, names=names)
locals().update(blades)
```

This leads to the commutations relations familiar to quaternion users

```
[14]:
```

```
for m in [i,j,k]:
for n in [i,j,k]:
print ('%s*%s=%s'%(str(m),str(n),m*n))
```

```
(1^i)*(1^i)=-1.0
(1^i)*(1^j)=(1.0^k)
(1^i)*(1^k)=-(1.0^j)
(1^j)*(1^i)=-(1.0^k)
(1^j)*(1^j)=-1.0
(1^j)*(1^k)=(1.0^i)
(1^k)*(1^i)=(1.0^j)
(1^k)*(1^j)=-(1.0^i)
(1^k)*(1^k)=-1.0
```

Quaternion data could be stored in a variety of ways. Assuming you have the scalar components for the quaternion, all you will need to do is setup a map each component to the correct bivector.

```
[15]:
```

```
def q2S(*args):
'''
convert tuple of quaternion coefficients to a spinor'''
q = args
return q[0] + q[1]*i +q[2]*j + q[3]*k
```

Then all the quaternion computations can be done using GA

```
[16]:
```

```
q1 = q2S(1,2,3,4)
q1
```

```
[16]:
```

```
1.0 + (4.0^k) + (3.0^j) + (2.0^i)
```

This prints \(i,j\) and \(k\) in reverse order but whatever,

```
[17]:
```

```
# 'scalar' part
q1(0)
```

```
[17]:
```

```
1.0
```

```
[18]:
```

```
# 'vector' part (more like bivector part!)
q1(2)
```

```
[18]:
```

```
(4.0^k) + (3.0^j) + (2.0^i)
```

*quaternion conjugation* is implemented with reversion

```
[19]:
```

```
~q1
```

```
[19]:
```

```
1.0 - (4.0^k) - (3.0^j) - (2.0^i)
```

The norm

```
[20]:
```

```
abs(q1)
```

```
[20]:
```

```
5.477225575051661
```

Taking the `dual()`

of the “vector” part actually returns a vector,

```
[21]:
```

```
q1(2).dual()
```

```
[21]:
```

```
(2.0^z) - (3.0^y) + (4.0^x)
```

```
[22]:
```

```
q1 = q2S(1,2,3,4)
q2 = q2S(5,6,7,8)
# quaternion product
q1*q2
```

```
[22]:
```

```
-60.0 + (24.0^k) + (30.0^j) + (12.0^i)
```

If you want to keep using a left-handed frame and names like \(i,j\) and \(k\) to label the planes in 3D space, ok. If you think it makes more sense to use the consistent and transparent approach provided by GA, we think you have good taste. If we make this switch, the basis and `q2S()`

functions will be changed to

```
[23]:
```

```
import clifford as cf
layout, blades = cf.Cl(3)
locals().update(blades)
blades
```

```
[23]:
```

```
{'e1': (1^e1),
'e2': (1^e2),
'e3': (1^e3),
'e12': (1^e12),
'e13': (1^e13),
'e23': (1^e23),
'e123': (1^e123)}
```

```
[24]:
```

```
def q2S(*args):
'''
convert tuple of quaternion coefficients to a spinor'''
q = args
return q[0] + q[1]*e13 +q[2]*e23 + q[3]*e12
q1 = q2S(1,2,3,4)
q1
```

```
[24]:
```

```
1.0 + (4.0^e12) + (2.0^e13) + (3.0^e23)
```