Ticket #9651: trac_9651_CombinatorialFreeModule_Addition.patch

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

    # HG changeset patch
    # User Christian Stump <christian.stump@univie.ac.at>
    # Date 1280599740 14400
    # Node ID ea28a0eac15b62fc30c3f6793d5454a2691d9d41
    # Parent  76441d22e8cb279d306245aea44fd972783aa9a4
    Trac 9651: Improved speed for CombinatorialFreeModule
    
    diff -r 76441d22e8cb -r ea28a0eac15b 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
  • sage/categories/modules_with_basis.py

    diff -r 76441d22e8cb -r ea28a0eac15b sage/categories/modules_with_basis.py
    a b  
    11611161
    11621162        Todo: add more tests for multi-parameter module morphisms.
    11631163        """
     1164        domain = self.domain()
     1165        codomain = self.codomain()
     1166
    11641167        before = args[0:self._position]
    11651168        after = args[self._position+1:len(args)]
    11661169        x = args[self._position]
    1167         assert(x.parent() is self.domain())
     1170        assert( x.parent() is domain )
    11681171
    1169         if self.codomain() in ModulesWithBasis( self.codomain().base_ring() ):
    1170             return sum([self._on_basis(*(before+(index,)+after))._lmul_(coeff) for (index, coeff) in args[self._position]], self._zero)
     1172
     1173
     1174        if codomain in ModulesWithBasis( codomain.base_ring() ):
     1175            if hasattr( codomain, 'sum' ):
     1176                return codomain.sum([self._on_basis(*(before+(index,)+after))._lmul_(coeff) for (index, coeff) in args[self._position]], self._zero)
     1177            else:
     1178                return sum([self._on_basis(*(before+(index,)+after))._lmul_(coeff) for (index, coeff) in args[self._position]], self._zero)
    11711179        else:
    11721180            return sum([ coeff * self._on_basis(*(before+(index,)+after)) for (index, coeff) in args[self._position]], self._zero)
    11731181
  • new file sage/combinat/dict_addition.pyx

    diff -r 76441d22e8cb -r ea28a0eac15b sage/combinat/dict_addition.pyx
    - +  
     1"""
     2Provides function to add dictionaries pointwise with values in a common ring
     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 dict_addition( list dict_list, list factor_list = [], bool diff_keys = False, bool remove_zeros = False, bool factor_on_left = True ):
     12    """
     13    returns the pointwise addition of dictionaries with coefficients
     14   
     15    INPUT:
     16        dict_list               - list of dictionaries with values in a common ring R
     17        factor_list (optional)  - list of coefficients in R
     18       
     19    OUTPUT:
     20        dictionary containing all keys of dictionaries in dict_list and non-zero values being the the sum of the values in the different
     21        dictionaries each one first multiplied by the given factor
     22    """
     23    cdef dict D, D_tmp
     24
     25    if factor_list == []:
     26        D = dict_list[0].copy()
     27        for D_tmp in dict_list[1:]:
     28            for key, value in D_tmp.iteritems():
     29                if diff_keys or key not in D:
     30                    D[ key ]  = value
     31                else:
     32                    D[ key ] += value
     33    else:
     34        assert len( dict_list ) == len( factor_list )
     35       
     36        if 1 in factor_list:
     37            i = factor_list.index( 1 )
     38            D = dict_list[ i ].copy()
     39            del factor_list[ i ]
     40            del dict_list[ i ]
     41        else:
     42            D = {}
     43        for i in range( len( factor_list ) ):
     44            D_tmp = dict_list[ i ]
     45            fac_tmp = factor_list[ i ]
     46           
     47            if fac_tmp == 1:
     48                for key, value in D_tmp.iteritems():
     49                    if diff_keys or key not in D:
     50                        D[ key ]  = value
     51                    else:
     52                        D[ key ] += value
     53            elif fac_tmp == -1:
     54                for key, value in D_tmp.iteritems():
     55                    if diff_keys or key not in D:
     56                        D[ key ]  = -value
     57                    else:
     58                        D[ key ]  -= value
     59            else:
     60                if factor_on_left:
     61                    for key, value in D_tmp.iteritems():
     62                        if diff_keys or key not in D:
     63                            D[ key ]  = fac_tmp * value
     64                        else:
     65                            D[ key ] += fac_tmp * value
     66
     67                else:
     68                    for key, value in D_tmp.iteritems():
     69                        if diff_keys or key not in D:
     70                            D[ key ]  = value * fac_tmp
     71                        else:
     72                            D[ key ] += value * fac_tmp
     73    if remove_zeros:
     74        for_removal = [ key for key, value in D.iteritems() if not value ]
     75        for key in for_removal:
     76            del D[key]
     77
     78    return D
  • sage/combinat/free_module.py

    diff -r 76441d22e8cb -r ea28a0eac15b 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
    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]
     222        F = self.parent()
    239223
    240         return A._from_dict(z_elt)
     224        D1 = self._monomial_coefficients
     225        D2 = other._monomial_coefficients
    241226
     227        D = dict_addition( [ D1, D2 ] )
     228
     229        return F._from_dict( D )
    242230
    243231    def _neg_(self):
    244232        """
     
    256244            sage: -s([2,1]) # indirect doctest
    257245            -s[2, 1]
    258246        """
    259         return self.map_coefficients(lambda c: -c)
     247        F = self.parent()
     248        D = self._monomial_coefficients
     249        D = dict_addition( [ D ], [ -1 ] )
     250       
     251        return F._from_dict( D, remove_zeros=False )
    260252
    261 
    262     def _sub_(self, y):
     253    def _sub_(self, other):
    263254        """
    264255        EXAMPLES::
    265256
     
    274265            sage: s([2,1]) - s([5,4]) # indirect doctest
    275266            s[2, 1] - s[5, 4]
    276267        """
    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
     268        assert hasattr( other, 'parent' ) and other.parent() == self.parent()
     269        F = self.parent()
     270        D1 = self._monomial_coefficients
     271        D2 = other._monomial_coefficients
    289272
    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]
     273        D = dict_addition( [ D1, D2 ], [ 1, -1 ] )
    298274
    299         return A._from_dict(z_elt)
    300 
     275        return F._from_dict( D )
    301276
    302277    def _coefficient_fast(self, m, default=None):
    303278        """
     
    401376            True
    402377        """
    403378        BR = self.parent().base_ring()
    404         return all(v == BR(0) for v in self._monomial_coefficients.values())
     379        zero = BR( 0 )
     380        return all( v == zero for v in self._monomial_coefficients.values() )
    405381
    406382    def __len__(self):
    407383        """
     
    445421            sage: z.length()
    446422            4
    447423        """
    448         return len([mon for mon,coeff in self._monomial_coefficients.items() if coeff !=0 ])
     424        BR = self.parent().base_ring()
     425        zero = BR( 0 )
     426        return len( [ key for key, coeff in self._monomial_coefficients.iteritems() if coeff != zero ] )
    449427
    450428    def support(self):
    451429        """
     
    467445            sage: z.support()
    468446            [[1], [1, 1, 1], [2, 1], [4]]
    469447        """
    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
     448        BR = self.parent().base_ring()
     449        zero = BR( 0 )
     450
     451        supp = [ key for key, coeff in self._monomial_coefficients.iteritems() if coeff != zero ]
     452        supp.sort()
     453       
     454        return supp
    476455
    477456    def monomials(self):
    478457        """
     
    484463            sage: f.monomials()
    485464            [B['a'], B['c']]
    486465        """
    487         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]
     466        BR = self.parent().base_ring()
     467        zero = BR( 0 )
     468        one = BR( 1 )
     469
     470        supp = [ key for key, coeff in self._monomial_coefficients.iteritems() if coeff != zero ]
     471        supp.sort()
     472       
     473        return [ P._from_dict( { key : one } ) for key in supp ]
    492474
    493475    def terms(self):
    494476        """
     
    500482            sage: f.terms()
    501483            [B['a'], 2*B['c']]
    502484        """
    503         P = self.parent()
    504         v = self._monomial_coefficients.items()
     485        BR = self.parent().base_ring()
     486        zero = BR( 0 )
     487        v = [ ( key, value ) for key, value in self._monomial_coefficients.iteritems() if value != zero ]
    505488        v.sort()
    506         return [P._from_dict({key:value}) for key,value in v]
     489        return [ P._from_dict( { key : value } ) for key,value in v ]
    507490
    508491    def coefficients(self):
    509492        """
     
    525508            sage: z.coefficients()
    526509            [1, 1, 1, 1]
    527510        """
    528         v = self._monomial_coefficients.items()
     511        BR = self.parent().base_ring()
     512        zero = BR( 0 )
     513        v = [ ( key, value ) for key, value in self._monomial_coefficients.iteritems() if value != zero ]
    529514        v.sort()
    530         cffs = [ c for (_, c) in v ]
    531         return cffs
     515        return [ value for key,value in v ]
    532516
    533517    def _vector_(self, new_base_ring=None):
    534518        """
     
    587571        """
    588572        return self._vector_()
    589573
    590 
    591 
    592574    def _acted_upon_(self, scalar, self_on_left = False):
    593575        """
    594576        Returns the action of a scalar on self
     
    649631        # enough information to detect apriori that this method only
    650632        # accepts scalars; so it tries on some elements(), and we need
    651633        # to make sure to report an error.
    652         if scalar.parent() != self.base_ring():
     634        if hasattr( scalar, 'parent' ) and scalar.parent() != self.base_ring():
    653635            # Temporary needed by coercion (see Polynomial/FractionField tests).
    654636            if self.base_ring().has_coerce_map_from(scalar.parent()):
    655                 scalar = self.base_ring()(scalar)
     637                scalar = self.base_ring()( scalar )
    656638            else:
    657639                return None
     640
     641        F = self.parent()
     642        D = self._monomial_coefficients
    658643        if self_on_left:
    659             return self.map_coefficients(lambda c: c*scalar)
     644            D = dict_addition( [ D ], [ scalar ], factor_on_left = False )
    660645        else:
    661             return self.map_coefficients(lambda c: scalar*c)
     646            D = dict_addition( [ D ], [ scalar ] )
     647
     648        return F._from_dict( D )
    662649
    663650    # For backward compatibility
    664651    _lmul_ = _acted_upon_
    665652    _rmul_ = _acted_upon_
    666653
    667     def __div__(self, x):
     654    def __div__(self, x, self_on_left=False ):
    668655        """
    669656        Division by coefficients
    670657
     
    684671            B[2] + 2*B[3]
    685672        """
    686673        if self.base_ring().is_field():
    687             x = self.base_ring()(x)
    688             return self.map_coefficients(lambda c: c/x)
     674            F = self.parent()
     675            x = self.base_ring()( x )
     676            x_inv = x^-1
     677            D = self._monomial_coefficients
     678            if self_on_left:
     679                D = dict_addition( [ D ], [ x_inv ], factor_on_left=False )
     680            else:
     681                D = dict_addition( [ D ], [ x_inv ] )
     682
     683            return F._from_dict( D )
    689684        else:
    690685            return self.map_coefficients(lambda c: _divide_if_possible(c, x))
    691686
     
    12621257        if c: return c
    12631258        return 0
    12641259
    1265     def _apply_module_morphism(self, x, f):
     1260    def _apply_module_morphism( self, x, on_basis, codomain=False ):
    12661261        """
    12671262        Returns the image of x under the module morphism defined by
    12681263        extending f by linearity.
     
    12721267
    12731268        - ``x`` : a element of self
    12741269
    1275         - ``f``f - a function that takes in a combinatorial
     1270        - ``on_basis`` - a function that takes in a combinatorial
    12761271          object indexing a basis element and returns an element of the
    1277           target domain
     1272          codomain
     1273         
     1274        - ``codomain`` (optional) - the codomain of the morphism, otherwise it is computed using on_basis
    12781275
    12791276
    12801277        EXAMPLES::
     
    12881285            sage: s._apply_module_morphism(b, f) #2*(1+2+3)
    12891286            12
    12901287        """
    1291         res = 0
    1292         for m, c in x._monomial_coefficients.iteritems():
    1293             res += c*f(m)
    1294         return res
    12951288
     1289        if x == self.zero():
     1290            if not codomain:
     1291                B = self.basis()
     1292                keys = list( B.keys() )
     1293                if len( keys ) > 0:
     1294                    key = keys[0]
     1295                    codomain = on_basis( key ).parent()
     1296                else:
     1297                    raise ValueError, 'Codomain could not be determined'
    12961298
    1297     def _apply_module_endomorphism(self, a, f):
     1299            return codomain.zero()
     1300                   
     1301        else:
     1302            if not codomain:
     1303                keys = x.support()
     1304                key = keys[0]
     1305                codomain = on_basis( key ).parent()
     1306
     1307            if hasattr( codomain, 'linear_combination' ):
     1308                dict_list = []
     1309                coefficient_list = []
     1310                for key, coeff in x._monomial_coefficients.iteritems():
     1311                    dict_list.append( on_basis( key ) )
     1312                    coefficient_list.append( coeff )
     1313                return codomain.linear_combination( dict_list, coefficient_list )
     1314            else:
     1315                return_sum = codomain.zero()
     1316                for key, coeff in x._monomial_coefficients.iteritems():
     1317                    return_sum += coeff * on_basis( key )
     1318                return return_sum
     1319
     1320    def _apply_module_endomorphism(self, x, on_basis):
    12981321        """
    12991322        This takes in a function from the basis elements to the elements of
    13001323        self and applies it linearly to a. Note that
     
    13081331            sage: s._apply_module_endomorphism( s([2,1]) + s([1,1,1]), f)
    13091332            2*s[2, 1] + 2*s[3]
    13101333        """
    1311         mcs = a.monomial_coefficients()
    1312         base_ring = self.base_ring()
    1313         zero = base_ring(0)
     1334        dict_list = []
     1335        factor_list = []
     1336        for key, coeff in x._monomial_coefficients.iteritems():
     1337            dict_list.append( on_basis( key )._monomial_coefficients )
     1338            factor_list.append( coeff )
     1339       
     1340        D = dict_addition( dict_list, factor_list, diff_keys=True )
     1341       
     1342        return self._from_dict( D )
    13141343
    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]
     1344    def sum( self, list_of_elements ):
     1345        """
     1346        overrides method inherited from commutative additive monoid as it is much faster on dicts directly
     1347       
     1348        INPUT:
     1349         - list_of_elements: list of elements of self
    13201350
    1321         return self._from_dict(z_elt)
     1351        Returns the sum of all elements in list_of_elements
     1352
     1353        EXAMPLES::
     1354            sage: F = CombinatorialFreeModule(QQ,[1,2])
     1355            sage: f = F.an_element(); f
     1356            2*B[1] + 2*B[2]
     1357            sage: g = -f;g
     1358            -2*B[1] - 2*B[2]
     1359            sage: F.sum([f,f,g])
     1360            2*B[1] + 2*B[2]
     1361        """
     1362   
     1363        assert all( hasattr( element, 'parent' ) and element.parent() == self for element in list_of_elements )
     1364       
     1365        dict_list = [ element._monomial_coefficients for element in list_of_elements ]
     1366        D = dict_addition( dict_list )
     1367        return self._from_dict( D )
     1368
     1369    def linear_combination( self, list_of_elements, list_of_coefficients, factor_on_left=True ):
     1370        """
     1371        INPUT:
     1372         - list_of_elements         - list of elements of self
     1373         - list_of_coefficients     - list in self.base_ring()
     1374         - factor_on_left (optional)- if True, the coefficients are multiplied from the left
     1375                                      if False, the coefficients are multiplied from the right
     1376
     1377        Returns the linear combination lambda_1 v_1 + ... + lambda_k v_k resp.
     1378                the linear combination v_1 lambda_1 + ... + v_k lambda_k where
     1379                list_of_elements = [ v_1, ..., v_k ]
     1380                list_of_coefficients = [ lambda_1, ..., lambda_k ]
     1381
     1382        EXAMPLES::
     1383            sage: F = CombinatorialFreeModule(QQ,[1,2])
     1384            sage: f = F.an_element(); f
     1385            2*B[1] + 2*B[2]
     1386            sage: g = -f;g
     1387            -2*B[1] - 2*B[2]
     1388            sage: F.linear_combination([f,g],[2,1])
     1389            2*B[1] + 2*B[2]
     1390        """
     1391       
     1392        BR = self.base_ring()
     1393
     1394        assert all( hasattr( element, 'parent' ) and element.parent() == self for element in list_of_elements )
     1395        assert all( coeff in BR for coeff in list_of_coefficients )
     1396       
     1397        dict_list = [ element._monomial_coefficients for element in list_of_elements ]
     1398        coeff_list = list_of_coefficients
     1399       
     1400        D = dict_addition( dict_list, coeff_list, factor_on_left=factor_on_left )
     1401        return self._from_dict( D )
    13221402
    13231403    def term(self, index, coeff=None):
    13241404        """
     
    14631543        """
    14641544        return self._from_dict({})
    14651545
    1466     def _from_dict(self, d, coerce=False):
     1546    def _from_dict(self, d, coerce=False, remove_zeros=True):
    14671547        """
    14681548        Given a monomial coefficient dictionary ``d``, returns the
    14691549        element of self with those monomials
     
    14881568            Rational Field
    14891569        """
    14901570        assert isinstance(d, dict)
     1571       
     1572        D = d.copy()
     1573
    14911574        if coerce:
    14921575            R = self.base_ring()
    1493             # FIXME: this should remove zero entries
    1494             d = dict((m,R(c)) for m,c in d.iteritems())
     1576            zero = R( 0 )
     1577            for_removal = []
     1578            for key, coeff in D.iteritems():
     1579                if remove_remove_zeros and coeff == zero:
     1580                    for_removal.append( key )
     1581                else:
     1582                    D[ key ] = R( coeff )
     1583            for key in for_removal:
     1584                del D[ key ]
     1585        else:
     1586            D = dict_addition( [ D ], remove_zeros=remove_zeros )
    14951587
    1496         return self.element_class(self, d)
    1497 
     1588        return self.element_class( self, D )
    14981589
    14991590class CombinatorialFreeModule_Tensor(CombinatorialFreeModule):
    15001591        """