In [1]:
%nbtoc


Contents [hide]

Introduction

Abstract

This notebook demonstrates the application of geometric algebra to projective geometry. The calculations are performed using the symbolic galgebra module from sympy, which is found in Alan Bromborsky's GitHub repo's.

Motivation

A common theme in applied mathematics is that a complicated problem in a simple space can be traded for a simple problem in a more complicated space. Think of using the Fourier transform to linearize convolution.


In [2]:
from IPython.display import *
Image('pics/generic commutative diagram.png')


Out[2]:

Similarly, Projective Geometry is slightly more complicated than Euclidean space, but it makes certain transformations, like translation and shearing, simpler. Additionally, it removes the perculiarities of zero and infinity. Computer graphics people use projective geometry regularly.

While these features provide sufficient motivation, there are more fundamental reason to study alternative geometries. One reason is to illustrate the freedom of algebraic interpretation. In Eucliean space a vector is a vector is a vector. In projective geometry a vector may be represented as a bivector. We can choose to think of a vector as a vector or as a bivector to suite our needs.

So, having more ways to view a given problem is never a bad thing, and can only add to the richness of possible solutions. Lets roll.

More information can be found in the references below.

References

  • [1]C. Doran, Geometric Algebra for Physicists, 1st Pbk. Ed. with Corr edition. Cambridge; New York: Cambridge University Press, 2007.
  • [2]J. Vince, Geometric Algebra for Computer Graphics, 2008 edition. London: Springer, 2008.
  • [3]C. Perwass, Geometric Algebra with Applications in Engineering, Softcover reprint of hardcover 1st ed. 2009. Springer, 2010.

The Projective Plane

Theory

The projective plane describes the application of projective geomtry to 2D space, illustrated in the image below (taken from [2] ). In this example, the orginal 2D space created by the basis $e_1, e_2$ is extended by a third orthonormal vector $n$. A vector in the plane ( $q$ ) is represented by the vector in 3D pace ( $p$ ). From this geometric construction we can relate vectors in both spaces.


In [3]:
Image('pics/projective plane.png')


Out[3]:

Projecting Down onto the plane

First we need to define the mapping between a vector in G3, and the projective plane. From the illustration above it is clear that

\begin{eqnarray*} n+q & = & \lambda p \end{eqnarray*}

Taking the inner product with $n$, solving for $\lambda$

\begin{eqnarray*} n^{2}+n\cdot q & = & \lambda p\cdot n\\ n^{2} & = & \lambda p\cdot n\\ \lambda & = & \frac{n^{2}}{p\cdot n} \end{eqnarray*}

Combining this with the equation above, solving for $q$

\begin{eqnarray*} q & = & \frac{n^{2}}{p\cdot n}p-n \end{eqnarray*}

This can be re-arranged, to provide

\begin{eqnarray*} q & = & \frac{n^{2}}{p\cdot n}p-n\\ & = & \frac{n^{2}p-nn\cdot p}{p\cdot n}\\ & = & n\frac{np-n\cdot p}{p\cdot n}\\ & = & n\frac{n\wedge p}{n\cdot p}\\ q & = & \frac{p\wedge n}{p\cdot n} n\\ \end{eqnarray*}

This form illustrates that the vector q can be put into correspondence with the bivector $P= \frac{p \wedge n}{p\cdot n}$. This is a nice visual interpretation: the bivector $P$ is formed by extending the $n$ vector to the $p$ vector, which sweeps out $q$ in the projection plane.

So a vector is the extension of the zero point ( $n$ ) to a point $p$.

It also demonstrates that the magnitude of $p$ is irrelevant to the vector $q$, as it will get factored out of the fraction.

Projecting up into G3

Projecting a vector from the plane into 3D space is trivial, due to the homogeneity of the perspective relationship. Setting $\lambda =1$

$$p= q+n$$

note that this implies,

$$\lambda n\wedge p= n\wedge q$$

Ok lets do it

First we build the geometric algebra of 3D space using an orthonormal basis of $\boldsymbol{(e_1, e_2, n)}$, using Ga.build(). This returns the object representing the algebra, and the basis vectors.

  • (For an intruduction to the sympy.galgebra module, see the [[ Sould make an Intro Tutorial ]] )

In [4]:
from sympy import * 
from galgebra.ga import Ga

# create G3 from the  orthonoormal basis: e1, e2,n. 
(ga,e1,e2,n) = Ga.build('e1 e2 n',g=[1,1,1])

In [5]:
# assign some more commonly used variables
e12 = e1^e2 # projection plane
ZERO = e1|e2 # this is dumb
I = ga.I()

In [6]:
# projection of vector a onto Blade B
proj = lambda a,B:  (a|B)*B.inv()
# rejection  of vector a onto Blade B
rej  = lambda a,B: (a^B)*B.inv()

# up project a vector from  original space to projection space
up = lambda q: q+n
# down project a vector from  projection space to the original space
down = lambda p: n*(n^p)*(n|p).inv()

# normalize projection vector so that p_n = 1
norm = lambda p:  p*(p|n).inv()

# 'normalized projection vector': creates a projection vector with n = 1
npv = lambda s: proj(ga.mv(s,'vector'),e12)+n

Verify Projection Functions

Given a projection vector


In [7]:
p = ga.mv('p','vector')
p


Out[7]:
\begin{equation*} p = p^{1} e_{1} + p^{2} e_{2} + p^{3} n \end{equation*}

Ensure that projecting up, and then down gives us what we started with.


In [8]:
q = proj(p,e12)
q == down(up(q))


Out[8]:
True

Vectors $ \rightarrow $ Points

The projection vector $p$ can be thought of as representing the point in the projection plane $q$. Vectors in projective space are homogeneous, meaning that two projection vectors with different magnitudes represent the same point in the original space.


In [9]:
p


Out[9]:
\begin{equation*} p = p^{1} e_{1} + p^{2} e_{2} + p^{3} n \end{equation*}

In [10]:
q = (p^n*(n|p).inv())*n
q


Out[10]:
\begin{equation*} \frac{p^{1}}{p^{3}} e_{1} + \frac{p^{2}}{p^{3}} e_{2} \end{equation*}

So its clear that the magnitude of $p$ will cancel out in the fraction, hence its homogeneous.

Points at $0$ and $\infty$

The values of $0$ and $\infty$ are represented by projective vectors parallel and perpendicular to $n$.

[ THESE CAUSE EXCEPTIONS]

AttributeError: 'int' object has no attribute 'is_commutative'

In [11]:
#down(proj(a,n)) # a vector parallel  to a

In [12]:
#down(rej(a, n)) # a vector perpendicular to n

Bivectors $\rightarrow$ Vectors, or Lines

Recall that the relation between a vector in the projection space and orginal space is given by

$$ q = \underbrace{\frac{p\wedge n}{p\cdot n}}_{P} n = Pn\\ $$

In [13]:
P = p^n*(n|p).inv()
P


Out[13]:
\begin{equation*} \frac{p^{1}}{p^{3}} e_{1}\wedge n + \frac{p^{2}}{p^{3}} e_{2}\wedge n \end{equation*}

In [14]:
P*n


Out[14]:
\begin{equation*} \frac{p^{1}}{p^{3}} e_{1} + \frac{p^{2}}{p^{3}} e_{2} \end{equation*}

In [14]:

Given that a line in the original space is represented as a bivector in the projection space. The advantage of this is that the geometrical entities ( like lines, planes, etc) become objects which can be operated on like vectors, ie rotated, reflected, etc.

Below is an illustration (taken from [3]), which shows two projection vector $p_h$ and $q_h$, and their bivector $p_h\wedge q_h$


In [15]:
Image('pics/projective bivector.png')


Out[15]:

Taking the outer product of two projection vectors. (Note i dropped the '$_h$' subscript because its a little too much ).


In [16]:
p, q = [ga.mv(k, 'vector') for k in ['p','q']]
p,q = npv('p'), npv('q') # would be nice to have a cleaner way to do this

Z = p^q
Z


Out[16]:
\begin{equation*} \left ( p^{1} q^{2} - p^{2} q^{1}\right ) e_{1}\wedge e_{2} + \left ( p^{1} - q^{1}\right ) e_{1}\wedge n + \left ( p^{2} - q^{2}\right ) e_{2}\wedge n \end{equation*}

In [16]:

The coefficients of this bivector are $\Delta_y$, $\Delta_x$ and $x_0\Delta-y_0\Delta_x$, which define the parameters of a line.

The dual of this is


In [17]:
z=Z.dual()
z


Out[17]:
\begin{equation*} \left ( - p^{2} + q^{2}\right ) e_{1} + \left ( p^{1} - q^{1}\right ) e_{2} + \left ( - p^{1} q^{2} + p^{2} q^{1}\right ) n \end{equation*}

Intersections (the meet operator)


In [18]:
# the meet of A and B
meet = lambda A,B:  A.dual()|B

To demonstrate, we can show that meet'ing our basis bivectors together yields their vector of intersection


In [19]:
e12 = e1^e2
e2n = e2^n
en1 = n^e1

In [20]:
meet(e12, e2n)


Out[20]:
\begin{equation*} e_{2} \end{equation*}

In [21]:
meet(en1, e2n)


Out[21]:
\begin{equation*} - n \end{equation*}

In projective space, a line is represented by joining two vectors. This is just the outerproduct.


In [22]:


In [22]:

This yields a nice visualization; a bivector in G3 will intersect the projection plane in a line, defined by the points $p$ and $q$.

Numerical Example

Here is a numerical exmaples which demonstrates that the outer product in projective space determines a line in the original space. Given the two points $p$ and $q$


In [23]:
p = 1*e1 + 2*e2
q = 3*e1 + 3*e2

The line $PQ$ is represented by $( p_h\wedge q_h)^*$


In [24]:
PQ =(up(p)^up(q)).dual()
PQ


Out[24]:
\begin{equation*} e_{1} -2 e_{2} + 3 n \end{equation*}

To see where two arbitrary line interset we can use the meet


In [29]:
rv = lambda : randint(-10,10)*e1 + randint(-10,10)*e2
p,q,r,s = [rv() for k in range(4)]

Scratch


In [30]:
p, q = [ga.mv(k, 'vector') for k in ['p','q']]
p,q = npv('p'), npv('q') 

p_,q_ = down(p),down(q)

In [31]:
e0


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-31-6e4b87f59d80> in <module>()
----> 1 e0

NameError: name 'e0' is not defined

In [31]:


In [ ]: