Conformal Geometric Algebra with Python
Part 1
Hugo Hadfield 2018
Why Python?
- Swiftly becoming THE STANDARD for scientific computing
- Clean and easy syntax
- Huge number of third party libraries
- Good interoperability with Julia, Fortran, C++ etc.
-
Free and open source
- Tying your skill set to a specific company's product (eg. Matlab) is a bad idea!
Why Conformal GA?
- Geometric primitives are elements of the algebra
- All conformal transformations are elements of the algebra
- Good for representing 3D space!
The Python GA ecosystem
The Clifford library - History
- Clifford - GitHub page
- Originally written in the early 2000s by Robert Kern
- Maintained by Alex Arsenovic for many years
- Now maintained by: pygae (Pythonic Geometric Alegbra Enthusiasts)
FOCUSES ON CLEAN SYNTAX AND USEFUL ALGORITHMS
THE GOAL IS TO BE EASY TO USE NOT SUPER SUPER FAST (although it is pretty fast too...)
Installation
pip install clifford
or
conda install clifford -c conda-forge
The operators
Syntax | Operation |
---|---|
| | Symetric inner product |
<< | Left contraction |
^ | Outer product |
* | Geometric product |
X(i) | Return the section of the multivector X of grade i |
X(ei) | Return the section of the multivector X for which ei is the pseudo scalar |
X[i] | Return the i'th coefficient from the multivector X |
X.normal() | Return the normalised multivector so that X*~X is +- 1 |
up(x) | Maps a multivector from ND euclidean space to its (N+1,1) conformal equivalent point |
from clifford.g3c import *
A = up( 4*e1 - 5.1*e2 + 9*e3 ) # Mapping 3DGA to Conformal:
(4.0^e1) - (5.1^e2) + (9.0^e3) + (61.005^e4) + (62.005^e5)
B = up( -2.2*e1 + 3*e2 + 6.8*e3 ) # Mapping 3DGA to Conformal
-(2.2^e1) + (3.0^e2) + (6.8^e3) + (29.54^e4) + (30.54^e5)
A|B # Symmetric inner product
-54.445
A^B # Outer product
(0.78^e12) + (47.0^e13) + (252.371^e14) + (258.571^e15) - (61.68^e23) - (333.669^e24) - (341.769^e25) - (148.974^e34) - (146.774^e35) + (31.465^e45)
A << (A ^ B) # Asymmetric left contraction
(217.78^e1) - (277.6695^e2) + (490.005^e3) + (3321.41723^e4) + (3375.86223^e5)
A*B # Geometric product
-54.445 + (0.78^e12) + (47.0^e13) + (252.371^e14) + (258.571^e15) - (61.68^e23) - (333.669^e24) - (341.769^e25) - (148.974^e34) - (146.774^e35) + (31.465^e45)
(A * B)(2) # Grade selection
(0.78^e12) + (47.0^e13) + (252.371^e14) + (258.571^e15) - (61.68^e23) - (333.669^e24) - (341.769^e25) - (148.974^e34) - (146.774^e35) + (31.465^e45)
(A * B)(1) # Grade selection
0
(A * B)(e123) # Subalgebra selection
-54.445 + (0.78^e12) + (47.0^e13) - (61.68^e23)
(A * B)[0] # Coefficient selection
-54.445000000000164
Geometric primitives in CGA
Grade | Construction | Interpretation |
---|---|---|
1 | up(x) | Point |
2 | A^B | Point pair |
3 | A^B^C | Circle |
3 | A^B^einf | Line |
4 | A^B^C^D | Sphere |
4 | A^B^C^einf | Plane |
from clifford.g3c import * # Import Conformal GA (4,1)
from clifford.tools.g3c import * # Import prebuilt tools for conformal GA
random_conformal_point()
(0.3492^e1) + (16.36818^e2) - (1.09622^e3) + (134.12051^e4) + (135.12051^e5)
random_point_pair()
(0.14781^e12) + (0.2147^e13) + (3.91966^e14) + (3.94971^e15) + (0.26825^e23) + (6.25298^e24) + (6.31054^e25) + (1.9694^e34) + (1.99847^e35) + (0.25491^e45)
random_circle()
(0.00893^e123) - (1.58332^e124) - (1.6043^e125) - (4.67362^e134) - (4.73433^e135) - (0.21387^e145) + (2.53838^e234) + (2.57207^e235) - (0.0124^e245) - (0.37947^e345)
random_line()
(0.54733^e124) + (0.54733^e125) + (4.48487^e134) + (4.48487^e135) - (0.29013^e145) - (9.78048^e234) - (9.78048^e235) + (0.53594^e245) - (0.79284^e345)
random_sphere()
(4.85087^e1234) + (4.84582^e1235) + (0.01843^e1245) - (1.01612^e1345) - (0.12664^e2345)
random_plane()
-(14.57447^e1234) - (14.57447^e1235) + (0.75755^e1245) - (0.45902^e1345) + (0.46414^e2345)
Visualisation
# Import Conformal GA (4,1)
from clifford.g3c import *
# Import prebuilt tools for conformal GA
from clifford.tools.g3c import *
# Import pyganja for visualisation
from pyganja import *
object_array = [
random_conformal_point(),
random_point_pair(),
random_circle(),
random_line(),
random_sphere(),
random_plane()
]
# The simplest way to use pyganja
draw(object_array,
static=False ,
scale=0.1)
Reflections in CGA
- Reflections in CGA are carried out by "sandwiching" the object to be reflected
X_2 = PX_1P
# Import Conformal GA (4,1)
from clifford.g3c import *
# Import prebuilt tools for conformal GA
from clifford.tools.g3c import *
# Import pyganja for visualisation
from pyganja import *
# Make a load of stuff
object_array = [
random_conformal_point(),
random_point_pair(),
random_circle(),
random_line()
]
# Make a plane to reflect it in
P = random_plane()
# Reflect it all in the plane
object_array2 = [P*X*P
for X in object_array]
# Pyganja scene api
gs = GanjaScene()
gs.add_objects(object_array,
color=int('AA000000',16),
static=False)
gs.add_objects(object_array2,
color=int('AAFF0000',16),
static=False)
gs.add_objects([P], color=int('0000FF00',16),
static=False)
render_cef_script(str(gs), scale=0.1)
Conformal Rotors
Rotors in CGA perform
conformal transformations
Rotation
Translation
Dilation
Inversion
X_2 = RX_1\tilde{R}
# Import Conformal GA (4,1)
from clifford.g3c import *
# Import prebuilt tools for conformal GA
from clifford.tools.g3c import *
# Import pyganja for visualisation
from pyganja import *
# Create a random circle
X1 = random_circle()
# Create a random rigid rotor
R_TR = random_rotation_translation_rotor(maximum_translation=0.25,
maximum_angle=np.pi/16)
# Create a dilation rotor
R_d = generate_dilation_rotor(0.9)
# Combine the rotors
R = R_TR*R_d
# Apply the rotor taken to successively higher powers to create a list of circles
circle_list = [ (R**i) * X1 * ~(R**i) for i in range(20) ]
# Draw the list of circles
draw(circle_list, static=False , scale=0.1)
Logarithm of rotors
- In CGA each conformal rotor can be mapped to a bivector with the generalised log function
- These bivectors can be interpolated, allowing you to interpolate rotors
- An alternate interpolation technique is the square root of a rotor, we can generalise this to an n'th root
- Both are implemented in the clifford library
from clifford.tools.g3c import *
from clifford.tools.g3c.rotor_parameterisation import *
from pyganja import *
# Create two objects
X1 = random_circle()
X2 = random_circle()
# Find the rotor between them
R = rotor_between_objects(X1, X2)
# Take the log of the rotor
logR = general_logarithm(R)
# Blend from 0 to 1
rotor_list = [general_exp(0.1*alpha*logR)
for alpha in range(10)]
# Apply the interpolated rotors
object_list = [R_int*X1*~R_int
for R_int in rotor_list]
# The simplest way to use pyganja
draw(object_list ,
static=False ,
scale=0.1)
Direct linear interpolation of conformal objects
- Adding objects does not in general give a valid object multivector
- Need to project to the object manifold
- This can be acheived by considering the form of the added object
X_1 + X_2 = (\alpha + \mu(X_1X_2 + X_2X_1))X_3
This allows us to acheive complex tasks like splining and clustering
from clifford.g3c import *
from clifford.tools.g3c import *
from clifford.tools.g3c.object_clustering import *
from pyganja import *
# Set up parameters for clustering
object_generator = random_circle
n_clusters = 3
n_objects_per_cluster = 10
n_shotgunning = 60
# Generate the objects for clustering
all_objects, object_clusters = generate_n_clusters(object_generator,
n_clusters,
n_objects_per_cluster)
# Perform the k-means clustering
cluster_result = n_clusters_objects(n_clusters, all_objects,
initial_centroids=None,
n_shotgunning=n_shotgunning,
averaging_method='unweighted')
[new_labels, centroids, start_labels, start_centroids] = cluster_result
# Visualise the result
gs = GanjaScene()
gs.add_objects([all_objects[i] for i,l in enumerate(new_labels) if l==0],
static=False, color=int('00FF0000',16))
gs.add_objects([all_objects[i] for i,l in enumerate(new_labels) if l==1],
static=False, color=int('0000FF00',16))
gs.add_objects([all_objects[i] for i,l in enumerate(new_labels) if l==2],
static=False, color=int('000000FF',16))
# render_notebook_script(str(gs),scale=0.1)
# render_cef_script(str(gs),scale=0.1)
Saving and Loading MultiVector Arrays
- Clifford contains functionality for saving arrays of multivectors in a compressed file format .ga based on the open source file format HDF5
- Also supports an uncompressed JSON output for transmission and decoding over the web
from clifford.g3c import *
from clifford.tools.g3c import *
from pyganja import *
# Make a load of lines in an MVArray
line_array = MVArray( generate_random_object_cluster(10, random_line) )
# Save the array as a .ga file
line_array.save('line_array.ga')
# We can also load .ga files
line_array_loaded = layout.load_ga_file('line_array.ga')
Summary
- Clifford is simple and easy to use
- The syntax is close to the mathematics
- Pyganja is compatible visualisation tool for CGA