Ts-geometric-algebra is a Clifford Algebra Generator for TypeScript and JavaScript. It generates Geometric Algebras of any signature and is mostly combatible with Ganja.js. There is no operator overloading or inline functions yet due to lack of support in TypeScript.
(Mathematically, an algebra generated by ts-geometric-algebra is a graded exterior (Grassmann) algebra with a non-metric outer product, extended (Clifford) with geometric and contraction inner products, a Poincare duality operator and the main involutions and morphisms.)
(Technically, ts-geometric-algebra is a code generator producing classes that reificate algebraic literals.)
(Practically, ts-geometric-algebra enables algebraic operations over reals, complex numbers, dual numbers, hyperbolic numbers, vectors, spacetime events, quaternions, dual quaternions, biquaternions or any other Clifford Algebra with full type support from TypeScript.)
Visit bivector.net for our forum and chat - the perfect place for questions and support.
Install ts-geometric-algebra
using npm :
npm install ts-geometric-algebra
And require it in your script :
const Algebra = require('ts-geometric-algebra').default;
To create an Algebra, call the Algebra
function specifying the metric
signature (number of positive, negative and zero dimensions). The result is
an ES6 class implementing the requested clifford algebra.
function Algebra( p, q, r );
// p = number of positive dimensions.
// q = optional number of negative dimensions.
// r = optional number of zero dimensions.
An extended syntax is also available that allows you to further tweak the created Algebra.
function Algebra( p, q, r, {baseType, metric, mulTable, disableUnroll} )
// p = number of positive dimensions.
// q = optional number of negative dimensions.
// r = optional number of zero dimensions.
// baseType = Float32Array or similar
// metric = basis vector inner products in custom order, must agree with p, q and r
// mulTable = Custom signs for the Caley table
// disableUnroll = (boolean) prevent loop unrolling of performance critical methods
Here are some examples :
// Basic
const Hyper = Algebra(1); // Hyperbolic numbers.
const Complex = Algebra(0, 1); // Complex numbers.
const Dual = Algebra(0, 0, 1); // Dual numbers.
const H = Algebra(0, 2); // Quaternions.
// Clifford
const Cl2 = Algebra(2); // Clifford algebra for 2D vector space.
const Cl3 = Algebra(3); // Clifford algebra for 3D vector space.
const TimeSpace = Algebra(1, 3); // Clifford algebra for timespace vectors.
// Geometric
const PGA2D = Algebra(2, 0, 1); // Projective Euclidean 2D plane. (dual)
const PGA3D = Algebra(3, 0, 1); // Projective Euclidean 3D space. (dual)
const CGA2D = Algebra(3, 1); // Conformal 2D space.
const CGA3D = Algebra(4, 1); // Conformal 3D space.
// High-Dimensional GA
const DCGA3D = Algebra(6, 2); // Double Conformal 3D Space.
const TCGA3D = Algebra(9, 3); // Triple Conformal 3D Space.
const DCGSTA = Algebra(4, 8); // Double Conformal Geometric Space Time Algebra.
const QCGA = Algebra(9, 6); // Quadric Conformal Geometric Algebra.
You can now use these classes to generate algebraic elements. Those elements will have all of the
expected properties. (norm
, blade access, dot
, wedge
, mul
, dual
, inverse
, etc ...)
Unlike Ganja.js you must use them in a 'classic' programming style syntax like the example below.
const Complex = Algebra(0, 1); // Complex numbers.
const a = Complex.fromGanja([3, 2]); // 3 + 2i
const b = Complex.fromGanja([1, 4]); // 1 + 4i
return a.mul(b); // returns [-5, 14]
Altough not as pretty or fun as Ganja.js you have the full advantage of types and autocompletion.
Object oriented | Ganja Equivalent | Explanation |
---|---|---|
x.equals(y) |
N/A | Strict equality |
x.closeTo(y, tol?) |
N/A | Equality within given tolerance |
x.hasNaN() |
N/A | Check for Not-a-Numbers |
x.hasInfinity() |
N/A | Check for (negative) infinity |
x.isNil(tol?) |
N/A | Equal or close to zero |
x.isGrade(g, tol?) |
N/A | Only has components of grade g |
x.s |
x.s |
Scalar part (get/set) |
x.ps |
N/A | Pseudoscalar part (get/set) |
x.getAt(...idx) |
N/A | Metric-aware coefficient of the product of basis factors defined by idx |
x.setAt(...idx, a) |
N/A | Set coefficient of product of basis factors idx as a |
x.norm() |
x.Length |
Conjugate norm (metric-aware) |
x.vnorm() |
x.VLength |
Vector norm (ignores metric) {x.length is array length} |
x.taxicabNorm() |
N/A | L1 norm (ignores metric) |
x.maxNorm() |
N/A | Linfinity norm (ignores metric) |
x.neg() |
N/A | Negation (additive inverse) |
x.cwAbs() |
N/A | Component-wise absolute value |
x.involute() |
x.Involute |
Negation of basis factors |
x.rev() |
x.Reverse |
Reversal of basis factors {x.reverse is array reversal} |
x.conjugate() |
x.Conjugate |
Conjugation (combined involution and reversal) |
x.adjugate() |
N/A | Unscaled multiplicative inverse (suitable for int base class) |
x.inverse() |
x.Inverse |
Multiplicative inverse |
x.square() |
x.Mul(x) |
Multiplicative squaring (**) |
x.normalize(a?) |
x.Normalize |
x with norm set to a (default 1) |
x.rotorNormalize() |
N/A | Normalize rotor x |
x.sqrt() |
N/A | Square root. Currently reliable only in dimensions < 2 |
x.rotorSqrt() |
N/A | Rotor square root. Available in certain metrics. |
x.exp() |
x.Exp |
Exponential function |
x.bivectorExp() |
x.Exp |
Bivector exponential function (optimized) |
x.log() |
x.Log |
(Motor) Logarithm. Generic exp inverse available only in dimensions < 2 |
x.rotorLog() |
x.Log |
Rotor Logarithm |
x.clone() |
x.Scale(1) |
Independent copy |
x.dual() |
x.Dual (*) |
Metric independent dual: x.mul(x.dual()) = Cl.pseudoscalar() |
x.undual() |
N/A | Inverse of x.dual() |
x.scale(a) |
x.Scale(a) |
Scalar multiplication |
x.pow(n) |
x.Pow(n) |
Multiply x with itself n times |
x.applyWeights(ws) |
N/A | Replace each basis factor with a weighted copy for every weight in ws |
x.negateGrades(...gs) |
x.Map(...gs) |
Negate the given grades |
x.add(y) |
x.Add(y) |
Component-wise addition (**) |
x.sub(y) |
x.Sub(y) |
Component-wise subtraction (**) |
x.mul(y) |
x.Mul(y) |
Geometric product of x and y (**) |
x.lmul(y) |
y.Mul(x) |
Geometric product from the left (**) |
x.div(y) |
x.Div(y) |
Geometric division from the right |
x.ldiv(y) |
y.Inverse.Mul(x) |
Left inverse product |
x.ldivs(y) |
x.Inverse.Mul(y) |
Geometric division from the left |
x.wedge(y) |
x.Wedge(y) |
Wedge (outer) product. Metric independent (**) |
x.lwedge(y) |
y.Wedge(x) |
Wedge product from the left (**) |
x.vee(y) |
x.Vee(y) |
Vee (dual) product. Metric indepentend. x.vee(y) = y.dual().wedge(x.dual()).undual() (**) |
x.lvee(y) |
y.Vee(x) |
Vee product from the left (**) |
x.rotorMean(y) |
N/A | Geometric mean of rotors x and y |
x.contract(y, ctn) |
N/A | Contract x by y using criterion ctn |
x.dot(y) |
x.Dot(y) |
Dot product. Symmetric criterion (**) |
x.dotL(y) |
x.LDot(y) |
Left contraction of x by y (**) |
x.ldotL(y) |
y.LDot(x) |
Left contraction of y by x (**) |
x.dotR(y) |
N/A | Right contraction of x by y |
x.ldotR(y) |
N/A | Right contraction of y by x |
x.dotS(y) |
N/A | Scalar product. Nil criterion |
x.imag() |
N/A | Filter out grade 0 |
x.even() |
x.Even() |
Filter out odd grades |
x.grade(n) |
x.Grade(n) |
Keep only grade n components |
x.vector() |
x.Vector |
Array of vector components |
x.vector(n) |
N/A | Array of n -vector components |
x.rotor() |
N/A | Array of components of even grade |
x.ganja() |
x |
Array of all components in lexicographic order |
x.invScale(y, t?) |
N/A | Ratio of weights between coincident x and y . Returs NaN if non-coincidence exteeds threshold t . |
x.grades(t?) |
N/A | Array of grades present in x exceeding threshold t (default 0 ) |
x.meetJoin(y, t?) |
N/A | True (of threshold t ) meet and join of blades x and y |
x.star(y) |
N/A | Scalar product with number result |
Cl.zero() |
Cl.Scalar(0) |
Zero element |
Cl.scalar(a?) |
Cl.Scalar(a) |
Scalar element of size a (default 1 ) |
Cl.pseudoscalar(a?) |
N/A | Pseudoscalar element of size a (default 1 ) |
Cl.basisBlade(...idx) |
N/A | Unit basis blade given by the product of the given basis factors |
Cl.fromVector(vs) |
Cl.Vector(...vs) |
Vector element with given components (from an array) |
Cl.fromVector(vs, 2) |
Cl.Bivector(...vs) |
Bivector element with given components (lexicographic order) |
Cl.fromVector(vs, 3) |
Cl.Trivector(...vs) |
Trivector element with given components |
Cl.fromVector(vs, g) |
Cl.nVector(g, ...vs) |
g -vector element with given components |
Cl.fromRotor(vs) |
N/A | Even grade element with given components |
Cl.fromGanja(vs) |
new Cl(vs) |
New element with components given in lexicographic order |
Cl.dimensions |
--> |
Math.log(Cl.describe().basis.length)/Math.LN2 number of dimensions |
Cl.size |
--> |
Cl.describe().basis.length algebra size |
Cl.metric |
--> |
Cl.describe().metric.slice(1, n+1) basis vector metric |
Cl.mulTable |
--> |
Similar to Cl.describe().mulTable but signs only |
new Cl(vs) |
N/A | Not recommended, use fromGanja instead. New element with components given in bit field order |
(*) Only in degenerate metrics (**) Loop unrolled for maximum performance
Object oriented | Function Oriented | Ganja Equivalent | Explanation |
---|---|---|---|
x.podge() |
podge(x) |
x.Mul(ps) |
Right-multiplication by Cl.pseudoscalar() |
x.unpodge() |
unpodge(x) |
x.Div(ps) |
Right-division by Cl.pseudoscalar() |
x.podgeL() |
podgeL(x) |
ps.Mul(x) |
Left-multiplication by Cl.pseudoscalar() |
x.unpodgeL() |
unpodgeL(x) |
ps.Inverse.Mul(x) |
Left-division by Cl.pseudoscalar() |
x.star() |
star(x) |
N/A | Non-degenerate x.podge() |
x.unstar() |
unstar(x) |
N/A | Inverse of x.star() |
x.starL(x) |
starL(x) |
x.Dual (*) |
Non-degenerate x.podgeL() |
x.unstarL(x) |
unstarL(x) |
N/A | Inverse of x.starL() |
x.hodge() |
hodge(x) |
N/A | Hodge dual |
x.unhodge() |
unhodge(x) |
N/A | Inverse of x.hodge() |
x.hodgeL() |
hodgeL(x) |
N/A | Left Hodge dual |
x.unhodgeL() |
unhodgeL(x) |
N/A | Inverse of x.hodgeL() |
(*) Only in non-degenerate metrics
Metric (pqr) | Available operations |
---|---|
p+q+r <= 2 | (all) |
400 | rotorSqrt , rotorNormalize |
310 | rotorSqrt , rotorNormalize |
301 | rotorLog , rotorSqrt , rotorNormalize |
410 | rotorSqrt , rotorNormalize |
Function Oriented | Ganja Equivalent | Explanation |
---|---|---|
equals(x, y) |
N/A | Strict equality |
closeTo(x, y, tol?) |
N/A | Equality within given tolerance |
hasNaN(x) |
N/A | Check for Not-a-Numbers |
hasInfinity(x) |
N/A | Check for (negative) infinity |
isNil(x, tol?) |
N/A | Equal or close to zero |
isGrade(x, g, tol?) |
N/A | Only has components of grade g |
norm(x) |
x.Length |
Conjugate norm (metric-aware) |
vnorm(x) |
x.VLength |
Vector norm (ignores metric) {x.length is array length} |
taxicabNorm(x) |
N/A | L1 norm (ignores metric) |
maxNorm(x) |
N/A | Linfinity norm (ignores metric) |
neg(x) |
N/A | Negation (additive inverse) |
cwAbs(x) |
N/A | Component-wise absolute value |
involute(x) |
x.Involute |
Negation of basis factors |
rev(x) |
x.Reverse |
Reversal of basis factors {x.reverse is array reversal} |
conjugate(x) |
x.Conjugate |
Conjugation (combined involution and reversal) |
adjugate(x) |
x.Inverse |
Unscaled multiplicative inverse |
inverse(x) |
x.Inverse |
Multiplicative inverse |
square(x) |
x.Mul(x) |
Multiplicative squaring (**) |
normalize(x, a?) |
x.Normalize |
x with norm set to a (default 1) |
rotorNormalize(x) |
N/A | Normalize rotor x |
sqrt(x) |
N/A | Square root. Currently reliable only in dimensions < 2 |
rotorSqrt(x) |
N/A | Rotor square root. Available in certain metrics. |
exp(x) |
x.Exp |
Exponential function |
bivectorExp(x) |
x.Exp |
Bivector exponential function (optimized) |
log(x) |
x.Log |
(Motor) Logarithm. Generic exp inverse available only in dimensions < 2 |
rotorLog(x) |
x.Log |
Rotor Logarithm |
clone(x) |
x.Scale(1) |
Independent copy |
dual(x) |
x.Dual (*) |
Metric independent dual: x.mul(x.dual()) = Cl.pseudoscalar() |
undual(x) |
N/A | Inverse of x.dual() |
scale(x, a) |
x.Scale(a) |
Scalar multiplication |
pow(x, n) |
x.Pow(n) |
Multiply x with itself n times |
applyWeights(x, ws) |
N/A | Replace each basis factor with a weighted copy for every weight in ws |
negateGrades(x, ...gs) |
x.Map(...gs) |
Negate the given grades |
add(...args) |
x.Add(y) |
Component-wise addition (**) |
sub(x, y) |
x.Sub(y) |
Component-wise subtraction (**) |
mul(...args) |
x.Mul(y) |
Geometric product of x and y (**) |
div(x, y) |
x.Div(y) |
Geometric division from the right |
ldivs(x, y) |
x.Inverse.Mul(y) |
Geometric division from the left |
wedge(...args) |
x.Wedge(y) |
Wedge (outer) product. Metric independent (**) |
vee(...args) |
x.Vee(y) |
Vee (dual) product. Metric indepentend. vee(x, y) = undual(dual(y), dual(x)) (**) |
rotorMean(x, y) |
N/A | Geometric mean of rotors x and y |
contract(x, y, ctn) |
N/A | Contract x by y using criterion ctn |
dot(x, y) |
x.Dot(y) |
Dot product. Symmetric criterion (**) |
dotL(x, y) |
x.LDot(y) |
Left contraction of x by y (**) |
dotR(x, y) |
N/A | Right contraction of x by y |
dotS(x, y) |
N/A | Scalar product. Nil criterion |
imag(x) |
N/A | Filter out grade 0 |
even(x) |
x.Even() |
Filter out odd grades |
grade(x, n) |
x.Grade(n) |
Keep only grade n components |
invScale(x, y, t?) |
N/A | Ratio of weights between coincident x and y . Returs NaN if non-coincidence exteeds threshold t . |
grades(x, t?) |
N/A | Array of grades present in x exceeding threshold t (default 0 ) |
meetJoin(x, y, t?) |
N/A | True (of threshold t ) meet and join of blades x and y |
star(x, y) |
N/A | Scalar product with number result |
(*) Only in degenerate metrics (**) Loop unrolled for maximum performance
The functions equals
, closeTo
, add
, sub
, mul
, div
, ldivs
, wedge
, vee
also work on the number
type.
You can solver equations of the form (for the moment pretending that javascript has scalar multiplication of arrays)
x = coeffs[0] * basis[0] + coeffs[1] * basis[1] + ... + coeffs[n-1] * basis[n-1]
for the unknown coefficients coeffs
.
const coeffs = vLinSolve(x, basis);
This comes with the added cost of algebra creation on each call. If you need to do a lot of solving create your algebra beforehand.
import {Algebra, linSolve} from 'ts-geometric-algebra';
const Grassmann = Algebra(0, 0, 3);
const x = Grassmann.fromVector([1, 2, 3]);
const basis = [
Grassmann.fromVector([1, 1, 1]),
Grassmann.fromVector([0, 1, -1]),
Grassmann.fromVector([-1, 2, -2])
];
const coeffs = linSolve(x, basis); // [ 2.5, -3.5, 1.5 ]
While all generated geometric algebras are associative it's possible to produce non-associative algebras using custom multiplication tables.
This package comes with a pre-configured table for Octonions and monkey-patches conjugate
, inverse
, sqrt
, exp
and log
to work correctly.
import {makeOctonion} from 'ts-geometric-algebra';
const O = makeOctonion();
const e1 = Octonion.basisBlade(0);
const e2 = Octonion.basisBlade(1);
const e4 = Octonion.basisBlade(2);
e1.mul(e2).mul(e4) // [0, 0, 0, 0, 0, 0, +1]
e1.mul(e2.mul(e4)) // [0, 0, 0, 0, 0, 0, -1]