knitr::opts_chunk$set(echo = TRUE) library("clifford")
knitr::include_graphics(system.file("help/figures/clifford.png", package = "clifford"))
\newcommand{\grad}[2]{\left\langle #1\right\rangle_{#2}} \newcommand{\ak}{\grad{\mathbf{A}}{k}} \newcommand{\ba}{\mathbf{a}} \newcommand{\bb}{\mathbf{b}} \newcommand{\bn}{\mathbf{n}} \newcommand{\bA}{\mathbf{A}} \newcommand{\bS}{\mathbf{S}} \newcommand{\bX}{\mathbf{X}} \newcommand{\bB}{\mathbf{B}} \newcommand{\bx}{\mathbf{x}} \newcommand{\by}{\mathbf{y}}
\newcommand{\einf}{\mathbf{e}_\infty} \newcommand{\ezero}{\mathbf{e}_0}
To cite the clifford package in publications please use
@hankin2025_clifford_rmd. This short document shows how conformal
geometry may be implemented using the clifford R package; it follows
@hildenbrand2013 and @perwass2009. Here we work in
$\operatorname{Cl}(p,q)$, which Perwass denotes $\mathbb{G}{p,q}$.
The set of grade-$k$ objects is denoted $\mathbb{G}{p,q}^k$. First
we define the IPNS and OPNS [inner product null space and outer
product null space] of $\mathbf{A}\in\mathbb{G}_{p,q}^k$.
$$ \begin{eqnarray} \operatorname{IPNS}(\mathbf{A}) &=& \left\lbrace\bx\in\mathbb{G}^1_{p,q}\colon\bx\cdot\mathbf{A} = 0\right\rbrace\ \operatorname{OPNS}(\mathbf{A}) &=& \left\lbrace\bx\in\mathbb{G}^1_{p,q}\colon\bx\wedge\mathbf{A} = 0\right\rbrace \end{eqnarray} $$
Thus if $\mathbf{A}=\ak=\bigwedge_{i=1}^k\ba_i$, then $\operatorname{OPNS}(\mathbf{A})=\operatorname{span}(\ba_1,\ldots,\ba_k)\subseteq\mathbb{G}_{p,q}$. Further, $\operatorname{OPNS}(\mathbf{A})$ is linear in the sense that $\bx,\by\in\operatorname{OPNS}(\mathbf{A})$ implies $\alpha\bx+\beta\by\in\operatorname{OPNS}(\mathbf{A})$ for any real numbers $\alpha,\beta$. We will use this system to express a geometrical object $\mathcal{G}\subset\mathbb{R}^3$ (such as a sphere or a line) as a point $\mathbf{P}$ in $\operatorname{Cl}(4,1)$; the idea is that the IPNS or OPNS of $\mathbf{P}$ is $\mathcal{G}$. This allows one to express geometrical ideas in pure Clifford formalism, without needing to take a dangerous and unsightly basis.
To work in R, we set up some basic features of conformal geometry. Specifically, we consider the three Euclidean basis vectors $\mathbf{e}1$, $\mathbf{e}_2$, $\mathbf{e}_3$ together with two additional basis vectors $\mathbf{e}+$ and $\mathbf{e}-$ obeying $\mathbf{e}+^2=1$, $\mathbf{e}-^2=0$, $\mathbf{e}+\cdot\mathbf{e}_-=0$. If we define
[ \ezero=\frac{1}{2}\left(\mathbf{e}--\mathbf{e}+\right),\qquad \einf =\mathbf{e}- +\mathbf{e}+ ]
then we may say that $\ezero$ represents "the origin" and $\einf$
represents "the point at infinity". We see that $\ezero^2=\einf^2=0$
and $\einf\cdot\ezero=-1$; the geometric product is given by
$\einf\ezero=-E-1$ and $\ezero\einf=E-1$ where $E=\einf\wedge\ezero$.
It is straightforward to implement these objects using the clifford
package:
dimension <- 3 options("maxdim" = dimension+2) # paranoid safety measure signature(dimension + 1,1) eplus <- basis(dimension+1) eminus <- basis(dimension + 2) e0 <- (eminus - eplus)/2 einf <- eminus + eplus E <- e0 ^ einf
So
e0 einf E
With these definitions, we can consider Euclidean vectors $\ba,\bb\in\mathbb{R}^3$ and conformal embeddings $\bA,\bB$ given by
[ \bA=C(\ba)=\ba+\ba^2\einf/2 +\ezero\qquad\bB=C(\bb)=\bb+\bb^2\einf/2 +\ezero ]
This is straightforward in package idiom:
point <- function(x){ as.1vector(x) + sum(x^2)*einf/2 + e0 }
Thus point() takes an R vector of length 3 and returns its conformal
embedding. For example, we may translate points $(1,2,5)$ and
$(2,2,2)$ to their conformal equivalent:
a <- c(1,2,5) b <- c(2,2,2) point(a) point(b)
It can be shown that $\bA\cdot\bB=-\left\lVert\ba-\bb\right\rVert^2/2$
[where the dot product is the Clifford inner product, %.%]. Package
idiom to verify this would be
c(conformal=drop(point(a) %.% point(b)), Euclidean = -sum((a-b)^2)/2)
showing that the two results match.
We can define a sphere with center $\ba$ and radius $\rho$ as
[ \bS=C(\ba) - \rho^2\einf/2 ]
and then the sphere is just the inner product null space of $\bS$, that is $\left\lbrace\bx\colon\bS\cdot\bx=0\right\rbrace$. This is straightforward to implement computationally. Suppose we have a sphere of radius 5, center $(1,2,3)$:
sphere <- function(x,r){ point(x) - r^2*einf/2} S <- sphere(1:3,5) # center (1,2,3) radius 5: S
Then object S is a conformal representation of such a sphere. The
radius $\rho$ can be calculated from $\bS^2=\rho^2$. Package idiom:
drop(S^2) # 5^2 = 25
Finding the center of the sphere is slightly more involved; Hildenbrand calls this the sandwich product given by $P=\bS\einf\bS$:
S*einf*S
Hildenbrand shows that the scaling factor is $-2$ so this gives us
-S*einf*S/2
which we recognise as the point $(1,2,3)$:
point(1:3)
We can also consider the OPNS for a sphere, defined in terms of four points that lie on it. For example, if our four points are $(0,0,0)$, $(1,0,0)$, $(0,1,0)$, $(0,0,1)$ we would have a rather involved algebraic calculation resulting in a sphere of radius $\frac{\sqrt{3}}{2}$ and center $\left(\frac{1}{2},\frac{1}{2},\frac{1}{2}\right)$.
The conformal representation for a sphere in OPNS would be
[ S^*=P_1\wedge P_2\wedge P_3\wedge P_4 ]
and points that lie on the sphere are the set
[\left\lbrace\bx\colon\bx\wedge S^*=0\right\rbrace.]
Observe again that this parameterization does not require one to take a basis of $\mathbb{R}^3$, as all the results are presented in terms of vector quantities: at no point does one need to consider components or elements of any vector. The R idiom for defining the sphere touching $\left\lbrace P_1,P_2,P_3,P_4\right\rbrace$ is straightforward:
origin <- point(c(0,0,0)) px <- point(c(1,0,0)) py <- point(c(0,1,0)) pz <- point(c(0,0,1)) (S <- origin ^ px ^ py ^ pz)
And this is the representation for a sphere (translating this into a center and radius is not yet implemented). Slightly slicker R idiom might be:
spherestar <- function(...){Reduce(`^`,list(...))} spherestar(origin, px, py, pz)
As a verification, we may check that point $\left(\frac{1}{2},\frac{1}{2},\frac{1}{2}+\frac{\sqrt{3}}{2}\right)$ is on the sphere as well:
p <- point(c(1,1,1+sqrt(3))/2) Mod(p ^ S)
Above we see that $p\wedge S$ is zero to within numerical error. Again observe that this is established without taking a basis of $\mathbb{R}^3$.
A plane is defined as $\hat{\bn} + d\einf$, where $\hat{\bn}$ is the unit normal and $d$ the distance to the origin.
plane <- function(n,d){ as.1vector(n/sqrt(sum(n^2))) + d*einf}
For example, we consider the plane $\Pi$ with normal
$\mathbf{n}=\left(1,2,5\right)$ and distance 7. We may use plane()
above but first need to calculate
$\hat{\mathbf{n}}=\mathbf{n}/\left|\mathbf{n}\right|$:
n <- c(1,2,5) nhat <- n/sqrt(sum(n^2)) d <- 7 Pi <- plane(nhat,d) Pi
Just to check, we may verify that some points known to lie in $\Pi$ are in fact in its IPNS. We observe that the point $7\hat{\mathbf{n}}\in\Pi$, and also that vectors $\mathbf{u}=(2,-1,0)$ and $\mathbf{v}=(5,0,-1)$ are orthogonal to the normal of $\Pi$. So $7\hat{\mathbf{n}} + \alpha\mathbf{u} + \beta\mathbf{v}\in\Pi$ for any real numbers $\alpha,\beta$. In R:
u <- c(2,-1,0) v <- c(5,0,-1) P1 <- point(d*nhat) P2 <- point(d*nhat + 1.3*u + 3.44*v) P3 <- point(d*nhat - 6.1*u + 1.02*v)
Above we use some made-up values of $\alpha,\beta$ to generate P1,
P2, P3 which are known to lie in plane $\Pi$. Then to verify that
these points lie in the IPNS of $\Pi$ we need to take the inner
product with the conformal representation of $\Pi$:
c(drop(Pi %.% P1),drop(Pi %.% P2),drop(Pi %.% P3))
Above we see zero (to numerical precision), showing that we do indeed have $7\hat{\bn} + \alpha\mathbf{u} + \beta\mathbf{v}\in\operatorname{IPNS}(\Pi)$ for at least these values of $\alpha$ and $\beta$. Alternatively, a plane can be thought of as a sphere that touches the point at infinity; thus the OPNS of plane is given by
[ \pi^\star = P_1\wedge P_2\wedge P_3\wedge\einf ]
where $P_1,P_2,P_3$ are any points that lie in the plane. To illustrate this we will use the three points used in the IPNS above:
Pi2 <- P1 ^ P2 ^ P3 ^ einf
Then for verification we can create another point known to be in the
plane and check that this is in the OPNS. Below we will use p4
which is $d\hat{\mathbf{n}} + 7.6\mathbf{u} - 9.23\mathbf{v}$:
p4 <- point(d*nhat + 7.6*u - 9.23*v) Mod(Pi2 ^ p4)
Above we see a very small result showing that p4 is indeed in the
OPNS of plane Pi2.
A circle is defined as the intersection of two spheres:
circle <- function(S1,S2){ # IPNS S1 ^ S2 }
For example
circle(sphere(1:3,5),sphere(c(1.1,2.1,3.4),6))
A circle may be represented in the OPNS by specifying three points that lie on it:
[ Z^*=P_1\wedge P_2\wedge P_3]
circlestar <- function(...){ # OPNS; A^B^C jj <- list(...) stopifnot(length(jj) == dimension) Reduce(`^`,lapply(jj,point)) } (CIRC <- circlestar(c(1,2,3),c(5,6,3),c(8,8,-2)))
verify:
M <- rbind (c(1,2,3),c(5,6,3),c(8,8,-2)) badness_center <- function(pos){ bad1 <- sd(c( sum((pos-M[1,])^2), sum((pos-M[2,])^2), sum((pos-M[3,])^2))) # pos should be equidistant from rows of M bad2 <- abs(det(sweep(M,2,pos))) # pos should be collinear with rows of M return(bad1+bad2) } center <- nlm(badness_center,c(0,0,0))$estimate badness_pointoncircle <- function(pos){ # pos is a point [that should be] on the circle bad1 <- var(c(sum((center-M[1,])^2), sum((center-M[2,])^2), sum((center-M[3,])^2), sum((center-pos )^2))) bad2 <- abs(det(sweep(M,2,pos))) # pos should be coplanar with M[i,] return(bad1+bad2) } poc <- point(nlm(badness_pointoncircle,c(0,0,0))$estimate)
poc # point on circle, found numerically [chunk omitted]
poc ^ CIRC
Above we see that poc is at least close to the circle from the small
magnitude of the terms in the wedge product.
A line is the intersection of two planes; in R:
line <- function(P1,P2){ P1 ^ P2 }
and a "point pair" is the intersection of three spheres; in R:
pointpair <- function(S1,S2,S3){ S1 ^ S2 ^ S3 }
It is not at all obvious that three spheres intersect in a pair of points; and still less obvious that the process is associative. However, we may verify associativity explicitly:
S1 <- sphere(c(3,2,4),3) S2 <- sphere(c(3,1,4),4) S3 <- sphere(c(1,3,3),3) (S1^S2)^S3 (S1^S2)^S3 == S1^(S2^S3)
options("maxdim" = NULL) # restore default
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.