# 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
# 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 from sage.categories.all import Modules, from category_types import Category_over_base_ring from sage.misc.lazy_attribute import lazy_attribute from sage.misc.cachefunc import cached_method from sage.misc.misc import attrcall from sage.misc.sage_itertools import max_cmp, min_cmp from sage.structure.element import ModuleElement from sage.categories.morphism import SetMorphism, Morphism from sage.categories.homset import Hom class ModulesWithBasis(Category_over_bas sage: C = ModulesWithBasis(QQ) x is returned unchanged if it is readilly in this category:: x is returned unchanged if it is already in this category:: sage: C(CombinatorialFreeModule(QQ, ('a','b','c'))) Free module generated by {'a', 'b', 'c'} over Rational Field class ModulesWithBasis(Category_over_bas # TODO: find something better to get this inheritance from CategoryWithTensorProduct.Element class ParentMethods(CategoryWithTensorProduct.ParentMethods, CategoryWithCartesianProduct.ParentMethods): def module_morphism(self, on_basis = None, diagonal = None, **keywords): def module_morphism(self, on_basis = None, diagonal = None, triangular = None, **keywords): """ Constructs functions by linearity INPUT: - self: a parent X in ModulesWithBasis(R), with basis x indexed by I - codomain: the codomain Y of f: defaults to f.codomain if the later is defined - zero: the zero of the codomain; defaults to codomain.zero() or 0 if codomain is not specified - position: a non negative integer; defaults to 0 - on_basis: a function f which accepts elements of I - self - a parent X in ModulesWithBasis(R), with basis x indexed by I - codomain - the codomain Y of f: defaults to f.codomain if the later is defined - zero - the zero of the codomain; defaults to codomain.zero() or 0 if codomain is not specified - position - a non negative integer; defaults to 0 - on_basis - a function f which accepts elements of I as position-th argument and returns  elements of Y - diagonal: a function d from I to R - category: a category. By default, this is - diagonal - a function d from I to R - triangular a boolean (default: False) - category - a category. By default, this is ModulesWithBasis(R) if Y is in this category, and otherwise this lets Hom(X,Y) decide class ModulesWithBasis(Category_over_bas This assumes that the respective bases x and y of X and Y have the same index set I. With triangular = upper, the constructed module morphism is assumed to be upper triangular; that is its matrix in the distinguished basis of X and Y would be upper triangular with invertible elements on its diagonal. This currently assumes that X and Y have the same index set I. This is used to compute preimages and inverting the morphism:: sage: I = range(1,200) sage: X = CombinatorialFreeModule(QQ, I); X.rename("X"); x = X.basis() sage: Y = CombinatorialFreeModule(QQ, I); Y.rename("Y"); y = Y.basis() sage: f = Y.sum_of_monomials * divisors sage: phi = X.module_morphism(f, triangular="upper", codomain = Y) sage: phi(x[2]) B[1] + B[2] sage: phi(x[6]) B[1] + B[2] + B[3] + B[6] sage: phi(x[30]) B[1] + B[2] + B[3] + B[5] + B[6] + B[10] + B[15] + B[30] sage: phi.preimage(y[2]) -B[1] + B[2] sage: phi.preimage(y[6]) B[1] - B[2] - B[3] + B[6] sage: phi.preimage(y[30]) -B[1] + B[2] + B[3] + B[5] - B[6] - B[10] - B[15] + B[30] sage: (phi^-1)(y[30]) -B[1] + B[2] + B[3] + B[5] - B[6] - B[10] - B[15] + B[30] For details and further optional arguments, see :class:sage.categories.modules_with_basis.TriangularModuleMorphism. Caveat: the returned element is in Hom(codomain, domain, category). This is only correct for unary functions. Todo: should codomain be self by default in the diagonal case? Todo: should codomain be self by default in the diagonal and triangular cases? """ if diagonal is not None: return DiagonalModuleMorphism(diagonal = diagonal, domain = self, **keywords) elif on_basis is not None: if triangular is not None: return TriangularModuleMorphism(on_basis, domain = self, triangular = triangular, **keywords) return ModuleMorphismByLinearity(on_basis = on_basis, domain = self, **keywords) else: raise ValueError("module morphism requires either on_basis or diagonal argument") class ModulesWithBasis(Category_over_bas # TODO: find something better to get this inheritance from CategoryWithTensorProduct.Element class ElementMethods(CategoryWithTensorProduct.ElementMethods, CategoryWithCartesianProduct.ElementMethods): # TODO: Define the appropriate element methods here (instead of in # subclasses).  These methods should be consistent with those on # polynomials. #         def _neg_(self): #             """ #             Default implementation of negation by trying to multiply by -1. #             TODO: doctest #             """ #             return self._lmul_(-self.parent().base_ring().one(), self) def support_of_term(self): """ class ModulesWithBasis(Category_over_bas else: raise ValueError, "%s is not a single term"%(self) #         def _neg_(self): #             """ #             Default implementation of negation by trying to multiply by -1. #             """ #             return self._lmul_(-self.parent().base_ring().one(), self) def leading_support(self, cmp=None): r""" Returns the maximal element of the support of self. Note that this may not be the term which actually appears first when self is printed. If the default ordering of the basis elements is not what is desired, a comparison function, cmp(x,y), can be provided. This should return a negative value if x < y, 0 if x == y and a positive value if x > y. EXAMPLES:: sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() sage: x = 3*X.monomial(1) + 2*X.monomial(2) + 4*X.monomial(3) sage: x.leading_support() 3 sage: def cmp(x,y): return y-x sage: x.leading_support(cmp=cmp) 1 sage: s = SymmetricFunctions(QQ).schur() sage: f = 2*s[1] + 3*s[2,1] - 5*s[3] sage: f.leading_support() [3] """ return max_cmp(self.support(), cmp) def leading_item(self, cmp=None): r""" Returns the pair (k, c) where c * (the basis elt. indexed by k) is the leading term of self. 'leading term' means that the corresponding basis element is maximal.  Note that this may not be the term which actually appears first when self is printed.  If the default term ordering is not what is desired, a comparison function, cmp(x,y), can be provided.  This should return a negative value if x < y, 0 if x == y and a positive value if x > y. EXAMPLES:: sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() sage: x = 3*X.monomial(1) + 2*X.monomial(2) + 4*X.monomial(3) sage: x.leading_item() (3, 4) sage: def cmp(x,y): return y-x sage: x.leading_item(cmp=cmp) (1, 3) sage: s = SymmetricFunctions(QQ).schur() sage: f = 2*s[1] + 3*s[2,1] - 5*s[3] sage: f.leading_item() ([3], -5) """ k = self.leading_support(cmp=cmp) return k, self[k] def leading_monomial(self, cmp=None): r""" Returns the leading monomial of self. This is the monomial whose corresponding basis element is maximal. Note that this may not be the term which actually appears first when self is printed. If the default term ordering is not what is desired, a comparison function, cmp(x,y), can be provided. This should return a negative value if x < y, 0 if x == y and a positive value if x > y. EXAMPLES:: sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() sage: x = 3*X.monomial(1) + 2*X.monomial(2) + X.monomial(3) sage: x.leading_monomial() B[3] sage: def cmp(x,y): return y-x sage: x.leading_monomial(cmp=cmp) B[1] sage: s = SymmetricFunctions(QQ).schur() sage: f = 2*s[1] + 3*s[2,1] - 5*s[3] sage: f.leading_monomial() s[3] """ return self.parent().monomial( self.leading_support(cmp=cmp) ) def leading_coefficient(self, cmp=None): r""" Returns the leading coefficient of self. This is the coefficient of the term whose corresponding basis element is maximal. Note that this may not be the term which actually appears first when self is printed.  If the default term ordering is not what is desired, a comparison function, cmp(x,y), can be provided. This should return a negative value if x < y, 0 if x == y and a positive value if x > y. EXAMPLES:: sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X") sage: x = 3*X.monomial(1) + 2*X.monomial(2) + X.monomial(3) sage: x.leading_coefficient() 1 sage: def cmp(x,y): return y-x sage: x.leading_coefficient(cmp=cmp) 3 sage: s = SymmetricFunctions(QQ).schur() sage: f = 2*s[1] + 3*s[2,1] - 5*s[3] sage: f.leading_coefficient() -5 """ return self.leading_item(cmp=cmp)[1] def leading_term(self, cmp=None): r""" Returns the leading term of self. This is the term whose corresponding basis element is maximal. Note that this may not be the term which actually appears first when self is printed. If the default term ordering is not what is desired, a comparison function, cmp(x,y), can be provided. This should return a negative value if x < y, 0 if x == y and a positive value if x > y. EXAMPLES:: sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() sage: x = 3*X.monomial(1) + 2*X.monomial(2) + X.monomial(3) sage: x.leading_term() B[3] sage: def cmp(x,y): return y-x sage: x.leading_term(cmp=cmp) 3*B[1] sage: s = SymmetricFunctions(QQ).schur() sage: f = 2*s[1] + 3*s[2,1] - 5*s[3] sage: f.leading_term() -5*s[3] """ return self.parent().term(*self.leading_item(cmp=cmp)) def trailing_support(self, cmp=None): r""" Returns the minimal element of the support of self. Note that this may not be the term which actually appears last when self is printed. If the default ordering of the basis elements is not what is desired, a comparison function, cmp(x,y), can be provided. This should return a negative value if x < y, 0 if x == y and a positive value if x > y. EXAMPLES:: sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() sage: x = 3*X.monomial(1) + 2*X.monomial(2) + 4*X.monomial(3) sage: x.trailing_support() 1 sage: def cmp(x,y): return y-x sage: x.trailing_support(cmp=cmp) 3 sage: s = SymmetricFunctions(QQ).schur() sage: f = 2*s[1] + 3*s[2,1] - 5*s[3] sage: f.trailing_support() [1] """ return min_cmp(self.support(), cmp) def trailing_item(self, cmp=None): r""" Returns the pair (c, k) where c*self.parent().monomial(k) is the trailing term of self. This is the monomial whose corresponding basis element is minimal. Note that this may not be the term which actually appears last when self is printed.  If the default term ordering is not what is desired, a comparison function cmp(x,y), can be provided. This should return a negative value if x < y, 0 if x == y and a positive value if x > y. EXAMPLES:: sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() sage: x = 3*X.monomial(1) + 2*X.monomial(2) + X.monomial(3) sage: x.trailing_item() (1, 3) sage: def cmp(x,y): return y-x sage: x.trailing_item(cmp=cmp) (3, 1) sage: s = SymmetricFunctions(QQ).schur() sage: f = 2*s[1] + 3*s[2,1] - 5*s[3] sage: f.trailing_item() ([1], 2) """ k = self.trailing_support(cmp=cmp) return k, self[k] def trailing_monomial(self, cmp=None): r""" Returns the trailing monomial of self. This is the monomial whose corresponding basis element is minimal. Note that this may not be the term which actually appears last when self is printed. If the default term ordering is not what is desired, a comparison function cmp(x,y), can be provided. This should return a negative value if x < y, 0 if x == y and a positive value if x > y. EXAMPLES:: sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() sage: x = 3*X.monomial(1) + 2*X.monomial(2) + X.monomial(3) sage: x.trailing_monomial() B[1] sage: def cmp(x,y): return y-x sage: x.trailing_monomial(cmp=cmp) B[3] sage: s = SymmetricFunctions(QQ).schur() sage: f = 2*s[1] + 3*s[2,1] - 5*s[3] sage: f.trailing_monomial() s[1] """ return self.parent().monomial( self.trailing_support(cmp=cmp) ) def trailing_coefficient(self, cmp=None): r""" Returns the trailing coefficient of self. This is the coefficient of the monomial whose corresponding basis element is minimal. Note that this may not be the term which actually appears last when self is printed. If the default term ordering is not what is desired, a comparison function cmp(x,y), can be provided. This should return a negative value if x < y, 0 if x == y and a positive value if x > y. EXAMPLES:: sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() sage: x = 3*X.monomial(1) + 2*X.monomial(2) + X.monomial(3) sage: x.trailing_coefficient() 3 sage: def cmp(x,y): return y-x sage: x.trailing_coefficient(cmp=cmp) 1 sage: s = SymmetricFunctions(QQ).schur() sage: f = 2*s[1] + 3*s[2,1] - 5*s[3] sage: f.trailing_coefficient() 2 """ return self.trailing_item(cmp=cmp)[1] def trailing_term(self, cmp=None): r""" Returns the trailing term of self. This is the term whose corresponding basis element is minimal. Note that this may not be the term which actually appears last when self is printed. If the default term ordering is not what is desired, a comparison function cmp(x,y), can be provided. This should return a negative value if x < y, 0 if x == y and a positive value if x > y. EXAMPLES:: sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() sage: x = 3*X.monomial(1) + 2*X.monomial(2) + X.monomial(3) sage: x.trailing_term() 3*B[1] sage: def cmp(x,y): return y-x sage: x.trailing_term(cmp=cmp) B[3] sage: s = SymmetricFunctions(QQ).schur() sage: f = 2*s[1] + 3*s[2,1] - 5*s[3] sage: f.trailing_term() 2*s[1] """ return self.parent().term( *self.trailing_item(cmp=cmp) ) class HomCategory(HomCategory): """ class ModuleMorphismByLinearity(Morphism # To be cleaned up _call_ = __call__ class TriangularModuleMorphism(ModuleMorphismByLinearity): """ A class for triangular module morphisms; that is module morphisms from X to Y whose matrix in the distinguished basis of X and Y would be upper triangular with invertible elements on its diagonal. This currently assumes that X and Y have the same index set I. However, I needs not be finite. See :meth:module_morphism of ModulesWithBasis EXAMPLES:: sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() sage: def ut(i): return sum(j*x[j] for j in range(i,4)) sage: import __main__; __main__.ut = ut sage: phi = X.module_morphism(ut, triangular="lower", codomain = X) sage: phi(x[2]) 2*B[2] + 3*B[3] sage: phi.preimage(x[2]) 1/2*B[2] - 1/2*B[3] sage: phi(phi.preimage(x[2])) B[2] """ def __init__(self, on_basis, domain, triangular = "upper", unitriangular=False, codomain = None, category = None, cmp = None, inverse = None, **keywords): """ INPUT: - domain, codomain - two modules with basis F and G - on_basis - a function from the index set of the basis of F to the elements of G - unitriangular - boolean (default: False) - triangular - "upper" or "lower" (default: "upper") - "upper": if the leading_support()  of the image of F(i) is i, or - "lower": if the trailing_support() of the image of F(i) is i. - cmp - an optional comparison function on the index set I of the basis (see :meth:.leading_support for details). Assumptions: - F and G have the same base ring R - Their respective bases f and g have the same index set I OUTPUT: The triangular module morphism from F to G which maps f_\lambda to on_basis(\lambda) and is extended by linearity. EXAMPLES:: sage: I = range(1,200) sage: X = CombinatorialFreeModule(QQ, I); X.rename("X"); x = X.basis() sage: Y = CombinatorialFreeModule(QQ, I); Y.rename("Y"); y = Y.basis() sage: f = Y.sum_of_monomials * divisors sage: phi = X.module_morphism(f, triangular="upper", unitriangular = True, codomain = Y) sage: phi(x[2]) B[1] + B[2] sage: phi(x[6]) B[1] + B[2] + B[3] + B[6] sage: phi(x[30]) B[1] + B[2] + B[3] + B[5] + B[6] + B[10] + B[15] + B[30] sage: phi.preimage(y[2]) -B[1] + B[2] sage: phi.preimage(y[6]) B[1] - B[2] - B[3] + B[6] sage: phi.preimage(y[30]) -B[1] + B[2] + B[3] + B[5] - B[6] - B[10] - B[15] + B[30] sage: (phi^-1)(y[30]) -B[1] + B[2] + B[3] + B[5] - B[6] - B[10] - B[15] + B[30] TESTS:: sage: phi.__class__ sage: TestSuite(phi).run() # known issue; see ModuleMorphism above Failure in _test_category: ... The following tests failed: _test_category """ assert codomain is not None assert domain.basis().keys() == codomain.basis().keys() assert domain.base_ring()    == codomain.base_ring() if category is None: category = ModulesWithBasis(domain.base_ring()) if triangular == "upper": self._dominant_item = attrcall("leading_item",  cmp) else: self._dominant_item = attrcall("trailing_item", cmp) # We store those two just be able to pass them down to the inverse function self._triangular = triangular self._cmp = cmp self._unitriangular = unitriangular self._inverse = inverse ModuleMorphismByLinearity.__init__(self, domain = domain, codomain = codomain, category = category) self.on_basis = on_basis # should this be called on_basis (or _on_basis)? def _on_basis(self, i): """ Returns the image, by self, of the basis element indexed by i. TESTS:: sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); x = X.basis() sage: Y = CombinatorialFreeModule(QQ, [1, 2, 3]); y = Y.basis() sage: f = lambda i: sum(  y[j] for j in range(i,4)  ) sage: phi = X.module_morphism(f, triangular="lower", codomain = Y) sage: phi._on_basis(2) B[2] + B[3] """ return self.codomain()(self.on_basis(i)) def __invert__(self): """ Returns the triangular morphism which is the inverse of self. TESTS:: sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); x = X.basis() sage: Y = CombinatorialFreeModule(QQ, [1, 2, 3]); y = Y.basis() sage: uut = lambda i: sum(  y[j] for j in range(1,i+1)) # uni-upper sage: ult = lambda i: sum(  y[j] for j in range(i,4)  ) # uni-lower sage: ut =  lambda i: sum(j*y[j] for j in range(1,i+1)) # upper sage: lt =  lambda i: sum(j*y[j] for j in range(i,4  )) # lower sage: f_uut = X.module_morphism(uut, triangular="upper", unitriangular=True,  codomain = Y) sage: f_ult = X.module_morphism(ult, triangular="lower", unitriangular=True,  codomain = Y) sage: f_ut =  X.module_morphism(ut,  triangular="upper",                      codomain = Y) sage: f_lt =  X.module_morphism(lt,  triangular="lower",                      codomain = Y) sage: (~f_uut)(y[2]) -B[1] + B[2] sage: (~f_ult)(y[2]) B[2] - B[3] sage: (~f_ut)(y[2]) -1/2*B[1] + 1/2*B[2] sage: (~f_lt)(y[2]) 1/2*B[2] - 1/2*B[3] """ if self._inverse is not None: return self._inverse return self.__class__( self._invert_on_basis, domain = self.domain(),               codomain = self.codomain(), unitriangular = self._unitriangular,  triangular = self._triangular, cmp = self._cmp, inverse = self,                       category = self.category_for()) def _invert_on_basis(self, i): r""" Returns the image, by the inverse of self, of the basis element indexed by i. TESTS:: sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); x = X.basis() sage: Y = CombinatorialFreeModule(QQ, [1, 2, 3]); y = Y.basis() sage: uut = lambda i: sum(  y[j] for j in range(i,4)  ) # uni-upper sage: phi = X.module_morphism(uut, triangular=True, codomain = Y) sage: phi._invert_on_basis(2) B[2] - B[3] """ return self.preimage( self.codomain().monomial(i) ) def preimage(self, f): """ Returns the image of f by the inverse of self. TESTS:: sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); x = X.basis() sage: Y = CombinatorialFreeModule(QQ, [1, 2, 3]); y = Y.basis() sage: uut = lambda i: sum(  y[j] for j in range(i,4)  ) # uni-upper sage: phi = X.module_morphism(uut, triangular=True, codomain = Y) sage: phi.preimage(y[1] + y[2]) B[1] - B[3] """ F = self.domain() G = self.codomain() map = self._on_basis assert f in G remainder = f out = F.zero() while not remainder.is_zero(): (j,c) = self._dominant_item(remainder) s = map(j) assert j == self._dominant_item(s)[0] if not self._unitriangular: c /= s[j] remainder -= s._lmul_(c) out       += F.term(j, c) return out class DiagonalModuleMorphism(ModuleMorphismByLinearity): """ A class for diagonal module morphisms. class DiagonalModuleMorphism(ModuleMorph TESTS:: sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() sage: Y = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("Y"); y = Y.basis() sage: Y = CombinatorialFreeModule(QQ, [1, 2, 3]); Y.rename("Y"); y = Y.basis() sage: phi = X.module_morphism(diagonal = factorial, codomain = X) sage: phi._on_basis(3) 6*B[3] class DiagonalModuleMorphism(ModuleMorph EXAMPLES:: sage: X = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("X"); x = X.basis() sage: Y = CombinatorialFreeModule(QQ, [1, 2, 3]); X.rename("Y"); y = Y.basis() sage: Y = CombinatorialFreeModule(QQ, [1, 2, 3]); Y.rename("Y"); y = Y.basis() sage: phi = X.module_morphism(diagonal = factorial, codomain = X) sage: phi_inv = ~phi sage: phi_inv class PointwiseInverseFunction(SageObjec sage: f = PointwiseInverseFunction(factorial) sage: f(0), f(1), f(2), f(3) (1, 1, 1/2, 1/6) sage: loads(dumps(f)) == f True sage: TestSuite(f).run() """ self._pointwise_inverse = f
• ## sage/misc/misc_c.pyx

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

diff --git a/sage/misc/sage_itertools.py b/sage/misc/sage_itertools.py
 a def unique_merge(*lists): """ return (k for k,g in itertools.groupby(heapq.merge(*lists))) def min_cmp(L, cmp=None): """ Returns the smallest item of a list (or iterable) with respect to a comparison function. INPUT: L   -- an iterable cmp -- an optional comparison function. cmp(x, y) should return a negative value if x < y, 0 if x == y, and a positive value if x > y. OUTPUT: the smallest item of L with respect to cmp. EXAMPLES:: sage: from sage.misc.sage_itertools import min_cmp sage: L = [1,-1,3,-1,3,2] sage: min_cmp(L) -1 sage: def mycmp(x,y): return y - x sage: min_cmp(L, mycmp) 3 The input can be any iterable:: sage: min_cmp( (x^2 for x in L) ) 1 sage: min_cmp( (x^2 for x in L), mycmp) 9 Computing the min of an empty iterable raises and error:: sage: min_cmp([]) Traceback (most recent call last): ... ValueError: min() arg is an empty sequence sage: min_cmp([], mycmp) Traceback (most recent call last): ... ValueError: min_cmp() arg is an empty sequence """ if cmp is None: return min(L) # Resort to Python's standard min iterator = iter(L) try: m = iterator.next() except StopIteration: raise ValueError, "min_cmp() arg is an empty sequence" for item in iterator: if cmp(item, m) < 0: m = item return m def max_cmp(L, cmp=None): """ Returns the largest item of a list (or iterable) with respect to a comparison function. INPUT: L   -- an iterable cmp -- an optional comparison function. cmp(x, y) should return a negative value if x < y, 0 if x == y, and a positive value if x > y. OUTPUT: the largest item of L with respect to cmp. EXAMPLES:: sage: from sage.misc.sage_itertools import max_cmp sage: L = [1,-1,3,-1,3,2] sage: max_cmp(L) 3 sage: def mycmp(x,y): return y - x sage: max_cmp(L, mycmp) -1 The input can be any iterable:: sage: max_cmp( (x^2 for x in L) ) 9 sage: max_cmp( (x^2 for x in L), mycmp) 1 Computing the max of an empty iterable raises and error:: sage: max_cmp([]) Traceback (most recent call last): ... ValueError: max() arg is an empty sequence sage: max_cmp([], mycmp) Traceback (most recent call last): ... ValueError: max_cmp() arg is an empty sequence """ if cmp is None: return max(L) # Resort to Python's standard max iterator = iter(L) try: m = iterator.next() except StopIteration: raise ValueError, "max_cmp() arg is an empty sequence" for item in iterator: if cmp(item, m) > 0: m = item return m