Ticket #9651: trac_9651_CombinatorialFreeModule_Addition-cs.patch

File trac_9651_CombinatorialFreeModule_Addition-cs.patch, 21.4 KB (added by stumpc5, 11 years ago)
  • module_list.py

    # HG changeset patch
    # User Christian Stump <christian.stump@univie.ac.at>
    # Date 1281041363 25200
    # Node ID 4e32b2a6222d9c27fcbdd9308b9954b2e572b491
    # Parent  8a631e73ed0b24061f915394c1403e0832b18f64
    * * *
    Trac 9651: Improved speed for CombinatorialFreeModule
    
    diff -r 881b0e4072e2 module_list.py
    a b  
    203203    Extension('sage.combinat.permutation_cython',
    204204              sources=['sage/combinat/permutation_cython.pyx']),
    205205
     206    Extension('sage.combinat.dict_addition',
     207              sources=['sage/combinat/dict_addition.pyx']),
     208
    206209    ################################
    207210    ##
    208211    ## sage.crypto
  • new file sage/combinat/dict_addition.pyx

    diff -r 881b0e4072e2 sage/combinat/dict_addition.pyx
    - +  
     1"""
     2Provides function to add dictionaries pointwise with values in a common ring and to compute linear combinations
     3"""
     4#*****************************************************************************
     5#       Copyright (C) 2010 Christian Stump christian.stump@univie.ac.at
     6#
     7#  Distributed under the terms of the GNU General Public License (GPL)
     8#                  http://www.gnu.org/licenses/
     9#*****************************************************************************
     10
     11def remove_zeros( dict D ):
     12    """
     13    deletes all (key,0) in D
     14   
     15    EXAMPLES:
     16        sage: from sage.combinat.dict_addition import remove_zeros
     17        sage: D = { 0:1, 1:1, 2:0 }; D
     18        {0: 1, 1: 1, 2: 0}
     19        sage: remove_zeros( D ); D
     20        {0: 1, 1: 1}
     21    """
     22    for_removal = [ key for key, value in D.iteritems() if value == 0 ]
     23    for key in for_removal:
     24        del D[key]   
     25
     26def dict_addition( dict_iter ):
     27    """
     28    returns the pointwise addition of dictionaries with coefficients
     29   
     30    INPUT:
     31        dict_iter         -- iterator of dictionaries with values in a common ring R
     32       
     33    OUTPUT:
     34        dictionary containing all keys of dictionaries in dict_list (and non-zero values) being the the sum of the values in the different
     35        dictionaries
     36   
     37    EXAMPLES:
     38        sage: from sage.combinat.dict_addition import dict_addition
     39        sage: from sage.combinat.dict_addition import remove_zeros
     40        sage: D = { 0:1, 1:1 }; D
     41        {0: 1, 1: 1}
     42        sage: dict_addition( D for _ in range(5) )
     43        {0: 5, 1: 5}
     44    """
     45    cdef dict D, D_tmp
     46
     47    D = {}
     48    for D_tmp in dict_iter:
     49        if D == {}:
     50            D = D_tmp.copy()
     51        else:   
     52            for key, value in D_tmp.iteritems():
     53                if key in D:
     54                    D[ key ] += value
     55                else:
     56                    D[ key ]  = value
     57    remove_zeros( D )
     58    return D
     59
     60def dict_linear_combination( dict_factor_iter, factor_on_left=True ):
     61    """
     62    returns the pointwise addition of dictionaries with coefficients
     63   
     64    INPUT:
     65        dict_factor_iter            -- iterator of pairs D, coeff, where
     66                                        the D's are dictionaries with values in a common ring R
     67                                        the coeff's are coefficients in R
     68        factor_on_left(optional)    -- if True, the coefficients are multiplied on the left, otherwise they are multiplied on the right
     69       
     70    OUTPUT:
     71        dictionary containing all keys of dictionaries in dict_list (and non-zero values) being the the sum of the values in the different
     72        dictionaries each one first multiplied by the given factor
     73   
     74    EXAMPLES:
     75        sage: from sage.combinat.dict_addition import dict_linear_combination
     76        sage: from sage.combinat.dict_addition import remove_zeros
     77        sage: D = { 0:1, 1:1 }; D
     78        {0: 1, 1: 1}
     79        sage: dict_linear_combination( (D,i) for i in range(5) )
     80        {0: 10, 1: 10}
     81        sage: dict_linear_combination( [(D,1),(D,-1)] )
     82        {}
     83    """
     84    D = {}
     85    for D_tmp, fac_tmp in dict_factor_iter:
     86        if D == {} and fac_tmp == 1:
     87            D = D_tmp.copy()
     88        elif fac_tmp == 1:
     89            for key, value in D_tmp.iteritems():
     90                if key in D:
     91                    D[ key ] += value
     92                else:
     93                    D[ key ]  = value
     94        elif fac_tmp == -1:
     95            for key, value in D_tmp.iteritems():
     96                if key in D:
     97                    D[ key ]  -= value
     98                else:
     99                    D[ key ]   = -value
     100        else:
     101            if factor_on_left:
     102                for key, value in D_tmp.iteritems():
     103                    if key in D:
     104                        D[ key ] += fac_tmp * value
     105                    else:
     106                        D[ key ]  = fac_tmp * value
     107            else:
     108                for key, value in D_tmp.iteritems():
     109                    if key in D:
     110                        D[ key ] += value * fac_tmp
     111                    else:
     112                        D[ key ]  = value * fac_tmp
     113
     114    remove_zeros( D )
     115    return D
  • sage/combinat/free_module.py

    diff -r 881b0e4072e2 sage/combinat/free_module.py
    a b  
    44#*****************************************************************************
    55#       Copyright (C) 2007      Mike Hansen <mhansen@gmail.com>,
    66#                     2007-2009 Nicolas M. Thiery <nthiery at users.sf.net>
     7#                     2010      Christian Stump <christian.stump@univie.ac.at>
    78#
    89#  Distributed under the terms of the GNU General Public License (GPL)
    910#                  http://www.gnu.org/licenses/
     
    2526from sage.misc.cachefunc import cached_method
    2627from sage.misc.all import lazy_attribute
    2728from sage.categories.poor_man_map import PoorManMap
     29from sage.combinat.dict_addition import dict_addition, dict_linear_combination
    2830
    2931# TODO: move the content of this class to CombinatorialFreeModule.Element and ModulesWithBasis.Element
    3032class CombinatorialFreeModuleElement(Element):
     
    196198        w.sort()
    197199        return cmp(v, w)
    198200
    199     def _add_(self, y):
     201    def _add_(self, other):
    200202        """
    201203        EXAMPLES::
    202204
     
    214216            sage: len(a.monomial_coefficients())
    215217            1
    216218        """
    217         A = self.parent()
    218         BR = A.base_ring()
    219         z_elt = dict(self._monomial_coefficients)
    220         for m, c in y._monomial_coefficients.iteritems():
    221             if z_elt.has_key(m):
    222                 cm = z_elt[m] + c
    223                 if cm == 0:
    224                     del z_elt[m]
    225                 else:
    226                     z_elt[m] = cm
    227             else:
    228                 z_elt[m] = c
    229219
     220        assert hasattr( other, 'parent' ) and other.parent() == self.parent()
    230221
    231         #Remove all entries that are equal to 0
    232         del_list = []
    233         zero = BR(0)
    234         for m, c in z_elt.iteritems():
    235             if c == zero:
    236                 del_list.append(m)
    237         for m in del_list:
    238             del z_elt[m]
    239 
    240         return A._from_dict(z_elt)
    241 
     222        F = self.parent()
     223        return F._from_dict( dict_addition( [ self._monomial_coefficients, other._monomial_coefficients ] ) )
    242224
    243225    def _neg_(self):
    244226        """
     
    256238            sage: -s([2,1]) # indirect doctest
    257239            -s[2, 1]
    258240        """
    259         return self.map_coefficients(lambda c: -c)
     241        F = self.parent()
     242        return F._from_dict( dict_linear_combination( [ ( self._monomial_coefficients, -1 ) ] ) )
    260243
    261 
    262     def _sub_(self, y):
     244    def _sub_(self, other):
    263245        """
    264246        EXAMPLES::
    265247
     
    274256            sage: s([2,1]) - s([5,4]) # indirect doctest
    275257            s[2, 1] - s[5, 4]
    276258        """
    277         A = self.parent()
    278         BR = A.base_ring()
    279         z_elt = dict(self._monomial_coefficients)
    280         for m, c in y._monomial_coefficients.iteritems():
    281             if z_elt.has_key(m):
    282                 cm = z_elt[m] - c
    283                 if cm == 0:
    284                     del z_elt[m]
    285                 else:
    286                     z_elt[m] = cm
    287             else:
    288                 z_elt[m] = -c
    289 
    290         #Remove all entries that are equal to 0
    291         zero = BR(0)
    292         del_list = []
    293         for m, c in z_elt.iteritems():
    294             if c == zero:
    295                 del_list.append(m)
    296         for m in del_list:
    297             del z_elt[m]
    298 
    299         return A._from_dict(z_elt)
    300 
     259        assert hasattr( other, 'parent' ) and other.parent() == self.parent()
     260        F = self.parent()
     261        return F._from_dict( dict_linear_combination( [ ( self._monomial_coefficients, 1 ), (other._monomial_coefficients, -1 ) ] ) )
    301262
    302263    def _coefficient_fast(self, m, default=None):
    303264        """
     
    401362            True
    402363        """
    403364        BR = self.parent().base_ring()
    404         return all(v == BR(0) for v in self._monomial_coefficients.values())
     365        zero = BR( 0 )
     366        return all( v == zero for v in self._monomial_coefficients.values() )
    405367
    406368    def __len__(self):
    407369        """
     
    445407            sage: z.length()
    446408            4
    447409        """
    448         return len([mon for mon,coeff in self._monomial_coefficients.items() if coeff !=0 ])
     410        BR = self.parent().base_ring()
     411        zero = BR( 0 )
     412        return len( [ key for key, coeff in self._monomial_coefficients.iteritems() if coeff != zero ] )
    449413
    450414    def support(self):
    451415        """
     
    467431            sage: z.support()
    468432            [[1], [1, 1, 1], [2, 1], [4]]
    469433        """
    470         v = self._monomial_coefficients.items()
    471         v.sort()
    472         mons = [ m for (m, _) in v ]
    473         # Usually we test the coeff for being non zero, why not here?
    474         # The same question arises in the following functions.
    475         return mons
     434        BR = self.parent().base_ring()
     435        zero = BR( 0 )
     436
     437        supp = [ key for key, coeff in self._monomial_coefficients.iteritems() if coeff != zero ]
     438        supp.sort()
     439       
     440        return supp
    476441
    477442    def monomials(self):
    478443        """
     
    485450            [B['a'], B['c']]
    486451        """
    487452        P = self.parent()
    488         one = P.base_ring()(1)
    489         v = self._monomial_coefficients.items()
    490         v.sort()
    491         return [P._from_dict({key:one}) for key,value in v]
     453        BR = P.base_ring()
     454        zero = BR( 0 )
     455        one = BR( 1 )
     456
     457        supp = [ key for key, coeff in self._monomial_coefficients.iteritems() if coeff != zero ]
     458        supp.sort()
     459       
     460        return [ P._from_dict( { key : one } ) for key in supp ]
    492461
    493462    def terms(self):
    494463        """
     
    500469            sage: f.terms()
    501470            [B['a'], 2*B['c']]
    502471        """
    503         P = self.parent()
    504         v = self._monomial_coefficients.items()
     472        BR = self.parent().base_ring()
     473        zero = BR( 0 )
     474        v = [ ( key, value ) for key, value in self._monomial_coefficients.iteritems() if value != zero ]
    505475        v.sort()
    506         return [P._from_dict({key:value}) for key,value in v]
     476        from_dict = self.parent()._from_dict
     477        return [ from_dict( { key : value } ) for key,value in v ]
    507478
    508479    def coefficients(self):
    509480        """
     
    525496            sage: z.coefficients()
    526497            [1, 1, 1, 1]
    527498        """
    528         v = self._monomial_coefficients.items()
     499        BR = self.parent().base_ring()
     500        zero = BR( 0 )
     501        v = [ ( key, value ) for key, value in self._monomial_coefficients.iteritems() if value != zero ]
    529502        v.sort()
    530         cffs = [ c for (_, c) in v ]
    531         return cffs
     503        return [ value for key,value in v ]
    532504
    533505    def _vector_(self, new_base_ring=None):
    534506        """
     
    560532        if new_base_ring is None:
    561533            new_base_ring = parent.base_ring()
    562534        # FIXME: should return a sparse vector
    563         return vector(new_base_ring,
     535        return vector(new_base_ring, 
    564536                      [new_base_ring(self._monomial_coefficients.get(m, 0))
    565537                       for m in list(cc)])
    566538
     
    587559        """
    588560        return self._vector_()
    589561
    590 
    591 
    592562    def _acted_upon_(self, scalar, self_on_left = False):
    593563        """
    594564        Returns the action of a scalar on self
     
    649619        # enough information to detect apriori that this method only
    650620        # accepts scalars; so it tries on some elements(), and we need
    651621        # to make sure to report an error.
    652         if scalar.parent() != self.base_ring():
     622        if hasattr( scalar, 'parent' ) and scalar.parent() != self.base_ring():
    653623            # Temporary needed by coercion (see Polynomial/FractionField tests).
    654624            if self.base_ring().has_coerce_map_from(scalar.parent()):
    655                 scalar = self.base_ring()(scalar)
     625                scalar = self.base_ring()( scalar )
    656626            else:
    657627                return None
     628
     629        F = self.parent()
     630        D = self._monomial_coefficients
    658631        if self_on_left:
    659             return self.map_coefficients(lambda c: c*scalar)
     632            D = dict_linear_combination( [ ( D, scalar ) ], factor_on_left = False )
    660633        else:
    661             return self.map_coefficients(lambda c: scalar*c)
     634            D = dict_linear_combination( [ ( D, scalar ) ] )
     635
     636        return F._from_dict( D )
    662637
    663638    # For backward compatibility
    664639    _lmul_ = _acted_upon_
    665640    _rmul_ = _acted_upon_
    666641
    667     def __div__(self, x):
     642    def __div__(self, x, self_on_left=False ):
    668643        """
    669644        Division by coefficients
    670645
     
    684659            B[2] + 2*B[3]
    685660        """
    686661        if self.base_ring().is_field():
    687             x = self.base_ring()(x)
    688             return self.map_coefficients(lambda c: c/x)
     662            F = self.parent()
     663            x = self.base_ring()( x )
     664            x_inv = x**-1
     665            D = self._monomial_coefficients
     666            if self_on_left:
     667                D = dict_linear_combination( [ ( D, x_inv ) ], factor_on_left=False )
     668            else:
     669                D = dict_linear_combination( [ ( D, x_inv ) ] )
     670
     671            return F._from_dict( D )
    689672        else:
    690673            return self.map_coefficients(lambda c: _divide_if_possible(c, x))
    691674
     
    12621245        if c: return c
    12631246        return 0
    12641247
    1265     def _apply_module_morphism(self, x, f):
     1248    def _apply_module_morphism( self, x, on_basis, codomain=False ):
    12661249        """
    12671250        Returns the image of x under the module morphism defined by
    12681251        extending f by linearity.
     
    12721255
    12731256        - ``x`` : a element of self
    12741257
    1275         - ``f``f - a function that takes in a combinatorial
     1258        - ``on_basis`` - a function that takes in a combinatorial
    12761259          object indexing a basis element and returns an element of the
    1277           target domain
     1260          codomain
     1261         
     1262        - ``codomain`` (optional) - the codomain of the morphism, otherwise it is computed using on_basis
    12781263
    12791264
    12801265        EXAMPLES::
     
    12821267            sage: s = SFASchur(QQ)
    12831268            sage: a = s([3]) + s([2,1]) + s([1,1,1])
    12841269            sage: b = 2*a
    1285             sage: f = lambda part: len(part)
     1270            sage: f = lambda part: Integer( len(part) )
    12861271            sage: s._apply_module_morphism(a, f) #1+2+3
    12871272            6
    12881273            sage: s._apply_module_morphism(b, f) #2*(1+2+3)
    12891274            12
    12901275        """
    1291         res = 0
    1292         for m, c in x._monomial_coefficients.iteritems():
    1293             res += c*f(m)
    1294         return res
    12951276
     1277        if x == self.zero():
     1278            if not codomain:
     1279                B = self.basis()
     1280                keys = list( B.keys() )
     1281                if len( keys ) > 0:
     1282                    key = keys[0]
     1283                    codomain = on_basis( key ).parent()
     1284                else:
     1285                    raise ValueError, 'Codomain could not be determined'
    12961286
    1297     def _apply_module_endomorphism(self, a, f):
     1287            return codomain.zero()
     1288                   
     1289        else:
     1290            if not codomain:
     1291                keys = x.support()
     1292                key = keys[0]
     1293                codomain = on_basis( key ).parent()
     1294
     1295            if hasattr( codomain, 'linear_combination' ):
     1296                return codomain.linear_combination( ( on_basis( key ), coeff ) for key, coeff in x._monomial_coefficients.iteritems() )
     1297            else:
     1298                return_sum = codomain.zero()
     1299                for key, coeff in x._monomial_coefficients.iteritems():
     1300                    return_sum += coeff * on_basis( key )
     1301                return return_sum
     1302
     1303    def _apply_module_endomorphism(self, x, on_basis):
    12981304        """
    12991305        This takes in a function from the basis elements to the elements of
    13001306        self and applies it linearly to a. Note that
     
    13081314            sage: s._apply_module_endomorphism( s([2,1]) + s([1,1,1]), f)
    13091315            2*s[2, 1] + 2*s[3]
    13101316        """
    1311         mcs = a.monomial_coefficients()
    1312         base_ring = self.base_ring()
    1313         zero = base_ring(0)
     1317        return self.linear_combination( ( on_basis( key ), coeff ) for key, coeff in x._monomial_coefficients.iteritems() )
    13141318
    1315         z_elt = {}
    1316         for basis_element in mcs:
    1317             f_mcs = f(basis_element).monomial_coefficients()
    1318             for f_basis_element in f_mcs:
    1319                 z_elt[ f_basis_element ] = z_elt.get(f_basis_element, zero) + mcs[basis_element]*f_mcs[f_basis_element]
     1319    def sum( self, iter_of_elements ):
     1320        """
     1321        overrides method inherited from commutative additive monoid as it is much faster on dicts directly
     1322       
     1323        INPUT:
     1324         - iter_of_elements: iterator of elements of self
    13201325
    1321         return self._from_dict(z_elt)
     1326        Returns the sum of all elements in iter_of_elements
     1327
     1328        EXAMPLES::
     1329            sage: F = CombinatorialFreeModule(QQ,[1,2])
     1330            sage: f = F.an_element(); f
     1331            2*B[1] + 2*B[2]
     1332            sage: F.sum( f for _ in range(5) )
     1333            10*B[1] + 10*B[2]
     1334        """
     1335   
     1336        D = dict_addition( element._monomial_coefficients for element in iter_of_elements )
     1337        return self._from_dict( D )
     1338
     1339    def linear_combination( self, iter_of_elements_coeff, factor_on_left=True ):
     1340        """
     1341        INPUT:
     1342         - iter_of_elements_coeff   -- iterator of pairs ( element, coeff ) with element in self and coeff in self.base_ring()
     1343         - factor_on_left (optional)- if True, the coefficients are multiplied from the left
     1344                                      if False, the coefficients are multiplied from the right
     1345
     1346        Returns the linear combination lambda_1 v_1 + ... + lambda_k v_k resp.
     1347                the linear combination v_1 lambda_1 + ... + v_k lambda_k where
     1348                list_of_elements = [ v_1, ..., v_k ]
     1349                list_of_coefficients = [ lambda_1, ..., lambda_k ]
     1350
     1351        EXAMPLES::
     1352            sage: F = CombinatorialFreeModule(QQ,[1,2])
     1353            sage: f = F.an_element(); f
     1354            2*B[1] + 2*B[2]
     1355            sage: F.linear_combination( (f,i) for i in range(5) )
     1356            20*B[1] + 20*B[2]
     1357        """
     1358        return self._from_dict( dict_linear_combination( ( ( element._monomial_coefficients, coeff ) for element, coeff in iter_of_elements_coeff ), factor_on_left=factor_on_left ) )
    13221359
    13231360    def term(self, index, coeff=None):
    13241361        """
     
    13411378        """
    13421379        if coeff is None:
    13431380            coeff = self.base_ring().one()
    1344         return self._from_dict({index: coeff})
     1381        return self._from_dict( {index: coeff} )
    13451382
    13461383    def _monomial(self, index):
    13471384        """
     
    13511388            sage: F._monomial('a')
    13521389            B['a']
    13531390        """
    1354         return self.term(index, self.base_ring().one())
     1391        return self.term( index, self.base_ring().one() )
    13551392
    13561393    # This is generic, and should be lifted into modules_with_basis
    13571394    @lazy_attribute
     
    14631500        """
    14641501        return self._from_dict({})
    14651502
    1466     def _from_dict(self, d, coerce=False):
     1503    def _from_dict( self, d, coerce=False, remove_zeros=True ):
    14671504        """
    14681505        Given a monomial coefficient dictionary ``d``, returns the
    14691506        element of self with those monomials
     
    14881525            Rational Field
    14891526        """
    14901527        assert isinstance(d, dict)
     1528
     1529        R = self.base_ring()
     1530        zero = R( 0 )
     1531        for_removal = []
     1532
    14911533        if coerce:
    1492             R = self.base_ring()
    1493             # FIXME: this should remove zero entries
    1494             d = dict((m,R(c)) for m,c in d.iteritems())
    1495 
    1496         return self.element_class(self, d)
     1534            D = d.copy()
     1535            for key, coeff in D.iteritems():
     1536                if remove_zeros and coeff == zero:
     1537                    for_removal.append( key )
     1538                else:
     1539                    D[ key ] = R( coeff )
     1540            for key in for_removal:
     1541                del D[ key ]
     1542            return self.element_class( self, D )   
     1543        elif remove_zeros:
     1544            D = d.copy()
     1545            for key, coeff in D.iteritems():
     1546                if coeff == zero:
     1547                    for_removal.append( key )
     1548            for key in for_removal:
     1549                del D[ key ]
     1550            return self.element_class( self, D )
     1551        else:
     1552            return self.element_class( self, d )
    14971553
    14981554
    14991555class CombinatorialFreeModule_Tensor(CombinatorialFreeModule):
  • sage/combinat/sf/powersum.py

    diff -r 881b0e4072e2 sage/combinat/sf/powersum.py
    a b  
    8989        """
    9090        p = self.parent()
    9191        if 1 not in part:
    92             return 0
     92            return p(0)
    9393        else:
    9494            return len([i for i in part if i == 1])*p(part[:-1])
    9595