Mathematics 138
          Some Hyperbolic Calculations

This notebook shows how to do calculations with hyperbolic points, lines, and triangles.  We focus attention on points, representing lines by their spacelike poles. Each point can be represented by two different but proportional vectors. One is a unit vector, useful for parameterizing a line and for quick calculation of distances and angles. The other is a vector whose x and y components can be plotted in a Klein model diagram.
    We use the simple, standard choice for the polarity B.

B= {{1,0,0},{0,1,0},{0,0,-1}};
B//MatrixForm

Because B is its own inverse, the calculation is the same to convert polar to pole or pole to polar.

Polarize[v_List]:=B.v
Depolarize[v_List]:=B.v

Start with two timelike points to define a line.The "K" means that these are Klein model vectors whose third component is either 1 or 0. Then compute the line joining these points and its pole.

PK={-0.4,0.2,1};
QK={0.5,0.6,1};
LinePQ = PK\[Cross]QK
PolePQ = LinePQ//Polarize

This pole is neither a unit vector nor a point for Klein model display.  Let's write some functions to fix that.
The first one changes the third component to +1 if possible; in that case, the first two components are the x and y coordinates that you should plot. If the third component is 0, we are dealing with a point at infinity, and we convert to a unit vector in order to indicate the direction more clearly.

Kleinify[v_List]:=If[v[[3]]==0,v/Sqrt[v[[1]]^2+v[[2]]^2],v/v[[3]]]

PolePQ//Kleinify

Kleinify[{3,4,0}]

To normalize a spacelike or timelike vector, we nust make <v,Bv> equal to +1 or -1, and for the sake of concreteness we make the third component non-negative. For a lightlike vector we just convert to the Klein-model vector.

TheNorm[v_List]:=v.B.v
Normalize[v_List]:=If[TheNorm[v]==0,Kleinify[v],Kleinify[v]/Sqrt[Abs[TheNorm[Kleinify[v]]]]]

Now we can compute  three orthonormal basis vectors. Following the conventions in Ryan and in the notes, the third one is timelike, and the second one is the pole of the line with which we started.

Basis3U=PK//Normalize
Basis2U=PolePQ//Normalize

The remaining basis vector has to be orthogonal to P and to the pole of PQ. We can use any convenient vectors to represent P and the pole of PQ, but since they are orthogonal, if we use the normalized vectors in the correct order, we don't need to normalize the result.

Basis3U.B.Basis2U
Basis1U=Polarize[Basis3U\[Cross]Basis2U]

Here is a check that we have an orthonormal basis.
The pole of the line determined by any two of the vectors
must be the third vector. To make the signs agree, we have to select the appropriate order for the cross product.

Polarize[Basis1U\[Cross]Basis2U]

Polarize[Basis1U\[Cross]Basis3U]

Each basis vector is a unit vector. The first two are spacelike and the third is timelike.

TheNorm[Basis1U]
TheNorm[Basis2U]
TheNorm[Basis3U]

If we want to show this basis in a Klein model diagram, then we need the Klein versions of the vectors. The first two will lie outside the unit circle and the third one inside it.

B1K = Basis1U//Kleinify
B2K = Basis2U//Kleinify
B3K = Basis3U//Kleinify

Now that we have a basis, we can represent the line parametrically.

Alpha[t_]:=Basis3U*Cosh[t]+Basis1U*Sinh[t]

Alpha[0]

To represent the original point Q, we must first determine its hyperbolic distance from P. In this case, point Q does not lie between the basis points, so the correct parameter value is the negative of this distance.

HypDist[u_,v_]:=ArcCosh[Abs[Normalize[u].B.Normalize[v]]]

DistPQ=HypDist[PK,QK]

PQU=Alpha[-DistPQ]

This still doesn't look like Q.  That's because it's a unit vector, and Q was specified in the Klein model.

PQU//Kleinify

Now let's do a complete triangle. The vertices have been chosen, as specified on the "Hyperbolic Trigonometry" page (page 15) of the notes,so that poles calculated by cross products will have a positive third component.

VertPK  =  {0.6,  -0.7, 1};
VertQK  =  {-0.4,  -0.8, 1};
VertRK  =  {0.1,  0.7, 1};

From these we can compute the length of each side.

p  =  
      HypDist[VertQK,  VertRK]
q  =  HypDist[VertPK,  VertRK]
r  =  HypDist[VertPK,  VertQK]

Now compute all the poles as unit vectors.

PolePQ = 
      Polarize[VertPK\[Cross]VertQK] // Normalize
PoleQR = 
      Polarize[VertQK\[Cross]VertRK] // Normalize
PoleRP = Polarize[VertRK\[Cross]VertPK] // Normalize

From these poles we can calculate the angles.But we have to insert an extra minus sign, because the cross-product definition of angle has the vertex first in both cross products.

P = 
      ArcCos[-PolePQ . B . PoleRP]
Q = ArcCos[-PolePQ . B . PoleQR]
R = ArcCos[-PoleRP . B . PoleQR]

The Law of Sines can now be checked.

Sin[P]/Sinh[p]
Sin[Q]/Sinh[q]
Sin[R]/Sinh[r]
Abs[(Normalize[
              VertPK] . (Normalize[VertQK]\[Cross]
                Normalize[VertRK]))]/(Sinh[p]*Sinh[q]*
          Sinh[r])

And here is the Law of Cosines. The fact that this works shows that we got the correct angles and not their supplements.

Cos[P]
(Cosh[q] Cosh[r] - Cosh[p])/(Sinh[q] 
          Sinh[r])
Cos[Q]
(Cosh[p] Cosh[r] - Cosh[q])/(Sinh[p] 
          Sinh[r])
Cos[R]
(Cosh[q] Cosh[p] - Cosh[r])/(Sinh[q] Sinh[p])

Now for the tricky issue.  The original definition of angle was in terms of direction vectors, not in terms of poles. For each line there are two different direction vectors. For example, for line PQ there is one direction vector for going from P to Q (tangent to line PQ at P, orthogonal to P) and a different one for going from Q to P (tangent to line PQ at Q, orthogonal to Q). This is the case also in spherical geometry: givern a triangle on the surface of the Earth there are six different direction vectors, two for each vertex.

DirectionPQ = 
      Polarize[PolePQ\[Cross]VertPK] // Normalize
DirectionPR = 
      Polarize[PoleRP\[Cross]VertPK] // Normalize
DirectionQP = 
      Polarize[PolePQ\[Cross]VertQK] // Normalize
DirectionQR = 
      Polarize[PoleQR\[Cross]VertQK] // Normalize
DirectionRP = 
      Polarize[PoleRP\[Cross]VertRK] // Normalize
DirectionRQ = Polarize[PoleQR\[Cross]VertRK] // Normalize

For the given triangle, it turns out the all these direction vectors point away from the given vertex; e.g DirectionPQ points from P along line PQ in the direction opposite to Q. This is because we chose a positive third component to agree with our Klein model diagrams.
But since every vector is backwards, the angles that they determine are equal to the interior angles of the triangle.

PfromDir = 
      ArcCos[DirectionPQ . B . DirectionPR]
QfromDir = 
      ArcCos[DirectionQR . B . DirectionQP]
RfromDir = 
      ArcCos[DirectionRP . B . DirectionRQ]

Now let's set up a hyperbolic right triangle with a right angle at Q as in the notes. Start with two vertices.

RightQK  =  {-0.4, -0.5, 1};
RightPK  =  {0.8,  -0.2, 1};

To get the third side, first calculate the pole of PQ, then use it to get vertex R on the line orthogonal to PQ.  Let's give the side QR a hyperbolic length of 2.

RPoleQP  =  
      Polarize[RightQK\[Cross]RightPK] // 
        Normalize
RightR = 
      Cosh[2] Normalize[RightQK] + Sinh[2] RPoleQP
RightRK  =  RightR // Kleinify

Here are the lengths of the sides.

Sidep  =  HypDist[RightQK,  RightRK]
Sideq  =  HypDist[RightPK,  RightRK]
Sider  =  HypDist[RightPK,  RightQK]

Since this is a right triangle, the Pythagorean theorem must hold.

Cosh[Sideq]
Cosh[Sidep] Cosh[Sider]

To compute angles we need the remaining poles.

RPoleQR  =  
      Polarize[RightQK\[Cross]RightRK] // 
        Normalize
RPoleRP  =  Polarize[RightRK\[Cross]RightPK] // Normalize

Now we can get the angles, remembering to insert a minus sign if the chosen vertex was first in the cross product for one pole and second for the other.  We get a right angle and two acute angles.

AngleQ = ArcCos[RPoleQR . B . RPoleQP]
AngleP = ArcCos[RPoleRP . B . RPoleQP]
AngleR = ArcCos[-RPoleQR . B . RPoleRP]

A check on the angle at R.

Sin[AngleR]
Sinh[Sider]/Sinh[Sideq]
Cos[AngleR]
Tanh[Sidep]/Tanh[Sideq]

To construct the hyperbolic midpoint of a segment AB, the rule is simple. Just represent timelike points A and B by unit vectors with positive third components, and form their sum.  This sum will usually be neither a unit vector nor a Klein model vector, but that is easily fixed.
We start with points A and B represented by Klein vectors, and compute a Klein vector as the answer.

AK  =  {-0.6,  0.4,  1};
BK  =  {0.4,  0.2, 1};
Midpoint[u_, v_] := (Normalize[u] + Normalize[v]) // 
        Kleinify
MK  =  Midpoint[AK, BK]

To check the answer we verify that the distance from M to A or B is half the distance from A to B.

HypDist[AK, BK]
HypDist[MK, AK]
HypDist[MK, BK]

To construct the hyperbolic midline of lines r and s, the rule is equally simple. Just represent spacelike lines r and s by their spacelike poles, unit vectors with positive third components, and form their sum.This sum is the pole of the midline. It will be neither a unit vector nor a Klein model vector, but that is easily fixed.
We start with poles R and S represented by Klein vectors,and compute a Klein vector as the answer.
    The first case is for intersecting lines.

PoleRK  =  {2, 0.5, 1}
PoleSK  =  {0.4, 3, 1}
PoleMK  =  Midpoint[PoleRK,  PoleSK]

In this case the test of correctness is that we have found the bisector of the angle formed by lines r and s. In this case it happens that, because of our choice of poles, we are bisecting the acute angle.

AngleRS = 
      ArcCos[Normalize[PoleRK] . B . 
          Normalize[PoleSK]]
AngleRM = 
      ArcCos[Normalize[PoleRK] . B . 
          Normalize[PoleMK]]
AngleSM = 
      ArcCos[Normalize[PoleSK] . B . 
          Normalize[PoleMK]]

To get the other midline, which bisects the obtuse angle, we have to change the sign of one of the poles.

PoleMKObtuse  =  
      Normalize[PoleRK] - Normalize[PoleSK] // 
        Kleinify
AngleRSObtuse = 
      ArcCos[-Normalize[PoleRK] . B . 
            Normalize[PoleSK]]
AngleRMObtuse = 
      ArcCos[Normalize[PoleRK] . B . 
          Normalize[PoleMKObtuse]]
AngleSMObtuse = 
      ArcCos[-Normalize[PoleSK] . B . 
            Normalize[PoleMKObtuse]]

In the case where we start with two ultraparallel lines t and u, there is a unique midline, which passes through the (spacelike) point where t and u intersect. Since the midline must be spacelike, we need to be sure that the sum of the poles of t and u is spacelike.  If it were not, we would multiply one of the poles by -1.

PoleTK  =  {4, 0, 1}
PoleUK  =  {1.5, 0.2, 1}
PoleMK  =  Midpoint[PoleTK,  PoleUK]

One test of correctness is that the three lines are concurrent -- that their poles are collinear.

PoleTK . PoleUK\[Cross]PoleMK

Since there is no angle defined between ultraparallel lines, the best way to test that we have found the midline is by measuring distances along the line that is perpendicular to t, u, and the midline m. The pole of this line is the point where t, u, and m intersect.

ThePole  =  
      Polarize[PoleTK\[Cross]PoleUK] // Kleinify
IntT  =  
      Polarize[ThePole\[Cross]PoleTK] // Kleinify
IntU  =  
      Polarize[ThePole\[Cross]PoleUK] // Kleinify
IntM  =  
      Polarize[ThePole\[Cross]PoleMK] // Kleinify
HypDist[IntT, IntU]
HypDist[IntT, IntM]
HypDist[IntU, IntM]

Now we can do reflections in hyperbolic lines. Since we are reflecting in a spacelike line, we can specify it by a spacelike pole. We must write a reflection function that refects a point in the line represented by a spacelike pole.
    The formula for reflection requires that the pole be a spacelike unit vector, but there is no such restriction on the vector that represents the point being reflected. We can build the normalization of the pole into the function.

ReflectUsingPole[u_List, xi_List] := 
      u - 2  Normalize[xi]*(u . B . Normalize[xi])

As a test, we let the line be a vertical line (but not a diameter) whose pole lies on the x axis.

RefPole  =  {2, 0, 1}
PtC  =  {0, 0, 1}
PtCPrime = ReflectUsingPole[PtC, RefPole]
PtCPrime // Kleinify
PtD  =  {-0.5, 0.5, 1}
PtDPrime = ReflectUsingPole[PtD, RefPole]
PtDPrime // Kleinify

So the origin is reflected into the point x = 4/5, y=0, while the point x = -1/2, y = 1/2 is reflected into a point that is does not have y = 1/2. To check that this is correct, compute distances.

HypDist[PtC, PtD]
HypDist[PtCPrime, PtDPrime]

A further test is that the midpoint of the segment joing a point to its reflected image lies on the line of reflection, x = 1/2.

Midpoint[PtD, PtDPrime] // Kleinify

At this point we can create a reflection matrix and use it to replicate the results that we just obtained.

MakeRefMatrix[pole_List] := 
      Transpose[{ReflectUsingPole[{1, 0, 0}, pole], 
          ReflectUsingPole[{0, 1, 0}, pole], 
          ReflectUsingPole[{0, 0, 1}, pole]}]
Omega = MakeRefMatrix[RefPole];
Omega // MatrixForm
(Omega . PtC) // Kleinify
(Omega . PtD) // Kleinify