Getting Started

First we import the library

[1]:
import biquaternion_py as bq

Creating Biquaternions

Biquaternions can be created via arrays, comma separated arguments or by inputing a BiQuaternion object. If less than 8 arguments are given, the rest are assumed to be 0.

[2]:
a = bq.BiQuaternion([1,2,3,4,5,6,7,8])
b = bq.BiQuaternion(1,1,1,1,1,1,1,1)
c = bq.BiQuaternion(1,2,3,4)
d = bq.BiQuaternion([3,1,4,1,5])
e = bq.BiQuaternion(a-c)
print(a, "\n", b, "\n", c, "\n", d, "\n", e)
(( 1 ) + ( 2 ) * i + ( 3 ) * j + ( 4 ) * k) + eps * (( 5 ) + ( 6 ) * i + ( 7 ) * j + ( 8 ) * k)
 (( 1 ) + ( 1 ) * i + ( 1 ) * j + ( 1 ) * k) + eps * (( 1 ) + ( 1 ) * i + ( 1 ) * j + ( 1 ) * k)
 (( 1 ) + ( 2 ) * i + ( 3 ) * j + ( 4 ) * k) + eps * (( 0 ) + ( 0 ) * i + ( 0 ) * j + ( 0 ) * k)
 (( 3 ) + ( 1 ) * i + ( 4 ) * j + ( 1 ) * k) + eps * (( 5 ) + ( 0 ) * i + ( 0 ) * j + ( 0 ) * k)
 (( 0 ) + ( 0 ) * i + ( 0 ) * j + ( 0 ) * k) + eps * (( 5 ) + ( 6 ) * i + ( 7 ) * j + ( 8 ) * k)

Basic arithmetic

All basic operations between dual quaternions are supported. Addition, subtraction, multiplication and division, if the divisor is an invertible dual quaternion.

[3]:
a+b
[3]:
$\displaystyle \operatorname{BiQuaternion}\left(2, 3, 4, 5, 6, 7, 8, 9\right)$
[4]:
a-b
[4]:
$\displaystyle \operatorname{BiQuaternion}\left(0, 1, 2, 3, 4, 5, 6, 7\right)$

a*b

[5]:
a/b
[5]:
$\displaystyle \operatorname{BiQuaternion}\left(\frac{5}{2}, \frac{1}{2}, 0, 1, 4, 0, 0, 0\right)$

Also basic unary operations such as quaternion conjugation, epsilon conjugation and finding of inverses are implemented

[6]:
a.conjugate()
[6]:
$\displaystyle \operatorname{BiQuaternion}\left(1, -2, -3, -4, 5, -6, -7, -8\right)$
[7]:
a.eps_conjugate()
[7]:
$\displaystyle \operatorname{BiQuaternion}\left(1, 2, 3, 4, -5, -6, -7, -8\right)$
[8]:
a.inv()
[8]:
$\displaystyle \operatorname{BiQuaternion}\left(\frac{1}{30}, - \frac{1}{15}, - \frac{1}{10}, - \frac{2}{15}, \frac{1}{90}, \frac{1}{9}, \frac{7}{30}, \frac{16}{45}\right)$

Sympy integration

biquaternion_py can easily work sympy for symbolic computations.

[9]:
import sympy as sy

x_ind = sy.symbols("x:8")
y_ind = sy.symbols("y:8")
z_ind = sy.symbols("z:8")

x = bq.BiQuaternion(x_ind)
y = bq.BiQuaternion(y_ind)
z = bq.BiQuaternion(z_ind)
[10]:
x
[10]:
$\displaystyle \operatorname{BiQuaternion}\left(x_{0}, x_{1}, x_{2}, x_{3}, x_{4}, x_{5}, x_{6}, x_{7}\right)$

Polynomials

Thanks to the sympy integration, polynomials can easliy be constructed.

[11]:
t = sy.symbols("t")
s = sy.symbols("s")
[12]:
p = bq.Poly(x*t**2 + y*t + z, t)

print(p)

Polynomials also support all basic operations of the biquaternions.

Multivariate polynomials can also be declared.

[13]:
q =  bq.Poly(x*t**2 + y*t*s + z*s**2, t,s)
[14]:
print(q)
Poly((( s**2*z0 + s*t*y0 + t**2*x0 ) + ( s**2*z1 + s*t*y1 + t**2*x1 ) * i + ( s**2*z2 + s*t*y2 + t**2*x2 ) * j + ( s**2*z3 + s*t*y3 + t**2*x3 ) * k) + eps * (( s**2*z4 + s*t*y4 + t**2*x4 ) + ( s**2*z5 + s*t*y5 + t**2*x5 ) * i + ( s**2*z6 + s*t*y6 + t**2*x6 ) * j + ( s**2*z7 + s*t*y7 + t**2*x7 ) * k),[t, s])

The coefficients of polynomials can be calculated with respect to specific variables, or for all. In the second case the list will be ordered the same way as the variables are ordered.

[15]:
q.all_indet_coeffs(t)
[15]:
[(( s**2*z0 ) + ( s**2*z1 ) * II + ( s**2*z2 ) * JJ + ( s**2*z3 ) * KK) + EE * (( s**2*z4 ) + ( s**2*z5 ) * II + ( s**2*z6 ) * JJ + ( s**2*z7 ) * KK),
 (( s*y0 ) + ( s*y1 ) * II + ( s*y2 ) * JJ + ( s*y3 ) * KK) + EE * (( s*y4 ) + ( s*y5 ) * II + ( s*y6 ) * JJ + ( s*y7 ) * KK),
 (( x0 ) + ( x1 ) * II + ( x2 ) * JJ + ( x3 ) * KK) + EE * (( x4 ) + ( x5 ) * II + ( x6 ) * JJ + ( x7 ) * KK)]
[16]:
q.all_coeffs()
[16]:
[[(( 0 ) + ( 0 ) * II + ( 0 ) * JJ + ( 0 ) * KK) + EE * (( 0 ) + ( 0 ) * II + ( 0 ) * JJ + ( 0 ) * KK),
  (( 0 ) + ( 0 ) * II + ( 0 ) * JJ + ( 0 ) * KK) + EE * (( 0 ) + ( 0 ) * II + ( 0 ) * JJ + ( 0 ) * KK),
  (( z0 ) + ( z1 ) * II + ( z2 ) * JJ + ( z3 ) * KK) + EE * (( z4 ) + ( z5 ) * II + ( z6 ) * JJ + ( z7 ) * KK)],
 [(( 0 ) + ( 0 ) * II + ( 0 ) * JJ + ( 0 ) * KK) + EE * (( 0 ) + ( 0 ) * II + ( 0 ) * JJ + ( 0 ) * KK),
  (( y0 ) + ( y1 ) * II + ( y2 ) * JJ + ( y3 ) * KK) + EE * (( y4 ) + ( y5 ) * II + ( y6 ) * JJ + ( y7 ) * KK)],
 [(( x0 ) + ( x1 ) * II + ( x2 ) * JJ + ( x3 ) * KK) + EE * (( x4 ) + ( x5 ) * II + ( x6 ) * JJ + ( x7 ) * KK)]]

Polynomial factorization

Given a polynomial \(p\) with real norm, we want to calculate a factorization of it. This can be achived in the following way.

Let us first define the polynomial.

[17]:
h1 = bq.rand_rational() + bq.rand_line()
h2 = bq.rand_rational() + bq.rand_line()
h3 = bq.rand_rational() + bq.rand_line()

p = bq.Poly((t-h1)*(t-h2)*(t-h3),t)

To calculate a factorization we first need to find the irreducible factors of the norm polynomial. Since the norm is supposed to be real, we refine the result to eliminate numerical errors and only take the scalar part of the norm polynomial. Also since irreducible_factors uses a sympy algorithm to find the irreducible factors, it needs the norm polynomial to be of numeric type and not of BiQuaternion.

[18]:
norm_poly = bq.Poly(p.norm().poly.scal, *p.indets)

Now we can find the irreducible factors of the norm polynomial, which will generate our factorization of \(p\).

[19]:
_, factors = bq.irreducible_factors(norm_poly)

This gives a list of irreducible factors. Different permutations of these factors give different factorizations. The factorizations are given as a list of linear polynomials.

[20]:
factorization1  = bq.factorize_from_list(p, factors)
factorization2  = bq.factorize_from_list(p, factors[::-1])
[21]:
factorization1
[21]:
[Poly((( t + 1 ) + ( 32311809984245/44725094719148 ) * II + ( 34578632525362/33543821039361 ) * JJ + ( 351896696664851/805051704944664 ) * KK) + EE * (( 0 ) + ( -44284801123314932780983936589/10126691369286099104801594889 ) * II + ( 29680483103375341676412817091/13502255159048132139735459852 ) * JJ + ( 2299604199269621439590760626/1125187929920677678311288321 ) * KK),[t]),
 Poly((( t + 3/8 ) + ( 4078668814369778519021530352221/24129008318361963739967827133700 ) * II + ( 222568068782882460815700835949/8043002772787321246655942377900 ) * JJ + ( 379078450896418831103352832781/3619351247754294560995174070055 ) * KK) + EE * (( 0 ) + ( 93153509251375240617392298755023996825833280903051939224028186/65498517273102844647276809738090807963757311156549170238515125 ) * II + ( -5261551565198463547793840539880940884262623128351926304656166/21832839091034281549092269912696935987919103718849723412838375 ) * JJ + ( -9744759479815913735884924427374697576551688099866177736541233/4366567818206856309818453982539387197583820743769944682567675 ) * KK),[t]),
 Poly((( t - 4 ) + ( 879901041143907952/1258823927887787975 ) * II + ( -116364559407336308423/22658830701980183550 ) * JJ + ( 4828352527998813938/755294356732672785 ) * KK) + EE * (( 0 ) + ( 7173871441066101040305204903230527877/1629913043749205643278320942513303500 ) * II + ( -208038679024699983679882448499348562/58211180133900201545654319375475125 ) * JJ + ( -1639181395297842644170893262659911797/488973913124761692983496282753991050 ) * KK),[t])]

We can now check, if these factorizations are indeed correct.

[22]:
poly1 = 1
for fac in factorization1:
    poly1 *= fac

poly2 = 1
for fac in factorization2:
    poly2 *= fac
[23]:
p == poly1 == poly2
[23]:
True