Metadata-Version: 2.4
Name: Cayley_Calculator
Version: 1.1.0
Summary: Installing a fast table calculator for Cayley-Dickson numbers
Project-URL: Homepage, https://github.com/pypa/Cayley_Calculator
Project-URL: Issues, https://github.com/pypa/Cayley_Calculator/issues
Author-email: Dieter Kadelka <dieterkadelka@aol.com>
Maintainer-email: Dieter Kadelka <dieterkadelka@aol.com>
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# Cayley Dickson Calculator 

**The aim of module Cayley_Calculator is to convert the REPL shell, ipython shell or idle into a fast, programmable table calculator for doing scientific and linear calculations with Cayley-Dickson numbers.** 

**What are Cayley-Dickson numbers (CDN)? CDN's extend real (dimension 1), complex (dimension 2) and quaternionic (dimension 4) numbers (either split or non-split) to octonions (dimension 8), sedenions (dimension 16), pathions (dimension 32), chingons (dimension 64) or routons (dimension 128). These classes are included in the Cayley_Calculator. Further included are extensions of standard functions like `exp, log, sin, arcsin, sinh, arsinh, ...` to these classes. It is even possible to extend scientific functions in `scipy.special`. (Of course in this case scipy has to be installed.) As an example `jv = sedenize(scipy.special.jv,1)` extends the Bessel function of the first kind. Routines for doing linear algebra with CDN's are included. Thus solving equations like `a*x + x*a = b` are possible (even much more complicated equations).** 

**To run Cayley_Calculator the module numpy must be installed. Modules scipy and numpy-quaternion are not necessary, but they extend the functionality of Cayley_Calculator.**

**Elementary background**

(Citation from https://pypi.org/project/hypercomplex/): The [complex numbers](https://en.wikipedia.org/wiki/Complex_number) may be viewed as an extension of the everyday [real numbers](https://en.wikipedia.org/wiki/Real_number). A complex number has two real-number coefficients, one multiplied by 1, the other multiplied by [i](https://en.wikipedia.org/wiki/Imaginary_unit).

In a similar way, a [quaternion](https://en.wikipedia.org/wiki/Quaternion), which has 4 components, can be constructed by combining two complex numbers. Likewise, two quaternions can construct an [octonion](https://en.wikipedia.org/wiki/Octonion) (8 components), and two octonions can construct a [sedenion](https://en.wikipedia.org/wiki/Sedenion) (16 components).

The method for this construction is known as the [Cayley-Dickson construction][2] and the resulting classes of numbers are types of [hypercomplex numbers][1]. 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.

[![Hypercomplex numbers containment diagram][8]][8]

**Theoretical background:** The only aim of this background is to fix notation, not understanding. A Cayley algebra over `R` is an ordered pair `(A,s)`, where `A` is an algebra over `R` with unit element `1` and `s`, the **conjugation**, is an antiautomorphism of `A` such that **trace** `T(a) = a + s(a)` in `R` and **norm** `N(a) = a * s(a) in R`. Here `s(a0,a1,...,a7) = (a0,-a1,...,a7)` and `*` is the not necessary commutative or associative multiplication, where `(a0,...,a7)` is a special **Octonion**. To show how **Sedenions** are defined, let `B = A x A` with usual addition and let the multiplication in `B` be defined by
```py
(x1,y1) * (x2,y2) = (x1*x2 + gamma4*s(y2)*y1, y1*s(x2)+y2*x1)
```
for `(x1,y1)` and `(x2,y2)` in `B`. Note that order matters. `gamma1` is used for the definition of complex numbers from the real numbers, `gamma2` for quaternions from complex numbers, ... `gamma1 = 1` gives **split complex numbers** while `gamma1 = -1` gives the (usual) complex numbers.

With `t((x,y)) = (s(x),-y)` the ordered pair `(B,t)` is a Cayley algebra again with trace `T((x,y)) = T(x)` and norm `N((x,y)) = N(x) - gamma4 * N(y)`, in this case the algebra of **Sedenions**.

**Literature:** A standard text is [N. Bourbaki (1989): Algebra I][9], in particular ch. III.2.4, III.7.4 and ch. III, Appendix. Here you find full proofs about commutativity, associativity and alternativity of Cayley algebras. A well known article is from [J. Baez (2001), The Octonions](https://arxiv.org/pdf/math/0105155.pdf), unfortunately only about Octonions and without proofs of some relevant results.

## Installation

```text
pip install Cayley_Calculator
```
and possibly
```text
pip uninstall Octonion_Sedenion
```
(Cayley_Calculator generalizes Octonion_Sedenion and name conflicts are possible.)

[View on PyPI](https://pypi.org/project/Cayley_Calculator) - [View on GitHub](https://github.com/kadelka/Cayley_Calculator)

This package was built with Python 3.14.6 and has been tested to be compatible with python 3.9 through 3.14 on Linux. Since there is no compiled code it should be compatible with python on Mac and Windows. **An installation of numpy is absolutely necessary**. 

## Basic Usage

```py
import Cayley_Calculator
from octonion import *
```
That's all. To test your installation, try
```py
a,b,c = randinteger(-10,11,3)
a,b
# (Octonion(-9, 9, -9, 2, 10, -5, -9, 6), Octonion(-3, 7, 3, 6, 1, -6, 4, -7))
a*b, b*a
# (Octonion( 17, -244, 80, 28, 39, 25, -171, -43), Octonion( 17, 64, -80, -148, -117, 113, 153, 133)) 
sin(a)
# Octonion(-1.21986179e+08, -1.20165952e+08, 1.20165952e+08, -2.67035450e+07, -1.33517725e+08, 6.67588625e+07, 1.20165952e+08, -8.01106349e+07)
d = randinteger(-10,11,Class=QQ)
# Quaternion(-8, 7, 5, -3)
log(d)
# Quaternion( 2.49521629, 1.76058705, 1.25756218, -0.75453731)
```
The first line in the initialization is always necessary, but the second line may be replaced by `from pathion import *`. Then by default `randinteger(-10,11)` generates a random Pathion with integer coefficients. If we do not want to change the defaults then `randinteger(-10,11,Class=PP)` has the same effect. 

Input of numbers is always handled by `__init__(self,*v,array=None)`. For example
```py
a = Sedenion(Quaternion(1,2,3))
a
# Sedenion(1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
```
first generates `Quaternion(1,2,3,0)` and then converts this into a Sedenion. As a further example (Sedenion may be written as SS)
```py
Sedenion(3,4,5)
SS(array = (3,4,5))
Sedenion(array = np.array([3,4,5]))
SS(*range(3,6))
```
all result in
```py
Sedenion(3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0).
```
A final example (after `from sedenion import *`, otherwise e10 is not defined) with some special functions 
```py
from sedenion import *
e10
# Sedenion(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0)
u = Sedenion(4+5j)
v = Sedenion(4+5*e10)
w = 4+5*e10
print(exp(u))
print(log(v))
print(sin(w))
```
results in
```py
15.48743 -52.35549i
1.85679 +0.89606r
-56.16227 -48.50246r
```

These initializations imply that the numbers generated are non-split. If we want to gerate **split** Cayley numbers, then we have to add the line `set_split()` as in
```py
import Cayley_Calculator
from octonion import *
set_split()
```
(`set_split()` is equivalent to `set_gamma(gamma1=1,gamma2=1,gamma3=1,gamma4=1,gamma5=1,gamma6=1,gamma7=1)`. The result are split Cayley numbers. Without this command `gamma1=-1,...,gamma7=-1`, which results in non split Cayley numbers.

Let's try again with the same values.
```
a = Octonion(-9, 9, -9, 2, 10, -5, -9, 6)
b = Octonion(-3, 7, 3, 6, 1, -6, 4, -7)
a*b,b*a
# (Octonion( 25, -14, 62, 32, -17, -9, 75, -43), Octonion( 25, -166, -62, -152, -61, 147, -93, 133))
a._gamma
# [1, 1, 1, 1, 1, 1, 1]
```
When a Cayley number is generated, it stores the actual gamma as an attribute as in `a._gamma`.

(Note: The last values in the corresponding results of `a*b` and `b*a` are always equal to the values for non split Cayley numbers. Why?)

Each single initialization makes **all** classes **Complex, Quaternion, Octonion, Sedenion, Pathion, Chingon, Routon** accessible. 
## Functionality 

- **Arithmetic**

  After initialization of Cayley_Calculator addition, subtraction, right and left multiplication, right and left division (as far as possible) and the power function are definied. This allows standard notation. Calculations like `(1+2j)*Octonion(3,4,5,6,7)` or `OO(1,2,3)/PP(*range(32))` (same as `Octonion(1,2,3,0,0,0,0,0)/Pathion(0,1,2,...,31)`) are possible. If numpy.quaternion is installed a calculation like `quaternion(1,2,3,0)*OO(*range(1,9))` is possible. The result should be `Octonion(-12, 16, -2, 4, -28, -8, 38, 12)` (for the non split case).

  We have two sorts of division: `(a/b)*b = a = b*(a//b) = a`. Note that `(a*b)/b = a` and `(a*b)//a = b` hold true also (if `a` resp. `b` is no zero divisor). Division by zero divisors is not implemented, but should be possible sometimes by replacing `numpy.linalg.solve` by something more appropriate for ill-conditioned systems. 

  As usual `a**b` is the power of `a` by `b`. Often the power is not defined if the norm `N(a)` is not positive (possible for split Cayley numbers), except for integer `b`. If `N(a) > 0` then we have (approximately) `(a**b)**(1/b) = a`. (Implemented are calculations with polar coordinates which solves partially the problem with multivalued `log`. The module cmath does not implement this algorithm.) *Remark*: For equality in `(a**b)**(1/b) = a` `b` must be an element in an alternative algebra, like reals, complex, quaternion or octonions. Is `b` is a Sedenion, then equality no longer holds true.

  getitem and setitem methods are available for Cayley numbers. `a.z1` gets/sets the first part of `a`, `a.z2` the second. Thus
  ```
  from octonion import *
  set_split()
  a = Octonion( 2, 4, -8, -5, 10, 10, -8, -4)
  a.z1
  # Quaternion( 2, 4, -8, -5)
  a.z2 = -a.z1
  a
  # Octonion( 2, 4, -8, -5, -2, -4, 8, 5)
  print(a*a.conj)
  # 0
  a[1:8:2] = -a[:8:2]
  a
  # Octonion( 2, -2, -8, 8, -2, 2, 8, -8)
  print(a*a.conj)
  # 0
  ```
  Here `a.conj` is the conjugation.

- **Functions**

  1. Standard functions known for the fields R and C are extended to Cayley Numbers. We get the functions *exp*, *sin*, *cos*, *tan*, *sinh*, *cosh* and *tanh* and their inverses *log*, *arcsin*, *arccos*, *arctan*, *arsinh*, *arcosh*, *artanh*, further *sqrt*, *pow*, *norm* (the l2-norm). All these functions return values of the same type as the input. Note that some of these don't return a valid value, if the argument is split. Often these functions are implemented as methods. As an example, `a.sin()` and `sin(a)` return the same value.

  2. The function `sedenize` extends functions `f : C -> C` to functions `f : B -> B`, where `B` is some **non split Cayley algebra**. Example:
  ```py
  from octonion import *
  a = Octonion(1,2,3,4,5)
  ssin = sedenize(cmath.sin)
  sin(a) # implemented sin
  ssin(a) # sedenized sin
  series.sin(a) # Taylor series of sin
  # Always Octonion(653.74588161, 114.24557755, 171.36836632, 228.4911551 , 285.61394387, 0, 0, 0)
  ```
  Note: series is a module, which (for testing) computes some functions the resulting value with Taylor series (slow).

  3. Similar, for split Cayley numbers (after `set_split()`) there is `split_sedenize`, which extends functions `f : R -> R` to functions `f : B -> B`, where `B` now is some **split Cayley algebra**. Example:
  ```py
  from octonion import *
  set_split()
  a = Octonion(1,2,3,4,5)
  ssin = split_sedenize(math.sin)
  sin(a) # implemented sin
  ssin(a) # sedenized sin
  series.sin(a) # Taylor series of sin
  # Always Octonion(-0.01848834, -0.23033006, -0.34549509, -0.46066012, -0.57582515, 0, 0, 0)
  ```

   4. There are random generators `randinteger(a,b,n=1,Class=CLASS)` and `uniform(a,b,n=1,Class=CLASS)`, which generate `n` random Cayley numbers of class CLASS with integer resp. real values. After starting with `from sedenion import *` `randinteger(a,b,Class=Sedenion)` and `randinteger(a,b)` have the same effect. Similarly, `random_ball(r=1,n=1,Class=CLASS)` gerates random Cayley numbers in the ball with radius `r`.

- **Linear Algebra**

  After initialization a class `Operator` exist. `from octonion import *` actually initializes two generators `self.left_matrix()` and `self.right_matrix()` for each class of Cayley numbers. Try
  ```py
  a = Octonion(*range(1,9))
  a.left_matrix()
  a.right_matrix()
  ```
  **Fast left and right multiplication** is done with these generators. Essentially `a * b` is `a.__class__(array = a.left_matrix().array @ b._list)`. Even these simple operators allow solving the equation `a*x+x*a = b` with `(a.left_matrix()+a.right_matrix()).solve(b)`. (Note that `a*x+x*a` and `2*a*x` are different.)

  More linear algebra is available with
  ```py
  from linear_octonion import *
  ```
  (or `linear_pathion`, `linear_chingon`, etc.)
  As a simple example try
  ```py
  from linear_octonion import *
  A = random_operator(5,5)
  b = random_matrix(5)
  x = solve(A,b)
  A @ x - b
  ```
  The result is an array of 5 Octonions of value 0. Here `random_operator(5,5)` generates a random 5 x 5 - array of Octonion-Operators, `random_matrix(5)` a random 5-array of Octonions, `x = solve(A,b)` solves the equation `A * x = b`. Note that the coefficients all are Octonions.

  To solve a system of equations like `a*x + c*(y*b) = d, g*(x*f) + u*y = h`, where `a,b,c,d,f,g,h,u` are known Octonions (maybe generated with `a,b,c,d,f,g,h,u = randinteger(-10,11,8)`, we can proceed like 
  ```py
  L = lambda a : a.left_matrix()
  R = lambda a : a.right_matrix()
  A = np.zeros((2,2),dtype = object)
  A[0,0] = L(a)
  A[0,1] = L(c)*R(b) # first multiplication from right with `b`, then from left with `c` (order matters)
  A[1,0] = L(g)*R(f)
  A[1,1] = L(u)
  v = np.array(((d),(h))) # v now is a column vector of dtype object
  w = solve(A,v)
  ```
  Then with `x = w[0]` and `y = w[1]` the equations hold true.

  **Remark:** There are more routines in this paket, probably mostly not of general interest. As an example, rankalg([a,b]) computes the rank of the algebra generated by the elements a and b. You are free to search through the files. 

## About me

I have been a mathematician (retired) at the KIT in Karlsruhe, Germany. I have studied physics as well. Programming, particularly in python, is a hobby, not a profession. 

[1]: https://en.wikipedia.org/wiki/Hypercomplex_number
[2]: https://en.wikipedia.org/wiki/Cayley%E2%80%93Dickson_construction
[8]: https://raw.githubusercontent.com/discretegames/hypercomplex/main/diagram/diagram.png
[9]: https://link.springer.com/book/9783540642435
