Exemple #1
0
class Isogeny_graph:

    # Class for construction isogeny graphs with 1 or more degrees
    # ls is a list of primes which define the defining degrees
    def __init__(self, E, ls, special=False):

        self._ls = ls
        j = E.j_invariant()

        self.field = E.base_field()
        self._graph = Graph(multiedges=True, loops=True)

        self._special = False
        self._graph.add_vertex(j)
        queue = [j]
        R = rainbow(len(ls) + 1)
        self._rainbow = R
        self._edge_colors = {R[i]: [] for i in range(len(ls))}

        while queue:
            color = 0
            s = queue.pop(0)

            if s == 0 or s == 1728:
                self._special = True

            for l in ls:
                neighb = isogenous_curves(s, l)

                for i in neighb:
                    if not self._graph.has_vertex(i[0]):
                        queue.append(i[0])
                        self._graph.add_vertex(i[0])
                    if not ((s, i[0], l) in self._edge_colors[R[color]] or
                            (i[0], s, l) in self._edge_colors[R[color]]):
                        for _ in range(i[1]):
                            self._graph.add_edge((s, i[0], l))
                        self._edge_colors[R[color]].append((s, i[0], l))
                color += 1

        if self._special and special:
            print("Curve with j_invariant 0 or 1728 found, may malfunction.")

    def __repr__(self):
        return "Isogeny graph of degrees %r" % (self._ls)

    # Returns degrees of isogenies
    def degrees(self):
        return self._ls

    # Returns list of all edges
    def edges(self):
        return self._graph.edges()

    # Returns list of all vertices
    def vertices(self):
        return self._graph.vertices()

    # Plots the graph
    # Optional arguments figsize, vertex_size, vertex_labels and layout which are the same as in igraph
    def plot(self,
             figsize=None,
             edge_labels=False,
             vertex_size=None,
             layout=None):
        if vertex_size == None:
            return self._graph.plot(edge_colors=self._edge_colors,
                                    figsize=figsize,
                                    edge_labels=edge_labels,
                                    layout=layout)
        else:
            return self._graph.plot(edge_colors=self._edge_colors,
                                    figsize=figsize,
                                    edge_labels=edge_labels,
                                    vertex_size=vertex_size,
                                    layout=layout)
Exemple #2
0
class Volcano:

    # Class for l-volcano of elliptic curve E over finite field
    # We can construct Volcano either using Velu algorithm (Velu = True) or modular polynomials (Velu = False)

    # If Velu algorithm is chosen, constructor tries to find kernels of isogenies in extension,
    # if the size of the base field of extension is higher than upper_bit_limit, then the algorithm stops

    # In case of the option of modular polynomials, we assume that l<127
    # You can turn off the 0,1728 warning with special = False
    def __init__(self, E, l, Velu=False, upper_bit_limit=100, special=True):

        self._l = l
        self._E = E
        global VELU
        VELU = Velu

        global UPPER_BITS
        UPPER_BITS = upper_bit_limit

        self.field = E.base_field()

        try:
            self._neighbours, self._vertices, self._special = BFS(
                E.j_invariant(), l)

        except large_extension_error as e:
            print(
                "Upper limit for bitlength of size of extension field exceeded"
            )
            print(e.msg)
            return

        if self._special and special:
            print("Curve with j_invariant 0 or 1728 found, may malfunction.")
        self._depths = {}
        self._levels = []
        self._depth = 0
        self._graph = Graph(multiedges=True, loops=True)
        for s in self._neighbours.values():
            self._graph.add_vertex(s[0])

        for s in self._neighbours.values():
            for i in range(1, len(s)):
                if not self._graph.has_edge(s[0], s[i][0]):
                    for j in range(s[i][1]):
                        self._graph.add_edge((s[0], s[i][0], j))
        if len(self._vertices) > 1 and E.is_ordinary():
            self.compute_depths()
        else:
            self._depths = {str(E.j_invariant()): 0}
            self._levels = [self._vertices]

    # Method for computing depths and sorting vertices into levels
    def compute_depths(self):
        heights = {}
        level = []

        if len(list(self._neighbours.values())[0]) == 3:
            self._levels = [self._vertices]
            self._depths = {str(i): 0 for i in self._vertices}
            self._depth = 0
            return

        for s in self._neighbours.keys():
            if len(self._neighbours[s]) == 2:
                heights[s] = 0
                level.append(self._neighbours[s][0])

        self._levels.append(level)
        h = 1
        while len(heights.keys()) != len(self._vertices):

            level = []
            for s in self._neighbours.keys():

                if s in heights.keys():
                    continue
                if len(self._neighbours[s]) > 2:
                    if str(self._neighbours[s][1][0]) in heights.keys(
                    ) and heights[str(self._neighbours[s][1][0])] == h - 1:
                        heights[s] = h
                        level.append(self._neighbours[s][0])

                        continue

                    if str(self._neighbours[s][2][0]) in heights.keys(
                    ) and heights[str(self._neighbours[s][2][0])] == h - 1:
                        heights[s] = h
                        level.append(self._neighbours[s][0])

                        continue

                if len(self._neighbours[s]) > 3:
                    if str(self._neighbours[s][3][0]) in heights.keys(
                    ) and heights[str(self._neighbours[s][3][0])] == h - 1:
                        heights[s] = h
                        level.append(self._neighbours[s][0])

                        continue
            h += 1
            self._levels.append(level)

        self._depth = h - 1
        self._depths = {}
        for k in heights.keys():
            self._depths[k] = h - 1 - heights[k]
        self._levels.reverse()

    # Returns the defining degree of volcano
    def degree(self):
        return self._l

    # Returns the level at depth i
    def level(self, i):
        return self._levels[i]

    # Returns list of all edges
    def edges(self):
        return self._graph.edges()

    # Returns depth of volcano
    def depth(self):
        return self._depth

    # Returns the crater of volcano
    def crater(self):
        return self._levels[0]

    # Plots the volcano
    # Optional arguments figsize, vertex_size, vertex_labels and layout which are the same as in igraph (sage Class)
    def plot(self,
             figsize=None,
             vertex_labels=True,
             vertex_size=None,
             layout=None):
        try:
            self._graph.layout(layout=layout, save_pos=True)
        except:
            pass
        if vertex_size != None:
            return self._graph.plot(figsize=figsize,
                                    vertex_labels=vertex_labels,
                                    vertex_size=vertex_size)
        else:
            return self._graph.plot(figsize=figsize,
                                    vertex_labels=vertex_labels)

    # Returns all vertices of volcano
    def vertices(self):
        return self._vertices

    # Returns all neighbours of vertex j
    def neighbors(self, j):
        return self._neighbours[str(j)][1:]

    # Returns depth of vertex j
    def vertex_depth(self, j):
        return self._depths[str(j)]

    # Returns true if the volcano contains vertex 1728 or 0
    def special(self):
        return self._special

    # Returns parent (upper level neighbour) of j
    def volcano_parent(self, j):
        h = self._depths[str(j)]
        for i in self._neighbours[str(j)][1:]:
            if self._depths[str(i[0])] < h:
                return i[0]
        return None

    # Returns all children (lower level neighbours) of vertex j
    def volcano_children(self, j):
        children = []
        h = self._depths[str(j)]
        for i in self._neighbours[str(j)][1:]:
            if self._depths[str(i[0])] > h:
                children.append(i[0])
        return children

    # Finds an extension of curve over which the volcano has depth h
    def expand_volcano(self, h):
        return Volcano(expand_volcano(self._E, h, self._l), self._l)

    def __repr__(self):
        return "Isogeny %r-volcano of depth %r over %r" % (
            self._l, self.depth(), self.field)