Ejemplo n.º 1
0
def spanning_forest(M):
    r"""
    Return a list of edges of a spanning forest of the bipartite
    graph defined by `M`

    INPUT:

    - ``M`` -- a matrix defining a bipartite graph G. The vertices are the 
      rows and columns, if `M[i,j]` is non-zero, then there is an edge
      between row `i` and column `j`.

    OUTPUT:

    A list of tuples `(r_i,c_i)` representing edges between row `r_i` and column `c_i`.

    EXAMPLES::

        sage: len(sage.matroids.utilities.spanning_forest(matrix([[1,1,1],[1,1,1],[1,1,1]])))
        5
        sage: len(sage.matroids.utilities.spanning_forest(matrix([[0,0,1],[0,1,0],[0,1,0]])))
        3
    """
    # Given a matrix, produce a spanning tree
    G = Graph()
    m = M.ncols()
    for (x, y) in M.dict():
        G.add_edge(x + m, y)
    T = []
    # find spanning tree in each component
    for component in G.connected_components():
        spanning_tree = kruskal(G.subgraph(component))
        for (x, y, z) in spanning_tree:
            if x < m:
                t = x
                x = y
                y = t
            T.append((x - m, y))
    return T
Ejemplo n.º 2
0
def spanning_forest(M):
    r"""
    Return a list of edges of a spanning forest of the bipartite
    graph defined by `M`

    INPUT:

    - ``M`` -- a matrix defining a bipartite graph G. The vertices are the
      rows and columns, if `M[i,j]` is non-zero, then there is an edge
      between row `i` and column `j`.

    OUTPUT:

    A list of tuples `(r_i,c_i)` representing edges between row `r_i` and column `c_i`.

    EXAMPLES::

        sage: len(sage.matroids.utilities.spanning_forest(matrix([[1,1,1],[1,1,1],[1,1,1]])))
        5
        sage: len(sage.matroids.utilities.spanning_forest(matrix([[0,0,1],[0,1,0],[0,1,0]])))
        3
    """
    # Given a matrix, produce a spanning tree
    G = Graph()
    m = M.ncols()
    for (x,y) in M.dict():
        G.add_edge(x+m,y)
    T = []
    # find spanning tree in each component
    for component in G.connected_components():
        spanning_tree = kruskal(G.subgraph(component))
        for (x,y,z) in spanning_tree:
            if x < m:
                t = x
                x = y
                y = t
            T.append((x-m,y))
    return T
Ejemplo n.º 3
0
def spanning_stars(M):
    r"""
    Returns the edges of a connected subgraph that is a union of 
    all edges incident some subset of vertices.

    INPUT:

    - ``M`` -- a matrix defining a bipartite graph G. The vertices are the 
      rows and columns, if `M[i,j]` is non-zero, then there is an edge
      between row i and column 0.

    OUTPUT:

    A list of tuples `(row,column)` in a spanning forest of the bipartite graph defined by ``M``
    
    EXAMPLES::

        sage: edges = sage.matroids.utilities.spanning_stars(matrix([[1,1,1],[1,1,1],[1,1,1]]))
        sage: Graph([(x+3, y) for x,y in edges]).is_connected()
        True
    """

    G = Graph()
    m = M.ncols()
    for (x, y) in M.dict():
        G.add_edge(x + m, y)

    delta = (M.nrows() + m)**0.5
    # remove low degree vertices
    H = []
    # candidate vertices
    V_0 = set([])
    d = 0
    while G.order() > 0:
        (x, d) = min(G.degree_iterator(labels=True), key=itemgetter(1))
        if d < delta:
            V_0.add(x)
            H.extend(G.edges_incident(x, False))
            G.delete_vertex(x)
        else:
            break

    # min degree is at least sqrt(n)
    # greedily remove vertices
    G2 = G.copy()
    # set of picked vertices
    V_1 = set([])
    while G2.order() > 0:
        # choose vertex with maximum degree in G2
        (x, d) = max(G2.degree_iterator(labels=True), key=itemgetter(1))
        V_1.add(x)
        G2.delete_vertices(G2.neighbors(x))
        G2.delete_vertex(x)

    # G2 is a graph of all edges incident to V_1
    G2 = Graph()
    for v in V_1:
        for u in G.neighbors(v):
            G2.add_edge(u, v)

    V = V_0 | V_1
    # compute a spanning tree
    T = spanning_forest(M)
    for (x, y) in T:
        if not x in V and not y in V:
            V.add(v)

    for v in V:
        if G.has_vertex(v):  # some vertices are not in G
            H.extend(G.edges_incident(v, False))

    # T contain all edges in some spanning tree
    T = []
    for (x, y) in H:
        if x < m:
            t = x
            x = y
            y = t
        T.append((x - m, y))
    return T
Ejemplo n.º 4
0
def spanning_stars(M):
    r"""
    Returns the edges of a connected subgraph that is a union of
    all edges incident some subset of vertices.

    INPUT:

    - ``M`` -- a matrix defining a bipartite graph G. The vertices are the
      rows and columns, if `M[i,j]` is non-zero, then there is an edge
      between row i and column 0.

    OUTPUT:

    A list of tuples `(row,column)` in a spanning forest of the bipartite graph defined by ``M``

    EXAMPLES::

        sage: edges = sage.matroids.utilities.spanning_stars(matrix([[1,1,1],[1,1,1],[1,1,1]]))
        sage: Graph([(x+3, y) for x,y in edges]).is_connected()
        True
    """

    G = Graph()
    m = M.ncols()
    for (x,y) in M.dict():
        G.add_edge(x+m,y)

    delta = (M.nrows()+m)**0.5
    # remove low degree vertices
    H = []
    # candidate vertices
    V_0 = set([])
    d = 0
    while G.order()>0:
        (x,d) = min(G.degree_iterator(labels=True),key=itemgetter(1))
        if d < delta:
            V_0.add(x)
            H.extend(G.edges_incident(x,False))
            G.delete_vertex(x)
        else:
            break

    # min degree is at least sqrt(n)
    # greedily remove vertices
    G2 = G.copy()
    # set of picked vertices
    V_1 = set([])
    while G2.order()>0:
        # choose vertex with maximum degree in G2
        (x,d) = max(G2.degree_iterator(labels=True),key=itemgetter(1))
        V_1.add(x)
        G2.delete_vertices(G2.neighbors(x))
        G2.delete_vertex(x)

    # G2 is a graph of all edges incident to V_1
    G2 = Graph()
    for v in V_1:
        for u in G.neighbors(v):
            G2.add_edge(u,v)

    V = V_0 | V_1
    # compute a spanning tree
    T = spanning_forest(M)
    for (x,y) in T:
        if not x in V and not y in V:
            V.add(v)

    for v in V:
        if G.has_vertex(v): # some vertices are not in G
            H.extend(G.edges_incident(v,False))

    # T contain all edges in some spanning tree
    T = []
    for (x,y) in H:
        if x < m:
            t = x
            x = y
            y = t
        T.append((x-m,y))
    return T
Ejemplo n.º 5
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)
Ejemplo n.º 6
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)