# Python Code to Multiply Octonions and Sedenions

# Python Code to Multiply Octonions and Sedenions

### In this post, a data scientist and mathematician, will implement octonion multiplication in Python, and then sedenion multiplication.

Join the DZone community and get the full member experience.

Join For FreeHortonworks Sandbox for HDP and HDF is your chance to get started on learning, developing, testing and trying out new features. Each download comes preconfigured with interactive tutorials, sample data and developments from the Apache community.

The previous post discussed octonions. This post will implement octonion multiplication in Python, and then sedenion multiplication.

## Cayley-Dickson Construction

There's a way to bootstrap quaternion multiplication into octonion multiplication, so we'll reuse the quaternion multiplication code from an earlier post. It's called the Cayley-Dickson construction. For more on this construction, see John Baez's treatise on octonions.

If you represent an octonion as a pair of quaternions, then multiplication can be defined by

(*a*, *b*) (*c*, *d*) = (*ac* – *db**, *a***d* + *cb*)

where a star superscript on a variable means (quaternion) conjugate.

Note that this isn't the only way to define the multiplication of octonions. There are many (480?) isomorphic ways to define octonion multiplication.

(You can take the Cayley-Dickson construction one step further, creating **sedenions** as pairs of octonions. We'll also provide code for sedenion multiplication below.)

## Code for Octonion Multiplication

```
import numpy as np
# quaternion multiplication
def qmult(x, y):
return np.array([
x[0]*y[0] - x[1]*y[1] - x[2]*y[2] - x[3]*y[3],
x[0]*y[1] + x[1]*y[0] + x[2]*y[3] - x[3]*y[2],
x[0]*y[2] - x[1]*y[3] + x[2]*y[0] + x[3]*y[1],
x[0]*y[3] + x[1]*y[2] - x[2]*y[1] + x[3]*y[0]
])
# quaternion conjugate
def qstar(x):
return x*np.array([1, -1, -1, -1])
# octonion multiplication
def omult(x, y):
# Split octonions into pairs of quaternions
a, b = x[:4], x[4:]
c, d = y[:4], y[4:]
z = np.zeros(8)
z[:4] = qmult(a, c) - qmult(d, qstar(b))
z[4:] = qmult(qstar(a), d) + qmult(c, b)
return z
```

## Unit Tests

Here are some unit tests to verify that our multiplication has the expected properties. We begin by generating three octonions with norm 1.

```
from numpy.linalg import norm
def random_unit_octonion():
x = np.random.normal(size=8)
return x / norm(x)
x = random_unit_octonion()
y = random_unit_octonion()
z = random_unit_octonion()
```

We said in the previous post that octonions satisfy the "alternative" condition, a weak form of associativity. Here we verify this property as a unit test.

```
eps = 1e-15
# alternative identities
a = omult(omult(x, x), y)
b = omult(x, omult(x, y))
assert( norm(a - b) < eps )
a = omult(omult(x, y), y)
b = omult(x, omult(y, y))
assert( norm(a - b) < eps )
```

We also said in the previous post that the octonions satisfy the "Moufang" conditions.

```
# Moufang identities
a = omult(z, omult(x, omult(z, y)))
b = omult(omult(omult(z, x), z), y)
assert( norm(a - b) < eps )
a = omult(x, omult(z, omult(y, z)))
b = omult(omult(omult(x, z), y), z)
assert( norm(a - b) < eps )
a = omult(omult(z,x), omult(y, z))
b = omult(omult(z, omult(x, y)), z)
assert( norm(a - b) < eps )
a = omult(omult(z,x), omult(y, z))
b = omult(z, omult(omult(x, y), z))
assert( norm(a - b) < eps )
```

And, finally, we verify that the product of unit length octonions has unit length.

```
# norm condition
n = norm(omult(omult(x, y), z))
assert( abs(n - 1) < eps )
```

The next post uses the octonion multiplication code above to look at the distribution of the associator (*xy*) *z* - *x*(*yz*) to see how far multiplication is from being associative.

## Sedenion Multiplication

The only normed division algebras over the reals are the real numbers, complex numbers, quaternions, and octonions. These are real vector spaces of dimension 1, 2, 4, and 8.

You can proceed analogously and define a real algebra of dimension 16 called the **sedenions**. When we go from complex numbers to quaternions we lose commutativity. When we go from quaternions to octonions we lose full associativity but retain a weak version of associativity. Even weak associativity is lost when we move from octonions to sedenions. Non-zero octonions form an alternative loop with respect to multiplication, but sedenions do not.

Sedenions have multiplicative inverses, but there are also some zero divisors, i.e. non-zero vectors whose product is zero. So sedenions do not form a division algebra. If you continue the Cayley-Dickson construction past the sedenions, you keep getting zero divisors, so no more division algebras.

Here's Python code for sedenion multiplication, building on the code above.

```
def ostar(x):
mask = -np.ones(8)
mask[0] = 1
return x*mask
# sedenion multiplicaton
def smult(x, y):
# Split sedenions into pairs of octonions
a, b = x[:8], x[8:]
c, d = y[:8], y[8:]
z = np.zeros(16)
z[:8] = omult(a, c) - omult(d, ostar(b))
z[8:] = omult(ostar(a), d) + omult(c, b)
return z
```

Hortonworks Community Connection (HCC) is an online collaboration destination for developers, DevOps, customers and partners to get answers to questions, collaborate on technical articles and share code examples from GitHub. Join the discussion.

Published at DZone with permission of John Cook , DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

## {{ parent.title || parent.header.title}}

## {{ parent.tldr }}

## {{ parent.linkDescription }}

{{ parent.urlSource.name }}