Ticket #7914: trac_7914_folded_rebased.patch

File trac_7914_folded_rebased.patch, 31.7 KB (added by nthiery, 3 years ago)

Apply only this one (includes the rebasing by Florent)

  • sage/categories/modules_with_basis.py

    # HG changeset patch
    # User Jason Bandlow <jbandlow at gmail.com>
    # Date 1263305729 18000
    # Node ID abe3c674933ae7ad9aa15c283b8e40880fcada0e
    # Parent  dfb4e381dc1a78f189042e5ece3d03bcc51e4342
    #7914: Implementation of triangular morphisms for modules with basis
    imported patch triangular-morphisms-jb.patch
    
    diff --git a/sage/categories/modules_with_basis.py b/sage/categories/modules_with_basis.py
    a b from sage.categories.all import Modules, 
    1313from category_types import Category_over_base_ring 
    1414from sage.misc.lazy_attribute import lazy_attribute 
    1515from sage.misc.cachefunc import cached_method 
     16from sage.misc.misc import attrcall 
     17from sage.misc.sage_itertools import max_cmp, min_cmp 
    1618from sage.structure.element import ModuleElement 
    1719from sage.categories.morphism import SetMorphism, Morphism 
    1820from sage.categories.homset import Hom 
    class ModulesWithBasis(Category_over_bas 
    132134 
    133135            sage: C = ModulesWithBasis(QQ) 
    134136 
    135         ``x`` is returned unchanged if it is readilly in this category:: 
     137        ``x`` is returned unchanged if it is already in this category:: 
    136138 
    137139            sage: C(CombinatorialFreeModule(QQ, ('a','b','c'))) 
    138140            Free module generated by {'a', 'b', 'c'} over Rational Field 
    class ModulesWithBasis(Category_over_bas 
    175177 
    176178    # TODO: find something better to get this inheritance from CategoryWithTensorProduct.Element 
    177179    class ParentMethods(CategoryWithTensorProduct.ParentMethods, CategoryWithCartesianProduct.ParentMethods): 
    178         def module_morphism(self, on_basis = None, diagonal = None, **keywords): 
     180        def module_morphism(self, on_basis = None, diagonal = None, triangular = None, **keywords): 
    179181            """ 
    180182            Constructs functions by linearity 
    181183 
    182184            INPUT: 
    183              - self: a parent `X` in ModulesWithBasis(R), with basis `x` indexed by `I` 
    184              - codomain: the codomain `Y` of f: defaults to `f.codomain` if the later is defined 
    185              - zero: the zero of the codomain; defaults to codomain.zero() or 0 if codomain is not specified 
    186              - position: a non negative integer; defaults to 0 
    187              - on_basis: a function `f` which accepts elements of `I` 
     185             
     186             - ``self`` - a parent `X` in ModulesWithBasis(R), with basis `x` 
     187               indexed by `I` 
     188             - ``codomain`` - the codomain `Y` of f: defaults to ``f.codomain`` 
     189               if the later is defined 
     190             - ``zero`` - the zero of the codomain; defaults to 
     191               `codomain.zero()` or 0 if codomain is not specified 
     192             - ``position`` - a non negative integer; defaults to 0 
     193             - ``on_basis`` - a function `f` which accepts elements of `I` 
    188194               as position-th argument and returns  elements of `Y` 
    189              - diagonal: a function `d` from `I` to `R` 
    190              - category: a category. By default, this is 
     195             - ``diagonal`` - a function `d` from `I` to `R` 
     196             - ``triangular`` a boolean (default: False) 
     197             - ``category`` - a category. By default, this is 
    191198               ``ModulesWithBasis(R)`` if `Y` is in this category, and 
    192199               otherwise this lets `Hom(X,Y)` decide 
    193200 
    class ModulesWithBasis(Category_over_bas 
    221228            This assumes that the respective bases `x` and `y` of `X` 
    222229            and `Y` have the same index set `I`. 
    223230 
     231            With ``triangular = upper``, the constructed module 
     232            morphism is assumed to be upper triangular; that is its 
     233            matrix in the distinguished basis of `X` and `Y` would be 
     234            upper triangular with invertible elements on its 
     235            diagonal. This currently assumes that `X` and `Y` have the 
     236            same index set `I`. This is used to compute preimages and 
     237            inverting the morphism:: 
     238 
     239                sage: I = range(1,200) 
     240                sage: X = CombinatorialFreeModule(QQ, I); X.rename("X"); x = X.basis() 
     241                sage: Y = CombinatorialFreeModule(QQ, I); Y.rename("Y"); y = Y.basis() 
     242                sage: f = Y.sum_of_monomials * divisors 
     243                sage: phi = X.module_morphism(f, triangular="upper", codomain = Y) 
     244                sage: phi(x[2]) 
     245                B[1] + B[2] 
     246                sage: phi(x[6]) 
     247                B[1] + B[2] + B[3] + B[6] 
     248                sage: phi(x[30]) 
     249                B[1] + B[2] + B[3] + B[5] + B[6] + B[10] + B[15] + B[30] 
     250                sage: phi.preimage(y[2]) 
     251                -B[1] + B[2] 
     252                sage: phi.preimage(y[6]) 
     253                B[1] - B[2] - B[3] + B[6] 
     254                sage: phi.preimage(y[30]) 
     255                -B[1] + B[2] + B[3] + B[5] - B[6] - B[10] - B[15] + B[30] 
     256                sage: (phi^-1)(y[30]) 
     257                -B[1] + B[2] + B[3] + B[5] - B[6] - B[10] - B[15] + B[30] 
     258 
     259            For details and further optional arguments, see 
     260            :class:`sage.categories.modules_with_basis.TriangularModuleMorphism`. 
     261 
    224262 
    225263            Caveat: the returned element is in ``Hom(codomain, domain, 
    226264            category``). This is only correct for unary functions. 
    227265 
    228             Todo: should codomain be self by default in the diagonal case? 
     266            Todo: should codomain be ``self`` by default in the 
     267            diagonal and triangular cases? 
    229268 
    230269            """ 
    231270            if diagonal is not None: 
    232271                return DiagonalModuleMorphism(diagonal = diagonal, domain = self, **keywords) 
    233272            elif on_basis is not None: 
     273                if triangular is not None: 
     274                    return TriangularModuleMorphism(on_basis, domain = self, triangular = triangular, **keywords) 
    234275                return ModuleMorphismByLinearity(on_basis = on_basis, domain = self, **keywords) 
    235276            else: 
    236277                raise ValueError("module morphism requires either on_basis or diagonal argument") 
    class ModulesWithBasis(Category_over_bas 
    239280 
    240281    # TODO: find something better to get this inheritance from CategoryWithTensorProduct.Element 
    241282    class ElementMethods(CategoryWithTensorProduct.ElementMethods, CategoryWithCartesianProduct.ElementMethods): 
     283        # TODO: Define the appropriate element methods here (instead of in 
     284        # subclasses).  These methods should be consistent with those on 
     285        # polynomials. 
     286 
     287#         def _neg_(self): 
     288#             """ 
     289#             Default implementation of negation by trying to multiply by -1. 
     290#             TODO: doctest 
     291#             """ 
     292#             return self._lmul_(-self.parent().base_ring().one(), self) 
    242293 
    243294        def support_of_term(self): 
    244295            """ 
    class ModulesWithBasis(Category_over_bas 
    269320            else: 
    270321                raise ValueError, "%s is not a single term"%(self) 
    271322 
    272 #         def _neg_(self): 
    273 #             """ 
    274 #             Default implementation of negation by trying to multiply by -1. 
    275 #             """ 
    276 #             return self._lmul_(-self.parent().base_ring().one(), self) 
     323        def leading_support(self, cmp=None): 
     324            r""" 
     325            Returns the maximal element of the support of ``self``. Note 
     326            that this may not be the term which actually appears first when 
     327            ``self`` is printed. 
     328 
     329            If the default ordering of the basis elements is not what is 
     330            desired, a comparison function, ``cmp(x,y)``, can be provided. 
     331            This should return a negative value if `x < y`, `0` if `x == y` 
     332            and a positive value if `x > y`. 
     333 
     334            EXAMPLES:: 
     335 
     336                sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() 
     337                sage: x = 3*X.monomial(1) + 2*X.monomial(2) + 4*X.monomial(3) 
     338                sage: x.leading_support() 
     339                3 
     340                sage: def cmp(x,y): return y-x 
     341                sage: x.leading_support(cmp=cmp) 
     342                1 
     343 
     344                sage: s = SymmetricFunctions(QQ).schur() 
     345                sage: f = 2*s[1] + 3*s[2,1] - 5*s[3] 
     346                sage: f.leading_support() 
     347                [3] 
     348            """ 
     349            return max_cmp(self.support(), cmp) 
     350 
     351 
     352        def leading_item(self, cmp=None): 
     353            r""" 
     354            Returns the pair ``(k, c)`` where ``c`` * (the basis elt. indexed 
     355            by ``k``) is the leading term of ``self``. 
     356 
     357            'leading term' means that the corresponding basis element is 
     358            maximal.  Note that this may not be the term which actually appears 
     359            first when ``self`` is printed.  If the default term ordering is not 
     360            what is desired, a comparison function, ``cmp(x,y)``, can be 
     361            provided.  This should return a negative value if `x < y`, `0` if 
     362            `x == y` and a positive value if `x > y`. 
     363 
     364            EXAMPLES:: 
     365 
     366                sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() 
     367                sage: x = 3*X.monomial(1) + 2*X.monomial(2) + 4*X.monomial(3) 
     368                sage: x.leading_item() 
     369                (3, 4) 
     370                sage: def cmp(x,y): return y-x 
     371                sage: x.leading_item(cmp=cmp) 
     372                (1, 3) 
     373 
     374                sage: s = SymmetricFunctions(QQ).schur() 
     375                sage: f = 2*s[1] + 3*s[2,1] - 5*s[3] 
     376                sage: f.leading_item() 
     377                ([3], -5) 
     378            """ 
     379            k = self.leading_support(cmp=cmp) 
     380            return k, self[k] 
     381 
     382        def leading_monomial(self, cmp=None): 
     383            r""" 
     384            Returns the leading monomial of ``self``. 
     385 
     386            This is the monomial whose corresponding basis element is 
     387            maximal. Note that this may not be the term which actually appears 
     388            first when ``self`` is printed. If the default term ordering is not 
     389            what is desired, a comparison function, cmp(x,y), can be provided. 
     390            This should return a negative value if x < y, 0 if x == y 
     391            and a positive value if x > y. 
     392 
     393            EXAMPLES:: 
     394 
     395                sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() 
     396                sage: x = 3*X.monomial(1) + 2*X.monomial(2) + X.monomial(3) 
     397                sage: x.leading_monomial() 
     398                B[3] 
     399                sage: def cmp(x,y): return y-x 
     400                sage: x.leading_monomial(cmp=cmp) 
     401                B[1] 
     402 
     403                sage: s = SymmetricFunctions(QQ).schur() 
     404                sage: f = 2*s[1] + 3*s[2,1] - 5*s[3] 
     405                sage: f.leading_monomial() 
     406                s[3] 
     407            """ 
     408            return self.parent().monomial( self.leading_support(cmp=cmp) ) 
     409 
     410        def leading_coefficient(self, cmp=None): 
     411            r""" 
     412            Returns the leading coefficient of ``self``. 
     413 
     414            This is the coefficient of the term whose corresponding basis element is 
     415            maximal. Note that this may not be the term which actually appears 
     416            first when ``self`` is printed.  If the default term ordering is not 
     417            what is desired, a comparison function, cmp(x,y), can be provided. 
     418            This should return a negative value if x < y, 0 if x == y 
     419            and a positive value if x > y. 
     420 
     421            EXAMPLES:: 
     422 
     423                sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X") 
     424                sage: x = 3*X.monomial(1) + 2*X.monomial(2) + X.monomial(3) 
     425                sage: x.leading_coefficient() 
     426                1 
     427                sage: def cmp(x,y): return y-x 
     428                sage: x.leading_coefficient(cmp=cmp) 
     429                3 
     430 
     431                sage: s = SymmetricFunctions(QQ).schur() 
     432                sage: f = 2*s[1] + 3*s[2,1] - 5*s[3] 
     433                sage: f.leading_coefficient() 
     434                -5 
     435            """ 
     436            return self.leading_item(cmp=cmp)[1] 
     437 
     438        def leading_term(self, cmp=None): 
     439            r""" 
     440            Returns the leading term of ``self``. 
     441 
     442            This is the term whose corresponding basis element is 
     443            maximal. Note that this may not be the term which actually appears 
     444            first when ``self`` is printed. If the default term ordering is not 
     445            what is desired, a comparison function, cmp(x,y), can be provided. 
     446            This should return a negative value if x < y, 0 if x == y  
     447            and a positive value if x > y. 
     448 
     449            EXAMPLES:: 
     450 
     451                sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() 
     452                sage: x = 3*X.monomial(1) + 2*X.monomial(2) + X.monomial(3) 
     453                sage: x.leading_term() 
     454                B[3] 
     455                sage: def cmp(x,y): return y-x 
     456                sage: x.leading_term(cmp=cmp) 
     457                3*B[1] 
     458 
     459                sage: s = SymmetricFunctions(QQ).schur() 
     460                sage: f = 2*s[1] + 3*s[2,1] - 5*s[3] 
     461                sage: f.leading_term() 
     462                -5*s[3] 
     463            """ 
     464            return self.parent().term(*self.leading_item(cmp=cmp)) 
     465 
     466        def trailing_support(self, cmp=None): 
     467            r""" 
     468            Returns the minimal element of the support of ``self``. Note 
     469            that this may not be the term which actually appears last when 
     470            ``self`` is printed. 
     471 
     472            If the default ordering of the basis elements is not what is 
     473            desired, a comparison function, ``cmp(x,y)``, can be provided. 
     474            This should return a negative value if `x < y`, `0` if `x == y`  
     475            and a positive value if `x > y`. 
     476 
     477            EXAMPLES:: 
     478 
     479                sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() 
     480                sage: x = 3*X.monomial(1) + 2*X.monomial(2) + 4*X.monomial(3) 
     481                sage: x.trailing_support() 
     482                1 
     483                sage: def cmp(x,y): return y-x 
     484                sage: x.trailing_support(cmp=cmp) 
     485                3 
     486 
     487                sage: s = SymmetricFunctions(QQ).schur() 
     488                sage: f = 2*s[1] + 3*s[2,1] - 5*s[3] 
     489                sage: f.trailing_support() 
     490                [1] 
     491            """ 
     492            return min_cmp(self.support(), cmp) 
     493 
     494        def trailing_item(self, cmp=None): 
     495            r""" 
     496            Returns the pair (c, k) where c*self.parent().monomial(k) 
     497            is the trailing term of ``self``. 
     498 
     499            This is the monomial whose corresponding basis element is 
     500            minimal. Note that this may not be the term which actually appears 
     501            last when ``self`` is printed.  If the default term ordering is not 
     502            what is desired, a comparison function cmp(x,y), can be provided. 
     503            This should return a negative value if x < y, 0 if x == y 
     504            and a positive value if x > y. 
     505 
     506            EXAMPLES:: 
     507 
     508                sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() 
     509                sage: x = 3*X.monomial(1) + 2*X.monomial(2) + X.monomial(3) 
     510                sage: x.trailing_item() 
     511                (1, 3) 
     512                sage: def cmp(x,y): return y-x 
     513                sage: x.trailing_item(cmp=cmp) 
     514                (3, 1) 
     515 
     516                sage: s = SymmetricFunctions(QQ).schur() 
     517                sage: f = 2*s[1] + 3*s[2,1] - 5*s[3] 
     518                sage: f.trailing_item() 
     519                ([1], 2) 
     520            """ 
     521            k = self.trailing_support(cmp=cmp) 
     522            return k, self[k] 
     523 
     524        def trailing_monomial(self, cmp=None): 
     525            r""" 
     526            Returns the trailing monomial of ``self``. 
     527 
     528            This is the monomial whose corresponding basis element is 
     529            minimal. Note that this may not be the term which actually appears 
     530            last when ``self`` is printed. If the default term ordering is not 
     531            what is desired, a comparison function cmp(x,y), can be provided. 
     532            This should return a negative value if x < y, 0 if x == y 
     533            and a positive value if x > y. 
     534 
     535            EXAMPLES:: 
     536 
     537                sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() 
     538                sage: x = 3*X.monomial(1) + 2*X.monomial(2) + X.monomial(3) 
     539                sage: x.trailing_monomial() 
     540                B[1] 
     541                sage: def cmp(x,y): return y-x 
     542                sage: x.trailing_monomial(cmp=cmp) 
     543                B[3] 
     544 
     545                sage: s = SymmetricFunctions(QQ).schur() 
     546                sage: f = 2*s[1] + 3*s[2,1] - 5*s[3] 
     547                sage: f.trailing_monomial() 
     548                s[1] 
     549            """ 
     550            return self.parent().monomial( self.trailing_support(cmp=cmp) ) 
     551 
     552        def trailing_coefficient(self, cmp=None): 
     553            r""" 
     554            Returns the trailing coefficient of ``self``. 
     555 
     556            This is the coefficient of the monomial whose corresponding basis element is 
     557            minimal. Note that this may not be the term which actually appears 
     558            last when ``self`` is printed. If the default term ordering is not 
     559            what is desired, a comparison function cmp(x,y), can be provided. 
     560            This should return a negative value if x < y, 0 if x == y  
     561            and a positive value if x > y. 
     562 
     563            EXAMPLES:: 
     564 
     565                sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() 
     566                sage: x = 3*X.monomial(1) + 2*X.monomial(2) + X.monomial(3) 
     567                sage: x.trailing_coefficient() 
     568                3 
     569                sage: def cmp(x,y): return y-x 
     570                sage: x.trailing_coefficient(cmp=cmp) 
     571                1 
     572 
     573                sage: s = SymmetricFunctions(QQ).schur() 
     574                sage: f = 2*s[1] + 3*s[2,1] - 5*s[3] 
     575                sage: f.trailing_coefficient() 
     576                2 
     577            """ 
     578 
     579            return self.trailing_item(cmp=cmp)[1] 
     580 
     581        def trailing_term(self, cmp=None): 
     582            r""" 
     583            Returns the trailing term of ``self``. 
     584 
     585            This is the term whose corresponding basis element is 
     586            minimal. Note that this may not be the term which actually appears 
     587            last when ``self`` is printed. If the default term ordering is not 
     588            what is desired, a comparison function cmp(x,y), can be provided. 
     589            This should return a negative value if x < y, 0 if x == y 
     590            and a positive value if x > y. 
     591 
     592            EXAMPLES:: 
     593 
     594                sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() 
     595                sage: x = 3*X.monomial(1) + 2*X.monomial(2) + X.monomial(3) 
     596                sage: x.trailing_term() 
     597                3*B[1] 
     598                sage: def cmp(x,y): return y-x 
     599                sage: x.trailing_term(cmp=cmp) 
     600                B[3] 
     601 
     602                sage: s = SymmetricFunctions(QQ).schur() 
     603                sage: f = 2*s[1] + 3*s[2,1] - 5*s[3] 
     604                sage: f.trailing_term() 
     605                2*s[1] 
     606            """ 
     607            return self.parent().term( *self.trailing_item(cmp=cmp) ) 
    277608 
    278609    class HomCategory(HomCategory): 
    279610        """ 
    class ModuleMorphismByLinearity(Morphism 
    601932    # To be cleaned up 
    602933    _call_ = __call__ 
    603934 
     935class TriangularModuleMorphism(ModuleMorphismByLinearity): 
     936    """ 
     937    A class for triangular module morphisms; that is module morphisms 
     938    from `X` to `Y` whose matrix in the distinguished basis of `X` and 
     939    `Y` would be upper triangular with invertible elements on its 
     940    diagonal. This currently assumes that `X` and `Y` have the same 
     941    index set `I`. However, `I` needs not be finite. 
     942 
     943    See :meth:`module_morphism` of ModulesWithBasis 
     944 
     945    EXAMPLES:: 
     946 
     947        sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() 
     948        sage: def ut(i): return sum(j*x[j] for j in range(i,4)) 
     949        sage: import __main__; __main__.ut = ut 
     950        sage: phi = X.module_morphism(ut, triangular="lower", codomain = X) 
     951        sage: phi(x[2]) 
     952        2*B[2] + 3*B[3] 
     953        sage: phi.preimage(x[2]) 
     954        1/2*B[2] - 1/2*B[3] 
     955        sage: phi(phi.preimage(x[2])) 
     956        B[2] 
     957    """ 
     958 
     959    def __init__(self, on_basis, domain, triangular = "upper", unitriangular=False, 
     960                 codomain = None, category = None, cmp = None, inverse = None, **keywords): 
     961        """ 
     962        INPUT: 
     963 
     964         - ``domain``, ``codomain`` - two modules with basis `F` and `G` 
     965         - ``on_basis`` - a function from the index set of the basis of `F` to the 
     966           elements of `G` 
     967         - ``unitriangular`` - boolean (default: False) 
     968         - ``triangular`` - "upper" or "lower" (default: "upper") 
     969             - "upper": if the `leading_support()`  of the image of `F(i)` is `i`, or 
     970             - "lower": if the `trailing_support()` of the image of `F(i)` is `i`. 
     971 
     972         - ``cmp`` - an optional comparison function on the index set `I` of the basis 
     973           (see :meth:`.leading_support` for details). 
     974 
     975        Assumptions: 
     976 
     977         - `F` and `G` have the same base ring `R` 
     978         - Their respective bases `f` and `g` have the same index set `I` 
     979 
     980        OUTPUT: 
     981            The triangular module morphism from `F` to `G` which maps `f_\lambda` 
     982            to `on_basis(\lambda)` and is extended by linearity. 
     983 
     984        EXAMPLES:: 
     985 
     986                sage: I = range(1,200) 
     987                sage: X = CombinatorialFreeModule(QQ, I); X.rename("X"); x = X.basis() 
     988                sage: Y = CombinatorialFreeModule(QQ, I); Y.rename("Y"); y = Y.basis() 
     989                sage: f = Y.sum_of_monomials * divisors 
     990                sage: phi = X.module_morphism(f, triangular="upper", unitriangular = True, codomain = Y) 
     991                sage: phi(x[2]) 
     992                B[1] + B[2] 
     993                sage: phi(x[6]) 
     994                B[1] + B[2] + B[3] + B[6] 
     995                sage: phi(x[30]) 
     996                B[1] + B[2] + B[3] + B[5] + B[6] + B[10] + B[15] + B[30] 
     997                sage: phi.preimage(y[2]) 
     998                -B[1] + B[2] 
     999                sage: phi.preimage(y[6]) 
     1000                B[1] - B[2] - B[3] + B[6] 
     1001                sage: phi.preimage(y[30]) 
     1002                -B[1] + B[2] + B[3] + B[5] - B[6] - B[10] - B[15] + B[30] 
     1003                sage: (phi^-1)(y[30]) 
     1004                -B[1] + B[2] + B[3] + B[5] - B[6] - B[10] - B[15] + B[30] 
     1005 
     1006        TESTS:: 
     1007 
     1008            sage: phi.__class__ 
     1009            <class 'sage.categories.modules_with_basis.TriangularModuleMorphism'> 
     1010            sage: TestSuite(phi).run() # known issue; see ModuleMorphism above 
     1011            Failure in _test_category: 
     1012            ... 
     1013            The following tests failed: _test_category 
     1014        """ 
     1015        assert codomain is not None 
     1016        assert domain.basis().keys() == codomain.basis().keys() 
     1017        assert domain.base_ring()    == codomain.base_ring() 
     1018        if category is None: 
     1019            category = ModulesWithBasis(domain.base_ring()) 
     1020        if triangular == "upper": 
     1021            self._dominant_item = attrcall("leading_item",  cmp) 
     1022        else: 
     1023            self._dominant_item = attrcall("trailing_item", cmp) 
     1024        # We store those two just be able to pass them down to the inverse function 
     1025        self._triangular = triangular 
     1026        self._cmp = cmp 
     1027 
     1028        self._unitriangular = unitriangular 
     1029        self._inverse = inverse 
     1030        ModuleMorphismByLinearity.__init__(self, domain = domain, codomain = codomain, category = category) 
     1031        self.on_basis = on_basis # should this be called on_basis (or _on_basis)? 
     1032 
     1033 
     1034    def _on_basis(self, i): 
     1035        """ 
     1036        Returns the image, by self, of the basis element indexed by `i`. 
     1037 
     1038        TESTS:: 
     1039 
     1040            sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); x = X.basis() 
     1041            sage: Y = CombinatorialFreeModule(QQ, [1, 2, 3]); y = Y.basis() 
     1042            sage: f = lambda i: sum(  y[j] for j in range(i,4)  ) 
     1043            sage: phi = X.module_morphism(f, triangular="lower", codomain = Y) 
     1044            sage: phi._on_basis(2) 
     1045            B[2] + B[3] 
     1046        """ 
     1047        return self.codomain()(self.on_basis(i)) 
     1048 
     1049    def __invert__(self): 
     1050        """ 
     1051        Returns the triangular morphism which is the inverse of `self`. 
     1052 
     1053        TESTS:: 
     1054            sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); x = X.basis() 
     1055            sage: Y = CombinatorialFreeModule(QQ, [1, 2, 3]); y = Y.basis() 
     1056            sage: uut = lambda i: sum(  y[j] for j in range(1,i+1)) # uni-upper 
     1057            sage: ult = lambda i: sum(  y[j] for j in range(i,4)  ) # uni-lower 
     1058            sage: ut =  lambda i: sum(j*y[j] for j in range(1,i+1)) # upper 
     1059            sage: lt =  lambda i: sum(j*y[j] for j in range(i,4  )) # lower 
     1060            sage: f_uut = X.module_morphism(uut, triangular="upper", unitriangular=True,  codomain = Y) 
     1061            sage: f_ult = X.module_morphism(ult, triangular="lower", unitriangular=True,  codomain = Y) 
     1062            sage: f_ut =  X.module_morphism(ut,  triangular="upper",                      codomain = Y) 
     1063            sage: f_lt =  X.module_morphism(lt,  triangular="lower",                      codomain = Y) 
     1064            sage: (~f_uut)(y[2]) 
     1065            -B[1] + B[2] 
     1066            sage: (~f_ult)(y[2]) 
     1067            B[2] - B[3] 
     1068            sage: (~f_ut)(y[2]) 
     1069            -1/2*B[1] + 1/2*B[2] 
     1070            sage: (~f_lt)(y[2]) 
     1071            1/2*B[2] - 1/2*B[3] 
     1072        """ 
     1073        if self._inverse is not None: 
     1074            return self._inverse 
     1075        return self.__class__( self._invert_on_basis, 
     1076                domain = self.domain(),               codomain = self.codomain(), 
     1077                unitriangular = self._unitriangular,  triangular = self._triangular, 
     1078                cmp = self._cmp, 
     1079                inverse = self,                       category = self.category_for()) 
     1080 
     1081    def _invert_on_basis(self, i): 
     1082        r""" 
     1083        Returns the image, by the inverse of ``self``, of the basis element 
     1084        indexed by ``i``. 
     1085 
     1086        TESTS:: 
     1087            sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); x = X.basis() 
     1088            sage: Y = CombinatorialFreeModule(QQ, [1, 2, 3]); y = Y.basis() 
     1089            sage: uut = lambda i: sum(  y[j] for j in range(i,4)  ) # uni-upper 
     1090            sage: phi = X.module_morphism(uut, triangular=True, codomain = Y) 
     1091            sage: phi._invert_on_basis(2) 
     1092            B[2] - B[3] 
     1093        """ 
     1094        return self.preimage( self.codomain().monomial(i) ) 
     1095 
     1096    def preimage(self, f): 
     1097        """ 
     1098        Returns the image of f by the inverse of ``self``. 
     1099 
     1100        TESTS:: 
     1101            sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); x = X.basis() 
     1102            sage: Y = CombinatorialFreeModule(QQ, [1, 2, 3]); y = Y.basis() 
     1103            sage: uut = lambda i: sum(  y[j] for j in range(i,4)  ) # uni-upper 
     1104            sage: phi = X.module_morphism(uut, triangular=True, codomain = Y) 
     1105            sage: phi.preimage(y[1] + y[2]) 
     1106            B[1] - B[3] 
     1107        """ 
     1108        F = self.domain() 
     1109        G = self.codomain() 
     1110        map = self._on_basis 
     1111        assert f in G 
     1112 
     1113        remainder = f 
     1114 
     1115        out = F.zero() 
     1116        while not remainder.is_zero(): 
     1117            (j,c) = self._dominant_item(remainder) 
     1118 
     1119            s = map(j) 
     1120            assert j == self._dominant_item(s)[0] 
     1121 
     1122            if not self._unitriangular: 
     1123                c /= s[j] 
     1124 
     1125            remainder -= s._lmul_(c) 
     1126            out       += F.term(j, c) 
     1127 
     1128        return out 
     1129 
    6041130class DiagonalModuleMorphism(ModuleMorphismByLinearity): 
    6051131    """ 
    6061132    A class for diagonal module morphisms. 
    class DiagonalModuleMorphism(ModuleMorph 
    6631189        TESTS:: 
    6641190 
    6651191            sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() 
    666             sage: Y = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("Y"); y = Y.basis() 
     1192            sage: Y = CombinatorialFreeModule(QQ, [1, 2, 3]); Y.rename("Y"); y = Y.basis() 
    6671193            sage: phi = X.module_morphism(diagonal = factorial, codomain = X) 
    6681194            sage: phi._on_basis(3) 
    6691195            6*B[3] 
    class DiagonalModuleMorphism(ModuleMorph 
    6771203        EXAMPLES:: 
    6781204 
    6791205            sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() 
    680             sage: Y = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("Y"); y = Y.basis() 
     1206            sage: Y = CombinatorialFreeModule(QQ, [1, 2, 3]); Y.rename("Y"); y = Y.basis() 
    6811207            sage: phi = X.module_morphism(diagonal = factorial, codomain = X) 
    6821208            sage: phi_inv = ~phi 
    6831209            sage: phi_inv 
    class PointwiseInverseFunction(SageObjec 
    7571283            sage: f = PointwiseInverseFunction(factorial) 
    7581284            sage: f(0), f(1), f(2), f(3) 
    7591285            (1, 1, 1/2, 1/6) 
    760             sage: loads(dumps(f)) == f 
    761             True 
     1286            sage: TestSuite(f).run() 
    7621287        """ 
    7631288        self._pointwise_inverse = f 
    7641289 
  • sage/misc/misc_c.pyx

    diff --git a/sage/misc/misc_c.pyx b/sage/misc/misc_c.pyx
    a b def prod(x, z=None, Py_ssize_t recursion 
    124124        prod = z*prod 
    125125         
    126126    return prod 
    127      
     127 
    128128 
    129129cdef balanced_list_prod(L, Py_ssize_t offset, Py_ssize_t count, Py_ssize_t cutoff): 
    130130    """ 
  • sage/misc/sage_itertools.py

    diff --git a/sage/misc/sage_itertools.py b/sage/misc/sage_itertools.py
    a b def unique_merge(*lists): 
    2828    """ 
    2929    return (k for k,g in itertools.groupby(heapq.merge(*lists))) 
    3030 
     31def min_cmp(L, cmp=None): 
     32    """ 
     33    Returns the smallest item of a list (or iterable) with respect to 
     34    a comparison function. 
     35 
     36    INPUT: 
     37        ``L``   -- an iterable 
     38        ``cmp`` -- an optional comparison function. 
     39 
     40    ``cmp(x, y)`` should return a negative value if `x < y`, `0` if 
     41    `x == y`, and a positive value if `x > y`. 
     42 
     43    OUTPUT: the smallest item of ``L`` with respect to ``cmp``. 
     44 
     45    EXAMPLES:: 
     46 
     47        sage: from sage.misc.sage_itertools import min_cmp 
     48        sage: L = [1,-1,3,-1,3,2] 
     49        sage: min_cmp(L) 
     50        -1 
     51        sage: def mycmp(x,y): return y - x 
     52        sage: min_cmp(L, mycmp) 
     53        3 
     54 
     55    The input can be any iterable:: 
     56 
     57        sage: min_cmp( (x^2 for x in L) ) 
     58        1 
     59        sage: min_cmp( (x^2 for x in L), mycmp) 
     60        9 
     61 
     62    Computing the min of an empty iterable raises and error:: 
     63 
     64        sage: min_cmp([]) 
     65        Traceback (most recent call last): 
     66        ... 
     67        ValueError: min() arg is an empty sequence 
     68        sage: min_cmp([], mycmp) 
     69        Traceback (most recent call last): 
     70        ... 
     71        ValueError: min_cmp() arg is an empty sequence 
     72    """ 
     73    if cmp is None: 
     74        return min(L) # Resort to Python's standard min 
     75 
     76    iterator = iter(L) 
     77    try: 
     78        m = iterator.next() 
     79    except StopIteration: 
     80        raise ValueError, "min_cmp() arg is an empty sequence" 
     81    for item in iterator: 
     82        if cmp(item, m) < 0: 
     83            m = item 
     84    return m 
     85 
     86def max_cmp(L, cmp=None): 
     87    """ 
     88    Returns the largest item of a list (or iterable) with respect to a 
     89    comparison function. 
     90 
     91    INPUT: 
     92        ``L``   -- an iterable 
     93        ``cmp`` -- an optional comparison function. 
     94 
     95    ``cmp(x, y)`` should return a negative value if `x < y`, `0` if 
     96    `x == y`, and a positive value if `x > y`. 
     97 
     98    OUTPUT: the largest item of ``L`` with respect to ``cmp``. 
     99 
     100    EXAMPLES:: 
     101 
     102        sage: from sage.misc.sage_itertools import max_cmp 
     103        sage: L = [1,-1,3,-1,3,2] 
     104        sage: max_cmp(L) 
     105        3 
     106        sage: def mycmp(x,y): return y - x 
     107        sage: max_cmp(L, mycmp) 
     108        -1 
     109 
     110    The input can be any iterable:: 
     111 
     112        sage: max_cmp( (x^2 for x in L) ) 
     113        9 
     114        sage: max_cmp( (x^2 for x in L), mycmp) 
     115        1 
     116 
     117    Computing the max of an empty iterable raises and error:: 
     118 
     119        sage: max_cmp([]) 
     120        Traceback (most recent call last): 
     121        ... 
     122        ValueError: max() arg is an empty sequence 
     123        sage: max_cmp([], mycmp) 
     124        Traceback (most recent call last): 
     125        ... 
     126        ValueError: max_cmp() arg is an empty sequence 
     127    """ 
     128    if cmp is None: 
     129        return max(L) # Resort to Python's standard max 
     130 
     131    iterator = iter(L) 
     132    try: 
     133        m = iterator.next() 
     134    except StopIteration: 
     135        raise ValueError, "max_cmp() arg is an empty sequence" 
     136    for item in iterator: 
     137        if cmp(item, m) > 0: 
     138            m = item 
     139    return m