Attachment 'f5.py'

Download

   1 """
   2 Faugere's F5
   3 
   4 These implementations are heavily inspired by John Perry's pseudocode
   5 and Singular implementation of these algorithms.
   6 
   7 See http://www.math.usm.edu/perry/Research/ for details.
   8 
   9 This implementation runs faster than the Singular script
  10 implementation but uses much more RAM for some reason.
  11 
  12 AUTHOR:
  13     -- 20081013 Martin Albrecht (initial version based on John Perry's pseudocode)
  14     -- 20081013 John Perry (loop from 0 to m-1 instead of m-1 to 0)
  15 
  16 EXAMPLE:
  17     sage: R.<x,y,z> = PolynomialRing(GF(29))
  18     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, \
  19                    3*x^3*y^2 + 7*x^2*y^3 + 24*y^2*z^3, 
  20                    12*x*y^4 + 17*x^4*z + 27*y^4*z + 11*x^3*z^2]
  21     sage: J = I.homogenize()
  22 
  23     sage: f5 = F5() # original F5
  24     sage: gb = f5(J)
  25     sage: f5.zero_reductions, len(gb)
  26     d 7
  27     d 9
  28     d 11
  29     d 6
  30     d 7
  31     d 8
  32     d 9
  33     d 10
  34     d 11
  35     verbose 0 (179: 283.py, top_reduction) reduction to zero.
  36     verbose 0 (179: 283.py, top_reduction) reduction to zero.
  37     verbose 0 (179: 283.py, top_reduction) reduction to zero.
  38     d 12
  39     d 13
  40     d 14
  41     d 16
  42     (3, 27)
  43 
  44     sage: f5 = F5R() # F5 with interreduced B
  45     sage: gb = f5(J)
  46     sage: f5.zero_reductions, len(gb)
  47     d 7
  48     d 9
  49     d 11
  50     d 6
  51     d 7
  52     d 8
  53     d 9
  54     d 10
  55     d 11
  56     verbose 0 (179: 283.py, top_reduction) reduction to zero.
  57     verbose 0 (179: 283.py, top_reduction) reduction to zero.
  58     verbose 0 (179: 283.py, top_reduction) reduction to zero.
  59     d 12
  60     d 13
  61     d 14
  62     d 16
  63     (3, 18)
  64 
  65     sage: f5 = F5C() # F5 with interreduced B and Gprev
  66     sage: gb = f5(J)
  67     sage: f5.zero_reductions, len(gb)
  68     d 7
  69     d 9
  70     d 11
  71     d 6
  72     d 7
  73     d 8
  74     d 9
  75     d 10
  76     d 11
  77     verbose 0 (179: 283.py, top_reduction) reduction to zero.
  78     verbose 0 (179: 283.py, top_reduction) reduction to zero.
  79     verbose 0 (179: 283.py, top_reduction) reduction to zero.
  80     d 12
  81     d 13
  82     d 14
  83     d 16
  84     (3, 18)
  85 """
  86 
  87 divides = lambda x,y: x.parent().monomial_divides(x,y)
  88 LCM = lambda f,g: f.parent().monomial_lcm(f,g)
  89 LM = lambda f: f.lm()
  90 LT = lambda f: f.lt()
  91 
  92 def compare_by_degree(f,g):
  93     if f.total_degree() > g.total_degree():
  94         return 1
  95     elif f.total_degree() < g.total_degree():
  96         return -1
  97     else:
  98         return cmp(f.lm(),g.lm())
  99 
 100 class F5:
 101     def __init__(self, F=None):
 102         if F is not None:
 103             self.Rules = [[] for _ in range(len(F))]
 104             self.L = [0]
 105             self.zero_reductions = 0
 106 
 107     def poly(self, i):
 108         return self.L[i][1]
 109 
 110     def sig(self, i):
 111         return self.L[i][0]
 112 
 113     def __call__(self, F):
 114         if isinstance(F, sage.rings.polynomial.multi_polynomial_ideal.MPolynomialIdeal):
 115             F = F.reduced_basis()
 116         else:
 117             F = Ideal(list(F)).reduced_basis()
 118         if not all(f.is_homogeneous() for f in F):
 119             F = Ideal(F).homogenize()
 120             F = F.gens()
 121         return self.basis(F)
 122 
 123     def basis(self, F):
 124         poly = self.poly
 125         incremental_basis = self.incremental_basis
 126 
 127         self.__init__(F)
 128 
 129         Rules = self.Rules
 130         L = self.L
 131 
 132         m = len(F)
 133         F = sorted(F, cmp=compare_by_degree)
 134         
 135         f0 = F[0]
 136         L[0] = (Signature(1, 0), f0*f0.lc()**(-1))
 137         Rules[0] = list()
 138 
 139         Gprev = set([0])
 140         B = [f0]
 141 
 142         for i in xrange(1,m):
 143             Gcurr = incremental_basis(F[i], i, B, Gprev)
 144             if any(poly(lambd) == 1 for lambd in Gcurr):
 145                 return set(1)
 146             Gprev = Gcurr
 147             B = [poly(l) for l in Gprev]
 148         return B
 149 
 150     def incremental_basis(self, f, i, B, Gprev):
 151         L = self.L
 152         critical_pair = self.critical_pair
 153         compute_spols = self.compute_spols
 154         reduction = self.reduction
 155         Rules = self.Rules
 156 
 157         L.append( (Signature(1,i), f*f.lc()**(-1)) )
 158         curr_idx = len(L) - 1
 159         Gcurr = Gprev.union([curr_idx])
 160         Rules[i] = list()
 161 
 162         P = reduce(lambda x,y: x.union(y), [critical_pair(curr_idx, j, i, Gprev) for j in Gprev], set())
 163         while len(P) != 0:
 164             d = min(t.degree() for (t,k,u,l,v) in P)
 165             print "d", d
 166             Pd = [(t,k,u,l,v) for (t,k,u,l,v) in P if t.degree() == d]
 167             P = P.difference(Pd)
 168             S = compute_spols(Pd)
 169             R = reduction(S, B, Gprev, Gcurr)
 170             for k in R:
 171                 P = reduce(lambda x,y: x.union(y), [critical_pair(j, k, i, Gprev) for j in Gcurr], P)
 172                 Gcurr.add(k)
 173         return Gcurr
 174 
 175     def critical_pair(self, k, l, i, Gprev):
 176         poly = self.poly
 177         sig = self.sig
 178         is_top_reducible = self.is_top_reducible
 179         is_rewritable = self.is_rewritable
 180 
 181         #print "crit_pair(%s,%s,%s,%s)"%(k, l, i, Gprev)
 182         #print self.L
 183         tk = poly(k).lt()
 184         tl = poly(l).lt()
 185         t = LCM(tk, tl)
 186         u0 = t//tk
 187         u1 = t//tl
 188         m0, e0 = sig(k)
 189         m1, e1 = sig(l)
 190         if e0 == e1 and u0*m0 == u1*m1:
 191             return set()
 192         #print "test1", e0, i, u0, m0
 193         #print "test2", e1, i, u1, m1
 194         if e0 == i and is_top_reducible(u0*m0, Gprev):
 195             #print "test1 done"
 196             return set()
 197         if e1 == i and is_top_reducible(u1*m1, Gprev):
 198             #print "test2 done"
 199             return set()
 200         if is_rewritable(u0, k) or is_rewritable(u1, l):
 201             #print "test3", is_rewritable(u0, k), is_rewritable(u1, l)
 202             return set()
 203         if u0 * sig(k) < u1 * sig(l):
 204             u0, u1 = u1, u0
 205             k, l = l, k
 206         return set([(t,k,u0,l,u1)])
 207         
 208     def compute_spols(self, P):
 209         poly = self.poly
 210         sig = self.sig
 211         spol = self.spol
 212         is_rewritable = self.is_rewritable
 213         add_rule = self.add_rule
 214 
 215         L = self.L
 216 
 217         S = list()
 218         P = sorted(P, key=lambda x: x[0])
 219         for (t,k,u,l,v) in P:
 220             if not is_rewritable(u,k) and not is_rewritable(v,l):
 221                 s = spol(poly(k), poly(l))
 222                 if s != 0:
 223                     L.append( (u * sig(k), s) )
 224                     add_rule(u * sig(k), len(L)-1)
 225                     S.append(len(L)-1)
 226         S = sorted(S, key=lambda x: sig(x))
 227         return S
 228 
 229     def spol(self, f, g):
 230         return LCM(LM(f),LM(g)) // LT(f) * f - LCM(LM(f),LM(g)) // LT(g) * g
 231 
 232     def reduction(self, S, B, Gprev, Gcurr):
 233         L = self.L
 234         sig = self.sig
 235         poly = self.poly
 236         top_reduction = self.top_reduction
 237 
 238         to_do = S
 239         completed = set()
 240         while len(to_do):
 241             k, to_do = to_do[0], to_do[1:]
 242             h = poly(k).reduce(B)
 243             L[k] = (sig(k), h)
 244             newly_completed, redo = top_reduction(k, Gprev, Gcurr.union(completed))
 245             completed = completed.union( newly_completed )
 246             for j in redo:
 247                 # insert j in to_do, sorted by increasing signature
 248                 to_do.append(j)
 249                 to_do.sort(key=lambda x: sig(x))
 250         return completed
 251 
 252     def top_reduction(self, k, Gprev, Gcurr):
 253         find_reductor = self.find_reductor
 254         add_rule = self.add_rule
 255         poly = self.poly
 256         sig = self.sig
 257         L = self.L
 258 
 259         if poly(k) == 0:
 260             verbose("reduction to zero.",level=0)
 261             self.zero_reductions += 1
 262             return set(),set()
 263         p = poly(k)
 264         J = find_reductor(k, Gprev, Gcurr)
 265         if J == set():
 266             L[k] = ( sig(k), p * p.lc()**(-1) )
 267             return set([k]),set()
 268         j = J.pop()
 269         q = poly(j)
 270         u = p.lt()//q.lt()
 271         p = p - u*q
 272         if p != 0:
 273             p = p * p.lc()**(-1)
 274         if u * sig(j) < sig(k):
 275             L[k] = (sig(k), p)
 276             return set(), set([k])
 277         else:
 278             L.append((u * sig(j), p))
 279             add_rule(u * sig(j), len(L)-1)
 280             return set(), set([k, len(L)-1])
 281 
 282     def find_reductor(self, k, Gprev, Gcurr):
 283         is_rewritable = self.is_rewritable
 284         is_top_reducible = self.is_top_reducible
 285         
 286         poly = self.poly
 287         sig = self.sig
 288         t = poly(k).lt()
 289         for j in Gcurr:
 290             tprime = poly(j).lt()
 291             if divides(tprime,t):
 292                 u = t // tprime
 293                 mj, ej = sig(j)
 294                 if u * sig(j) != sig(k) and not is_rewritable(u, j) \
 295                         and not is_top_reducible(u*mj, Gprev):
 296                     return set([j])
 297         return set()
 298                 
 299     def add_rule(self, s, k):
 300         self.Rules[s[1]].append( (s[0],k) )
 301 
 302     def is_rewritable(self, u, k):
 303         j = self.find_rewriting(u, k)
 304         return j != k
 305 
 306     def find_rewriting(self, u, k):
 307         Rules = self.Rules
 308         mk, v = self.sig(k)
 309         for ctr in reversed(xrange(len(Rules[v]))):
 310             mj, j = Rules[v][ctr]
 311             if divides(mj, u * mk):
 312                 return j
 313         return k
 314 
 315     def is_top_reducible(self, t, l):
 316         poly = self.poly
 317         for g in l:
 318             if divides(poly(g).lt(), t):
 319                 return True
 320         return False
 321 
 322 class F5R(F5):
 323     def basis(self, F):
 324         poly = self.poly
 325         incremental_basis = self.incremental_basis
 326 
 327         self.__init__(F)
 328 
 329         Rules = self.Rules
 330         L = self.L
 331 
 332         m = len(F)
 333         F = sorted(F, cmp=compare_by_degree)
 334         
 335         f0 = F[0]
 336         L[0] = (Signature(1, 0), f0*f0.lc()**(-1))
 337         Rules[0] = list()
 338 
 339         Gprev = set([0])
 340         B = [f0]
 341 
 342         for i in xrange(1,m):
 343             Gcurr = incremental_basis(F[i], i, B, Gprev)
 344             if any(poly(lambd) == 1 for lambd in Gcurr):
 345                 return set(1)
 346             Gprev = Gcurr
 347             B = Ideal([poly(l) for l in Gprev]).reduced_basis()
 348             
 349         return B
 350 
 351 class F5C(F5):
 352     def basis(self, F):
 353         poly = self.poly
 354 
 355         self.__init__(F)
 356 
 357         Rules = self.Rules
 358         L = self.L
 359 
 360         m = len(F)
 361         F = sorted(F, cmp=compare_by_degree)
 362         
 363         f0 = F[0]
 364         L[0] = (Signature(1, 0), f0*f0.lc()**(-1))
 365         Rules[0] = list()
 366 
 367         Gprev = set([0])
 368         B = set([f0])
 369 
 370         for i in xrange(1,m):
 371             Gcurr = self.incremental_basis(F[i], B, Gprev)
 372             if any(poly(lambd) == 1 for lambd in Gcurr):
 373                 return set(1)
 374             B = Ideal([poly(l) for l in Gcurr]).reduced_basis()
 375             if i != m-1:
 376                 Gprev = self.setup_reduced_basis(B)
 377         return B
 378 
 379     def incremental_basis(self, f, B, Gprev):
 380         L = self.L
 381         critical_pair = self.critical_pair
 382         compute_spols = self.compute_spols
 383         reduction = self.reduction
 384         Rules = self.Rules
 385 
 386         i = len(L)
 387         L.append( (Signature(1,i), f*f.lc()**(-1)) )
 388         Rules.append( list() )
 389         Gcurr = Gprev.union([i])
 390         P = reduce(lambda x,y: x.union(y), [critical_pair(i, j, i, Gprev) for j in Gprev], set())
 391         while len(P) != 0:
 392             d = min(t.degree() for (t,k,u,l,v) in P)
 393             print "d", d
 394             Pd = [(t,k,u,l,v) for (t,k,u,l,v) in P if t.degree() == d]
 395             P = P.difference(Pd)
 396             S = compute_spols(Pd)
 397             R = reduction(S, B, Gprev, Gcurr)
 398             for k in R:
 399                 P = reduce(lambda x,y: x.union(y), [critical_pair(j, k, i, Gprev) for j in Gcurr], P)
 400                 Gcurr.add(k)
 401         return Gcurr
 402 
 403     def setup_reduced_basis(self, B):
 404         add_rule = self.add_rule
 405         self.L = range(len(B))
 406         self.Rules = [[] for _ in range(len(B))]
 407 
 408         L = self.L
 409         Rules = self.Rules
 410         Gcurr = set()
 411 
 412         for i in range(len(B)):
 413             L[i] = (Signature(1,i), B[i])
 414             Gcurr.add( i )
 415             Rules[i] = []
 416             t = B[i].lt()
 417             for j in range(i+1, len(B)):
 418                 u = LCM(t, B[j].lt())//B[j].lt()
 419                 add_rule( Signature(u, j), -1 )
 420         return Gcurr
 421 
 422 from UserList import UserList
 423 
 424 class Signature(UserList):
 425      def __init__(self, monomial, index):
 426          UserList.__init__(self,[monomial, index])
 427          
 428      def __lt__(self, other):
 429          if self[1] < other[1]:
 430              return True
 431          elif self[1] > other[1]:
 432              return False
 433          else:
 434              return (self[0] < other[0])
 435 
 436      def __eq__(self, other):
 437          return self[0] == other[0] and self[1] == other[1]
 438    
 439      def __neq__(self, other):
 440          return self[0] != other[0] or self[1] != other[1]
 441    
 442      def __rmul__(self, other):
 443          if isinstance(self, Signature):
 444              return Signature(other * self[0], self[1])
 445          else:
 446              raise TypeError
 447 
 448 f5 = F5C()

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.