Пример #1
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
Пример #2
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
Пример #3
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