Attachment 'f5.pyx'

Download

   1 """
   2 Faugere's F_5 algorithm
   3 
   4 This implementation is along the lines of John Perry's pseudocode 
   5 and Singular implementation. It was inspired by Martin Albrecht's
   6 implementation and discussions with him and Ludovic Perret.
   7 
   8 See http://www.math.usm.edu/perry/Research/ for details.
   9 
  10 AUTHOR:
  11     -- 20081022 Simon King
  12 EXAMPLE:
  13     sage: R.<x,y,z> = PolynomialRing(GF(29))
  14     sage: I =  R* [3*x^4*y + 18*x*y^4 + 4*x^3*y*z + 20*x*y^3*z + 3*x^2*z^3, \
  15                    3*x^3*y^2 + 7*x^2*y^3 + 24*y^2*z^3, 
  16                    12*x*y^4 + 17*x^4*z + 27*y^4*z + 11*x^3*z^2]
  17     sage: J = I.homogenize()
  18 
  19     sage: f5 = F5() # original F5
  20     sage: gb = f5(J)
  21     Generator 2/3
  22     1 critical pairs in degree 7
  23     1 critical pairs in degree 9
  24     1 critical pairs in degree 11
  25     Generator 3/3
  26     1 critical pairs in degree 6
  27     2 critical pairs in degree 7
  28     5 critical pairs in degree 8
  29     7 critical pairs in degree 9
  30     11 critical pairs in degree 10
  31     10 critical pairs in degree 11
  32     WARNING: top-reduction to zero
  33     WARNING: reduction to zero
  34     WARNING: reduction to zero
  35     3 critical pairs in degree 12
  36     5 critical pairs in degree 13
  37     2 critical pairs in degree 14
  38     1 critical pairs in degree 16
  39     Note that we encountered 3 S-polynomials reducing to zero
  40     sage: f5.zeroes(), len(gb)
  41     (3, 18)
  42 """
  43 
  44 import sage
  45 import sage.all
  46 from sage.all import copy
  47 import sys
  48 from sage.structure.sequence import Sequence
  49 from sage.rings.ideal import Ideal
  50 #from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular
  51 
  52 class CounterClass:
  53     def __init__(self):
  54         self.Gen = 0
  55         self.Del = 0
  56     def __call__(self,i):
  57         if i>0:
  58             self.Gen += 1
  59         else:
  60             self.Del += 1
  61 Counter = CounterClass()
  62 CounterB = CounterClass()
  63 
  64 def dehomogenize(I,R):
  65     "dehomogenize(I,R): dehomogenize an ideal I that was obtained by homogenizing an ideal of ring R"
  66     h = I[0].parent()('h')
  67     return ([R(X(h=1)) for X in I]*R).reduced_basis()
  68 
  69 cdef class DecoratedPolynomial:
  70     """
  71     A decorated polynomial is a polynomial p in an ideal I = <f_1,...,f_m> of a polynomial
  72     ring R, together with a 'signature' (u,i), where u is a monomial in R and 1 <= i <= m
  73     is an integer. The signature has the following meaning:
  74     * One can express p as an ideal combination $\sum_{k=1}^i t_k\cdot f_k$, and u is the leading
  75       term of $t_i$ of f_1,...,f_i.
  76     The signature allows for avoiding some useless critical pairs in the Buchberger algorithm.
  77     """
  78     cdef int nu
  79     cdef object mu  # the multiplier
  80     cdef tuple Emu  # exponents of the multiplier
  81     #cdef MPolynomial_libsingular p
  82     cdef object p
  83 
  84     def __init__(self,mu, int nu,  p):
  85         self.mu = mu
  86         self.Emu = mu.degrees()
  87         self.nu = nu
  88         self.p = p
  89         #self.rule = -1
  90         #self.hist = []
  91     #def __init__(self,mu, int nu, object p):
  92     #    pass
  93     def sig(DecoratedPolynomial self):
  94         return (self.mu,self.nu)
  95     def poly(DecoratedPolynomial self):
  96         return self.p
  97     #def info(self):
  98     #    return self.hist
  99 
 100     def __richcmp__(DecoratedPolynomial self, DecoratedPolynomial other, op):
 101         # < 0, <= 1, == 2, != 3, > 4, >= 5
 102         # Idea for applications: When considering a pair of decorated monomials and
 103         # reducing them, we always reduce the "bigger" one, 
 104         if op == 0:
 105             if self.nu<other.nu:
 106                 return True
 107             if (self.nu==other.nu) and (self.mu<other.mu):
 108                 return True
 109             return False
 110         if op == 1:
 111             if self.nu<other.nu:
 112                 return True
 113             if (self.nu==other.nu) and (self.mu<=other.mu):
 114                 return True
 115             return False
 116         if op == 2:
 117             return (self.nu==other.nu) and (self.Emu==other.Emu) and (self.p==other.p)
 118         if op == 3:
 119             return not ((self.nu==other.nu) and (self.Emu==other.Emu) and (self.p==other.p))
 120         if op == 4:
 121             if self.nu>other.nu:
 122                 return True
 123             if (self.nu==other.nu) and (self.mu>other.mu):
 124                 return True
 125             return False
 126         if op == 5:
 127             if self.nu>other.nu:
 128                 return True
 129             if (self.nu==other.nu) and (self.mu>=other.mu):
 130                 return True
 131             return False
 132 
 133 
 134 cdef class F5:
 135     cdef dict Rules       # rewriting rules (indexed by leading monomial, yielding a monomial ideal)
 136     cdef object Rgb       # reduced GB of the first few input generators
 137     cdef object RgbMon    # their leading ideal
 138     cdef list Inputgen    # the ideal generators we got, interreduced and increasingly sorted
 139     cdef list Basis       # list of decorated polynomials that yields self.Rgb
 140     cdef object Ring      # the polynomial ring we started with
 141     cdef object HRing     # homogenized ring
 142     cdef object One       # "One" in the ring we work with (may be homogenisation of self.Ring
 143     cdef int nvars        # number of variables of HRing
 144     cdef int homog        # 1, if the input was homogeneous; 0 otherwise (then we homogenize/dehomogenize)
 145     cdef int Zeroes       # Number of S-polynomials reducing to zero, to test the efficiency of 
 146                           # our implementation of the F5 criterion.
 147                           # We are not keeping track of the corresponding critical pairs.
 148     def __init__(F5 self):
 149         "F = F5() set up the machinery to compute Groebner bases with the F5 algorithm"
 150         self.Rules={}
 151         self.Rgb = None
 152         self.RgbMon = None
 153         self.Inputgen = []
 154         self.Basis = []
 155         self.Ring = None
 156         self.HRing = None
 157         self.One = None
 158         self.nvars = 0
 159         self.homog = 0
 160         self.Zeroes = 0
 161 
 162     #def __init__(self):
 163     #    pass
 164 
 165     #def __dealloc__(self):
 166     #    self.Rules={}
 167     #    self.Rgb = None
 168     #    self.RgbMon = None
 169     #    self.Inputgen = []
 170     #    self.Basis = []
 171     #    self.Ring = None
 172     #    self.HRing = None
 173     #    self.One = None
 174     #    self.nvars = 0
 175     #    self.homog = 0
 176     #    self.Zeroes = 0
 177     #    print "Deleted"
 178 
 179     def __call__(F5 self, I):
 180         """
 181         F(I): Apply the F5 algorithm to an ideal I and return its reduced Groebner basis
 182         """
 183         if not isinstance(I, sage.rings.polynomial.multi_polynomial_ideal.MPolynomialIdeal):
 184             I = Ideal(I)
 185         self.Ring = I.ring()
 186         self.Zeroes = 0 # S-polynomials reducing to zero.
 187                          # We do not keep track of the corresponding critical pair.
 188         self.Rgb            = Sequence([I.ring()(0)],I.ring()) # (reduced) GB of the first few input generators
 189         self.RgbMon = self.Rgb
 190         # TODO: is homogenization really needed?
 191         # Certainly it is needed in the F5 matrix version. But here??
 192         if not I.is_homogeneous():
 193             print "homogenizing the input"
 194             J = I.homogenize()
 195             self.homog = 0
 196         else:
 197             J = I
 198             self.homog = 1
 199         if J.gens()==(0,):
 200             self.Inputgen = [J.ring()(0)]     # reduced_basis would yield a segfault
 201         else:
 202             self.Inputgen       = list(J.reduced_basis())   # the ideal generators we got, sorted increasingly
 203         self.HRing = J.ring()
 204         self.Inputgen.sort()
 205         self.Rules          = {} # rewriting rules
 206         self.Basis          = [] # List of decorated polynomials that will eventually form a
 207                                  # a Groebner basis of J
 208         self.One            = J.ring()(1) # self.One.parent() =self.HRing != self.Ring, if there was a homogenisation
 209         self.nvars          = len(J.ring().gens())
 210         return self.basis()
 211 
 212     ## Some methods that reveal the C-attributes of self.
 213     def rules(self):
 214         return self.Rules
 215     def zeroes(self):
 216         return self.Zeroes
 217 
 218     #############
 219     ## Main procedure
 220     ## Return the previously computed basis, or do the computation when first called
 221     def basis(slf, maxgen=None):
 222         """
 223         F.basis(): Compute a reduced Groebner basis using the F5 algorithm.
 224         """
 225         cdef F5 self = slf
 226         if self.Basis:
 227             if self.homog:
 228                 return self.Rgb # or [X.poly for X in self.Basis]*self.HRing, currently without reduction
 229             else:
 230                 return dehomogenize(self.Rgb,self.Ring)
 231         if not self.homog:
 232             h = self.HRing('h')
 233         if self.Inputgen[0] == 0:
 234             return Sequence([self.HRing(0)], self.HRing)
 235         cdef DecoratedPolynomial newDP = DecoratedPolynomial(self.One,0,self.Inputgen[0]/self.Inputgen[0].lc())
 236         
 237         self.Rules[0]=[]
 238         self.Basis.append(newDP)
 239         cdef int i
 240         cdef int UpTo
 241         if maxgen is None:
 242             UpTo = len(self.Inputgen)
 243         else:
 244             UpTo = maxgen
 245         cdef int laststop = 0
 246         cdef DecoratedPolynomial X,Y
 247         for i in range(1,UpTo):
 248             print "Generator %d/%d"%(i+1,UpTo)
 249             # by induction, we have a reduced GB for the generators 1,...,i-1.
 250             self.Rgb = ([Y.p for Y in self.Basis]*self.HRing).reduced_basis()
 251             self.RgbMon = ([Y.p.lm() for Y in self.Basis]*self.HRing).reduced_basis()
 252             self.insert(i)
 253             for X in self.Basis[laststop:]:
 254                 # it it contains a constant polynomial, the ideal is trivial
 255                 if self.homog:
 256                     if X.p.degree()==0:
 257                         self.Rgb = Sequence([self.One], self.HRing)
 258                         return Sequence([self.Ring(1)], self.Ring)
 259                 else:
 260                     if X.p(h=1).degree()==0:
 261                         self.Rgb = Sequence([self.One], self.HRing)
 262                         return Sequence([self.Ring(1)], self.Ring)
 263             laststop = len(self.Basis)
 264         # Now, there only remains to toss out the reduced GB
 265         if self.Zeroes:
 266             print "Note that we encountered %d S-polynomials reducing to zero"%(self.Zeroes)
 267         self.Rgb = ([Y.p for Y in self.Basis]*self.HRing).reduced_basis()
 268         if self.homog:
 269             return self.Rgb
 270         else:
 271             return dehomogenize(self.Rgb,self.Ring)
 272 
 273     cdef insert(F5 self,int i):
 274         """F.insert(i): Compute a Groebner basis for the ideal spanned by generators #0,...,i,
 275            when a Groebner basis for generators #0,...,i-1 is already known.
 276         """
 277         # We may reduce the i-th generator f_i by everything that is in the "old" ideal (f_0,...,f_{i-1})
 278         f = self.Inputgen[i].reduce(self.Rgb)
 279         self.Rules[i]=[]
 280         if f.lc()==0:
 281             return
 282         cdef DecoratedPolynomial newDP = DecoratedPolynomial(self.One, i, f/f.lc())
 283 
 284         cdef int newindices = len(self.Basis)  # self.Basis[:newindices] was our starting point
 285         self.Basis.append(newDP)
 286         cdef int j,d
 287         cdef list P
 288         cdef list Pd,tmpL,X
 289         cdef DecoratedPolynomial Y,Z
 290         # generate new S-polynomials
 291         # The argument i is used for the F5 criterion (it appears in the signature of 
 292         # polynomials we want to reduce with)
 293         P=self.crit_pairs(newDP,newindices,i,newindices)  # only crit pairs involving newDP and old GB elements
 294         if not self.homog:
 295             h = self.HRing('h')
 296         while P:
 297             #print i
 298             #print "len P =",len(P)
 299             d = min([X[0].degree() for X in P])
 300             tmpL,Pd = [],[]
 301             for X in P:
 302                 if X[0].degree() == d:
 303                     Pd.append(X)
 304                 else:
 305                     tmpL.append(X)
 306             print "%d critical pairs in degree %d"%(len(Pd),d)
 307             P = tmpL
 308             S = self.SPolys(Pd)
 309             R = self.reduction(S, i, newindices)
 310             for Y in R:
 311                 self.Basis.append(Y)
 312                 if self.homog:
 313                     if Y.p.degree()==0:
 314                         return
 315                 else:
 316                     if Y.p(h=1).degree()==0:
 317                         return
 318                 P.extend(self.crit_pairs(Y,len(self.Basis),i,newindices))   # crit pairs for all of self.Basis (including the new ones)
 319 
 320 
 321     cdef list crit_pair(self, DecoratedPolynomial X, DecoratedPolynomial Y, int i, int newindices):
 322         """
 323         F.crit_pair(X,Y,i,newindices) returns (t,u_X,X,u_Y,Y) corresponding to a critical pair
 324             X,Y when necessary for the computation of a Groebner basis of (f_1,...,f_i). Here,
 325             the F5 criterion for label i is used. By switching X and Y, if necessary, we will
 326             have X<Y in the output. 
 327             Assumption: F.Basis[:newindices] (a private attribute) corresponds to a GB of 
 328                         (f_1,...,f_{i-1}).
 329         """
 330         R = self.HRing
 331         t_X = X.p.lm()
 332         t_Y = Y.p.lm()
 333         t = t_X.lcm(t_Y)
 334         u_X = R.monomial_quotient(t,t_X)
 335         u_Y = R.monomial_quotient(t,t_Y)
 336         umuX = X.mu*u_X
 337         if X.nu==i and self.topreducible(umuX):
 338             #X.hist.append(('topred',umuX,i))
 339             return [] # since the row with signature (umuX,nu) is kicked out by F5, we don't need the S-poly
 340         umuY = Y.mu*u_Y
 341         if (X.nu==Y.nu) and (umuX==umuY):  # found in Perry's f5_library.lib
 342             # I guess this is part of "rewrite criterion".
 343             # But I confess I don't understand why this is correct
 344             return []
 345         if Y.nu==i and self.topreducible(umuY):
 346             #Y.hist.append(('topred',umuY,i))
 347             return []
 348         # adopt minor optimization (check is_rewritable)
 349         if self.is_rewritable(u_X,X) or self.is_rewritable(u_Y,Y):
 350             return []
 351         # Eventually we try to reduce from "top" to "bottom".
 352         # We return the pair X,Y, if u_X*X is on top of u_Y*Y.
 353         # Hence, later we will replace u_Y*Y by the S-poly of X and Y
 354         if (X.nu>Y.nu) or ((X.nu==Y.nu) and (umuX>umuY)): 
 355             return [[t,Y,u_Y,X,u_X]]
 356         return [[t,X,u_X,Y,u_Y]]
 357 
 358     cdef list crit_pairs(self, DecoratedPolynomial X, int ub, int i, int newindices):
 359         "compare with crit_pair, but here Y runs on self.Basis[:ub]"
 360         cdef list CP = []
 361         cdef DecoratedPolynomial Y
 362         for Y in self.Basis[:ub]:
 363             CP.extend(self.crit_pair(X,Y,i,newindices))
 364         return CP
 365 
 366     cdef list SPolys(self,list P):
 367         """
 368         F.SPolys(P), P a list of critical pairs, given by a 5-tuple of 
 369           1) a monomial t (the least common multiple of the leading monomials of X.poly() and Y.poly()), 
 370           2) the cofactor u_X = t/lm(X.poly()) for X, 
 371           3) a decorated polynomial X,
 372           4) the cofactor u_Y = t/lm(X.poly()) for Y,
 373           5) a decorated polynomial Y.
 374         Moreover, (u_X*X)<(u_Y*Y).
 375         The S-polynomials are computed and tested for rewritability. The non-rewritable S-polynomials
 376         are added to the rewrite rules and are returned in a list.
 377         """
 378         # P is a list of tuples (lcm, u_X,X, u_Y,Y), with u_X*lm(X) = u_Y*lm(Y) = lcm
 379         P.sort() # which means increasing by lcm
 380         cdef list S = []
 381         cdef DecoratedPolynomial X,Y,Z
 382         for (t,X,u_X,Y,u_Y) in P:
 383             if (not self.is_rewritable(u_X,X)) and (not self.is_rewritable(u_Y,Y)):
 384                 s = (u_X*X.p*Y.p.lc() - u_Y*Y.p*X.p.lc()) # it will be reduced soon, in a different method...
 385                 if s!=0:
 386                     # We know that u_X*X is on top of u_Y*Y.
 387                     # Hence, thinking abouz the F5 matrix version, we would 
 388                     # create rows labeled (X.mu*u_X,X.nu) and (Y.mu*u_Y,Y.nu),
 389                     # and a reduction from top to bottom would yield
 390                     # the S-polynomial at (Y.mu*u_Y,Y.nu). 
 391                     # TODO: Is this thinking correct?
 392                     Z = DecoratedPolynomial(Y.mu*u_Y,Y.nu, s/s.lc())
 393                     #Z.hist.append(('S-poly',X,X.sig(),Y,Y.sig()))
 394                     self.add_rule(Z)
 395                     S.append(Z)
 396         S.sort() # by increasing signature, hence, from top to bottom in the F5 matrix version
 397         return S
 398 
 399     cdef list reduction(self,list S, int i, int newindices):
 400         """
 401         F.reduction(S,i,newindices): S a list of decorated polynomials, and F.Basis[:newindices] (a private
 402         attribute) provides a Groebner basis for the ideal generatd by the generators #0,...,i-1. 
 403         It is assumed that the list is sorted by increasing signature. 
 404         We will reduce each element of S by the GB and the elements of S of lower signature.
 405         Returns the list of completely reduced decorated polynomials.
 406         """
 407         cdef DecoratedPolynomial X,Y
 408         cdef list truncGB = list(self.Rgb)
 409         # We use self.Rgb, since it is a reduced GB for the ideal of the first i-1 indices.
 410         # Later on, we add to it new reduced S-polynomials.
 411         cdef list completed  = []
 412         cdef list newly_completed = []
 413         cdef list redo = []
 414         #sys.stdout.write('r')
 415         #sys.stdout.flush()
 416         while S:
 417             #sys.stdout.write('.')
 418             #sys.stdout.flush()
 419             X = S.pop(0) # this is the one with the smallest signature
 420             # By construction, the signature of all our input polynomials has index i:
 421             #sys.stdout.write('r')
 422             #sys.stdout.flush()
 423             X.p = X.p.reduce(truncGB)
 424             #sys.stdout.write('.')
 425             #sys.stdout.flush()
 426             #X.hist.append('reduction')
 427             if X.p.lc()!=0:
 428                 X.p = X.p/X.p.lc()
 429             newly_completed, redo = self.top_reduction(X,newindices,completed)
 430             completed.extend(newly_completed)
 431             truncGB.extend([Y.p for Y in newly_completed]) # hence basis is "old basis" plus "completed"
 432             S.extend(redo)
 433             S.sort() # by increasing signature, since reductions must be done from top to bottom.
 434             #print "S",len(S)
 435             #print "rule",len(self.Rules[i])
 436             #print "Basis", len(self.Basis)
 437         #sys.stdout.write('\n')
 438         #sys.stdout.flush()
 439         return completed
 440 
 441     cdef tuple top_reduction(self, DecoratedPolynomial X, int newindices,list completed):
 442         if X.p.lc()==0:
 443             print "WARNING: reduction to zero"
 444             self.Zeroes = self.Zeroes+1
 445             return ([],[])
 446         #X.hist.append(('test',X.p))
 447         #sys.stdout.write('t')
 448         #sys.stdout.flush()
 449         J = self.find_reductor(X,newindices,completed)
 450         #sys.stdout.write('.')
 451         #sys.stdout.flush()
 452         if not J:
 453             return ([X],[])
 454         cdef DecoratedPolynomial Y = J[1]
 455         
 456         u = J[0]
 457         p = X.p-u*Y.p
 458         #sys.stdout.write('\n')
 459         #sys.stdout.flush()
 460         if p.lc()!=0:
 461             p = p/p.lc()
 462         else:
 463             print "WARNING: top-reduction to zero"
 464             self.Zeroes = self.Zeroes+1 
 465             return ([],[])
 466         newmono = Y.mu*u
 467         # TODO: is it newmono<X.mu or newmono<=X.mu?
 468         # At least it seems there is an infinite loop if it is "<" only
 469         if (Y.nu<X.nu) or (Y.nu==X.nu and newmono<=X.mu):
 470             # i.e., (signature of Y)*u is not bigger, hence, it is on top of sig(X)
 471             # hence, X ought to be replaced
 472             X.p = p
 473             #X.hist.append(('top-red by',Y,'with',u,X.p.lm(),Y.p.lm(),X.p.lm()))
 474             #X.hist.append('redo1')
 475             return ([],[X])
 476         # Otherwise, (signature of Y)*u is below sig(X). Hence, 
 477         # the corresponding row (that is now being created) must be reduced
 478         # (which is done right on the spot).
 479         cdef DecoratedPolynomial Z = DecoratedPolynomial(newmono,Y.nu,p)
 480         
 481         #Z.hist.append(('top-red obtained from',X,Y))
 482         #X.hist.append(('top-red 2',Y,Z))
 483         self.add_rule(Z)
 484         return ([],[X,Z])
 485 
 486     cdef list find_reductor(self, DecoratedPolynomial X, int newindices, list completed):
 487         """
 488         F.find_reductor(X,i,C): returns [], or [[u,Y]] if there is a decorated polynomial Y 
 489         in self.Basis+C (private attribute) and X is *safely* top-reducible by Y with multiplier u. 
 490         That's to say, u*Y has the same leading monomial as X, Y is neither detected by the F5 
 491         criterion nor the rewritten criterion, and u*Y has smaller signature than X.
 492         """
 493         cdef DecoratedPolynomial Y
 494         t = X.p.lm()
 495         R = self.HRing
 496         for Y in self.Basis:
 497             if Y<X:
 498                 tt = Y.p.lm()
 499                 if R.monomial_divides(tt,t):
 500                     u = R.monomial_quotient(t,tt)
 501                     umu = Y.mu*u
 502                     # Y is a reductor of X if u*Y has different signature from X,
 503                     # (u,Y) is not rewritable, and umu is not topreducible.
 504                     if ((Y.nu!=X.nu) or (umu!=X.mu)) and (not self.topreducible(umu)) and (not self.is_rewritable(u,Y)):
 505                         return [u,Y]
 506         for Y in completed: 
 507             if Y<X:
 508                 tt = Y.p.lm()
 509                 if R.monomial_divides(tt,t):
 510                     u = R.monomial_quotient(t,tt)
 511                     umu = Y.mu*u
 512                     if ((Y.nu!=X.nu) or (umu!=X.mu)) and (not self.topreducible(umu)) and (not self.is_rewritable(u,Y)):
 513                         return [u,Y]
 514         return []
 515 
 516     def topreducible(self, u):
 517         """
 518         F.topreducible(u): Tests whether the F5 criterion would apply to decorated polynomials 
 519         with signature (u,i), where u is a monomial and self.Rgb (private attribute) provides
 520         a Groebner basis for the ideal spanned by the generators #0,...,i-1.
 521         """
 522         return u.reduce(self.RgbMon)==0
 523 
 524     cdef add_rule(self, DecoratedPolynomial X):
 525         "F.add_rule(X) adds the rewriting rule corresponding to the decorated polynomial X"
 526         #print "Add", X.nu, X.mu
 527         self.Rules[X.nu].append(X)
 528 
 529     def is_rewritable(self, u, DecoratedPolynomial X):
 530         """
 531         F.is_rewritable(u,X) tests if the is a rewriting (corresponding to Gaussian 
 532         elimination "from top") for u*X, where u is a monomial and X a decorated polynomial.
 533         """
 534         cdef DecoratedPolynomial Y
 535         cdef list R = self.Rules[X.nu]
 536         cdef int l = len(R)
 537         HR = self.HRing
 538         umu = X.mu*u
 539         while (1):
 540             l-=1
 541             if l<0:
 542                 return False
 543             Y = R[l]
 544             if Y is X:
 545                 return False
 546             if HR.monomial_divides(Y.mu,umu):
 547                 return True
 548         #return (X.mu*u).reduce([Y.mu for Y in self.Rules[X.nu][X.rule+1:]])==0

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.

You are not allowed to attach a file to this page.