Torus as a two-comples

This is an introduction to the implementation of two-complexes, including issues with scala, ending with constructing a torus. To start with we load the compiled code.

In [1]:
import $cp.bin.`superficial-c3516ca6f4.jar`
import superficial._
Out[1]:
import $cp.$                               

import superficial._

A vertex

The code for vertices is rather simple.

class Vertex

Let us first create a vertex. As it has no abstract methods, we can just ask for a new one.

In [2]:
val v0 = new Vertex
Out[2]:
v0: Vertex = superficial.Vertex@75085bd8

Alternatively, we can declare a new object.

In [3]:
object v1 extends Vertex
Out[3]:
defined object v1
In [5]:
3: Int
Out[5]:
res4: Int = 3
In [4]:
v1 : v1.type
Out[4]:
res3: v1.type = ammonite.$sess.cmd2$Helper$v1$@5328d611

Edges

Edges come in pairs, an edge and the same with the opposite orientation (i.e., flipped). Further, each edge has an initial and terminal vertex. We can also describe the boundary as a formal sum. This is trait, i.e., an abstract class. This means some of its fields are undefined, and have to be defined in a subclass/object with this trait.

trait Edge {
  /**
   * the same edge with the opposite orientation.
   */
  def flip: Edge

  def terminal: Vertex

  def initial: Vertex

  def del : FormalSum[Vertex] = FormalSum.reduced(Vector(terminal -> 1, initial -> -1))
}

A loop

We construct a loop at v0, i.e., an edge beginning and ending at v0. But note that we cannot construct just one, as we need to flip it. Instead we will construct a pair of edges, each a flip of the other.

In [6]:
object E1 extends Edge{
    lazy val flip = E2
    
    val terminal = v0
    
    val initial = v0        
}

object E2 extends Edge{
    lazy val flip = E1
    
    val terminal = v0
    
    val initial = v0        
}
Out[6]:
defined object E1
defined object E2

Note a key feature above - we have defined the flip of each of the edges as lazy val. This means this is not computed until it is needed. Otherwise defining each of E1 and E2 leads to an infinite loop.

We can see some computations on demand.

In [7]:
E1.flip
Out[7]:
res6: E2.type = ammonite.$sess.cmd5$Helper$E2$@444af6b3
In [8]:
E1.flip == E2
Out[8]:
res7: Boolean = true
In [9]:
E1.flip.flip == E1
Out[9]:
res8: Boolean = true
In [10]:
E1 == E2
Out[10]:
res9: Boolean = false

Torus time

We now turn to constructing a torus. Recall that this has a single vertex, two edges that become loops and a single face. We will follow the same pattern as above but give things nicer names. We will also make objects case object definitions. This means the names are cleaner (as is equality).

In [11]:
case object V extends Vertex
Out[11]:
defined object V
In [12]:
case object VV extends Vertex
Out[12]:
defined object VV
In [13]:
V == VV
Out[13]:
res12: Boolean = false
In [14]:
case class VC() extends Vertex
case class VVC() extends Vertex
Out[14]:
defined class VC
defined class VVC
In [15]:
VC() == VVC()
Out[15]:
res14: Boolean = false
In [16]:
VC() == VC()
Out[16]:
res15: Boolean = true
In [17]:
case object A extends Edge{
    lazy val flip = Abar
    
    val terminal = V
    
    val initial = V        
}

case object Abar extends Edge{
    lazy val flip = A
    
    val terminal = V
    
    val initial = V        
}
Out[17]:
defined object A
defined object Abar
In [18]:
case object B extends Edge{
    lazy val flip = Bbar
    
    val terminal = V
    
    val initial = V        
}

case object Bbar extends Edge{
    lazy val flip = B
    
    val terminal = V
    
    val initial = V        
}
Out[18]:
defined object B
defined object Bbar
In [19]:
A.flip
Out[19]:
res18: Abar.type = Abar
In [20]:
Abar.flip
Out[20]:
res19: A.type = A

Now we construct the face of the torus. Here is an extract from the code for defining a polygon. We have omitted concrete methods, i.e., methods in the trait that are defined, so need not be implemented in subclasses/objects.

trait Polygon extends TwoComplex {
  val sides: Int

  val boundary: Vector[Edge]

  val vertices: Set[Vertex]
}

Note that this is a two-complex, so we are constructing a torus.

In [21]:
case object Torus extends Polygon{
    val sides = 4
    
    val boundary = Vector(A, B, Abar, Bbar)
    
    val vertices = Set(V)
}
Out[21]:
defined object Torus
In [22]:
Torus.faces
Out[22]:
res21: Set[Polygon] = Set(Torus)
In [23]:
Torus.edges
Out[23]:
res22: Set[Edge] = Set(A, Abar, B, Bbar)
In [25]:
Torus.boundary
Out[25]:
res24: Vector[Product with Edge with Serializable{def initial: ammonite.$sess.cmd10.instance.V.type;def terminal: ammonite.$sess.cmd10.instance.V.type;def flip: Product with superficial.Edge with java.io.Serializable}] = Vector(
  A,
  B,
  Abar,
  Bbar
)

Note that there are subclasses that let us construct more directly, but for now (i.e. as of Jan 13, 2020) they are not sufficient. We took a more direct approach to illustrate the structures.