Ticket #1046: 1046-mpoly-coerce-speed.patch

File 1046-mpoly-coerce-speed.patch, 20.2 kB (added by robertwb, 3 months ago)
  • a/sage/categories/pushout.py

    old new  
    44# TODO, think through the rankings, and override pushout where necessary.  
    55 
    66class ConstructionFunctor(Functor): 
     7 
    78    def __mul__(self, other): 
    89        if not isinstance(self, ConstructionFunctor) and not isinstance(other, ConstructionFunctor): 
    910            raise TypeError, "Non-constructive product" 
     
    3839             
    3940    def commutes(self, other): 
    4041        return False 
     42 
     43    def expand(self): 
     44        return [self] 
     45 
    4146         
    42 class CompositeConstructionFunctor(ConstructionFunctor): 
    43     def __init__(self, first, second): 
    44         Functor.__init__(self, first.domain(), second.codomain()) 
    45         self._first = first 
    46         self._second = second 
     47class CompositConstructionFunctor(ConstructionFunctor): 
     48 
     49    def __init__(self, *args): 
     50        self.all = [] 
     51        for c in args: 
     52            if isinstance(c, list): 
     53                self.all += c 
     54            elif isinstance(c, CompositConstructionFunctor): 
     55                self.all += c.all 
     56            else: 
     57                self.all.append(c) 
     58        Functor.__init__(self, self.all[0].domain(), self.all[-1].codomain()) 
    4759 
    4860    def __call__(self, R): 
    49         return self._second(self._first(R)) 
     61        for c in self.all: 
     62            R = c(R) 
     63        return R 
    5064 
    5165    def __cmp__(self, other): 
    52         c = cmp(self._first, other._first) 
    53         if c == 0: 
    54             c = cmp(self._second, other._second) 
    55         return c 
     66        if isinstance(other, CompositConstructionFunctor): 
     67            return cmp(self.all, other.all) 
     68        else: 
     69            return cmp(type(self), type(other)) 
     70             
     71    def __mul__(self, other): 
     72        if isinstance(self, CompositConstructionFunctor): 
     73            all = self.all + [other] 
     74        else: 
     75            all = [self] + other.all 
     76        return CompositConstructionFunctor(*all) 
    5677     
    5778    def __str__(self): 
    58         return "%s(%s)" % (self._second, self._first) 
     79        s = "..." 
     80        for c in self.all: 
     81            s = "%s(%s)" % (c,s) 
     82        return s 
     83         
     84    def expand(self): 
     85        return self.all 
     86 
    5987         
    6088class IdentityConstructionFunctor(ConstructionFunctor): 
     89 
     90    rank = -100 
     91 
    6192    def __init__(self): 
    6293        Functor.__init__(self, Sets(), Sets()) 
    63         self.rank = -100 
    6494    def __call__(self, R): 
    6595        return R 
    6696    def __mul__(self, other): 
     
    6999        else: 
    70100            return self 
    71101 
     102 
     103 
     104 
    72105class PolynomialFunctor(ConstructionFunctor): 
     106 
     107    rank = 9 
     108 
    73109    def __init__(self, var, multi_variate=False): 
    74110        Functor.__init__(self, Rings(), Rings()) 
    75111        self.var = var 
    76112        self.multi_variate = multi_variate 
    77         self.rank = 9 
     113 
    78114    def __call__(self, R): 
    79115        from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing 
    80         from sage.rings.polynomial.polynomial_ring import is_PolynomialRing 
    81         from sage.rings.polynomial.multi_polynomial_ring_generic import is_MPolynomialRing 
    82         if self.multi_variate and (is_MPolynomialRing(R) or is_PolynomialRing(R)): 
    83             return PolynomialRing(R.base_ring(), (list(R.variable_names()) + [self.var])) 
    84         else: 
    85             return PolynomialRing(R, self.var) 
     116        return PolynomialRing(R, self.var) 
     117 
    86118    def __cmp__(self, other): 
    87119        c = cmp(type(self), type(other)) 
    88120        if c == 0: 
    89121            c = cmp(self.var, other.var) 
     122        elif isinstance(other, MultiPolynomialFunctor): 
     123            return -cmp(other, self) 
    90124        return c 
     125 
    91126    def merge(self, other): 
    92         if self == other: 
    93             return PolynomialFunctor(self.var, (self.multi_variate or other.multi_variate)) 
    94         elif isinstance(other, LaurentPolynomialFunctor) and self.var == other.var: 
    95             return LaurentPolynomialFunctor(self.var, (self.multi_variate or other.multi_variate)) 
    96  
     127        if isinstance(other, MultiPolynomialFunctor): 
     128            return other.merge(self) 
     129        elif self == other: 
     130            return self 
    97131        else: 
    98132            return None 
    99 #    def __str__(self): 
    100 #        return "Poly(%s)" % self.var 
     133 
     134    def __str__(self): 
     135        return "Poly[%s]" % self.var 
     136 
     137class MultiPolynomialFunctor(ConstructionFunctor): 
     138    """ 
     139    A constructor for multivariate polynomial rings. 
     140    """ 
     141     
     142    rank = 9 
     143 
     144    def __init__(self, vars, term_order): 
     145        """ 
     146        EXAMPLES:  
     147            sage: F = sage.categories.pushout.MultiPolynomialFunctor(['x','y'], None) 
     148            sage: F 
     149            MPoly[x,y] 
     150            sage: F(ZZ) 
     151            Multivariate Polynomial Ring in x, y over Integer Ring 
     152            sage: F(CC) 
     153            Multivariate Polynomial Ring in x, y over Complex Field with 53 bits of precision 
     154        """ 
     155        Functor.__init__(self, Rings(), Rings()) 
     156        self.vars = vars 
     157        self.term_order = term_order 
     158 
     159    def __call__(self, R): 
     160        """ 
     161        EXAMPLES: 
     162            sage: R.<x,y,z> = QQ[] 
     163            sage: F = R.construction()[0]; F 
     164            MPoly[x,y,z] 
     165            sage: type(F) 
     166            <class 'sage.categories.pushout.MultiPolynomialFunctor'> 
     167            sage: F(ZZ) 
     168            Multivariate Polynomial Ring in x, y, z over Integer Ring 
     169            sage: F(RR) 
     170            Multivariate Polynomial Ring in x, y, z over Real Field with 53 bits of precision 
     171        """ 
     172        from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing 
     173        return PolynomialRing(R, self.vars) 
     174 
     175    def __cmp__(self, other): 
     176        """ 
     177        EXAMPLES:  
     178            sage: F = ZZ['x,y,z'].construction()[0] 
     179            sage: G = QQ['x,y,z'].construction()[0] 
     180            sage: F == G 
     181            True 
     182            sage: G = ZZ['x,y'].construction()[0] 
     183            sage: F == G 
     184            False 
     185        """ 
     186        c = cmp(type(self), type(other)) 
     187        if c == 0: 
     188            c = cmp(self.vars, other.vars) or cmp(self.term_order, other.term_order) 
     189        elif isinstance(other, PolynomialFunctor): 
     190            c = cmp(self.vars, (other.var,)) 
     191        return c 
     192         
     193    def __mul__(self, other): 
     194        """ 
     195        If two MPoly functors are given in a row, form a single MPoly functor 
     196        with all of the variables.  
     197         
     198        EXAMPLES: 
     199            sage: F = sage.categories.pushout.MultiPolynomialFunctor(['x','y'], None) 
     200            sage: G = sage.categories.pushout.MultiPolynomialFunctor(['t'], None) 
     201            sage: G*F 
     202            MPoly[x,y,t] 
     203        """ 
     204        if isinstance(other, MultiPolynomialFunctor): 
     205            if self.term_order != other.term_order: 
     206                raise TypeError, "Incompatible term orders (%s,%s)." % (self.term_order, other.term_order) 
     207            if set(self.vars).intersection(other.vars): 
     208                raise TypeError, "Overlaping variables (%s,%s)" % (self.vars, other.vars) 
     209            return MultiPolynomialFunctor(other.vars + self.vars, self.term_order) 
     210        elif isinstance(other, CompositConstructionFunctor) \ 
     211              and isinstance(other.all[-1], MultiPolynomialFunctor): 
     212            return CompositConstructionFunctor(other.all[:-1], self * other.all[-1]) 
     213        else: 
     214            return CompositConstructionFunctor(other, self) 
     215 
     216    def merge(self, other): 
     217        """ 
     218        EXAMPLES: 
     219            sage: F = sage.categories.pushout.MultiPolynomialFunctor(['x','y'], None) 
     220            sage: G = sage.categories.pushout.MultiPolynomialFunctor(['t'], None) 
     221            sage: F.merge(G) is None 
     222            True 
     223            sage: F.merge(F) 
     224            MPoly[x,y] 
     225        """ 
     226        if self == other: 
     227            return self 
     228        else: 
     229            return None 
     230 
     231    def expand(self): 
     232        """ 
     233        EXAMPLES:  
     234            sage: F = QQ['x,y,z,t'].construction()[0]; F 
     235            MPoly[x,y,z,t] 
     236            sage: F.expand() 
     237            [MPoly[t], MPoly[z], MPoly[y], MPoly[x]] 
     238             
     239        Now an actual use case: 
     240            sage: R.<x,y,z> = ZZ[] 
     241            sage: S.<z,t> = QQ[] 
     242            sage: x+t 
     243            x + t 
     244            sage: parent(x+t) 
     245            Multivariate Polynomial Ring in x, y, z, t over Rational Field 
     246            sage: T.<y,s> = QQ[] 
     247            sage: x + s 
     248            Traceback (most recent call last): 
     249            ... 
     250            TypeError: unsupported operand parent(s) for '+': 'Multivariate Polynomial Ring in x, y, z over Integer Ring' and 'Multivariate Polynomial Ring in y, s over Rational Field' 
     251            sage: R = PolynomialRing(ZZ, 'x', 500) 
     252            sage: S = PolynomialRing(GF(5), 'x', 200) 
     253            sage: R.gen(0) + S.gen(0) 
     254            2*x0 
     255        """ 
     256        if len(self.vars) <= 1: 
     257            return [self] 
     258        else: 
     259            return [MultiPolynomialFunctor((x,), self.term_order) for x in reversed(self.vars)] 
     260 
     261    def __str__(self): 
     262        """ 
     263        EXAMPLES: 
     264            sage: QQ['x,y,z,t'].construction()[0] 
     265            MPoly[x,y,z,t] 
     266        """ 
     267        return "MPoly[%s]" % ','.join(self.vars) 
     268 
    101269         
    102270class MatrixFunctor(ConstructionFunctor): 
     271 
     272    rank = 10 
     273 
    103274    def __init__(self, nrows, ncols, is_sparse=False): 
    104275#        if nrows == ncols: 
    105276#            Functor.__init__(self, Rings(), RingModules()) # takes a basering 
     
    109280        self.nrows = nrows 
    110281        self.ncols = ncols 
    111282        self.is_sparse = is_sparse 
    112         self.rank = 10 
    113283    def __call__(self, R): 
    114284        from sage.matrix.matrix_space import MatrixSpace 
    115285        return MatrixSpace(R, self.nrows, self.ncols, sparse=self.is_sparse) 
     
    125295            return MatrixFunctor(self.nrows, self.ncols, self.is_sparse and other.is_sparse) 
    126296 
    127297class LaurentPolynomialFunctor(ConstructionFunctor): 
     298 
     299    rank = 9 
     300     
    128301    def __init__(self, var, multi_variate=False): 
    129302        Functor.__init__(self, Rings(), Rings()) 
    130303        self.var = var 
    131304        self.multi_variate = multi_variate 
    132         self.rank = 9 
    133305    def __call__(self, R): 
    134306        from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing, is_LaurentPolynomialRing 
    135307        if self.multi_variate and is_LaurentPolynomialRing(R): 
     
    149321 
    150322         
    151323class VectorFunctor(ConstructionFunctor): 
     324 
     325    rank = 10 # ranking of functor, not rank of module 
     326 
    152327    def __init__(self, n, is_sparse=False, inner_product_matrix=None): 
    153328#        if nrows == ncols: 
    154329#            Functor.__init__(self, Rings(), RingModules()) # takes a basering 
     
    158333        self.n = n 
    159334        self.is_sparse = is_sparse 
    160335        self.inner_product_matrix = inner_product_matrix 
    161         self.rank = 10 # ranking of functor, not rank of module 
    162336    def __call__(self, R): 
    163337        from sage.modules.free_module import FreeModule 
    164338        return FreeModule(R, self.n, sparse=self.is_sparse, inner_product_matrix=self.inner_product_matrix) 
     
    172346            return None 
    173347        else: 
    174348            return VectorFunctor(self.n, self.is_sparse and other.is_sparse) 
    175          
     349 
     350 
    176351class SubspaceFunctor(ConstructionFunctor): 
     352    rank = 11 # ranking of functor, not rank of module 
    177353    def __init__(self, basis): 
    178354        self.basis = basis 
    179         self.rank = 11 # ranking of functor, not rank of module 
    180355    def __call__(self, ambient): 
    181356        return ambient.span_of_basis(self.basis) 
    182357    def __cmp__(self, other): 
     
    192367 
    193368         
    194369class FractionField(ConstructionFunctor): 
     370     
     371    rank = 5 
     372     
    195373    def __init__(self): 
    196374        Functor.__init__(self, Rings(), Fields()) 
    197         self.rank = 5 
    198375    def __call__(self, R): 
    199376        return R.fraction_field() 
    200377         
     378     
    201379class LocalizationFunctor(ConstructionFunctor): 
     380     
     381    rank = 6 
     382     
    202383    def __init__(self, t): 
    203384        Functor.__init__(self, Rings(), Rings()) 
    204385        self.t = t 
    205         self.rank = 6 
    206386    def __call__(self, R): 
    207387        return R.localize(t) 
    208388    def __cmp__(self, other): 
     
    210390        if c == 0: 
    211391            c = cmp(self.t, other.t) 
    212392        return c 
    213          
     393     
     394     
    214395class CompletionFunctor(ConstructionFunctor): 
     396     
     397    rank = 4 
     398     
    215399    def __init__(self, p, prec, extras=None): 
    216400        Functor.__init__(self, Rings(), Rings()) 
    217401        self.p = p 
    218402        self.prec = prec 
    219403        self.extras = extras 
    220         self.rank = 4 
    221404    def __call__(self, R): 
    222405        return R.completion(self.p, self.prec, self.extras) 
    223406    def __cmp__(self, other): 
     
    237420                return other 
    238421        else: 
    239422            return None 
    240          
     423     
     424     
    241425class QuotientFunctor(ConstructionFunctor): 
    242     def __init__(self, I): 
     426     
     427    rank = 7 
     428     
     429    def __init__(self, I, as_field=False): 
    243430        Functor.__init__(self, Rings(), Rings()) # much more general... 
    244431        self.I = I 
    245         self.rank = 7 
     432        self.as_field = as_field 
    246433    def __call__(self, R): 
    247434        I = self.I 
    248435        if I.ring() != R: 
    249436            I.base_extend(R) 
    250         return R.quo(I) 
     437        Q = R.quo(I) 
     438        if self.as_field and hasattr(Q, 'field'): 
     439            Q = Q.field() 
     440        return Q 
    251441    def __cmp__(self, other): 
    252442        c = cmp(type(self), type(other)) 
    253443        if c == 0: 
     
    266456            # TODO: Perhaps this should be detected at a higher level... 
    267457            raise TypeError, "Trivial quotient intersection." 
    268458        return QuotientFunctor(gcd) 
     459 
    269460 
    270461class AlgebraicExtensionFunctor(ConstructionFunctor): 
    271462     
     
    287478            c = cmp(self.embedding, other.embedding) 
    288479        return c 
    289480 
     481 
    290482class AlgebraicClosureFunctor(ConstructionFunctor): 
    291483 
    292484    rank = 3 
     
    298490    def merge(self, other): 
    299491        # Algebraic Closure subsumes Algebraic Extension 
    300492        return self 
     493 
    301494         
    302495def BlackBoxConstructionFunctor(ConstructionFunctor): 
     496 
     497    rank = 100 
     498 
    303499    def __init__(self, box): 
     500        if not callable(box): 
     501            raise TypeError, "input must be callable" 
    304502        self.box = box 
    305         self.rank = 100 
    306503    def __call__(self, R): 
    307504        return box(R) 
    308505    def __cmp__(self, other): 
     
    435632    Rc = [c[0] for c in R_tower[1:len(Rs)+1]] 
    436633    Sc = [c[0] for c in S_tower[1:len(Ss)+1]] 
    437634     
     635    Rc = sum([c.expand() for c in Rc], []) 
     636    Sc = sum([c.expand() for c in Sc], []) 
     637 
     638    all = IdentityConstructionFunctor() 
     639 
    438640    while len(Rc) > 0 or len(Sc) > 0: 
    439641        # print Z 
    440642        # if we are out of functors in either tower, there is no ambiguity 
    441643        if len(Sc) == 0: 
    442             c = Rc.pop() 
    443             Z = c(Z) 
     644            all = Rc.pop() * all 
    444645        elif len(Rc) == 0: 
    445             c = Sc.pop() 
    446             Z = c(Z) 
     646            all = Sc.pop() * all 
    447647        # if one of the functors has lower rank, do it first 
    448648        elif Rc[-1].rank < Sc[-1].rank: 
    449             c = Rc.pop() 
    450             Z = c(Z) 
     649            all = Rc.pop() * all 
    451650        elif Sc[-1].rank < Rc[-1].rank: 
    452             c = Sc.pop() 
    453             Z = c(Z) 
     651            all = Sc.pop() * all 
    454652        else: 
    455653            # the ranks are the same, so things are a bit subtler 
    456654            if Rc[-1] == Sc[-1]: 
     
    461659                cS = Sc.pop() 
    462660                c = cR.merge(cS) or cS.merge(cR) 
    463661                if c: 
    464                     Z = c(Z) 
     662                    all = c * all 
    465663                else: 
    466664                    raise TypeError, "Incompatable Base Extension %r, %r (on %r, %r)" % (R, S, cR, cS) 
    467665            else: 
     
    473671                    if Sc[-1] in Rc: 
    474672                        raise TypeError, "Ambiguous Base Extension" 
    475673                    else: 
    476                         c = Sc.pop() 
    477                         Z = c(Z) 
     674                        all = Sc.pop() * all 
    478675                elif Sc[-1] in Rc: 
    479                     c = Rc.pop(); 
    480                     Z = c(Z) 
     676                    all = Rc.pop() * all 
    481677                # If, perchance, the two functors commute, then we may do them in any order.  
    482678                elif Rc[-1].commutes(Sc[-1]): 
    483                     c = Rc.pop() 
    484                     Z = c(Z) 
    485                     c = Sc.pop() 
    486                     Z = c(Z) 
     679                    all = Sc.pop() * Rc.pop() * all 
    487680                else: 
    488681                    # try and merge (default merge is failure for unequal functors) 
    489682                    cR = Rc.pop() 
    490683                    cS = Sc.pop() 
    491684                    c = cR.merge(cS) or cS.merge(cR) 
    492685                    if c is not None: 
    493                         Z = c(Z) 
     686                        all = c * all 
    494687                    else: 
    495688                        # Otherwise, we cannot proceed.  
    496689                        raise TypeError, "Ambiguous Base Extension" 
    497     return Z 
     690     
     691    return all(Z) 
    498692     
    499693 
    500694     
  • a/sage/rings/polynomial/multi_polynomial_ring_generic.pyx

    old new  
    6868        """ 
    6969        Returns a functor F and basering R such that F(R) == self.  
    7070 
    71         In the multi-variate case, R is a polynomial ring with one 
    72         less variable, and F knows to adjoin the variable in the  
    73         correct way.  
    74          
    7571        EXAMPLES:  
    7672            sage: S = ZZ['x,y'] 
    7773            sage: F, R = S.construction(); R 
    78             Univariate Polynomial Ring in x over Integer Ring 
     74            Integer Ring 
     75            sage: F 
     76            MPoly[x,y] 
    7977            sage: F(R) == S 
    8078            True 
    8179            sage: F(R) == ZZ['x']['y'] 
     
    8381             
    8482        """ 
    8583        from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing 
    86         from sage.categories.pushout import PolynomialFunctor 
    87         vars = self.variable_names() 
    88         if len(vars) == 1: 
    89             return PolynomialFunctor(vars[0], False), self.base_ring() 
    90         else: 
    91             return PolynomialFunctor(vars[-1], True), PolynomialRing(self.base_ring(), vars[:-1]) 
     84        from sage.categories.pushout import MultiPolynomialFunctor 
     85        return MultiPolynomialFunctor(self.variable_names(), self.term_order()), self.base_ring() 
    9286             
    9387    def completion(self, p, prec=20, extras=None): 
    9488        try: 
  • a/sage/rings/quotient_ring.py

    old new  
    207207            sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2]) 
    208208            sage: R.quotient_ring(I).construction() 
    209209            (QuotientFunctor, Univariate Polynomial Ring in x over Integer Ring) 
     210             
     211        TESTS: 
     212            sage: F, R = Integers(5).construction() 
     213            sage: F(R) 
     214            Ring of integers modulo 5 
     215            sage: F, R = GF(5).construction() 
     216            sage: F(R) 
     217            Finite Field of size 5 
    210218        """ 
    211219        from sage.categories.pushout import QuotientFunctor 
    212         return QuotientFunctor(self.__I), self.__R 
     220        # Is there a better generic way to distinguish between things like Z/pZ as a field and Z/pZ as a ring? 
     221        from sage.rings.field import Field 
     222        return QuotientFunctor(self.__I, as_field=isinstance(self, Field)), self.__R 
    213223 
    214224    def _repr_(self): 
    215225        """ 
  • a/sage/structure/coerce.pyx

    old new  
    10251025                Z = pushout(R, S) 
    10261026                coerce_R = Z.coerce_map_from(R) 
    10271027                coerce_S = Z.coerce_map_from(S) 
    1028                 if coerce_R is not None and coerce_S is not None: 
    1029                     return coerce_R, coerce_S 
     1028                if coerce_R is None: 
     1029                    raise TypeError, "No coercion from %s to pushout %s" % (R, Z) 
     1030                if coerce_S is None: 
     1031                    raise TypeError, "No coercion from %s to pushout %s" % (S, Z) 
     1032                return coerce_R, coerce_S 
    10301033            except: 
    10311034                self._record_exception() 
    10321035