A Python library for working with arbitrary-dimension hypercomplex numbers following the Cayley-Dickson construction of algebras.

Overview

Hypercomplex

A Python library for working with quaternions, octonions, sedenions, and beyond following the Cayley-Dickson construction of hypercomplex numbers.

The complex numbers may be viewed as an extension of the everyday real numbers. A complex number has two real-number coefficients, one multiplied by 1, the other multiplied by i.

In a similar way, a quaternion, which has 4 components, can be constructed by combining two complex numbers. Likewise, two quaternions can construct an octonion (8 components), and two octonions can construct a sedenion (16 components).

The method for this construction is known as the Cayley-Dickson construction and the resulting classes of numbers are types of hypercomplex numbers. There is no limit to the number of times you can repeat the Cayley-Dickson construction to create new types of hypercomplex numbers, doubling the number of components each time.

This Python 3 package allows the creation of number classes at any repetition level of Cayley-Dickson constructions, and has built-ins for the lower, named levels such as quaternion, octonion, and sedenion.

Hypercomplex numbers containment diagram

Installation

pip install hypercomplex

View on PyPI - View on GitHub

This package was built in Python 3.9.6 and has been tested to be compatible with python 3.6 through 3.10.

Basic Usage

from hypercomplex import Complex, Quaternion, Octonion, Voudon, cayley_dickson_construction

c = Complex(0, 7)
print(c)        # -> (0 7)
print(c == 7j)  # -> True

q = Quaternion(1.1, 2.2, 3.3, 4.4)
print(2 * q)  # -> (2.2 4.4 6.6 8.8)

print(Quaternion.e_matrix())  # -> e0  e1  e2  e3
                              #    e1 -e0  e3 -e2
                              #    e2 -e3 -e0  e1
                              #    e3  e2 -e1 -e0

o = Octonion(0, 0, 0, 0, 8, 8, 9, 9)
print(o + q)  # -> (1.1 2.2 3.3 4.4 8 8 9 9)

v = Voudon()
print(v == 0)  # -> True
print(len(v))  # -> 256

BeyondVoudon = cayley_dickson_construction(Voudon)
print(len(BeyondVoudon()))  # -> 512

For more snippets see the Thorough Usage Examples section below.

Package Contents

Three functions form the core of the package:

  • reals(base) - Given a base type (float by default), generates a class that represents numbers with 1 hypercomplex dimension, i.e. real numbers. This class can then be extended into complex numbers and beyond with cayley_dickson_construction.

    Any usual math operations on instances of the class returned by reals behave as instances of base would but their type remains the reals class. By default they are printed with the g format-spec and surrounded by parentheses, e.g. (1), to remain consistent with the format of higher dimension hypercomplex numbers.

    Python's decimal.Decimal might be another likely choice for base.

    # reals example:
    from hypercomplex import reals
    from decimal import Decimal
    
    D = reals(Decimal)
    print(D(10) / 4)   # -> (2.5)
    print(D(3) * D(9)) # -> (27)
  • cayley_dickson_construction(basis) (alias cd_construction) generates a new class of hypercomplex numbers with twice the dimension of the given basis, which must be another hypercomplex number class or class returned from reals. The new class of numbers is defined recursively on the basis according the Cayley-Dickson construction. Normal math operations may be done upon its instances and with instances of other numeric types.

    # cayley_dickson_construction example:
    from hypercomplex import *
    RealNum = reals()
    ComplexNum = cayley_dickson_construction(RealNum)
    QuaternionNum = cayley_dickson_construction(ComplexNum)
    
    q = QuaternionNum(1, 2, 3, 4)
    print(q)         # -> (1 2 3 4)
    print(1 / q)     # -> (0.0333333 -0.0666667 -0.1 -0.133333)
    print(q + 1+2j)  # -> (2 4 3 4)
  • cayley_dickson_algebra(level, base) (alias cd_algebra) is a helper function that repeatedly applies cayley_dickson_construction to the given base type (float by default) level number of times. That is, cayley_dickson_algebra returns the class for the Cayley-Dickson algebra of hypercomplex numbers with 2**level dimensions.

    # cayley_dickson_algebra example:
    from hypercomplex import *
    OctonionNum = cayley_dickson_algebra(3)
    
    o = OctonionNum(8, 7, 6, 5, 4, 3, 2, 1)
    print(o)              # -> (8 7 6 5 4 3 2 1)
    print(2 * o)          # -> (16 14 12 10 8 6 4 2)
    print(o.conjugate())  # -> (8 -7 -6 -5 -4 -3 -2 -1)

For convenience, nine internal number types are already defined, built off of each other:

Name Aliases Description
Real R, CD1, CD[0] Real numbers with 1 hypercomplex dimension based on float.
Complex C, CD2, CD[1] Complex numbers with 2 hypercomplex dimensions based on Real.
Quaternion Q, CD4, CD[2] Quaternion numbers with 4 hypercomplex dimensions based on Complex.
Octonion O, CD8, CD[3] Octonion numbers with 8 hypercomplex dimensions based on Quaternion.
Sedenion S, CD16, CD[4] Sedenion numbers with 16 hypercomplex dimensions based on Octonion.
Pathion P, CD32, CD[5] Pathion numbers with 32 hypercomplex dimensions based on Sedenion.
Chingon X, CD64, CD[6] Chingon numbers with 64 hypercomplex dimensions based on Pathion.
Routon U, CD128, CD[7] Routon numbers with 128 hypercomplex dimensions based on Chingon.
Voudon V, CD256, CD[8] Voudon numbers with 256 hypercomplex dimensions based on Routon.
# built-in types example:
from hypercomplex import *
print(Real(4))               # -> (4)
print(C(3-7j))               # -> (3 -7)
print(CD4(.1, -2.2, 3.3e3))  # -> (0.1 -2.2 3300 0)
print(CD[3](1, 0, 2, 0, 3))  # -> (1 0 2 0 3 0 0 0)

The names and letter-abbreviations were taken from this image (mirror) found in Micheal Carter's paper Visualization of the Cayley-Dickson Hypercomplex Numbers Up to the Chingons (64D), but they also may be known according to their Latin naming conventions.

Thorough Usage Examples

This list follows examples.py exactly and documents nearly all the things you can do with the hypercomplex numbers created by this package.

Every example assumes the appropriate imports are already done, e.g. from hypercomplex import *.

  1. Initialization can be done in various ways, including using Python's built in complex numbers. Unspecified coefficients become 0.

    print(R(-1.5))                        # -> (-1.5)
    print(C(2, 3))                        # -> (2 3)
    print(C(2 + 3j))                      # -> (2 3)
    print(Q(4, 5, 6, 7))                  # -> (4 5 6 7)
    print(Q(4 + 5j, C(6, 7), pair=True))  # -> (4 5 6 7)
    print(P())                            # -> (0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
  2. Numbers can be added and subtracted. The result will be the type with more dimensions.

    print(Q(0, 1, 2, 2) + C(9, -1))                   # -> (9 0 2 2)
    print(100.1 - O(0, 0, 0, 0, 1.1, 2.2, 3.3, 4.4))  # -> (100.1 0 0 0 -1.1 -2.2 -3.3 -4.4)
  3. Numbers can be multiplied. The result will be the type with more dimensions.

    print(10 * S(1, 2, 3))                    # -> (10 20 30 0 0 0 0 0 0 0 0 0 0 0 0 0)
    print(Q(1.5, 2.0) * O(0, -1))             # -> (2 -1.5 0 0 0 0 0 0)
    
    # notice quaternions are non-commutative
    print(Q(1, 2, 3, 4) * Q(1, 0, 0, 1))      # -> (-3 5 1 5)
    print(Q(1, 0, 0, 1) * Q(1, 2, 3, 4))      # -> (-3 -1 5 5)
  4. Numbers can be divided and inverse gives the multiplicative inverse.

    print(100 / C(0, 2))                      # -> (0 -50)
    print(C(2, 2) / Q(1, 2, 3, 4))            # -> (0.2 -0.0666667 0.0666667 -0.466667)
    print(C(2, 2) * Q(1, 2, 3, 4).inverse())  # -> (0.2 -0.0666667 0.0666667 -0.466667)
    print(R(2).inverse(), 1 / R(2))           # -> (0.5) (0.5)
  5. Numbers can be raised to integer powers, a shortcut for repeated multiplication or division.

    q = Q(0, 3, 4, 0)
    print(q**5)               # -> (0 1875 2500 0)
    print(q * q * q * q * q)  # -> (0 1875 2500 0)
    print(q**-1)              # -> (0 -0.12 -0.16 0)
    print(1 / q)              # -> (0 -0.12 -0.16 0)
    print(q**0)               # -> (1 0 0 0)
  6. conjugate gives the conjugate of the number.

    print(R(9).conjugate())           # -> (9)
    print(C(9, 8).conjugate())        # -> (9 -8)
    print(Q(9, 8, 7, 6).conjugate())  # -> (9 -8 -7 -6)
  7. norm gives the absolute value as the base type (float by default). There is also norm_squared.

    print(O(3, 4).norm(), type(O(3, 4).norm()))  # -> 5.0 <class 'float'>
    print(abs(O(3, 4)))                          # -> 5.0
    print(O(3, 4).norm_squared())                # -> 25.0
  8. Numbers are considered equal if their coefficients all match. Non-existent coefficients are 0.

    print(R(999) == V(999))         # -> True
    print(C(1, 2) == Q(1, 2))       # -> True
    print(C(1, 2) == Q(1, 2, 0.1))  # -> False
  9. coefficients gives a tuple of the components of the number in their base type (float by default). The properties real and imag are shortcuts for the first two components. Indexing can also be used (but is inefficient).

    print(R(100).coefficients())   # -> (100.0,)
    q = Q(2, 3, 4, 5)
    print(q.coefficients())        # -> (2.0, 3.0, 4.0, 5.0)
    print(q.real, q.imag)          # -> 2.0 3.0
    print(q[0], q[1], q[2], q[3])  # -> 2.0 3.0 4.0 5.0
  10. e(index) of a number class gives the unit hypercomplex number where the index coefficient is 1 and all others are 0.

    print(C.e(0))  # -> (1 0)
    print(C.e(1))  # -> (0 1)
    print(O.e(3))  # -> (0 0 0 1 0 0 0 0)
  11. e_matrix of a number class gives the multiplication table of e(i)*e(j). Set string=False to get a 2D list instead of a string. Set raw=True to get the raw hypercomplex numbers.

    print(O.e_matrix())                        # -> e1  e2  e3  e4  e5  e6  e7
                                               #   -e0  e3 -e2  e5 -e4 -e7  e6
                                               #   -e3 -e0  e1  e6  e7 -e4 -e5
                                               #    e2 -e1 -e0  e7 -e6  e5 -e4
                                               #   -e5 -e6 -e7 -e0  e1  e2  e3
                                               #    e4 -e7  e6 -e1 -e0 -e3  e2
                                               #    e7  e4 -e5 -e2  e3 -e0 -e1
                                               #   -e6  e5  e4 -e3 -e2  e1 -e0
                                               #
    print(C.e_matrix(string=False, raw=True))  # -> [[(1 0), (0 1)], [(0 1), (-1 0)]]
  12. A number is considered truthy if it has has non-zero coefficients. Conversion to int, float and complex are only valid when the coefficients beyond the dimension of those types are all 0.

    print(bool(Q()))                    # -> False
    print(bool(Q(0, 0, 0.01, 0)))       # -> True
    
    print(complex(Q(5, 5)))             # -> (5+5j)
    print(int(V(9.9)))                  # -> 9
    # print(float(C(1, 2))) <- invalid
  13. Any usual format spec for the base type can be given in an f-string.

    o = O(0.001, 1, -2, 3.3333, 4e5)
    print(f"{o:.2f}")                 # -> (0.00 1.00 -2.00 3.33 400000.00 0.00 0.00 0.00)
    print(f"{R(23.9):04.0f}")         # -> (0024)
  14. The len of a number is its hypercomplex dimension, i.e. the number of components or coefficients it has.

    print(len(R()))      # -> 1
    print(len(C(7, 7)))  # -> 2
    print(len(U()))      # -> 128
  15. Using in behaves the same as if the number were a tuple of its coefficients.

    print(3 in Q(1, 2, 3, 4))  # -> True
    print(5 in Q(1, 2, 3, 4))  # -> False
  16. copy can be used to duplicate a number (but should generally never be needed as all operations create a new number).

    x = O(9, 8, 7)
    y = x.copy()
    print(x == y)   # -> True
    print(x is y)   # -> False
  17. base on a number class will return the base type the entire numbers are built upon.

    print(R.base())                      # -> <class 'float'>
    print(V.base())                      # -> <class 'float'>
    A = cayley_dickson_algebra(20, int)
    print(A.base())                      # -> <class 'int'>
  18. Hypercomplex numbers are weird, so be careful! Here two non-zero sedenions multiply to give zero because sedenions and beyond have zero devisors.

    s1 = S.e(5) + S.e(10)
    s2 = S.e(6) + S.e(9)
    print(s1)                                    # -> (0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0)
    print(s2)                                    # -> (0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0)
    print(s1 * s2)                               # -> (0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
    print((1 / s1) * (1 / s2))                   # -> (0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
    # print(1/(s1 * s2)) <- zero division error

About

I wrote this package for the novelty of it and as a math and programming exercise. The operations it can perform on hypercomplex numbers are not particularly efficient due to the recursive nature of the Cayley-Dickson construction.

I am not a mathematician, only a math hobbyist, and apologize if there are issues with the implementations or descriptions I have provided.

Official implementation of the article "Unsupervised JPEG Domain Adaptation For Practical Digital Forensics"

Unsupervised JPEG Domain Adaptation for Practical Digital Image Forensics @WIFS2021 (Montpellier, France) Rony Abecidan, Vincent Itier, Jeremie Boulan

Rony Abecidan 6 Jan 06, 2023
This is Official implementation for "Pose-guided Feature Disentangling for Occluded Person Re-Identification Based on Transformer" in AAAI2022

PFD:Pose-guided Feature Disentangling for Occluded Person Re-identification based on Transformer This repo is the official implementation of "Pose-gui

Tao Wang 93 Dec 18, 2022
Dist2Dec: A Simplicial Neural Network for Homology Localization

Dist2Dec: A Simplicial Neural Network for Homology Localization

Alexandros Keros 6 Jun 12, 2022
Top #1 Submission code for the first https://alphamev.ai MEV competition with best AUC (0.9893) and MSE (0.0982).

alphamev-winning-submission Top #1 Submission code for the first alphamev MEV competition with best AUC (0.9893) and MSE (0.0982). The code won't run

70 Oct 29, 2022
Code and Resources for the Transformer Encoder Reasoning Network (TERN)

Transformer Encoder Reasoning Network Code for the cross-modal visual-linguistic retrieval method from "Transformer Reasoning Network for Image-Text M

Nicola Messina 53 Dec 30, 2022
Deeplab-resnet-101 in Pytorch with Jaccard loss

Deeplab-resnet-101 Pytorch with Lovász hinge loss Train deeplab-resnet-101 with binary Jaccard loss surrogate, the Lovász hinge, as described in http:

Maxim Berman 95 Apr 15, 2022
ANEA: Automated (Named) Entity Annotation for German Domain-Specific Texts

ANEA The goal of Automatic (Named) Entity Annotation is to create a small annotated dataset for NER extracted from German domain-specific texts. Insta

Anastasia Zhukova 2 Oct 07, 2022
A PyTorch implementation of "CoAtNet: Marrying Convolution and Attention for All Data Sizes".

CoAtNet Overview This is a PyTorch implementation of CoAtNet specified in "CoAtNet: Marrying Convolution and Attention for All Data Sizes", arXiv 2021

Justin Wu 268 Jan 07, 2023
A simple, fast, and efficient object detector without FPN

You Only Look One-level Feature (YOLOF), CVPR2021 A simple, fast, and efficient object detector without FPN. This repo provides an implementation for

789 Jan 09, 2023
Code for our EMNLP 2021 paper "Learning Kernel-Smoothed Machine Translation with Retrieved Examples"

KSTER Code for our EMNLP 2021 paper "Learning Kernel-Smoothed Machine Translation with Retrieved Examples" [paper]. Usage Download the processed datas

jiangqn 23 Nov 24, 2022
BridgeGAN - Tensorflow implementation of Bridging the Gap between Label- and Reference-based Synthesis in Multi-attribute Image-to-Image Translation.

Bridging the Gap between Label- and Reference based Synthesis(ICCV 2021) Tensorflow implementation of Bridging the Gap between Label- and Reference-ba

huangqiusheng 8 Jul 13, 2022
Image restoration with neural networks but without learning.

Warning! The optimization may not converge on some GPUs. We've personally experienced issues on Tesla V100 and P40 GPUs. When running the code, make s

Dmitry Ulyanov 7.4k Jan 01, 2023
A Peer-to-peer Platform for Secure, Privacy-preserving, Decentralized Data Science

PyGrid is a peer-to-peer network of data owners and data scientists who can collectively train AI models using PySyft. PyGrid is also the central serv

OpenMined 615 Jan 03, 2023
The implementation of CVPR2021 paper Temporal Query Networks for Fine-grained Video Understanding, by Chuhan Zhang, Ankush Gupta and Andrew Zisserman.

Temporal Query Networks for Fine-grained Video Understanding 📋 This repository contains the implementation of CVPR2021 paper Temporal_Query_Networks

55 Dec 21, 2022
ISNAS-DIP: Image Specific Neural Architecture Search for Deep Image Prior [CVPR 2022]

ISNAS-DIP: Image-Specific Neural Architecture Search for Deep Image Prior (CVPR 2022) Metin Ersin Arican*, Ozgur Kara*, Gustav Bredell, Ender Konukogl

Özgür Kara 24 Dec 18, 2022
Transformer - Transformer in PyTorch

Transformer 完成进度 Embeddings and PositionalEncoding with example. MultiHeadAttent

Tianyang Li 1 Jan 06, 2022
Vector Quantization, in Pytorch

Vector Quantization - Pytorch A vector quantization library originally transcribed from Deepmind's tensorflow implementation, made conveniently into a

Phil Wang 665 Jan 08, 2023
Forecasting with Gradient Boosted Time Series Decomposition

ThymeBoost ThymeBoost combines time series decomposition with gradient boosting to provide a flexible mix-and-match time series framework for spicy fo

131 Jan 08, 2023
Anomaly Detection Based on Hierarchical Clustering of Mobile Robot Data

We proposed a new approach to detect anomalies of mobile robot data. We investigate each data seperately with two clustering method hierarchical and k-means. There are two sub-method that we used for

Zekeriyya Demirci 1 Jan 09, 2022