Title: | Arbitrary Dimensional Clifford Algebras |
---|---|
Description: | A suite of routines for Clifford algebras, using the 'Map' class of the Standard Template Library. Canonical reference: Hestenes (1987, ISBN 90-277-1673-0, "Clifford algebra to geometric calculus"). Special cases including Lorentz transforms, quaternion multiplication, and Grassmann algebra, are discussed. Vignettes presenting conformal geometric algebra, quaternions and split quaternions, dual numbers, and Lorentz transforms are included. The package follows 'disordR' discipline. |
Authors: | Robin K. S. Hankin [aut, cre] |
Maintainer: | Robin K. S. Hankin <[email protected]> |
License: | GPL (>= 2) |
Version: | 1.0-9 |
Built: | 2025-01-22 06:14:08 UTC |
Source: | https://github.com/robinhankin/clifford |
A suite of routines for Clifford algebras, using the 'Map' class of the Standard Template Library. Canonical reference: Hestenes (1987, ISBN 90-277-1673-0, "Clifford algebra to geometric calculus"). Special cases including Lorentz transforms, quaternion multiplication, and Grassmann algebra, are discussed. Vignettes presenting conformal geometric algebra, quaternions and split quaternions, dual numbers, and Lorentz transforms are included. The package follows 'disordR' discipline.
The DESCRIPTION file:
Package: | clifford |
Type: | Package |
Title: | Arbitrary Dimensional Clifford Algebras |
Version: | 1.0-9 |
Depends: | R (>= 4.1.0) |
Authors@R: | person(given=c("Robin", "K.", "S."), family="Hankin", role = c("aut","cre"), email="[email protected]", comment = c(ORCID = "0000-0001-5982-0415")) |
Maintainer: | Robin K. S. Hankin <[email protected]> |
Description: | A suite of routines for Clifford algebras, using the 'Map' class of the Standard Template Library. Canonical reference: Hestenes (1987, ISBN 90-277-1673-0, "Clifford algebra to geometric calculus"). Special cases including Lorentz transforms, quaternion multiplication, and Grassmann algebra, are discussed. Vignettes presenting conformal geometric algebra, quaternions and split quaternions, dual numbers, and Lorentz transforms are included. The package follows 'disordR' discipline. |
License: | GPL (>= 2) |
LazyData: | yes |
Suggests: | knitr, rmarkdown, testthat, onion (>= 1.5-3), lorentz (>= 1.1-1), emulator (>= 1.2-24), jordan (>= 1.0-5), permutations (>= 1.1-5), covr, quadform |
VignetteBuilder: | knitr |
Imports: | Rcpp (>= 0.12.5), disordR (>= 0.9-8-4), magrittr, methods, partitions (>= 1.10-4), freealg (>= 1.0-4) |
LinkingTo: | Rcpp,BH |
URL: | https://github.com/RobinHankin/clifford, https://robinhankin.github.io/clifford/ |
BugReports: | https://github.com/RobinHankin/clifford/issues |
Config/pak/sysreqs: | libgmp3-dev |
Repository: | https://robinhankin.r-universe.dev |
RemoteUrl: | https://github.com/robinhankin/clifford |
RemoteRef: | HEAD |
RemoteSha: | 21ff88f2065ed1786782d03dac063e7d8d416a04 |
Author: | Robin K. S. Hankin [aut, cre] (<https://orcid.org/0000-0001-5982-0415>) |
Index of help topics:
Ops.clifford Arithmetic Ops Group Methods for 'clifford' objects [.clifford Extract or Replace Parts of a clifford allcliff Clifford object containing all possible terms antivector Antivectors or pseudovectors as.vector Coerce a clifford vector to a numeric vector cartan Cartan map between clifford algebras clifford Create, coerce, and test for 'clifford' objects clifford-package Arbitrary Dimensional Clifford Algebras const The constant term of a Clifford object dot-class Class "dot" drop Drop redundant information even Even and odd clifford objects grade The grade of a clifford object homog Homogenous Clifford objects horner Horner's method involution Clifford involutions lowlevel Low-level helper functions for 'clifford' objects magnitude Magnitude of a clifford object minus Take the negative of a vector numeric_to_clifford Coercion from numeric to Clifford form print.clifford Print clifford objects pseudoscalar Coercion from numeric to Clifford form quaternion Quaternions using Clifford algebras rcliff Random clifford objects signature The signature of the Clifford algebra summary.clifford Summary methods for clifford objects term Deal with terms zap Zap small values in a clifford object zero The zero Clifford object
Robin K. S. Hankin [aut, cre] (<https://orcid.org/0000-0001-5982-0415>)
Maintainer: Robin K. S. Hankin <[email protected]>
J. Snygg (2012). A new approach to differential geometry using Clifford's geometric Algebra, Birkhauser. ISBN 978-0-8176-8282-8
D. Hestenes (1987). Clifford algebra to geometric calculus, Kluwer. ISBN 90-277-1673-0
C. Perwass (2009). Geometric algebra with applications in engineering, Springer. ISBN 978-3-540-89068-3
D. Hildenbrand (2013). Foundations of geometric algebra computing. Springer, ISBN 978-3-642-31794-1
as.1vector(1:4) as.1vector(1:4) * rcliff() # Following from Ablamowicz and Fauser (see vignette): x <- clifford(list(1:3,c(1,5,7,8,10)),c(4,-10)) + 2 y <- clifford(list(c(1,2,3,7),c(1,5,6,8),c(1,4,6,7)),c(4,1,-3)) - 1 x*y # signature irrelevant
as.1vector(1:4) as.1vector(1:4) * rcliff() # Following from Ablamowicz and Fauser (see vignette): x <- clifford(list(1:3,c(1,5,7,8,10)),c(4,-10)) + 2 y <- clifford(list(c(1,2,3,7),c(1,5,6,8),c(1,4,6,7)),c(4,1,-3)) - 1 x*y # signature irrelevant
The Clifford algebra on basis vectors has
independent multivectors. Function
allcliff()
generates a clifford object with a nonzero
coefficient for each multivector.
allcliff(n,grade)
allcliff(n,grade)
n |
Integer specifying dimension of underlying vector space |
grade |
Grade of multivector to be returned. If missing,
multivector contains every term of every grade |
Robin K. S. Hankin
allcliff(6) a <- allcliff(5) a[] <- rcliff()*100
allcliff(6) a <- allcliff(5) a[] <- rcliff()*100
Antivectors or pseudovectors
antivector(v, n = length(v)) as.antivector(v) is.antivector(C, include.pseudoscalar=FALSE)
antivector(v, n = length(v)) as.antivector(v) is.antivector(C, include.pseudoscalar=FALSE)
v |
Numeric vector |
n |
Integer specifying dimensionality of underlying vector space |
C |
Clifford object |
include.pseudoscalar |
Boolean: should the pseudoscalar be considered an antivector? |
An antivector is an -dimensional Clifford object, all of
whose terms are of grade
. An antivector has
degrees
of freedom. Function
antivector(v,n)
interprets v[i]
as
the coefficient of .
Function as.antivector()
is a convenience wrapper, coercing its
argument to an antivector of minimal dimension (zero entries are
interpreted consistently).
The pseudoscalar is a peculiar edge case. Consider:
A <- clifford(list(c(1,2,3))) B <- A + clifford(list(c(1,2,4))) > is.antivector(A) [1] FALSE > is.antivector(B) [1] TRUE > is.antivector(A,include.pseudoscalar=TRUE) [1] TRUE > is.antivector(B,include.pseudoscalar=TRUE) [1] TRUE
One could argue that A
should be an antivector as it is a term in
B
, which is definitely an antivector. Use
include.pseudoscalar=TRUE
to ensure consistency in this case.
Compare as.1vector()
, which returns a clifford object of grade 1.
An antivector is always a blade.
Robin K. S. Hankin
Wikipedia contributors. (2018, July 20). “Antivector”. In Wikipedia, The Free Encyclopedia. Retrieved 19:06, January 27, 2020, from https://en.wikipedia.org/w/index.php?title=Antivector&oldid=851094060
antivector(1:5) as.1vector(c(1,1,2)) %X% as.1vector(c(3,2,2)) c(1*2-2*2, 2*3-1*2, 1*2-1*3) # note sign of e_13
antivector(1:5) as.1vector(c(1,1,2)) %X% as.1vector(c(3,2,2)) c(1*2-2*2, 2*3-1*2, 1*2-1*3) # note sign of e_13
Given a clifford object with all terms of grade 1, return the corresponding numeric vector
## S3 method for class 'clifford' as.vector(x,mode = "any")
## S3 method for class 'clifford' as.vector(x,mode = "any")
x |
Object of class clifford |
mode |
ignored |
The awkward R idiom of this function is because the terms may be stored in any order; see the examples
Robin K. S. Hankin
x <- clifford(list(6,2,9),1:3) as.vector(x) as.1vector(as.vector(x)) == x # should be TRUE
x <- clifford(list(6,2,9),1:3) as.vector(x) as.1vector(as.vector(x)) == x # should be TRUE
Cartan's map isomorphisms from
to
and
cartan(C, n = 1) cartan_inverse(C, n = 1)
cartan(C, n = 1) cartan_inverse(C, n = 1)
C |
Object of class |
n |
Strictly positive integer |
Returns an object of class clifford
. The default value
n=1
maps to
(
cartan()
) and
to
.
Robin K. S. Hankin
E. Hitzer and S. Sangwine 2017. “Multivector and multivector matrix inverses in real Clifford algebras”, Applied Mathematics and Computation. 311:3755-89
a <- rcliff(d=7) # Cl(4,3) b <- rcliff(d=7) # Cl(4,3) signature(4,3) # e1^2 = e2^2 = e3^2 = e4^2 = +1; e5^2 = e6^2=e7^2 = -1 ab <- a*b # multiplication in Cl(4,3) signature(0,7) # e1^2 = ... = e7^2 = -1 cartan(a)*cartan(b) == cartan(ab) # multiplication in Cl(0,7); should be TRUE signature(Inf) # restore default
a <- rcliff(d=7) # Cl(4,3) b <- rcliff(d=7) # Cl(4,3) signature(4,3) # e1^2 = e2^2 = e3^2 = e4^2 = +1; e5^2 = e6^2=e7^2 = -1 ab <- a*b # multiplication in Cl(4,3) signature(0,7) # e1^2 = ... = e7^2 = -1 cartan(a)*cartan(b) == cartan(ab) # multiplication in Cl(0,7); should be TRUE signature(Inf) # restore default
clifford
objectsAn object of class clifford
is a member of a Clifford algebra.
These objects may be added and multiplied, and have various applications
in physics and mathematics.
clifford(terms, coeffs=1) is_ok_clifford(terms, coeffs) as.clifford(x) is.clifford(x) nbits(x) nterms(x) ## S3 method for class 'clifford' dim(x)
clifford(terms, coeffs=1) is_ok_clifford(terms, coeffs) as.clifford(x) is.clifford(x) nbits(x) nterms(x) ## S3 method for class 'clifford' dim(x)
terms |
A list of integer vectors with strictly increasing entries corresponding to the basis vectors of the underlying vector space |
coeffs |
Numeric vector of coefficients |
x |
Object of class |
Function clifford()
is the formal creation mechanism
for clifford
objects. If coeffs
is of length 1, it
will be recycled (even if terms
is empty, in which case the
zero Clifford object is returned). Argument terms
is passed
through list_modifier()
, so a zero entry is interpreted as
numeric(0)
Function as.clifford()
is much more user-friendly and
attempts to coerce a range of input arguments to clifford form
Function nbits()
returns the number of bits required in
the low-level C routines to store the terms (this is the largest
entry in the list of terms). For a scalar, this is zero and for the
zero clifford object it (currently) returns zero as well although
a case could be made for NULL
Function nterms()
returns the number of terms in the
expression
Function is_ok_clifford()
is a helper function that checks
for consistency of its arguments
Robin K. S. Hankin
Snygg 2012. “A new approach to differential geometry using Clifford's geometric algebra”. Birkhauser; Springer Science+Business.
(x <- clifford(list(1,2,1:4),1:3)) # Formal creation method (y <- as.1vector(4:2)) (z <- rcliff(include.fewer=TRUE)) terms(x+100) coeffs(z) ## Clifford objects may be added and multiplied: x + y x*y
(x <- clifford(list(1,2,1:4),1:3)) # Formal creation method (y <- as.1vector(4:2)) (z <- rcliff(include.fewer=TRUE)) terms(x+100) coeffs(z) ## Clifford objects may be added and multiplied: x + y x*y
Get and set the constant term of a clifford object.
const(C,drop=TRUE) is.real(C) ## S3 replacement method for class 'clifford' const(x) <- value
const(C,drop=TRUE) is.real(C) ## S3 replacement method for class 'clifford' const(x) <- value
C , x
|
Clifford object |
value |
Replacement value |
drop |
Boolean, with default |
Extractor method for specific terms. Function const()
returns
the constant element of a Clifford object. Note that const(C)
returns the same as grade(C,0)
, but is faster. If C
is a
numeric vector, the first element is returned: any other elements are
silently discarded, but this may change in future.
The R idiom in const<-()
is slightly awkward:
> body(`const<-.clifford`) { stopifnot(length(value) == 1) x <- x - const(x) return(x + value) }
The reason that it is not simply return(x-const(x)+value)
or
return(x+value-const(x))
is to ensure numerical accuracy; see
examples.
Robin K. S. Hankin
grade
,
clifford
,
getcoeffs
,
is.zero
X <- clifford(list(1,1:2,1:3,3:5),6:9) X X <- X + 1e300 X const(X) # should be 1e300 const(X) <- 0.6 const(X) # should be 0.6, no numerical error # compare naive approach: X <- clifford(list(1,1:2,1:3,3:5),6:9)+1e300 X+0.6-const(X) # constant gets lost in the numerics X <- clifford(list(1,1:2,1:3,3:5),6:9)+1e-300 X-const(X)+0.6 # answer correct by virtue of left-associativity x <- 2+rcliff(d=3,g=3) jj <- x*cliffconj(x) is.real(jj*rev(jj)) # should be TRUE
X <- clifford(list(1,1:2,1:3,3:5),6:9) X X <- X + 1e300 X const(X) # should be 1e300 const(X) <- 0.6 const(X) # should be 0.6, no numerical error # compare naive approach: X <- clifford(list(1,1:2,1:3,3:5),6:9)+1e300 X+0.6-const(X) # constant gets lost in the numerics X <- clifford(list(1,1:2,1:3,3:5),6:9)+1e-300 X-const(X)+0.6 # answer correct by virtue of left-associativity x <- 2+rcliff(d=3,g=3) jj <- x*cliffconj(x) is.real(jj*rev(jj)) # should be TRUE
The dot object is defined so that idiom like .[x,y]
returns the
commutator, that is, (x*y-y*x)/2
. The factor of 2 ensures that
.[x,y] == x %X% y
.
The dot object is generated by running script inst/dot.Rmd
,
which includes some further discussion and technical documentation,
and creates file dot.rda
which resides in the data/
directory.
x |
Object of any class |
i , j
|
elements to commute |
... |
Further arguments to |
Always returns an object of the same class as xy
.
Robin K. S. Hankin
x <- rcliff() y <- rcliff() z <- rcliff() .[x,.[y,z]] + .[y,.[z,x]] + .[z,.[x,y]] # Jacobi identity
x <- rcliff() y <- rcliff() z <- rcliff() .[x,.[y,z]] + .[y,.[z,x]] + .[z,.[x,y]] # Jacobi identity
Coerce scalar Clifford objects to numeric
drop(x) drop_clifford(x)
drop(x) drop_clifford(x)
x |
Clifford object |
If its argument is a pure scalar clifford object, or the pseudoscalar,
coerce to numeric. Scalar or pseudoscalar clifford objects are
coerced to an unnamed numeric vector (of length 1). Checking
for being the pseudoscalar requires that option maxdim
be set.
Function drop()
is generic, dispatching to helper function
drop_clifford()
for clifford objects. The logic of
drop_clifford()
prevents is.pseudoscalar()
being called
if maxdim
is NULL
.
Many functions in the package take drop
as an argument
which, if TRUE
, means that the function returns a
drop
ped value.
Robin K. S. Hankin
drop(as.clifford(5)) const(rcliff()) const(rcliff(),drop=FALSE)
drop(as.clifford(5)) const(rcliff()) const(rcliff(),drop=FALSE)
A clifford object is even if every term has even grade, and odd if every term has odd grade.
Functions is.even()
and is.odd()
test a clifford object
for evenness or oddness.
Functions evenpart()
and oddpart()
extract the even
or odd terms from a clifford object, and we write and
respectively; we have
is.even(C) is.odd(C) evenpart(C) oddpart(C)
is.even(C) is.odd(C) evenpart(C) oddpart(C)
C |
Clifford object |
Robin K. S. Hankin
A <- rcliff() A == evenpart(A) + oddpart(A) # should be true
A <- rcliff() A == evenpart(A) + oddpart(A) # should be true
Extract or replace subsets of cliffords.
## S3 method for class 'clifford' C[index, ...,drop=FALSE] ## S3 replacement method for class 'clifford' C[index, ...] <- value coeffs(x) coeffs(x) <- value list_modifier(B) getcoeffs(C, B) ## S3 method for class 'clifford' Im(z) ## S3 method for class 'clifford' Re(z)
## S3 method for class 'clifford' C[index, ...,drop=FALSE] ## S3 replacement method for class 'clifford' C[index, ...] <- value coeffs(x) coeffs(x) <- value list_modifier(B) getcoeffs(C, B) ## S3 method for class 'clifford' Im(z) ## S3 method for class 'clifford' Re(z)
C , x , z
|
A clifford object |
index |
elements to extract or replace |
value |
replacement value |
B |
A list of integer vectors, terms |
drop |
Boolean: should constant clifford objects be coerced to numeric? |
... |
Further arguments |
Extraction and replacement methods. The extraction method uses
getcoeffs()
and the replacement method uses low-level helper
function c_overwrite()
.
In the extraction function a[index]
, if index
is a list,
further arguments are ignored; if not, the dots are used. If
index
is a list, its elements are interpreted as integer vectors
indicating which terms to be extracted (even if it is a disord
object). If index
is a disord
object, standard
consistency rules are applied. The extraction methods are designed so
that idiom such as a[coeffs(a)>3]
works.
For replacement methods, the standard use-case is a[i] <- b
in
which argument i
is a list of integer vectors and b
a
length-one numeric vector; (replacement vectors of length greater than
one are currently not implemented, whether or not they violate
disordR
discipline). Otherwise, to manipulate parts of a
clifford object, use coeffs(a) <- value
; disord
discipline is enforced. Idiom such as a[coeffs(a)<2] <- 0
is
implemented experimentally, as syntactic sugar for
coeffs(a)[coeffs(a)<2] <- 0
. Replacement using a list-valued
index, as in A[i] <- value
uses an ugly hack if value
is
zero. Replacement methods are not yet finalised and not yet fully
integrated with the disordR
package.
Idiom such as a[] <- b
follows the spray
package. If
b
is a length-one scalar, then coeffs(a) <- b
has the same
effect as a[] <- b
.
Grade-based replacement methods such as grade(C,n) <- value
are
impemented and documented at grade.Rd
.
Functions terms()
[see term.Rd
] and coeffs()
extract the terms and coefficients from a clifford object. These
functions return disord
objects but the ordering is consistent
between them (an extended discussion of this phenomenon is presented
in the mvp package). Note that coeffs()
returns
numeric(0)
on the zero clifford object.
Function coeffs<-()
(idiom coeffs(a) <- b
) sets all
coefficients of a
to b
. This has the same effect as
a[] <- b
.
Extracting or replacing a list with a repeated elements is usually a Bad
Idea (tm). However, if option warn_on_repeats
is set to
FALSE
, no warning will be given (and the coefficient will be the
sum of the coefficients of the term; see the examples).
Function getcoeffs()
is a lower-level helper function that
lacks the succour offered by [.clifford()
. It returns a named
numeric vector [not a disord
object: the order of the elements
is determined by the order of argument B
]. Compare standard
extraction, eg a[index]
, which returns a clifford object. The
names of the returned vector are determined by function
catterm()
.
Attempting to extract a coefficient of a term that includes a negative
index will throw an error. The coefficient of a term not present in
the Clifford object (including term with an index larger than
indicated by maxyterm()
) will return zero.
The index for the constant is formally list(numeric(0))
, but
this is a pain to type. Square bracket extraction and
getcoeffs()
have special dispensation for zero entries, which
are translated by helper function list_modifier()
to
numeric(0)
and listified if necessary. The upshot is that
x[0]
and getcoeffs(x,0)
work as expected, returning the
constant.
Function Im()
is a generic, which sets the real component of its
argument to zero (as per the onion package). Function
Re()
is a convenience synonym for const()
.
Vignette getcoeffs
gives a more extended discussion of function
getcoeffs()
.
Ops.clifford
,
clifford
,
term
grade
A <- clifford(list(1,1:2,1:3),1:3) B <- clifford(list(1:2,1:6),c(44,45)) A[1,c(1,3,4)] A[2:3, 4] <- 99 A[] <- B X <- 5 + 6*e(1) -7*e(1:3) + 3*e(4:5) X[0] # special dispensation for zero X[0,drop=TRUE] # coerce to numeric X[list(0,1:3)] getcoeffs(X,0) getcoeffs(X,list(1,0,1:3)) # clifford(list(1,1:2,1:2),1:3) # would give a warning options("warn_on_repeats" = FALSE) clifford(list(1,1:2,1:2),1:3) # works; 1e1 + 5e_12 options("warn_on_repeats" = TRUE) # return to default behaviour.
A <- clifford(list(1,1:2,1:3),1:3) B <- clifford(list(1:2,1:6),c(44,45)) A[1,c(1,3,4)] A[2:3, 4] <- 99 A[] <- B X <- 5 + 6*e(1) -7*e(1:3) + 3*e(4:5) X[0] # special dispensation for zero X[0,drop=TRUE] # coerce to numeric X[list(0,1:3)] getcoeffs(X,0) getcoeffs(X,list(1,0,1:3)) # clifford(list(1,1:2,1:2),1:3) # would give a warning options("warn_on_repeats" = FALSE) clifford(list(1,1:2,1:2),1:3) # works; 1e1 + 5e_12 options("warn_on_repeats" = TRUE) # return to default behaviour.
The grade of a term is the number of basis vectors in it.
grade(C, n, drop=TRUE) grade(C,n) <- value grades(x) gradesplus(x) gradesminus(x) gradeszero(x)
grade(C, n, drop=TRUE) grade(C,n) <- value grades(x) gradesplus(x) gradesminus(x) gradeszero(x)
C , x
|
Clifford object |
n |
Integer vector specifying grades to extract |
value |
Replacement value, a numeric vector |
drop |
Boolean, with default |
A term is a single expression in a Clifford object. It has a
coefficient and is described by the basis vectors it comprises. Thus
is a term but
is not.
The grade of a term is the number of basis vectors in it. Thus
the grade of is 1, and the grade of
is 3. The grade operator
is used to extract terms
of a particular grade, with
for any Clifford object . Thus
is said to be homogenous of grade
.
Hestenes sometimes writes subscripts that specify grades using an
overbar as in
. It is conventional to denote
the zero-grade object
as
simply
.
We have
Function grades()
returns an (unordered) vector specifying the
grades of the constituent terms. Function grades<-()
allows
idiom such as grade(x,1:2) <- 7
to operate as expected [here to
set all coefficients of terms with grades 1 or 2 to value 7].
Function gradesplus()
returns the same but counting only basis
vectors that square to , and
gradesminus()
counts only
basis vectors that square to . Function
signature()
controls which basis vectors square to and which to
.
From Perwass, page 57, given a bilinear form
and a basis blade with
, then
Function gradeszero()
counts only the basis vectors squaring to
zero (I have not seen this anywhere else, but it is a logical
suggestion).
If the signature is zero, then the Clifford algebra reduces to a
Grassmann algebra and products match the wedge product of exterior
calculus. In this case, functions gradesplus()
and
gradesminus()
return NA
.
Function grade(C,n)
returns a clifford object with just the
elements of grade g
, where g %in% n
.
Idiom like grade(C,r) <- value
, where r
is a non-negative
integer (or vector of non-negative integers) should behave as expected.
It has two distinct cases: firstly, where value
is a length-one
numeric vector; and secondly, where value
is a clifford object:
Firstly, grade(C,r) <- value
with value
a
length-one numeric vector. This changes the coefficient of all
grade-r
terms to value
. Note that disordR
discipline must be respected, so if value
has length exceeding
one, a disordR
consistency error might be raised.
Secondly, grade(C,r) <- value
with value
a
clifford object. This should operate as expected: it will replace the
grade-r
components of C
with value
. If
value
has any grade component not in r
, a “grade
mismatch” error will be returned. Thus, only the grade-r
components of C
may be modified with this construction. It is
semi vectorised: if r
is a vector, it is interpreted as a set of
grades to replace.
The zero grade term, grade(C,0)
, is given more naturally by
const(C)
.
Function c_grade()
is a helper function that is documented at
Ops.clifford.Rd
.
In the C code, “term” has a slightly different meaning, referring to the vectors without the associated coefficient.
Robin K. S. Hankin
C. Perwass 2009. “Geometric algebra with applications in engineering”. Springer.
a <- clifford(sapply(seq_len(7),seq_len),seq_len(7)) a grades(a) grade(a,5) a <- clifford(list(0,3,7,1:2,2:3,3:4,1:3,1:4),1:8) b <- clifford(list(4,1:2,2:3),c(101,102,103)) grade(a,1) <- 13*e(6) grade(a,2) <- grade(b,2) grade(a,0:2) <- grade(b,0:2)*7 signature(2,2) x <- rcliff() drop(gradesplus(x) + gradesminus(x) + gradeszero(x) - grades(x)) a <- rcliff() a == Reduce(`+`,sapply(unique(grades(a)),function(g){grade(a,g)}))
a <- clifford(sapply(seq_len(7),seq_len),seq_len(7)) a grades(a) grade(a,5) a <- clifford(list(0,3,7,1:2,2:3,3:4,1:3,1:4),1:8) b <- clifford(list(4,1:2,2:3),c(101,102,103)) grade(a,1) <- 13*e(6) grade(a,2) <- grade(b,2) grade(a,0:2) <- grade(b,0:2)*7 signature(2,2) x <- rcliff() drop(gradesplus(x) + gradesminus(x) + gradeszero(x) - grades(x)) a <- rcliff() a == Reduce(`+`,sapply(unique(grades(a)),function(g){grade(a,g)}))
A clifford object is homogenous if all its terms are the same grade. A
scalar (including the zero clifford object) is considered to be
homogenous. This ensures that is.homog(grade(C,n))
always
returns TRUE
.
is.homog(C)
is.homog(C)
C |
Object of class clifford |
Nonzero homogenous clifford objects have a multiplicative inverse.
Robin K. S. Hankin
is.homog(rcliff()) is.homog(rcliff(include.fewer=FALSE))
is.homog(rcliff()) is.homog(rcliff(include.fewer=FALSE))
Horner's method for Clifford objects
horner(P,v)
horner(P,v)
P |
A Clifford object |
v |
Numeric vector of coefficients |
Given a polynomial
it is possible to express in the algebraically equivalent
form
which is much more efficient for evaluation, as it requires only
multiplications and
additions, and this is optimal. The output
of
horner()
depends on the signature()
.
Horner's method is not as cool for Clifford objects as it is for
(e.g.) multivariate polynomials or freealg
objects. This is
because powers of Clifford objects don't get more complicated as the
power increases.
Robin K. S. Hankin
horner(1+e(1:3)+e(2:3) , 1:6) rcliff() |> horner(1:4)
horner(1+e(1:3)+e(2:3) , 1:6) rcliff() |> horner(1:4)
An involution is a function that is its own inverse, or
equivalently . There are several important
involutions on Clifford objects; these commute past the grade operator
with
and are linear:
.
The dual is documented here for convenience, even though it is not an involution (applying the dual four times is the identity).
The reverse is given by
rev()
(both Perwass and Dorst use a tilde, as in
or
. However, both
Hestenes and Chisholm use a dagger, as in
. This page uses Perwass's notation).
The reverse of a term written as a product of basis vectors is
simply the product of the same basis vectors but written in reverse
order. This changes the sign of the term if the number of basis
vectors is 2 or 3 (modulo 4). Thus, for example,
and
.
Formally, if
, then
.
Perwass shows that
The Conjugate is given by
Conj()
(we use Perwass's notation, def 2.9 p59). This
depends on the signature of the Clifford algebra; see
grade.Rd
for notation. Given a basis blade
with
, then we have
,
where
. Alternatively,
we might say
where
[NB
I have changed Perwass's notation].
The main (grade) involution or grade involution
is given by
gradeinv()
. This
changes the sign of any term with odd grade:
(I don't see this in Perwass or Hestenes; notation follows Hitzer and Sangwine). It is a special case of grade negation.
The grade -negation
is given by
neg()
. This
changes the sign of the grade component of
. It is
formally defined as
but function
neg()
uses a more
efficient method. It is possible to negate all terms with specified
grades, so for example we might have and
the R idiom would be
neg(A,c(1,2,5))
. Note that Hestenes
uses “” to mean the same as
.
The Clifford conjugate is
given by
cliffconj()
. It is distinct from conjugation
, and is defined in Hitzer and Sangwine as
The dual of a clifford object
is
given by
dual(C,n)
; argument n
is the dimension of the
underlying vector space. Perwass gives
where is the unit pseudoscalar
[note that Hestenes uses
to mean something different].
The dual is sensitive to the signature of the Clifford algebra
and the dimension of the underlying vector space.
## S3 method for class 'clifford' rev(x) ## S3 method for class 'clifford' Conj(z) cliffconj(z) neg(C,n) gradeinv(C)
## S3 method for class 'clifford' rev(x) ## S3 method for class 'clifford' Conj(z) cliffconj(z) neg(C,n) gradeinv(C)
C , x , z
|
Clifford object |
n |
Integer vector specifying grades to be negated in |
Robin K. S. Hankin
x <- rcliff() x rev(x) A <- rblade(g=3) B <- rblade(g=4) rev(A %^% B) == rev(B) %^% rev(A) # should be TRUE rev(A * B) == rev(B) * rev(A) # should be TRUE options(maxdim=8) a <- rcliff(d=8) dual(dual(dual(dual(a,8),8),8),8) == a # should be TRUE options(maxdim=NULL) # restore default
x <- rcliff() x rev(x) A <- rblade(g=3) B <- rblade(g=4) rev(A %^% B) == rev(B) %^% rev(A) # should be TRUE rev(A * B) == rev(B) * rev(A) # should be TRUE options(maxdim=8) a <- rcliff(d=8) dual(dual(dual(dual(a,8),8),8),8) == a # should be TRUE options(maxdim=NULL) # restore default
clifford
objectsHelper functions for clifford
objects, written in C
using the
STL
map class.
c_identity(L, p, m) c_grade(L, c, m, n) c_add(L1, c1, L2, c2, m) c_multiply(L1, c1, L2, c2, m, sig) c_power(L, c, m, p, sig) c_equal(L1, c1, L2, c2, m) c_overwrite(L1, c1, L2, c2, m) c_cartan(L, c, m, n) c_cartan_inverse(L, c, m, n)
c_identity(L, p, m) c_grade(L, c, m, n) c_add(L1, c1, L2, c2, m) c_multiply(L1, c1, L2, c2, m, sig) c_power(L, c, m, p, sig) c_equal(L1, c1, L2, c2, m) c_overwrite(L1, c1, L2, c2, m) c_cartan(L, c, m, n) c_cartan_inverse(L, c, m, n)
L , L1 , L2
|
Lists of terms |
c1 , c2 , c
|
Numeric vectors of coefficients |
m |
Maximum entry of terms |
n |
Grade to extract |
p |
Integer power |
sig |
Two positive integers, |
The functions documented here are low-level helper functions that wrap
the C
code. They are called by functions like
clifford_plus_clifford()
, which are themselves called by the
binary operators documented at Ops.clifford.Rd
. The functions
documented here are not really intended for day-to-day use.
Function c_identity()
checks that the list of terms L
is
the same length as the vector coefficients p
; if not, an error
is given. Note that R function clifford()
will recycle the
coefficient vector if of length 1, so that
clifford(list(1,1:2),7)
works as expected (but
c_identity(list(1,1:2),7,2)
will throw an error).
Function clifford_inverse()
is problematic as nonnull blades
always have an inverse; but function is.blade()
is not yet
implemented. Blades (including null blades) have a pseudoinverse, but
this is not implemented yet either.
The high-level functions documented here return an object of class
clifford
. But don't use the low-level functions.
Robin K. S. Hankin
Following Perwass, the magnitude of a multivector is defined as
Where denotes the Euclidean scalar product
eucprod()
. Recall that the Euclidean scalar product is never
negative (the function body is sqrt(abs(eucprod(z)))
; the
abs()
is needed to avoid numerical roundoff errors in
eucprod()
giving a negative value).
## S3 method for class 'clifford' Mod(z)
## S3 method for class 'clifford' Mod(z)
z |
Clifford objects |
If you want the square,
and not
, it is faster and more accurate
to use
eucprod(A)
, because this avoids a needless square root.
There is a nice example of scalar product at rcliff.Rd
.
Robin K. S. Hankin
Mod(rcliff()) # Perwass, p68, asserts that if A is a k-blade, then (in his notation) # AA == A*A. # In package idiom, A*A == A %star% A: A <- rcliff() Mod(A*A - A %star% A) # meh A <- rblade() Mod(A*A - A %star% A) # should be small
Mod(rcliff()) # Perwass, p68, asserts that if A is a k-blade, then (in his notation) # AA == A*A. # In package idiom, A*A == A %star% A: A <- rcliff() Mod(A*A - A %star% A) # meh A <- rblade() Mod(A*A - A %star% A) # should be small
Very simple function that takes the negative of a vector, here so that idiom such as
coeffs(z)[gradesminus(z)%%2 != 0] %<>% minus
works as intended (this taken from Conj.clifford()
).
minus(x)
minus(x)
x |
Any vector or disord object |
Returns a vector or disord
Robin K. S. Hankin
Given a numeric value or vector, return a Clifford algebra element
numeric_to_clifford(x) as.1vector(x) is.1vector(x) scalar(x=1) as.scalar(x=1) is.scalar(C) basis(n,x=1) e(n,x=1)
numeric_to_clifford(x) as.1vector(x) is.1vector(x) scalar(x=1) as.scalar(x=1) is.scalar(C) basis(n,x=1) e(n,x=1)
x |
Numeric vector |
n |
Integer specifying dimensionality of underlying vector space |
C |
Object possibly of class Clifford |
Function as.scalar()
takes a length-one numeric vector and
returns a Clifford scalar of that value (to extract the scalar
component of a multivector, use const()
).
Function is.scalar()
is a synonym for is.real()
which is
documented at const.Rd
.
Function as.1vector()
takes a numeric vector and returns the
linear sum of length-one blades with coefficients given by x
;
function is.1vector()
returns TRUE
if every term is of
grade 1.
Function numeric_to_vector()
dispatches to either
as.scalar()
for length-one vectors or as.1vector()
if the length is greater than one.
Function basis()
returns a wedge product of basis vectors;
function e()
is a synonym. There is special dispensation for
zero, so e(0)
returns the Clifford scalar 1.
Function antivector()
should arguably be described here but is
actually documented at antivector.Rd
.
Robin K. S. Hankin
getcoeffs
,antivector
,const
,pseudoscalar
as.scalar(6) as.1vector(1:8) e(5:8) Reduce(`+`,sapply(seq_len(7),function(n){e(seq_len(n))},simplify=FALSE))
as.scalar(6) as.1vector(1:8) e(5:8) Reduce(`+`,sapply(seq_len(7),function(n){e(seq_len(n))},simplify=FALSE))
clifford
objectsDifferent arithmetic operators for clifford objects, including many different types of multiplication.
## S3 method for class 'clifford' Ops(e1, e2) clifford_negative(C) geoprod(C1,C2) clifford_times_scalar(C,x) clifford_plus_clifford(C1,C2) clifford_eq_clifford(C1,C2) clifford_inverse(C) cliffdotprod(C1,C2) fatdot(C1,C2) lefttick(C1,C2) righttick(C1,C2) wedge(C1,C2) scalprod(C1,C2=rev(C1),drop=TRUE) eucprod(C1,C2=C1,drop=TRUE) maxyterm(C1,C2=as.clifford(0)) C1 %.% C2 C1 %dot% C2 C1 %^% C2 C1 %X% C2 C1 %star% C2 C1 % % C2 C1 %euc% C2 C1 %o% C2 C1 %_|% C2 C1 %|_% C2
## S3 method for class 'clifford' Ops(e1, e2) clifford_negative(C) geoprod(C1,C2) clifford_times_scalar(C,x) clifford_plus_clifford(C1,C2) clifford_eq_clifford(C1,C2) clifford_inverse(C) cliffdotprod(C1,C2) fatdot(C1,C2) lefttick(C1,C2) righttick(C1,C2) wedge(C1,C2) scalprod(C1,C2=rev(C1),drop=TRUE) eucprod(C1,C2=C1,drop=TRUE) maxyterm(C1,C2=as.clifford(0)) C1 %.% C2 C1 %dot% C2 C1 %^% C2 C1 %X% C2 C1 %star% C2 C1 % % C2 C1 %euc% C2 C1 %o% C2 C1 %_|% C2 C1 %|_% C2
e1 , e2 , C , C1 , C2
|
Objects of class |
x |
Scalar, length one numeric vector |
drop |
Boolean, with default |
The function Ops.clifford()
passes unary and binary arithmetic
operators “+
”, “-
”, “*
”,
“/
” and “^
” to the appropriate specialist
function. Function maxyterm()
returns the maximum index in the
terms of its arguments.
The package has several binary operators:
Geometric product | A*B = geoprod(A,B) |
|
Inner product | A %.% B = cliffdotprod(A,B)
|
|
Outer product | A %^% B = wedge(A,B) |
|
Fat dot product | A %o% B = fatdot(A,B) |
|
Left contraction | A %_|% B = lefttick(A,B) |
|
Right contraction | A %|_% B = righttick(A,B) |
|
Cross product | A %X% B = cross(A,B) |
|
Scalar product | A %star% B = star(A,B) |
|
Euclidean product | A %euc% B = eucprod(A,B) |
|
In R idiom, the geometric product geoprod(.,.)
has to be
indicated with a “*
” (as in A*B
) and so the
binary operator must be %*%
: we need a different idiom for
scalar product, which is why %star%
is used.
Because geometric product is often denoted by juxtaposition, package
idiom includes a % % b
for geometric product.
Binary operator %dot%
is a synonym for %.%
, which
causes problems for rmarkdown.
Function clifford_inverse()
returns an inverse for nonnull
Clifford objects for
, and a few other special cases. The
functionality is problematic as nonnull blades always have an inverse;
but function
is.blade()
is not yet implemented. Blades
(including null blades) have a pseudoinverse, but this is not
implemented yet either.
The scalar product of two clifford objects is defined as the zero-grade component of their geometric product:
In package idiom the scalar product is given by A %star% B
or
scalprod(A,B)
. Hestenes and Perwass both use an asterisk for
scalar product as in “”, but in package idiom, the
asterisk is reserved for geometric product.
Note: in the package, A*B
is the geometric product.
The Euclidean product (or Euclidean scalar product) of two clifford objects is defined as
where denotes Conjugate [as in
Conj(a)
]. In
package idiom the Euclidean scalar product is given by
eucprod(A,B)
or A %euc% B
, both of which return
A * Conj(B)
.
Note that the scalar product can be positive or
negative [that is,
A %star% A
may be any sign], but the
Euclidean product is guaranteed to be non-negative [that is, A
%euc% A
is always positive or zero].
Dorst defines the left and right contraction (Chisholm calls these the
left and right inner product) as and
. See the vignette for more details.
Division, as in idiom x/y
, is defined as
x*clifford_inverse(y)
. Function clifford_inverse()
uses
the method set out by Hitzer and Sangwine but is limited to
.
The Lie bracket, is implemented in the
package using idiom such as
.[x,y]
, and this is documented at
dot.Rd
.
Many of the functions documented here use low-level helper functions
that wrap C code. For example, fatdot()
uses
c_fatdotprod()
. These are documented at lowlevel.Rd
.
The high-level functions documented here return a clifford
object. The low-level functions are not really intended for the
end-user.
All the different Clifford products have binary operators for
convenience including the wedge product %^%
. However, as an
experimental facility, the caret “^
” returns either
multiplicative powers [as in A^3=A*A*A
], or a wedge product [as
in A^B = A %^% B = wedge(A,B)
] depending on the class of the
second argument. I don't see that “A ^ B
” is at all
ambiguous but OTOH I might withdraw it if it proves unsatisfactory for
some reason.
Compare the stokes package, where multiplicative powers do
not really make sense and A^B
is interpreted as a wedge product
of differential forms and
. In stokes,
the wedge product is the sine qua non for the whole package and
needs a terse idiomatic representation (although there
A%^%B
returns the wedge product too).
Using %.%
causes severe and weird difficult-to-debug
problems in markdown documents.
Robin K. S. Hankin
E. Hitzer and S. Sangwine 2017. “Multivector and multivector matrix inverses in real Clifford algebras”. Applied Mathematics and Computation 311:375-389
u <- rcliff(5) v <- rcliff(5) w <- rcliff(5) u v u*v u+(v+w) == (u+v)+w # should be TRUE by associativity of "+" u*(v*w) == (u*v)*w # should be TRUE by associativity of "*" u*(v+w) == u*v + u*w # should be TRUE by distributivity # Now if x,y are _vectors_ we have: x <- as.1vector(sample(5)) y <- as.1vector(sample(5)) x*y == x%.%y + x%^%y x %^% y == x %^% (y + 3*x) x %^% y == (x*y-x*y)/2 # should be TRUE # above are TRUE for x,y vectors (but not for multivectors, in general) ## Inner product "%.%" is not associative: x <- rcliff(5,g=2) y <- rcliff(5,g=2) z <- rcliff(5,g=2) x %.% (y %.% z) == (x %.% y) %.% z ## Other products should work as expected: x %|_% y ## left contraction x %_|% y ## right contraction x %o% y ## fat dot product x ^ y ## Experimental wedge product idiom, plain caret
u <- rcliff(5) v <- rcliff(5) w <- rcliff(5) u v u*v u+(v+w) == (u+v)+w # should be TRUE by associativity of "+" u*(v*w) == (u*v)*w # should be TRUE by associativity of "*" u*(v+w) == u*v + u*w # should be TRUE by distributivity # Now if x,y are _vectors_ we have: x <- as.1vector(sample(5)) y <- as.1vector(sample(5)) x*y == x%.%y + x%^%y x %^% y == x %^% (y + 3*x) x %^% y == (x*y-x*y)/2 # should be TRUE # above are TRUE for x,y vectors (but not for multivectors, in general) ## Inner product "%.%" is not associative: x <- rcliff(5,g=2) y <- rcliff(5,g=2) z <- rcliff(5,g=2) x %.% (y %.% z) == (x %.% y) %.% z ## Other products should work as expected: x %|_% y ## left contraction x %_|% y ## right contraction x %o% y ## fat dot product x ^ y ## Experimental wedge product idiom, plain caret
Print methods for Clifford algebra
## S3 method for class 'clifford' print(x,...) ## S3 method for class 'clifford' as.character(x,...) catterm(a)
## S3 method for class 'clifford' print(x,...) ## S3 method for class 'clifford' as.character(x,...) catterm(a)
x |
Object of class |
... |
Further arguments, currently ignored |
a |
Integer vector representing a term |
The print method does not change the internal representation of a
clifford
object, which is a two-element list, the first of which
is a list of integer vectors representing terms, and the second is a
numeric vector of coefficients. The print method has special
dispensation for the zero clifford object.
The print method is sensitive to the value of options separate
and basissep
. If option separate
is FALSE
(the
default), the method prints the basis blades in a compact form, as in
“e_134
”. The indices of the basis vectors are separated
with the value of option basissep
which is usually NULL
;
but if , then setting option
basissep
to a comma
(“,
”) might look good as it will print e_10,11,12
instead of e_101112
:
options("basissep" = ",")
If option separate
is TRUE
, the method prints the basis
vectors separately, as in e10 e11 e12
:
options("separate" = TRUE)
Function catterm()
is a low-level helper function, used in the
print method, coercion to character, and also in function
getcoeffs()
to set the names of its output. It takes an integer
vector like c(1,5,6)
and returns a representation of the
corresponding basis blade, in this case “e_156
”. Function
catterm()
is where options basissep
and separate
are processed. Special dispensation is needed for length-zero vectors,
for which the empty string is returned. This is needed to ensure that
the constant term (which has a basis blade of numeric(0)
) is
treated appropriately. See also list_modifier()
which deals with
this issue.
Experimental bespoke print method print_clifford_quaternion()
and
print_clifford_pauli()
is included. This are executed if option
clifford_print_special
is quaternion
; if NULL
, then
print_clifford_default()
is used. It is straightforward to add
further bespoke print methods if needed (modify
print.clifford()
).
Robin K. S. Hankin
a <- rclifff(9) a # default print method incomprehensible options("separate" = TRUE) a # marginally better options("separate" = FALSE) options(basissep=",") a # clearer; YMMV options(basissep = NULL, maxdim=NULL) # restore default options("maxdim" = 3) signature(3) a <- clifford(list(0,c(1,2),c(1,3),c(2,3)),6:9) a options("clifford_print_special" = "quaternion") a options("maxdim" = NULL) options("clifford_print_special" = NULL) signature(Inf)
a <- rclifff(9) a # default print method incomprehensible options("separate" = TRUE) a # marginally better options("separate" = FALSE) options(basissep=",") a # clearer; YMMV options(basissep = NULL, maxdim=NULL) # restore default options("maxdim" = 3) signature(3) a <- clifford(list(0,c(1,2),c(1,3),c(2,3)),6:9) a options("clifford_print_special" = "quaternion") a options("maxdim" = NULL) options("clifford_print_special" = NULL) signature(Inf)
Given a numeric value or vector, return a Clifford algebra element
pseudoscalar() is.pseudoscalar(C)
pseudoscalar() is.pseudoscalar(C)
C |
Object possibly of class Clifford |
Function pseudoscalar()
returns the unit pseudoscalar of
dimensionality option("maxdim")
and function
is.pseudoscalar()
checks for a Clifford object being a
pseudoscalar. Note that these functions require maxdim
to be set; otherwise they are meaningless.
Usually, one will set option(maxdim)
at the start of a session,
together with the signature. Then one might define I <-
pseudoscalar()
in the interests of compactness and legibility.
Robin K. S. Hankin
getcoeffs
,numeric_to_clifford
,const
options(maxdim=6) I <- pseudoscalar() is.pseudoscalar(I) options(maxdim=NULL) # restore default
options(maxdim=6) I <- pseudoscalar() is.pseudoscalar(I) options(maxdim=NULL) # restore default
Converting quaternions to and from Clifford objects is not part of the
package but functionality and a short discussion is included in
inst/quaternion_clifford.Rmd
.
Given a quaternion , one may identify
with
,
with
, and
with
(the constant term is of course
).
A different mapping, from the quaternions to
is given at
signature.Rd
.
Robin K. S. Hankin
Random Clifford algebra elements, intended as quick
“get you going” examples of clifford
objects
rcliff(n=9, d=6, g=4, include.fewer=TRUE) rclifff(n=100,d=20,g=10,include.fewer=TRUE) rblade(d=7, g=3)
rcliff(n=9, d=6, g=4, include.fewer=TRUE) rclifff(n=100,d=20,g=10,include.fewer=TRUE) rblade(d=7, g=3)
n |
Number of terms |
d |
Dimensionality of underlying vector space |
g |
Maximum grade of any term |
include.fewer |
Boolean, with |
Function rcliff()
gives a quick nontrivial Clifford object,
typically with terms having a range of grades (see ‘grade.Rd’);
argument include.fewer=FALSE
ensures that all terms are of the
same grade. Function rclifff()
is the same but returns a more
complicated object by default.
Function rblade()
gives a Clifford object that is a
blade (see ‘term.Rd’). It returns the wedge product of a
number of 1-vectors, for example
.
Perwass gives the following lemma:
Given blades , then
In the proof he notes in an intermediate step that
Package idiom is shown in the examples.
If the grade exceeds the dimensionality, , then
the result is arguably zero;
rcliff()
returns an error.
Robin K. S. Hankin
rcliff() rcliff(d=3,g=2) rcliff(3,10,7) rcliff(3,10,7,include=TRUE) x1 <- rcliff() x2 <- rcliff() x3 <- rcliff() x1*(x2*x3) == (x1*x2)*x3 # should be TRUE rblade() # We can invert blades easily: a <- rblade() ainv <- rev(a)/scalprod(a) zap(a*ainv) # 1 (to numerical precision) zap(ainv*a) # 1 (to numerical precision) # Perwass 2009, lemma 3.9: A <- rblade(d=9,g=4) B <- rblade(d=9,g=5) C <- rblade(d=9,g=6) grade(A*B*C,0)-grade(C*A*B,0) # zero to numerical precision # Intermediate step x1 <- grade(A*B,3) %star% C x2 <- C %star% grade(A*B,3) x3 <- grade(C*A*B,0) max(x1,x2,x3) - min(x1,x2,x3) # zero to numerical precision
rcliff() rcliff(d=3,g=2) rcliff(3,10,7) rcliff(3,10,7,include=TRUE) x1 <- rcliff() x2 <- rcliff() x3 <- rcliff() x1*(x2*x3) == (x1*x2)*x3 # should be TRUE rblade() # We can invert blades easily: a <- rblade() ainv <- rev(a)/scalprod(a) zap(a*ainv) # 1 (to numerical precision) zap(ainv*a) # 1 (to numerical precision) # Perwass 2009, lemma 3.9: A <- rblade(d=9,g=4) B <- rblade(d=9,g=5) C <- rblade(d=9,g=6) grade(A*B*C,0)-grade(C*A*B,0) # zero to numerical precision # Intermediate step x1 <- grade(A*B,3) %star% C x2 <- C %star% grade(A*B,3) x3 <- grade(C*A*B,0) max(x1,x2,x3) - min(x1,x2,x3) # zero to numerical precision
Getting and setting the signature of the Clifford algebra
signature(p,q=0) is_ok_sig(s) showsig(s) ## S3 method for class 'sigobj' print(x,...)
signature(p,q=0) is_ok_sig(s) showsig(s) ## S3 method for class 'sigobj' print(x,...)
s , p , q
|
Integers, specifying number of positive elements on the
diagonal of the quadratic form, with |
x |
Object of class |
... |
Further arguments, currently ignored |
The signature functionality is modelled on the lorentz
package; clifford::signature()
operates in the same way as
lorentz::sol()
which gets and sets the speed of light. The idea
is that both the speed of light and the signature of a Clifford algebra
are generally set once, at the beginning of an R session, and
subsequently change only very infrequently.
Clifford algebras require a bilinear form
on
. If
we define
where . With this quadratic form the vector space
is denoted
and we say that
is the signature of the bilinear form
. This gives rise to
the Clifford algebra
.
If the signature is , then we have
Note that corresponds to a positive-semidefinite
quadratic form in which
for all
and
for all
.
Similarly,
corresponds to a negative-semidefinite
quadratic form in which
for all
and
for all
.
A strictly positive-definite quadratic form is specified by infinite
[in which case
is irrelevant], and
signature(Inf)
implements this. For a strictly negative-definite
quadratic form we would have which would
be
signature(0,Inf)
.
If we specify for all
, then the
operation reduces to the wedge product of a Grassmann algebra. Package
idiom for this is to set
with
signature(0,0)
, but
this is not recommended: use the stokes package for Grassmann
algebras, which is much more efficient and uses nicer idiom.
Function signature(p,q)
returns the signature invisibly; but
setting option show_signature
to TRUE
makes
showsig()
[which is called by signature()
] change the
default prompt so it displays the signature, much like showSOL
in
the lorentz package. Note that changing the signature changes
the prompt immediately (if show_signature
is TRUE
), but
changing option show_signature
has no effect until
showsig()
is called.
Calling signature()
[that is, with no arguments] returns an
object of class sigobj
with elements corresponding to and
. There is special dispensation for “infinite”
or
: the
sigobj
class ensures that a near-infinite integer
such as .Machine$integer.max
will be printed as
“Inf
” rather than, for example,
“2147483647
”.
Function is_ok_sig()
is a helper function that checks for a
proper signature. If we set signature(p,q)
, then technically
implies
, but usually we are not
interested in
when
and want this to be an error.
Option
maxdim
specifies the maximum value of , with
default
NULL
corresponding to infinity. If exceeds
maxdim
, then is_ok_sig()
throws an error. Note that it is
sometimes fine to have maxdim > p+q
[and indeed this is useful in
the context of dual numbers]. This option is intended to be a
super-strict safety measure.
> e(6) Element of a Clifford algebra, equal to + 1e_6 > options(maxdim=5) > e(5) Element of a Clifford algebra, equal to + 1e_5 > e(6) Error in is_ok_clifford(terms, coeffs) : option maxdim exceeded
Robin K. S. Hankin
signature() e(1)^2 e(2)^2 signature(1) e(1)^2 e(2)^2 # note sign signature(3,4) sapply(1:10,function(i){drop(e(i)^2)}) signature(Inf) # restore default # Nice mapping from Cl(0,2) to the quaternions (loading clifford and # onion simultaneously is discouraged): # library("onion") # signature(0,2) # Q1 <- rquat(1) # Q2 <- rquat(1) # f <- function(H){Re(H)+i(H)*e(1)+j(H)*e(2)+k(H)*e(1:2)} # f(Q1)*f(Q2) - f(Q1*Q2) # zero to numerical precision # signature(Inf)
signature() e(1)^2 e(2)^2 signature(1) e(1)^2 e(2)^2 # note sign signature(3,4) sapply(1:10,function(i){drop(e(i)^2)}) signature(Inf) # restore default # Nice mapping from Cl(0,2) to the quaternions (loading clifford and # onion simultaneously is discouraged): # library("onion") # signature(0,2) # Q1 <- rquat(1) # Q2 <- rquat(1) # f <- function(H){Re(H)+i(H)*e(1)+j(H)*e(2)+k(H)*e(1:2)} # f(Q1)*f(Q2) - f(Q1*Q2) # zero to numerical precision # signature(Inf)
Summary method for clifford objects, and a print method for summaries.
## S3 method for class 'clifford' summary(object, ...) ## S3 method for class 'summary.clifford' print(x, ...) first_n_last(x)
## S3 method for class 'clifford' summary(object, ...) ## S3 method for class 'summary.clifford' print(x, ...) first_n_last(x)
object , x
|
Object of class clifford |
... |
Further arguments, currently ignored |
Summary of a clifford object. Note carefully that the
“typical terms” are implementation specific. Function
first_n_last()
is a helper function.
Robin K. S. Hankin
summary(rcliff())
summary(rcliff())
By basis vector, I mean one of the basis vectors of the underlying
vector space , that is, an element of the set
. A
term is a wedge product of basis vectors (or a geometric product
of linearly independent basis vectors), something like
or
. Sometimes I use the
word “term” to mean a wedge product of basis vectors together
with its associated coefficient: so
would be
described as a term.
From Perwass: a blade is the outer product of a number of
1-vectors (or, equivalently, the wedge product of linearly independent
1-vectors). Thus and
are
blades, but
is not.
Function rblade()
, documented at ‘rcliff.Rd’, returns a
random blade.
Function is.blade()
is not currently implemented: there is no
easy way to detect whether a Clifford object is a product of 1-vectors.
terms(x) is.blade(x) is.basisblade(x)
terms(x) is.blade(x) is.basisblade(x)
x |
Object of class |
Functions terms()
and coeffs()
are the
extraction methods. These are unordered vectors but the ordering is
consistent between them (an extended discussion of this phenomenon
is presented in the mvp
package).
Function term()
returns a clifford object that comprises
a single term with unit coefficient.
Function is.basisterm()
returns TRUE
if its
argument has only a single term, or is a nonzero scalar; the zero
clifford object is not considered to be a basis term.
Robin K. S. Hankin
C. Perwass. “Geometric algebra with applications in engineering”. Springer, 2009.
x <- rcliff() terms(x) is.basisblade(x) a <- as.1vector(1:3) b <- as.1vector(c(0,0,0,12,13)) a %^% b # a blade
x <- rcliff() terms(x) is.basisblade(x) a <- as.1vector(1:3) b <- as.1vector(c(0,0,0,12,13)) a %^% b # a blade
Generic version of zapsmall()
zap(x, drop=TRUE, digits = getOption("digits"))
zap(x, drop=TRUE, digits = getOption("digits"))
x |
Clifford object |
drop |
Boolean with default |
digits |
number of digits to retain |
Given a clifford object, coefficients close to zero are
‘zapped’, i.e., replaced by ‘0’ in much the same way as
base::zapsmall()
.
The function should be called zapsmall()
, and dispatch to the
appropriate base function, but I could not figure out how to do this
with S3 (the docs were singularly unhelpful) and gave up.
Note, this function actually changes the numeric value, it is not just a print method.
Robin K. S. Hankin
a <- clifford(sapply(1:10,seq_len),90^-(1:10)) zap(a) options(digits=3) zap(a) a-zap(a) # nonzero B <- rblade(g=3) mB <- B*rev(B) zap(mB) drop(mB)
a <- clifford(sapply(1:10,seq_len),90^-(1:10)) zap(a) options(digits=3) zap(a) a-zap(a) # nonzero B <- rblade(g=3) mB <- B*rev(B) zap(mB) drop(mB)
Dealing with the zero Clifford object presents particular challenges. Some of the methods need special dispensation for the zero object.
is.zero(x)
is.zero(x)
x |
Clifford object |
To test for a Clifford object's being zero, use is.zero()
.
Idiom such as x==0
will work irregardless, but sometimes one
might prefer the functional form for stylistic reasons.
To create the zero object ab initio, use
clifford(list(),numeric(0))
although note that scalar(0)
will work too.
The coefficient of the zero clifford object, as in
coeff(scalar(0))
, is numeric(0)
(but note that 1 +
NULL
also returns numeric(0)
).
Function is.zero()
is problematic if another package which also
has an is.zero()
generic is loaded, for this will mask
clifford::is.zero()
. Specifically, the jordan
package includes jordan::is.zero()
and the two do not play
nicely together.
Robin K. S. Hankin
is.zero(rcliff())
is.zero(rcliff())