コード例 #1
0
def MatrixCharpoly(coefficients):
    """
    INPUT:
        - ``coefficients`` -- an array with n + 1, where the first entry is 1
    OUTPUT:
        - a matrix A such that det(1 - t A) = R[](coefficients)
    EXAMPLES:
        sage: list(reversed(yellow.MatrixCharpoly([1,2,3]).characteristic_polynomial().list())) == [1,2,3]
            True
    TESTS: 
        sage: test_pass = True
        sage: for i in range(10):
        ....:     cf = [1] + [ZZ.random_element() for _ in range(randint(1,10))];
        ....:     test_pass = test_pass and (list(reversed(MatrixCharpoly(cf).characteristic_polynomial().list())) == cf)
        sage: print test_pass
            True
    """
    assert coefficients[0] == 1
    n = len(coefficients) - 1
    M = Matrix(1, n - 1).stack(identity_matrix(n - 1, n - 1))
    M = M.augment(-vector(reversed(coefficients[1:])))
    return M
コード例 #2
0
def pade_linalg(input_series, m):
    ring = input_series.base_ring()
    polyring = input_series.parent()._poly_ring()
    an = vector(ring, input_series.padded_list())
    minus_an = -an;
    N = len(an) - 1
    n = N - m
    if n < 0:
        raise ValueError("the precision of the series is not large enough")
    Akj = Matrix(ring, N + 1, n + 1);
    for i in range(min(N + 1, n + 1)):
        Akj[i, i] = 1;

    Bkj = Matrix(ring, N + 1, m);
    for row in range(1, m+1):
        Bkj[row,:row] = minus_an[:row][::-1]
    for row in range(m+1, N+1):
        Bkj[row,:] = minus_an[row-m:row][::-1]
    C = Akj.augment(Bkj);
    pq = C.solve_right(an).list()
    p = pq[:n+1]
    q = [1] + pq[n+1:]
    return polyring(p), polyring(q)
コード例 #3
0
class StrataGraph(object):
    R = PolynomialRing(ZZ, "X", 1, order='lex')
    Xvar = R.gen()

    def __init__(self, M=None, genus_list=None):
        #print genus_list
        if M:
            self.M = copy(M)
        elif genus_list:
            self.M = Matrix(StrataGraph.R,
                            len(genus_list) + 1, 1, [-1] + genus_list)
        else:
            self.M = Matrix(StrataGraph.R, 1, 1, -1)

    def __repr__(self):
        """
    Drew added this.
    """
        name = self.nice_name()
        if name == None:
            return repr(self.nice_matrix())
        else:
            return name

    def num_vertices(self):
        return self.M.nrows() - 1

    def num_edges(self):
        return self.M.ncols() - 1

    def h1(self):
        return self.M.ncols() - self.M.nrows() + 1

    def add_vertex(self, g):
        self.M = self.M.stack(Matrix(1, self.M.ncols()))
        self.M[-1, 0] = g

    def add_edge(self, i1, i2, marking=0):
        self.M = self.M.augment(Matrix(self.M.nrows(), 1))
        self.M[0, -1] = marking
        if i1 > 0:
            self.M[i1, -1] += 1
        if i2 > 0:
            self.M[i2, -1] += 1

    def del_vertex(self, i):
        self.M = self.M[:i].stack(self.M[(i + 1):])

    def del_edge(self, i):
        self.M = self.M[0:, :i].augment(self.M[0:, (i + 1):])

    def compute_degree_vec(self):
        self.degree_vec = [
            sum(self.M[i, j][0] for j in range(1, self.M.ncols()))
            for i in range(1, self.M.nrows())
        ]

    def degree(self, i):
        return self.degree_vec[i - 1]

    def split_vertex(self, i, row1, row2):
        self.M = self.M.stack(Matrix(2, self.M.ncols(), row1 + row2))
        self.add_edge(self.M.nrows() - 2, self.M.nrows() - 1)
        self.del_vertex(i)

    def compute_parity_vec_original(self):
        X = StrataGraph.Xvar
        self.parity_vec = [
            (ZZ(1 + self.M[i, 0][0] + sum(self.M[i, k][1]
                                          for k in range(1, self.M.ncols())) +
                sum(self.M[i, k][2] for k in range(1, self.M.ncols())) +
                self.M[i, 0].derivative(X).substitute(X=1)) % 2)
            for i in range(1, self.M.nrows())
        ]

    def compute_parity_vec(self):
        self.parity_vec = [
            (ZZ(1 + self.M[i, 0][0] + sum(self.M[i, k][1]
                                          for k in range(1, self.M.ncols())) +
                sum(self.M[i, k][2] for k in range(1, self.M.ncols())) +
                self.last_parity_summand(i)) % 2)
            for i in range(1, self.M.nrows())
        ]

    def last_parity_summand(self, i):
        return sum(
            (expon[0] * coef for expon, coef in self.M[i, 0].dict().items()))

    def parity(self, i):
        return self.parity_vec[i - 1]

    def replace_vertex_with_graph(self, i, G):
        X = StrataGraph.Xvar
        nv = self.num_vertices()
        ne = self.num_edges()
        #i should have degree d, there should be no classes near i, and G should have markings 1,...,d and genus equal to the genus of i
        hedge_list = []
        for k in range(1, self.M.ncols()):
            for j in range(ZZ(self.M[i, k])):
                hedge_list.append(k)
        self.del_vertex(i)
        for j in range(G.num_edges() - len(hedge_list)):
            self.add_edge(0, 0)
        for j in range(G.num_vertices()):
            self.add_vertex(G.M[j + 1, 0])
        col = ne + 1
        for k in range(1, G.M.ncols()):
            if G.M[0, k] > 0:
                mark = ZZ(G.M[0, k])
                for j in range(G.num_vertices()):
                    if self.M[nv + j, hedge_list[mark - 1]] == 0:
                        self.M[nv + j, hedge_list[mark - 1]] = G.M[j + 1, k]
                    elif G.M[j + 1, k] != 0:
                        a = self.M[nv + j, hedge_list[mark - 1]][1]
                        b = G.M[j + 1, k][1]
                        self.M[nv + j,
                               hedge_list[mark - 1]] = 2 + max(a, b) * X + min(
                                   a, b) * X**2
            else:
                for j in range(G.num_vertices()):
                    self.M[nv + j, col] = G.M[j + 1, k]
                col += 1

    def compute_invariant(self):
        self.compute_parity_vec()
        self.compute_degree_vec()
        nr, nc = self.M.nrows(), self.M.ncols()
        self.invariant = [[self.M[i, 0], [], [], [[] for j in range(1, nr)]]
                          for i in range(1, nr)]
        for k in range(1, nc):
            L = [i for i in range(1, nr) if self.M[i, k] != 0]
            if len(L) == 1:
                if self.M[0, k] != 0:
                    self.invariant[L[0] - 1][2].append(
                        [self.M[0, k], self.M[L[0], k]])
                else:
                    self.invariant[L[0] - 1][1].append(self.M[L[0], k])
            else:
                self.invariant[L[0] - 1][3][L[1] - 1].append(
                    [self.M[L[0], k], self.M[L[1], k]])
                self.invariant[L[1] - 1][3][L[0] - 1].append(
                    [self.M[L[1], k], self.M[L[0], k]])
        for i in range(1, nr):
            self.invariant[i - 1][3] = [
                term for term in self.invariant[i - 1][3] if len(term) > 0
            ]
            for term in self.invariant[i - 1][3]:
                term.sort()
            self.invariant[i - 1][3].sort()
            self.invariant[i - 1][2].sort()
            self.invariant[i - 1][1].sort()
        vertex_invariants = [[i, self.invariant[i - 1]] for i in range(1, nr)]
        self.invariant.sort()
        vertex_invariants.sort(key=lambda x: x[1])
        self.vertex_groupings = []
        for i in range(nr - 1):
            if i == 0 or vertex_invariants[i][1] != vertex_invariants[i -
                                                                      1][1]:
                self.vertex_groupings.append([])
            self.vertex_groupings[-1].append(vertex_invariants[i][0])

        #Drew added this
        self.invariant = tupleit(self.invariant)
        self.hash = hash(self.invariant)

    def __eq__(self, other):
        """
        Drew added this.
        """
        return graph_isomorphic(self, other)

    def __hash__(self):
        """
        Drew added this.
        Note that it returns a value stored when "compute_invariant" is called.
        """
        try:
            return self.hash
        except:
            self.compute_invariant()
            return self.hash

    def codim(self):
        codim = 0
        for v in range(1, self.num_vertices() + 1):
            for expon, coef in self.M[v, 0].dict().items():
                codim += expon[0] * coef
            for e in range(1, self.num_edges() + 1):
                for expon, coef in self.M[v, e].dict().items():
                    if expon[0] > 0:
                        codim += coef
        for e in range(1, self.num_edges() + 1):
            if self.M[0, e] == 0:
                codim += 1
        return codim

    def codim_undecorated(self):
        codim = 0
        for e in range(1, self.num_edges() + 1):
            if self.M[0, e] == 0:
                codim += 1
        return codim

        print("not implemented!!!!!!")
        return 1

    def kappa_on_v(self, v):
        """
    Drew added this.
    
    """
        #print "kappa",self.M[v,0].dict()

        for ex, coef in self.M[v, 0].dict().items():
            if ex[0] != 0:
                yield ex[0], coef

    def psi_no_loop_on_v(self, v):
        for edge in range(1, self.num_edges() + 1):
            if self.M[v, edge][0] == 1:
                psi_expon = self.M[v, edge][1]
                if psi_expon > 0:
                    yield edge, psi_expon

    def psi_loop_on_v(self, v):
        for edge in range(1, self.num_edges() + 1):
            if self.M[v, edge][0] == 2:
                psi_expon1 = self.M[v, edge][1]
                psi_expon2 = self.M[v, edge][2]
                if psi_expon1 > 0:
                    yield edge, psi_expon1, psi_expon2

    def moduli_dim_v(self, v):
        """
    Drew added this.
    """
        return 3 * self.M[v, 0][0] - 3 + sum(
            (self.M[v, j][0] for j in range(1,
                                            self.num_edges() + 1)))

    def num_loops(self):
        count = 0
        for edge in range(1, self.num_edges() + 1):
            for v in range(1, self.num_vertices() + 1):
                if self.M[v, edge][0] == 2:
                    count += 1
                    break

        return count

    def num_undecorated_loops(self):
        count = 0
        for edge in range(1, self.num_edges() + 1):
            for v in range(1, self.num_vertices() + 1):
                if self.M[v, edge] == 2:
                    count += 1
                    break

        return count

    def num_full_edges(self):
        count = 0
        for edge in range(1, self.num_edges() + 1):
            if sum((self.M[v, edge][0]
                    for v in range(1,
                                   self.num_vertices() + 1))) == 2:
                count += 1
        return count

    def forget_kappas(self):
        M = copy(self.M)
        for v in range(1, self.num_vertices() + 1):
            M[v, 0] = M[v, 0][0]
        return StrataGraph(M)

    def forget_decorations(self):
        M = Matrix(StrataGraph.R,
                   [[self.M[r, c][0] for c in range(self.M.ncols())]
                    for r in range(self.M.nrows())])
        Gnew = StrataGraph(M)
        #Gnew.compute_invariant()
        return Gnew

    def is_loop(self, edge):
        for v in range(1, self.num_vertices() + 1):
            if self.M[v, edge][0] == 2:
                return True
            if self.M[v, edge][0] == 1:
                return False

        raise Exception("Unexpected!")

    ps_name = "ps"
    ps2_name = "ps_"

    def nice_matrix(self):
        Mnice = Matrix(SR, self.M.nrows(), self.M.ncols())
        for edge in range(1, self.num_edges() + 1):
            Mnice[0, edge] = self.M[0, edge]
        for v in range(1, self.num_vertices() + 1):
            kappas = 1
            for expon, coef in self.M[v, 0].dict().items():
                if expon[0] == 0:
                    Mnice[v, 0] += coef
                else:
                    kappas *= var("ka{0}".format(expon[0]))**coef
            if kappas != 1:
                Mnice[v, 0] += kappas

            for edge in range(1, self.num_edges() + 1):
                psis = 1
                for expon, coef in self.M[v, edge].dict().items():
                    if expon[0] == 0:
                        Mnice[v, edge] += coef
                    elif expon[0] == 1:
                        psis *= var(StrataGraph.ps_name)**coef
                    elif expon[0] == 2:
                        psis *= var(StrataGraph.ps2_name)**coef
                if psis != 1:
                    Mnice[v, edge] += psis
        return Mnice

    @staticmethod
    def from_nice_matrix(lists):
        Mx = Matrix(StrataGraph.R, len(lists), len(lists[0]))
        Mx[0, 0] = -1
        Mx[0, :] = Matrix([lists[0]])
        for v in range(1, len(lists)):
            if lists[v][0] in ZZ:
                Mx[v, 0] = lists[v][0]
                continue
            if lists[v][0].operator() == sage.symbolic.operators.add_vararg:
                operands = lists[v][0].operands()
                if len(operands) != 2:
                    raise Exception("Input error!")
                genus = operands[1]  #the genus
                kappas = operands[0]
            else:
                kappas = lists[v][0]
                genus = 0

            if kappas.operator() == sage.symbolic.operators.mul_vararg:
                for operand in kappas.operands():
                    Mx[v, 0] += StrataGraph._kappaSR_monom_to_X(operand)
                Mx[v, 0] += genus
            else:
                Mx[v, 0] = StrataGraph._kappaSR_monom_to_X(kappas) + genus

        X = StrataGraph.Xvar
        for v in range(1, len(lists)):
            for edge in range(1, len(lists[0])):
                if lists[v][edge] in ZZ:
                    Mx[v, edge] = lists[v][edge]
                else:
                    for operand in lists[v][edge].operands():
                        if operand in ZZ:
                            Mx[v, edge] += operand
                        elif operand.operator() is None:
                            #it is a ps or ps2
                            Mx[v, edge] += X
                        elif operand.operator() == operator.pow:
                            #it is ps^n or ps2^n
                            Mx[v, edge] += X * operand.operands()[1]
                        elif operand.operator(
                        ) == sage.symbolic.operators.mul_vararg:
                            #it is a ps^n*ps2^m
                            op1, op2 = operand.operands()
                            if op1.operator() is None:
                                exp1 = 1
                            else:
                                exp1 = op1.operand()[1]
                            if op2.operator() == None:
                                exp2 = 1
                            else:
                                exp2 = op2.operand()[1]

                            if exp1 >= exp2:
                                Mx[v, edge] += exp1 * X + exp2 * X**2
                            else:
                                Mx[v, edge] += exp1 * X**2 + exp2 * X
        return StrataGraph(Mx)

    @staticmethod
    def _kappaSR_monom_to_X(expr):
        X = StrataGraph.Xvar
        if expr in ZZ:
            return expr
        elif expr.operator() == None:
            ka_subscript = Integer(str(expr)[2:])
            return X**ka_subscript
        elif expr.operator() == operator.pow:
            ops = expr.operands()
            expon = ops[1]
            ka = ops[0]
            ka_subscript = Integer(str(ka)[2:])
            return expon * X**ka_subscript

    def nice_name(self):
        """
      :return:
      """
        if self.num_vertices() == 1:
            num_loops = self.num_loops()
            if num_loops > 1:
                return None
            elif num_loops == 1:
                if self.codim() == 1:
                    return "D_irr"
                else:
                    return None
            #else, there are no loops
            var_strs = []
            for expon, coef in self.M[1, 0].dict().items():
                if expon[0] == 0 or coef == 0:
                    continue
                if coef == 1:
                    var_strs.append("ka{0}".format(expon[0]))
                else:
                    var_strs.append("ka{0}^{1}".format(expon[0], coef))
            for he in range(1,
                            self.num_edges() +
                            1):  #should only be half edges now
                if self.M[1, he][1] > 1:
                    var_strs.append("ps{0}^{1}".format(self.M[0, he],
                                                       self.M[1, he][1]))
                elif self.M[1, he][1] == 1:
                    var_strs.append("ps{0}".format(self.M[0, he]))
            if len(var_strs) > 0:
                return "*".join(var_strs)
            else:
                return "one"
        if self.num_vertices() == 2 and self.num_full_edges(
        ) == 1 and self.codim() == 1:
            #it is a boundary divisor
            v1_marks = [
                self.M[0, j] for j in range(1,
                                            self.num_edges() + 1)
                if self.M[1, j] == 1 and self.M[0, j] != 0
            ]
            v1_marks.sort()
            v2_marks = [
                self.M[0, j] for j in range(1,
                                            self.num_edges() + 1)
                if self.M[2, j] == 1 and self.M[0, j] != 0
            ]
            v2_marks.sort()
            if v1_marks < v2_marks:
                g = self.M[1, 0]
            elif v1_marks == v2_marks:
                if self.M[1, 0] <= self.M[2, 0]:
                    g = self.M[1, 0]
                else:
                    g = self.M[2, 0]
            else:
                g = self.M[2, 0]
                #temp = v1_marks
                v1_marks = v2_marks
                #v2_marks = temp
            if len(v1_marks) == 0:
                return "Dg{0}".format(g)
            else:
                return "Dg{0}m".format(g) + "_".join(
                    [str(m) for m in v1_marks])
コード例 #4
0
ファイル: stratagraph.py プロジェクト: uberparagon/mgn
class StrataGraph(object):
  R = PolynomialRing(ZZ,"X",1,order='lex')
  Xvar = R.gen()
  
  def __init__(self,M=None,genus_list=None):
    #print genus_list
    if M:
      self.M = copy(M)
    elif genus_list:
      self.M = Matrix(StrataGraph.R,len(genus_list)+1,1,[-1]+genus_list)
    else:
      self.M = Matrix(StrataGraph.R,1,1,-1)
      
  def __repr__(self):
    """
    Drew added this.
    """
    name = self.nice_name()
    if name == None:
        return repr(self.nice_matrix())
    else:
        return name

  def num_vertices(self):
    return self.M.nrows() - 1

  def num_edges(self):
    return self.M.ncols() - 1

  def h1(self):
    return self.M.ncols()-self.M.nrows()+1

  def add_vertex(self,g):
    self.M = self.M.stack(Matrix(1,self.M.ncols()))
    self.M[-1,0] = g

  def add_edge(self,i1,i2,marking=0):
    self.M = self.M.augment(Matrix(self.M.nrows(),1))
    self.M[0,-1] = marking
    if i1 > 0:
      self.M[i1,-1] += 1
    if i2 > 0:
      self.M[i2,-1] += 1

  def del_vertex(self,i):
    self.M = self.M[:i].stack(self.M[(i+1):])

  def del_edge(self,i):
    self.M = self.M[0:,:i].augment(self.M[0:,(i+1):])

  def compute_degree_vec(self):
    self.degree_vec = [sum(self.M[i,j][0] for j in range(1,self.M.ncols())) for i in range(1,self.M.nrows())]

  def degree(self,i):
    return self.degree_vec[i-1]

  def split_vertex(self,i,row1,row2):
    self.M = self.M.stack(Matrix(2,self.M.ncols(),row1+row2))
    self.add_edge(self.M.nrows()-2, self.M.nrows()-1)
    self.del_vertex(i)

  def compute_parity_vec_original(self):
    X = StrataGraph.Xvar
    self.parity_vec = [(ZZ(1+self.M[i,0][0]+sum(self.M[i,k][1] for k in range(1,self.M.ncols()))+sum(self.M[i,k][2] for k in range(1,self.M.ncols()))+self.M[i,0].derivative(X).substitute(X=1)) % 2) for i in range(1,self.M.nrows())]
    
  def compute_parity_vec(self):
    X = StrataGraph.Xvar
    self.parity_vec = [(ZZ(1+self.M[i,0][0]+sum(self.M[i,k][1] for k in range(1,self.M.ncols()))+sum(self.M[i,k][2] for k in range(1,self.M.ncols()))+self.last_parity_summand(i)) % 2) for i in range(1,self.M.nrows())]
    
  def last_parity_summand(self,i):
    return sum(( expon[0] * coef for expon, coef in self.M[i,0].dict().items() ))

  def parity(self,i):
    return self.parity_vec[i-1]

  def replace_vertex_with_graph(self,i,G):
    X = StrataGraph.Xvar
    nv = self.num_vertices()
    ne = self.num_edges()
    #i should have degree d, there should be no classes near i, and G should have markings 1,...,d and genus equal to the genus of i
    hedge_list = []
    for k in range(1,self.M.ncols()):
      for j in range(self.M[i,k]):
        hedge_list.append(k)
    self.del_vertex(i)
    for j in range(G.num_edges() - len(hedge_list)):
      self.add_edge(0,0)
    for j in range(G.num_vertices()):
      self.add_vertex(G.M[j+1,0])
    col = ne+1
    for k in range(1,G.M.ncols()):
      if G.M[0,k] > 0:
        mark = ZZ(G.M[0,k])
        for j in range(G.num_vertices()):
          if self.M[nv+j,hedge_list[mark-1]] == 0:
            self.M[nv+j,hedge_list[mark-1]] = G.M[j+1,k]
          elif G.M[j+1,k] != 0:
            a = self.M[nv+j,hedge_list[mark-1]][1]
            b = G.M[j+1,k][1]
            self.M[nv+j,hedge_list[mark-1]] = 2 + max(a,b)*X + min(a,b)*X**2
      else:
        for j in range(G.num_vertices()):
          self.M[nv+j,col] = G.M[j+1,k]
        col += 1

  def compute_invariant(self):
    self.compute_parity_vec()
    self.compute_degree_vec()
    nr,nc = self.M.nrows(),self.M.ncols()
    self.invariant = [[self.M[i,0], [], [], [[] for j in range(1,nr)]] for i in range(1,nr)]
    for k in range(1,nc):
      L = [i for i in range(1,nr) if self.M[i,k] != 0]
      if len(L) == 1:
        if self.M[0,k] != 0:
          self.invariant[L[0]-1][2].append([self.M[0,k],self.M[L[0],k]])
        else:
          self.invariant[L[0]-1][1].append(self.M[L[0],k])
      else:
        self.invariant[L[0]-1][3][L[1]-1].append([self.M[L[0],k],self.M[L[1],k]])
        self.invariant[L[1]-1][3][L[0]-1].append([self.M[L[1],k],self.M[L[0],k]])
    for i in range(1,nr):
      self.invariant[i-1][3] = [term for term in self.invariant[i-1][3] if len(term) > 0]
      for term in self.invariant[i-1][3]:
        term.sort()
      self.invariant[i-1][3].sort()
      self.invariant[i-1][2].sort()
      self.invariant[i-1][1].sort()
    vertex_invariants = [[i,self.invariant[i-1]] for i in range(1,nr)]
    self.invariant.sort()
    vertex_invariants.sort(key=lambda x: x[1])
    self.vertex_groupings = []
    for i in range(nr-1):
      if i == 0 or vertex_invariants[i][1] != vertex_invariants[i-1][1]:
        self.vertex_groupings.append([])
      self.vertex_groupings[-1].append(vertex_invariants[i][0])
    
    #Drew added this
    self.invariant = tupleit(self.invariant)
    self.hash = hash(self.invariant)
    
  def __eq__(self,other):
        """
        Drew added this.
        """
        return graph_isomorphic(self, other)
        
  def __hash__(self):
        """
        Drew added this.
        Note that it returns a value stored when "compute_invariant" is called.
        """
        try:
            return self.hash
        except:
            self.compute_invariant()
            return self.hash
            
        
  def codim(self):
    codim = 0
    for v in range(1, self.num_vertices()+1):
        for expon, coef in self.M[v,0].dict().items():
            codim += expon[0]*coef
        for e in range(1, self.num_edges()+1):
            for expon, coef in self.M[v,e].dict().items():
                if expon[0] > 0:
                    codim += coef
    for e in range(1, self.num_edges()+1):
        if self.M[0,e] == 0:
            codim +=1   
    return codim
    
  def codim_undecorated(self):
    codim = 0
    for e in range(1, self.num_edges()+1):
        if self.M[0,e] == 0:
            codim +=1   
    return codim
    
                    
            
    print "not implemented!!!!!!"
    return 1

        
  def kappa_on_v(self,v):
    """
    Drew added this.
    
    """
    #print "kappa",self.M[v,0].dict()
    
    for ex, coef in self.M[v,0].dict().items():
        if ex[0] != 0:
            yield ex[0], coef
            
                
  def psi_no_loop_on_v(self,v):
    for edge in range(1,self.num_edges()+1):
        if self.M[v,edge][0] == 1:
            psi_expon = self.M[v,edge][1]
            if psi_expon > 0:
                yield edge, psi_expon
                
  def psi_loop_on_v(self,v):
    for edge in range(1,self.num_edges()+1):
        if self.M[v,edge][0] == 2:
            psi_expon1 = self.M[v,edge][1]
            psi_expon2 = self.M[v,edge][2]
            if psi_expon1 > 0:
                yield edge, psi_expon1, psi_expon2
                    
  def moduli_dim_v(self,v):
    """
    Drew added this.
    """
    return 3*self.M[v,0][0]-3 + sum(( self.M[v,j][0] for j in range(1, self.num_edges()+1 ) ))
   
  def num_loops(self):
    count = 0
    for edge in range(1, self.num_edges()+1):
        for v in range(1, self.num_vertices()+1):
            if self.M[v,edge][0] == 2:
                count+=1
                break
                
    return count
    
  def num_undecorated_loops(self):
    count = 0
    for edge in range(1, self.num_edges()+1):
        for v in range(1, self.num_vertices()+1):
            if self.M[v,edge] == 2:
                count+=1
                break
                
    return count

  def num_full_edges(self):
      count = 0
      for edge in range(1, self.num_edges()+1):
          if sum(( self.M[v,edge][0] for v in range(1, self.num_vertices()+1 ))) == 2:
              count += 1
      return count
    
  def forget_kappas(self):
    M = copy(self.M)
    for v in range(1, self.num_vertices()+1):
        M[v,0] = M[v,0][0]
    return StrataGraph(M)
    
  def forget_decorations(self):
    M = Matrix(StrataGraph.R,[[self.M[r,c][0] for c in range(self.M.ncols())] for r in range(self.M.nrows())] )
    Gnew = StrataGraph(M)
    #Gnew.compute_invariant()
    return Gnew
    
  def is_loop(self,edge):
    for v in range(1, self.num_vertices()+1):
      if self.M[v,edge][0] == 2:
        return True
      if self.M[v,edge][0] == 1:
        return False
        
    raise Exception("Unexpected!")

  ps_name = "ps"
  ps2_name = "ps_"

  def nice_matrix(self):
      Mnice = Matrix(SR, self.M.nrows(), self.M.ncols())
      for edge in range(1, self.num_edges()+1):
          Mnice[0,edge] = self.M[0,edge]
      for v in range(1, self.num_vertices()+1):
          kappas = 1
          for expon, coef in self.M[v,0].dict().items():
              if expon[0]==0:
                  Mnice[v,0] += coef
              else:
                  kappas *= var("ka{0}".format(expon[0]))**coef
          if kappas != 1:
              Mnice[v,0] += kappas

          for edge in range(1, self.num_edges()+1):
              psis = 1
              for expon, coef in self.M[v,edge].dict().items():
                  if expon[0]==0:
                      Mnice[v,edge] += coef
                  elif expon[0]==1:
                      psis *= var(StrataGraph.ps_name)**coef
                  elif expon[0]==2:
                      psis *= var(StrataGraph.ps2_name)**coef
              if psis != 1:
                  Mnice[v,edge] += psis
      return Mnice

  @staticmethod
  def from_nice_matrix(lists):
      Mx = Matrix(StrataGraph.R, len(lists), len(lists[0]))
      Mx[0,0]=-1
      Mx[0,:] = Matrix([lists[0]])
      for v in range(1, len(lists)):
          if lists[v][0] in ZZ:
              Mx[v,0]=lists[v][0]
              continue
          if lists[v][0].operator() == sage.symbolic.operators.add_vararg:
              operands = lists[v][0].operands()
              if len(operands) != 2:
                  raise Exception("Input error!")
              genus = operands[1] #the genus
              kappas = operands[0]
          else:
              kappas = lists[v][0]
              genus = 0


          if kappas.operator() == sage.symbolic.operators.mul_vararg:
              for operand in kappas.operands():
                  Mx[v,0] += StrataGraph._kappaSR_monom_to_X(operand)
              Mx[v,0] += genus
          else:
              Mx[v,0] = StrataGraph._kappaSR_monom_to_X(kappas) + genus

      X = StrataGraph.Xvar
      for v in range(1, len(lists)):
          for edge in range(1, len(lists[0])):
              if lists[v][edge] in ZZ:
                  Mx[v,edge] = lists[v][edge]
              else:
                  for operand in lists[v][edge].operands():
                      if operand in ZZ:
                          Mx[v,edge] += operand
                      elif operand.operator() is None:
                          #it is a ps or ps2
                          Mx[v,edge] += X
                      elif operand.operator() == operator.pow:
                          #it is ps^n or ps2^n
                          Mx[v,edge] += X * operand.operands()[1]
                      elif operand.operator() == sage.symbolic.operators.mul_vararg:
                          #it is a ps^n*ps2^m
                          op1,op2 = operand.operands()
                          if op1.operator() is None:
                              exp1 = 1
                          else:
                              exp1 = op1.operand()[1]
                          if op2.operator() == None:
                              exp2 = 1
                          else:
                              exp2 = op2.operand()[1]

                          if exp1 >= exp2:
                              Mx[v,edge] += exp1*X + exp2*X**2
                          else:
                              Mx[v,edge] += exp1*X**2 + exp2*X
      return StrataGraph(Mx)





  @staticmethod
  def _kappaSR_monom_to_X(expr):
      X = StrataGraph.Xvar
      if expr in ZZ:
          return expr
      elif expr.operator() == None:
          ka_subscript = Integer(str(expr)[2:])
          return  X ** ka_subscript
      elif expr.operator() == operator.pow:
          ops = expr.operands()
          expon = ops[1]
          ka = ops[0]
          ka_subscript = Integer(str(ka)[2:])
          return  expon * X ** ka_subscript

  def nice_name(self):
      """
      :return:
      """
      if self.num_vertices() == 1:
          num_loops = self.num_loops()
          if num_loops >1:
              return None
          elif num_loops == 1:
              if self.codim() == 1:
                  return "D_irr"
              else:
                  return None
          #else, there are no loops
          var_strs = []
          for expon, coef in self.M[1,0].dict().items():
              if expon[0] == 0 or coef == 0:
                  continue
              if coef == 1:
                  var_strs.append("ka{0}".format(expon[0]))
              else:
                  var_strs.append("ka{0}^{1}".format(expon[0],coef))
          for he in range(1,self.num_edges()+1): #should only be half edges now
              if self.M[1,he][1] > 1:
                  var_strs.append("ps{0}^{1}".format(self.M[0,he],self.M[1,he][1]))
              elif self.M[1,he][1] ==  1:
                  var_strs.append("ps{0}".format(self.M[0,he]))
          if len(var_strs) > 0:
              return "*".join(var_strs)
          else:
              return "one"
      if self.num_vertices() == 2 and self.num_full_edges() == 1 and self.codim() == 1:
          #it is a boundary divisor
          v1_marks = [self.M[0,j] for j in range(1, self.num_edges()+1) if self.M[1,j] == 1 and self.M[0,j] != 0]
          v1_marks.sort()
          v2_marks = [self.M[0,j] for j in range(1, self.num_edges()+1) if self.M[2,j] == 1 and self.M[0,j] != 0]
          v2_marks.sort()
          if v1_marks < v2_marks:
              g = self.M[1,0]
          elif v1_marks == v2_marks:
              if self.M[1,0] <= self.M[2,0]:
                  g = self.M[1,0]
              else:
                  g = self.M[2,0]
          else:
              g = self.M[2,0]
              #temp = v1_marks
              v1_marks = v2_marks
              #v2_marks = temp
          if len(v1_marks) == 0:
              return "Dg{0}".format(g)
          else:
              return "Dg{0}m".format(g) + "_".join([str(m) for m in v1_marks])