Ejemplo n.º 1
0
    def __init__(self, coxeter_diagram, init_dist):
        if len(coxeter_diagram) != 6 or len(init_dist) != 4:
            raise ValueError("Invalid input dimension")

        # Coxeter matrix and its rank
        self.cox_mat = helpers.make_symmetry_matrix(coxeter_diagram)
        self.rank = len(self.cox_mat)

        # generators of the symmetry group
        self.gens = tuple(range(self.rank))

        # symmetry group of this tiling
        self.G = CoxeterGroup(self.cox_mat)

        # a mirror is active iff the initial point is not on it
        self.active = tuple(bool(x) for x in init_dist)

        # reflection mirrors
        self.mirrors = self.get_mirrors(coxeter_diagram)

        # reflections (possibly affine) about the mirrors
        self.reflections = self.get_reflections()

        # coordinates of the initial point
        self.init_v = self.get_init_point(init_dist)

        self.edge_hash_set = set()

        self.num_vertices = 0
        self.num_edges = 0
Ejemplo n.º 2
0
class Cell(object):
    """A cell is a uniform polyhedra (or a "room") in the 3d tiling.
    """
    def __init__(self, cox_mat, v0, active, reflections):
        self.cox_mat = cox_mat
        self.v0 = v0
        self.active = active
        self.reflections = reflections
        self.G = CoxeterGroup(cox_mat)

        self.vertices_coords = []
        self.num_vertices = None
        self.edge_coords = []
        self.num_edges = None

    def build_geometry(self, depth, maxcount):
        self.G.init()
        self.word_generator = partial(self.G.traverse,
                                      depth=depth,
                                      maxcount=maxcount)
        self.get_vertices()
        self.get_edges()
        return self

    def transform(self, word, v):
        for w in reversed(word):
            v = self.reflections[w](v)
        return vround(v)

    def project(self, v):
        return helpers.project_poincare(v)

    def get_vertices(self):
        for word in self.word_generator():
            v = self.transform(word, self.v0)
            if v not in self.vertices_coords:
                self.vertices_coords.append(v)
        self.num_vertices = len(self.vertices_coords)

    def get_edges(self):
        """
        An edge is uniquely determined by the coordinates of its
        middle point. Here I simply use a set to maintain the middle
        points of known edges and avoid duplicates.
        """
        edgehash = set()
        for i, active in enumerate(self.active):
            if active:
                for word in self.word_generator():
                    p1 = self.transform(word, self.v0)
                    p2 = self.transform(word + (i, ), self.v0)
                    q = centroid((p1, p2))
                    if q not in edgehash:
                        self.edge_coords.append((p1, p2))
                        edgehash.add(q)
        self.num_edges = len(self.edge_coords)
Ejemplo n.º 3
0
    def __init__(self, cox_mat, v0, active, reflections):
        self.cox_mat = cox_mat
        self.v0 = v0
        self.active = active
        self.reflections = reflections
        self.G = CoxeterGroup(cox_mat)

        self.vertices_coords = []
        self.num_vertices = None
        self.edge_coords = []
        self.num_edges = None
Ejemplo n.º 4
0
    def __init__(self, coxeter_diagram, init_dist):
        if len(coxeter_diagram) != 3 or len(init_dist) != 3:
            raise ValueError("Invalid input dimension")

        self.diagram = coxeter_diagram

        # Coxeter matrix and its rank
        self.cox_mat = helpers.get_coxeter_matrix(coxeter_diagram)
        self.rank = len(self.cox_mat)

        # generators of the symmetry group
        self.gens = tuple(range(self.rank))

        # symmetry group of this tiling
        self.G = CoxeterGroup(self.cox_mat)

        # a mirror is active iff the initial point is not on it
        self.active = tuple(bool(x) for x in init_dist)

        # reflection mirrors
        self.mirrors = self.get_mirrors(coxeter_diagram)

        # reflections (possibly affine) about the mirrors
        self.reflections = self.get_reflections()

        # coordinates of the initial point
        self.init_v = self.get_init_point(init_dist)

        # vertices of the fundamental triangle
        self.triangle_verts = self.get_fundamental_triangle_verts()

        # ----------------------
        # to be calculated later
        # ----------------------

        # holds the words in the symmetry group up to a given depth
        self.words = None

        # holds the coset representatives of the standard parabolic
        # subgroup of vertex-stabilizing subgroup
        self.vwords = None

        self.vertices_coords = []
        self.num_vertices = None
        self.num_edges = None
        self.num_faces = None
        self.edge_indices = {}
        self.face_indices = {}
Ejemplo n.º 5
0
    def __init__(self, coxeter_diagram, init_dist):
        self.cox_mat = helpers.get_coxeter_matrix(coxeter_diagram)
        self.G = CoxeterGroup(self.cox_mat)
        self.active = tuple(bool(x) for x in init_dist)
        self.words = None

        self.mirrors = self.get_mirrors(coxeter_diagram)
        self.init_v = helpers.get_point_from_distance(self.mirrors, init_dist)
        self.reflections = self.get_reflections(init_dist)

        # to be calculated later
        self.vertices_coords = []
        self.num_vertices = None
        self.num_edges = None
        self.num_faces = None
        self.edge_indices = {}
        self.face_indices = {}
Ejemplo n.º 6
0
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("filename", help="Coxeter matrix data file")
    args = parser.parse_args()
    cox_mat = np.loadtxt(args.filename, dtype=np.int, delimiter=" ")
    print("Computing the shorlex DFA for Coxeter group:")
    print(cox_mat)
    G = CoxeterGroup(cox_mat).init()
    print("The minimal DFA contains {} states".format(G.dfa.num_states))
    imgname = os.path.splitext(os.path.basename(args.filename))[0] + ".png"
    G.dfa.draw(imgname)
Ejemplo n.º 7
0
class Honeycomb(object):
    def __init__(self, coxeter_diagram, init_dist):
        if len(coxeter_diagram) != 6 or len(init_dist) != 4:
            raise ValueError("Invalid input dimension")

        # Coxeter matrix and its rank
        self.cox_mat = helpers.make_symmetry_matrix(coxeter_diagram)
        self.rank = len(self.cox_mat)

        # generators of the symmetry group
        self.gens = tuple(range(self.rank))

        # symmetry group of this tiling
        self.G = CoxeterGroup(self.cox_mat)

        # a mirror is active iff the initial point is not on it
        self.active = tuple(bool(x) for x in init_dist)

        # reflection mirrors
        self.mirrors = self.get_mirrors(coxeter_diagram)

        # reflections (possibly affine) about the mirrors
        self.reflections = self.get_reflections()

        # coordinates of the initial point
        self.init_v = self.get_init_point(init_dist)

        self.edge_hash_set = set()

        self.num_vertices = 0
        self.num_edges = 0

    def get_reflections(self):
        def reflect(v, normal):
            return v - 2 * np.dot(v, normal) * normal

        return [partial(reflect, normal=n) for n in self.mirrors]

    def transform(self, word, v):
        for w in reversed(word):
            v = self.reflections[w](v)
        return vround(v)

    def project(self, v):
        return helpers.project_poincare(v)

    def get_init_point(self, init_dist):
        return helpers.get_point_from_distance(self.mirrors, init_dist)

    def get_mirrors(self, coxeter_diagram):
        return helpers.get_hyperbolic_honeycomb_mirrors(coxeter_diagram)

    def get_fundamental_cells(self, depth=None, maxcount=20000):
        """
        Generate the fundamental cells of the tiling, these cells
        are centered at the vertices of the fundamental tetrahedron
        and are generated by reflecting the initial point about
        the three mirrors meeting at each vertex.
        """
        result = {}
        for triple in combinations(self.gens, 3):
            cox_mat = self.cox_mat[np.ix_(triple, triple)]
            refs = [self.reflections[k] for k in triple]
            active = [self.active[k] for k in triple]
            if not helpers.is_degenerate(cox_mat, active):
                C = Cell(cox_mat, self.init_v, active,
                         refs).build_geometry(depth, maxcount)
                result[triple] = C
        return result

    def is_new_edge(self, edge):
        mid = centroid(edge)
        if mid not in self.edge_hash_set:
            self.edge_hash_set.add(mid)
            return True
        return False

    def collect_fundamental_cell_edges(self):
        result = []
        for C in self.fundamental_cells.values():
            for edge in C.edge_coords:
                if self.is_new_edge(edge):
                    result.append(edge)
        return result

    def export_edge(self, fobj, p1, p2):
        """Export the data of an edge to POV-Ray .inc file."""
        fobj.write("HyperbolicEdge({}, {})\n".format(helpers.pov_vector(p1),
                                                     helpers.pov_vector(p2)))

    def generate_povray_data(
            self,
            depth=100,
            maxcount=50000,
            cell_depth=None,
            cell_edges=10000,
            filename="./povray/honeycomb-data.inc",
            eye=(0, 0, 0.5),
            lookat=(0, 0, 0),
    ):
        self.G.init()
        self.word_generator = partial(self.G.traverse,
                                      depth=depth,
                                      maxcount=maxcount)
        self.fundamental_cells = self.get_fundamental_cells(
            cell_depth, cell_edges)
        init_edges = self.collect_fundamental_cell_edges()
        bar = tqdm.tqdm(desc="processing edges", total=maxcount)
        vertices = set()
        eye = np.array(eye)
        lookat = np.array(lookat)
        viewdir = helpers.normalize(lookat - eye)

        def add_new_edge(edge):
            p1 = self.project(edge[0])
            p2 = self.project(edge[1])
            if np.dot(p1 - eye, viewdir) > 0.5 or np.dot(p2 - eye,
                                                         viewdir) > 0.5:
                self.export_edge(f, p1, p2)
                self.num_edges += 1
                for v in [p1, p2]:
                    v = vround(v)
                    if v not in vertices:
                        vertices.add(v)
                        self.num_vertices += 1

        with open(filename, "w") as f:
            f.write("#declare camera_loc = {};\n".format(
                helpers.pov_vector(eye)))
            f.write("#declare lookat = {};\n".format(
                helpers.pov_vector(lookat)))
            for edge in init_edges:
                add_new_edge(edge)

            for word in self.word_generator():
                for edge in init_edges:
                    edge = [self.transform(word, v) for v in edge]
                    if self.is_new_edge(edge):
                        add_new_edge(edge)

                bar.update(1)
            bar.close()
            verts = "#declare num_vertices = {};\n"
            verts_coords = "#declare vertices = array[{}]{{{}}};\n"
            print("{} vertices and {} edges generated".format(
                self.num_vertices, self.num_edges))
            f.write(verts.format(self.num_vertices))
            f.write(
                verts_coords.format(self.num_vertices,
                                    helpers.pov_vector_list(vertices)))
Ejemplo n.º 8
0
class Tiling2D(object):
    """
    Base class for all three types of tilings.
    """
    def __init__(self, coxeter_diagram, init_dist):
        if len(coxeter_diagram) != 3 or len(init_dist) != 3:
            raise ValueError("Invalid input dimension")

        self.diagram = coxeter_diagram

        # Coxeter matrix and its rank
        self.cox_mat = helpers.get_coxeter_matrix(coxeter_diagram)
        self.rank = len(self.cox_mat)

        # generators of the symmetry group
        self.gens = tuple(range(self.rank))

        # symmetry group of this tiling
        self.G = CoxeterGroup(self.cox_mat)

        # a mirror is active iff the initial point is not on it
        self.active = tuple(bool(x) for x in init_dist)

        # reflection mirrors
        self.mirrors = self.get_mirrors(coxeter_diagram)

        # reflections (possibly affine) about the mirrors
        self.reflections = self.get_reflections()

        # coordinates of the initial point
        self.init_v = self.get_init_point(init_dist)

        # vertices of the fundamental triangle
        self.triangle_verts = self.get_fundamental_triangle_verts()

        # ----------------------
        # to be calculated later
        # ----------------------

        # holds the words in the symmetry group up to a given depth
        self.words = None

        # holds the coset representatives of the standard parabolic
        # subgroup of vertex-stabilizing subgroup
        self.vwords = None

        self.vertices_coords = []
        self.num_vertices = None
        self.num_edges = None
        self.num_faces = None
        self.edge_indices = {}
        self.face_indices = {}

    def vertex_at_mirrors(self, i, j):
        return 2 * (i + j) % 3

    def get_init_point(self, init_dist):
        raise NotImplementedError

    def get_mirrors(self, coxeter_diagram):
        raise NotImplementedError

    def get_fundamental_triangle_verts(self):
        raise NotImplementedError

    def build_geometry(self, depth=None, maxcount=20000):
        """Postpone the actual computations to this method.
        """
        self.G.init()
        self.word_generator = partial(self.G.traverse,
                                      depth=depth,
                                      maxcount=maxcount)
        self.get_vertices()
        self.get_edges()
        self.get_faces()
        return self

    def get_vertices(self):
        # generators of the vertex-stabilizing subgroup
        H = tuple(i for i, x in enumerate(self.active) if not x)
        # coset representatives of the vertex-stabilizing subgroup
        reps = set(self.word_generator(parabolic=H))
        # sort the words in shortlex order
        self.vwords = self.G.sort_words(reps)
        # build the coset table for these cosets
        self.vtable = self.G.get_coset_table(self.vwords, H)
        self.num_vertices = len(self.vwords)
        # apply each coset representative to the initial vertex
        self.vertices_coords = [
            self.transform(w, self.init_v) for w in self.vwords
        ]

    def get_edges(self):
        """
        Compute the indices of the edges.
        Steps:
          1. Use a generator to yield a list L of words in the group.
          2. Compute the coset representatives of the edge stabilizing
             subgroup for words in L and remove duplicates. (So each
             remaining representative maps to different edges)
          3. Apply each coset representative to the ends of an initial edge
             to get the transformed edge.
          4. Find the indices of the resulting edge in L.
        """
        for i in self.gens:
            if self.active[i]:
                elist = []
                H = (i, ) + self.get_orthogonal_stabilizing_mirrors((i, ))
                reps = set(self.word_generator(parabolic=H))
                reps = self.G.sort_words(reps)
                for word in reps:
                    v1 = self.G.move(self.vtable, 0, word)
                    v2 = self.G.move(self.vtable, 0, word + (i, ))
                    if v1 is not None and v2 is not None:
                        elist.append((v1, v2))

                self.edge_indices[i] = elist

        self.num_edges = sum(len(L) for L in self.edge_indices.values())

    def get_faces(self):
        """Compute the indices of the faces (and other information we will need).
        """
        for i, j in combinations(self.gens, 2):
            # this is the center of the initial face,
            # it's a vertex of the fundamental triangle.
            c0 = self.triangle_verts[self.vertex_at_mirrors(i, j)]
            # a list holds the vertices of the initial face.
            f0 = []
            m = self.cox_mat[i][j]
            # this is the stabilizing subgroup of the initial face f0.
            H = (i, j) + self.get_orthogonal_stabilizing_mirrors((i, j))
            # type indicates if this face is regular (0) or truncated (1).
            # it's truncated if and only if both mirrors are active.
            type = 0

            # compute the words (may not be in normal form) for the
            # vertices of the initial face
            if self.active[i] and self.active[j]:
                type = 1
                for k in range(m):
                    f0.append(self.G.move(self.vtable, 0, (i, j) * k))
                    f0.append(self.G.move(self.vtable, 0, (i, j) * k + (i, )))
            elif self.active[i] and m > 2:
                for k in range(m):
                    f0.append(self.G.move(self.vtable, 0, (j, i) * k))
            elif self.active[j] and m > 2:
                for k in range(m):
                    f0.append(self.G.move(self.vtable, 0, (i, j) * k))
            else:
                continue

            # compute coset representatives of the initial face,
            # each word in the result set maps f0 to a different face.
            reps = set(self.word_generator(parabolic=H))
            # sort the faces in shortlex order.
            reps = self.G.sort_words(reps)
            # a set holds faces, we use a set here because though a word w
            # in H stabilizes f0, it may change cyclically rotate f0 to another
            # different ordered tuple.
            flist = []
            for word in reps:
                # compute the indices of the vertices of the transformed face
                f = tuple(self.G.move(self.vtable, v, word) for v in f0)
                # check if `None` is in f (in this case f contains some
                # vertex that is not in the vertices list) or there already has
                # a rotated version of f in the set.
                if None not in f:
                    center = self.transform(word, c0)
                    coords = [self.vertices_coords[k] for k in f]
                    face = DihedralFace(word, f, center, coords, type)
                    flist.append(face)

            self.face_indices[(i, j)] = flist

        self.num_faces = sum(len(L) for L in self.face_indices.values())

    def get_reflections(self):
        def reflect(v, normal):
            return v - 2 * np.dot(v, normal) * normal

        return [partial(reflect, normal=n) for n in self.mirrors]

    def transform(self, word, v):
        for w in reversed(word):
            v = self.reflections[w](v)
        return v

    def get_orthogonal_stabilizing_mirrors(self, subgens):
        """
        :param subgens: a list of generators, e.g. [0, 1]

        Given a list of generators in `subgens`, return the generators that
        commute with all of those in `subgens` and fix the initial vertex.
        """
        result = []
        for s in self.gens:
            # check commutativity
            if all(self.cox_mat[x][s] == 2 for x in subgens):
                # check if it fixes v0
                if not self.active[s]:
                    result.append(s)
        return tuple(result)

    def get_info(self):
        """Return some statistics of the tiling.
        """
        pattern = "{}-{}-{}".format(*self.diagram).replace("/", "|")
        info = ""
        info += "name: triangle group {}\n".format(pattern)
        info += "cox_mat: {}\n".format(self.cox_mat)
        info += "vertices: {}\n".format(self.num_vertices)
        info += "edges: {}\n".format(self.num_edges)
        info += "faces: {}\n".format(self.num_faces)
        info += "states in the automaton: {}\n".format(self.G.dfa.num_states)
        info += "reflection table:\n{}\n".format(self.G.reftable)
        info += "the automaton is saved as {}_dfa.png".format(pattern)
        self.G.dfa.draw(pattern + "_dfa.png")
        return info

    def render(self, *arg, **kwargs):
        raise NotImplementedError
Ejemplo n.º 9
0
class Tiling2D(object):
    def __init__(self, coxeter_diagram, init_dist):
        if len(coxeter_diagram) != 3 or len(init_dist) != 3:
            raise ValueError("Invalid input dimension")

        self.diagram = coxeter_diagram

        # Coxeter matrix and its rank
        self.cox_mat = helpers.make_symmetry_matrix(coxeter_diagram)
        self.rank = len(self.cox_mat)

        # generators of the symmetry group
        self.gens = tuple(range(self.rank))

        # symmetry group of this tiling
        self.G = CoxeterGroup(self.cox_mat)

        # a mirror is active iff the initial point is not on it
        self.active = tuple(bool(x) for x in init_dist)

        # reflection mirrors
        self.mirrors = self.get_mirrors(coxeter_diagram)

        # reflections (possibly affine) about the mirrors
        self.reflections = self.get_reflections()

        # coordinates of the initial point
        self.init_v = self.get_init_point(init_dist)

        # vertices of the fundamental triangle
        self.triangle_verts = self.get_fundamental_triangle_verts()

        # ----------------------
        # to be calculated later
        # ----------------------

        # holds the words in the symmetry group up to a given depth
        self.words = None

        # holds the coset representatives of the standard parabolic
        # subgroup of vertex-stabilizing subgroup
        self.vwords = None

        self.vertices_coords = []
        self.num_vertices = None
        self.num_edges = None
        self.num_faces = None
        self.edge_indices = {}
        self.face_indices = {}

    def vertex_at_mirrors(self, i, j):
        return 2 * (i + j) % 3

    def get_init_point(self, init_dist):
        raise NotImplementedError

    def get_mirrors(self, coxeter_diagram):
        raise NotImplementedError

    def get_fundamental_triangle_verts(self):
        raise NotImplementedError

    def build_geometry(self, depth=None, maxcount=20000):
        """Postpone the actual computations to this method.
        """
        self.G.init()
        self.words = tuple(self.G.traverse(depth, maxcount))
        self.get_vertices()
        self.get_edges()
        self.get_faces()
        return self

    def get_vertices(self):
        # generators of the vertex-stabilizing subgroup
        H = tuple(i for i, x in enumerate(self.active) if not x)
        # coset representatives of the vertex-stabilizing subgroup
        reps = set(self.G.get_coset_representative(w, H) for w in self.words)
        self.vwords = self.G.sort_words(reps)
        self.vtable = self.G.get_coset_table(self.vwords, H)
        self.num_vertices = len(self.vwords)
        self.vertices_coords = [
            self.transform(w, self.init_v) for w in self.vwords
        ]

    def get_edges(self):
        for i in self.gens:
            if self.active[i]:
                elist = []
                H = (i, )  # edge-stabilizing subgroup
                reps = set(
                    self.G.get_coset_representative(w, H) for w in self.words)
                reps = self.G.sort_words(reps)
                for word in reps:
                    v1 = self.G.move(self.vtable, 0, word)
                    v2 = self.G.move(self.vtable, 0, word + (i, ))
                    if v1 is not None and v2 is not None:
                        if (v1, v2) not in elist and (v2, v1) not in elist:
                            elist.append((v1, v2))

                self.edge_indices[i] = elist

        self.num_edges = sum(len(L) for L in self.edge_indices.values())

    def get_faces(self):
        for i, j in combinations(self.gens, 2):
            c0 = self.triangle_verts[self.vertex_at_mirrors(i, j)]
            f0 = []
            m = self.cox_mat[i][j]
            H = (i, j)
            type = 0
            if self.active[i] and self.active[j]:
                type = 1
                for k in range(m):
                    f0.append(self.G.move(self.vtable, 0, (i, j) * k))
                    f0.append(self.G.move(self.vtable, 0, (i, j) * k + (i, )))
            elif self.active[i] and m > 2:
                for k in range(m):
                    f0.append(self.G.move(self.vtable, 0, (j, i) * k))
            elif self.active[j] and m > 2:
                for k in range(m):
                    f0.append(self.G.move(self.vtable, 0, (i, j) * k))
            else:
                continue

            reps = set(
                self.G.get_coset_representative(w, H) for w in self.words)
            reps = self.G.sort_words(reps)
            flist = []
            for word in reps:
                f = tuple(self.G.move(self.vtable, v, word) for v in f0)
                if None not in f and not helpers.check_duplicate_face(
                        f, flist):
                    center = self.transform(word, c0)
                    coords = [self.vertices_coords[k] for k in f]
                    face = DihedralFace(word, f, center, coords, type)
                    flist.append(face)

            self.face_indices[(i, j)] = flist

        self.num_faces = sum(len(L) for L in self.face_indices.values())

    def get_reflections(self):
        def reflect(v, normal):
            return v - 2 * np.dot(v, normal) * normal

        return [partial(reflect, normal=n) for n in self.mirrors]

    def transform(self, word, v):
        for w in reversed(word):
            v = self.reflections[w](v)
        return v

    def get_info(self):
        """Return some statistics of the tiling.
        """
        pattern = "{}-{}-{}".format(*self.diagram).replace("/", "|")
        info = ""
        info += "name: triangle group {}\n".format(pattern)
        info += "cox_mat: {}\n".format(self.cox_mat)
        info += "vertices: {}\n".format(self.num_vertices)
        info += "edges: {}\n".format(self.num_edges)
        info += "faces: {}\n".format(self.num_faces)
        info += "states in the automaton: {}\n".format(self.G.dfa.num_states)
        info += "reflection table:\n{}\n".format(self.G.reftable)
        info += "the automaton is saved as {}_dfa.png".format(pattern)
        self.G.dfa.draw(pattern + "_dfa.png")
        return info

    def render(self, *arg, **kwargs):
        raise NotImplementedError
Ejemplo n.º 10
0
class UniformTiling(object):
    def __init__(self, coxeter_diagram, init_dist):
        self.cox_mat = helpers.get_coxeter_matrix(coxeter_diagram)
        self.G = CoxeterGroup(self.cox_mat)
        self.active = tuple(bool(x) for x in init_dist)
        self.words = None

        self.mirrors = self.get_mirrors(coxeter_diagram)
        self.init_v = helpers.get_point_from_distance(self.mirrors, init_dist)
        self.reflections = self.get_reflections(init_dist)

        # to be calculated later
        self.vertices_coords = []
        self.num_vertices = None
        self.num_edges = None
        self.num_faces = None
        self.edge_indices = {}
        self.face_indices = {}

    def build_geometry(self, depth=None, maxcount=20000):
        self.G.init()
        self.words = tuple(self.G.traverse(depth, maxcount))
        self.get_vertices()
        self.get_edges()
        self.get_faces()

    def get_vertices(self):
        parabolic = tuple(i for i, x in enumerate(self.active) if not x)
        coset_reps = set([
            self.G.get_coset_representative(w, parabolic, True)
            for w in self.words
        ])
        self.vwords = self.G.sort_words(coset_reps)
        self.vtable = self.G.get_coset_table(self.vwords, parabolic)
        self.num_vertices = len(self.vwords)
        self.vertices_coords = [
            self.transform(word, self.init_v) for word in self.vwords
        ]

    def get_edges(self):
        for i in range(len(self.active)):
            if self.active[i]:
                elist = []
                coset_reps = set([
                    self.G.get_coset_representative(w, (i, ), True)
                    for w in self.words
                ])
                for word in self.G.sort_words(coset_reps):
                    v1 = self.G.move(self.vtable, 0, word)
                    v2 = self.G.move(self.vtable, 0, word + (i, ))
                    if v1 is not None and v2 is not None:
                        if (v1, v2) not in elist and (v2, v1) not in elist:
                            elist.append((v1, v2))

                self.edge_indices[i] = elist

        self.num_edges = sum(len(L) for L in self.edge_indices.values())

    def get_faces(self):
        for i, j in combinations(range(len(self.active)), 2):
            f0 = []
            m = self.cox_mat[i][j]
            parabolic = (i, j)
            if self.active[i] and self.active[j]:
                for k in range(m):
                    f0.append(self.G.move(self.vtable, 0, (i, j) * k))
                    f0.append(self.G.move(self.vtable, 0, (i, j) * k + (i, )))
            elif self.active[i] and m > 2:
                for k in range(m):
                    f0.append(self.G.move(self.vtable, 0, (j, i) * k))
            elif self.active[j] and m > 2:
                for k in range(m):
                    f0.append(self.G.move(self.vtable, 0, (i, j) * k))
            else:
                continue

            coset_reps = set([
                self.G.get_coset_representative(w, parabolic, True)
                for w in self.words
            ])
            flist = []
            for word in self.G.sort_words(coset_reps):
                f = tuple(self.G.move(self.vtable, v, word) for v in f0)
                if None not in f and not helpers.check_duplicate_face(
                        f, flist):
                    flist.append(f)

            self.face_indices[(i, j)] = flist

        self.num_faces = sum(len(L) for L in self.face_indices.values())

    def transform(self, word, v):
        for w in reversed(word):
            v = self.reflections[w](v)
        return v

    def get_reflections(self, init_dist):
        raise NotImplementedError

    def get_fundamental_triangle_vertices(self):
        raise NotImplementedError

    def project(self, v):
        raise NotImplementedError

    def get_mirrors(self, coxeter_diagram):
        raise NotImplementedError